This commit is contained in:
Alex 2025-06-09 12:02:08 -05:00
commit 429d339c6d
No known key found for this signature in database
GPG Key ID: 53CD082B3A5E1082
169 changed files with 7213 additions and 2846 deletions

View File

@ -1,4 +1,4 @@
ARG BASEIMAGE=mcr.microsoft.com/devcontainers/typescript-node:22@sha256:fb211a0ea31a6177507498c084682aae8c9c31ca27668ea122246aa16a4723a0 ARG BASEIMAGE=mcr.microsoft.com/devcontainers/typescript-node:22@sha256:7c2e711a4f7b02f32d2da16192d5e05aa7c95279be4ce889cff5df316f251c1d
FROM ${BASEIMAGE} FROM ${BASEIMAGE}
# Flutter SDK # Flutter SDK

View File

@ -177,13 +177,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: always() if: always()
steps: steps:
- name: Any jobs failed? - uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
if: ${{ contains(needs.*.result, 'failure') }} with:
run: exit 1 needs: ${{ toJSON(needs) }}
- name: All jobs passed or skipped
if: ${{ !(contains(needs.*.result, 'failure')) }}
# zizmor: ignore[template-injection]
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
success-check-ml: success-check-ml:
name: Docker Build & Push ML Success name: Docker Build & Push ML Success
@ -192,10 +188,6 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: always() if: always()
steps: steps:
- name: Any jobs failed? - uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
if: ${{ contains(needs.*.result, 'failure') }} with:
run: exit 1 needs: ${{ toJSON(needs) }}
- name: All jobs passed or skipped
if: ${{ !(contains(needs.*.result, 'failure')) }}
# zizmor: ignore[template-injection]
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"

View File

@ -58,6 +58,14 @@ jobs:
run: dart pub get run: dart pub get
working-directory: ./mobile working-directory: ./mobile
- name: Install DCM
run: |
sudo apt-get update
wget -qO- https://dcm.dev/pgp-key.public | sudo gpg --dearmor -o /usr/share/keyrings/dcm.gpg
echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list
sudo apt-get update
sudo apt-get install dcm
- name: Generate translation file - name: Generate translation file
run: make translation run: make translation
working-directory: ./mobile working-directory: ./mobile
@ -100,6 +108,10 @@ jobs:
run: dart run custom_lint run: dart run custom_lint
working-directory: ./mobile working-directory: ./mobile
- name: Run DCM
run: dcm analyze lib
working-directory: ./mobile
zizmor: zizmor:
name: zizmor name: zizmor
runs-on: ubuntu-latest runs-on: ubuntu-latest

View File

@ -479,13 +479,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: always() if: always()
steps: steps:
- name: Any jobs failed? - uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
if: ${{ contains(needs.*.result, 'failure') }} with:
run: exit 1 needs: ${{ toJSON(needs) }}
- name: All jobs passed or skipped
if: ${{ !(contains(needs.*.result, 'failure')) }}
# zizmor: ignore[template-injection]
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
mobile-unit-tests: mobile-unit-tests:
name: Unit Test Mobile name: Unit Test Mobile

View File

@ -52,10 +52,6 @@ jobs:
permissions: {} permissions: {}
if: always() if: always()
steps: steps:
- name: Any jobs failed? - uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
if: ${{ contains(needs.*.result, 'failure') }} with:
run: exit 1 needs: ${{ toJSON(needs) }}
- name: All jobs passed or skipped
if: ${{ !(contains(needs.*.result, 'failure')) }}
# zizmor: ignore[template-injection]
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"

280
cli/package-lock.json generated
View File

@ -27,7 +27,7 @@
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/micromatch": "^4.0.9", "@types/micromatch": "^4.0.9",
"@types/mock-fs": "^4.13.1", "@types/mock-fs": "^4.13.1",
"@types/node": "^22.15.21", "@types/node": "^22.15.29",
"@vitest/coverage-v8": "^3.0.0", "@vitest/coverage-v8": "^3.0.0",
"byte-size": "^9.0.0", "byte-size": "^9.0.0",
"cli-progress": "^3.12.0", "cli-progress": "^3.12.0",
@ -35,7 +35,7 @@
"eslint": "^9.14.0", "eslint": "^9.14.0",
"eslint-config-prettier": "^10.0.0", "eslint-config-prettier": "^10.0.0",
"eslint-plugin-prettier": "^5.1.3", "eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-unicorn": "^57.0.0", "eslint-plugin-unicorn": "^59.0.0",
"globals": "^16.0.0", "globals": "^16.0.0",
"mock-fs": "^5.2.0", "mock-fs": "^5.2.0",
"prettier": "^3.2.5", "prettier": "^3.2.5",
@ -61,7 +61,7 @@
"@oazapfts/runtime": "^1.0.2" "@oazapfts/runtime": "^1.0.2"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^22.15.21", "@types/node": "^22.15.29",
"typescript": "^5.3.3" "typescript": "^5.3.3"
} }
}, },
@ -79,21 +79,6 @@
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/@babel/code-frame": {
"version": "7.26.2",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
"integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-validator-identifier": "^7.25.9",
"js-tokens": "^4.0.0",
"picocolors": "^1.0.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-string-parser": { "node_modules/@babel/helper-string-parser": {
"version": "7.25.9", "version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
@ -1353,22 +1338,15 @@
} }
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "22.15.21", "version": "22.15.30",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.30.tgz",
"integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==", "integrity": "sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~6.21.0" "undici-types": "~6.21.0"
} }
}, },
"node_modules/@types/normalize-package-data": {
"version": "2.4.4",
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz",
"integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==",
"dev": true,
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.32.1", "version": "8.32.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz",
@ -1878,9 +1856,9 @@
} }
}, },
"node_modules/builtin-modules": { "node_modules/builtin-modules": {
"version": "4.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-4.0.0.tgz", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.0.0.tgz",
"integrity": "sha512-p1n8zyCkt1BVrKNFymOHjcDSAl7oq/gUvfgULv2EblgpPVQlQr9yHnWjg9IJ2MhfwPqiYqMMrr01OY7yQoK2yA==", "integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -2362,50 +2340,65 @@
} }
}, },
"node_modules/eslint-plugin-unicorn": { "node_modules/eslint-plugin-unicorn": {
"version": "57.0.0", "version": "59.0.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-57.0.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-59.0.1.tgz",
"integrity": "sha512-zUYYa6zfNdTeG9BISWDlcLmz16c+2Ck2o5ZDHh0UzXJz3DEP7xjmlVDTzbyV0W+XksgZ0q37WEWzN2D2Ze+g9Q==", "integrity": "sha512-EtNXYuWPUmkgSU2E7Ttn57LbRREQesIP1BiLn7OZLKodopKfDXfBUkC/0j6mpw2JExwf43Uf3qLSvrSvppgy8Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/helper-validator-identifier": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9",
"@eslint-community/eslint-utils": "^4.4.1", "@eslint-community/eslint-utils": "^4.5.1",
"ci-info": "^4.1.0", "@eslint/plugin-kit": "^0.2.7",
"ci-info": "^4.2.0",
"clean-regexp": "^1.0.0", "clean-regexp": "^1.0.0",
"core-js-compat": "^3.40.0", "core-js-compat": "^3.41.0",
"esquery": "^1.6.0", "esquery": "^1.6.0",
"globals": "^15.15.0", "find-up-simple": "^1.0.1",
"globals": "^16.0.0",
"indent-string": "^5.0.0", "indent-string": "^5.0.0",
"is-builtin-module": "^4.0.0", "is-builtin-module": "^5.0.0",
"jsesc": "^3.1.0", "jsesc": "^3.1.0",
"pluralize": "^8.0.0", "pluralize": "^8.0.0",
"read-package-up": "^11.0.0",
"regexp-tree": "^0.1.27", "regexp-tree": "^0.1.27",
"regjsparser": "^0.12.0", "regjsparser": "^0.12.0",
"semver": "^7.7.1", "semver": "^7.7.1",
"strip-indent": "^4.0.0" "strip-indent": "^4.0.0"
}, },
"engines": { "engines": {
"node": ">=18.18" "node": "^18.20.0 || ^20.10.0 || >=21.0.0"
}, },
"funding": { "funding": {
"url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1"
}, },
"peerDependencies": { "peerDependencies": {
"eslint": ">=9.20.0" "eslint": ">=9.22.0"
} }
}, },
"node_modules/eslint-plugin-unicorn/node_modules/globals": { "node_modules/eslint-plugin-unicorn/node_modules/@eslint/core": {
"version": "15.15.0", "version": "0.13.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
"integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
"dev": true, "dev": true,
"license": "MIT", "license": "Apache-2.0",
"engines": { "dependencies": {
"node": ">=18" "@types/json-schema": "^7.0.15"
}, },
"funding": { "engines": {
"url": "https://github.com/sponsors/sindresorhus" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/eslint-plugin-unicorn/node_modules/@eslint/plugin-kit": {
"version": "0.2.8",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz",
"integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/core": "^0.13.0",
"levn": "^0.4.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
} }
}, },
"node_modules/eslint-scope": { "node_modules/eslint-scope": {
@ -2792,19 +2785,6 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/hosted-git-info": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz",
"integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==",
"dev": true,
"license": "ISC",
"dependencies": {
"lru-cache": "^10.0.1"
},
"engines": {
"node": "^16.14.0 || >=18.0.0"
}
},
"node_modules/html-escaper": { "node_modules/html-escaper": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
@ -2862,27 +2842,14 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/index-to-position": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.1.0.tgz",
"integrity": "sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-builtin-module": { "node_modules/is-builtin-module": {
"version": "4.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-4.0.0.tgz", "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-5.0.0.tgz",
"integrity": "sha512-rWP3AMAalQSesXO8gleROyL2iKU73SX5Er66losQn9rWOWL4Gef0a/xOEOVqjWGMuR2vHG3FJ8UUmT700O8oFg==", "integrity": "sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"builtin-modules": "^4.0.0" "builtin-modules": "^5.0.0"
}, },
"engines": { "engines": {
"node": ">=18.20" "node": ">=18.20"
@ -3008,13 +2975,6 @@
"@pkgjs/parseargs": "^0.11.0" "@pkgjs/parseargs": "^0.11.0"
} }
}, },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true,
"license": "MIT"
},
"node_modules/js-yaml": { "node_modules/js-yaml": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
@ -3272,21 +3232,6 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/normalize-package-data": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz",
"integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"hosted-git-info": "^7.0.0",
"semver": "^7.3.5",
"validate-npm-package-license": "^3.0.4"
},
"engines": {
"node": "^16.14.0 || >=18.0.0"
}
},
"node_modules/optionator": { "node_modules/optionator": {
"version": "0.9.4", "version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@ -3357,24 +3302,6 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/parse-json": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz",
"integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.26.2",
"index-to-position": "^1.1.0",
"type-fest": "^4.39.1"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/path-exists": { "node_modules/path-exists": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@ -3573,44 +3500,6 @@
], ],
"license": "MIT" "license": "MIT"
}, },
"node_modules/read-package-up": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz",
"integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"find-up-simple": "^1.0.0",
"read-pkg": "^9.0.0",
"type-fest": "^4.6.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/read-pkg": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz",
"integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/normalize-package-data": "^2.4.3",
"normalize-package-data": "^6.0.0",
"parse-json": "^8.0.0",
"type-fest": "^4.6.0",
"unicorn-magic": "^0.1.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/readdirp": { "node_modules/readdirp": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
@ -3809,42 +3698,6 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/spdx-correct": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
"integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"spdx-expression-parse": "^3.0.0",
"spdx-license-ids": "^3.0.0"
}
},
"node_modules/spdx-exceptions": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
"integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
"dev": true,
"license": "CC-BY-3.0"
},
"node_modules/spdx-expression-parse": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
"integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"spdx-exceptions": "^2.1.0",
"spdx-license-ids": "^3.0.0"
}
},
"node_modules/spdx-license-ids": {
"version": "3.0.21",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz",
"integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==",
"dev": true,
"license": "CC0-1.0"
},
"node_modules/stackback": { "node_modules/stackback": {
"version": "0.0.2", "version": "0.0.2",
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
@ -4172,19 +4025,6 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/type-fest": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.40.0.tgz",
"integrity": "sha512-ABHZ2/tS2JkvH1PEjxFDTUWC8dB5OsIGZP4IFLhR293GqT5Y5qB1WwL2kMPYhQW9DVgVD8Hd7I8gjwPIf5GFkw==",
"dev": true,
"license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/typescript": { "node_modules/typescript": {
"version": "5.8.3", "version": "5.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
@ -4229,19 +4069,6 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/unicorn-magic": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz",
"integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/update-browserslist-db": { "node_modules/update-browserslist-db": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
@ -4283,17 +4110,6 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"node_modules/validate-npm-package-license": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
"integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"spdx-correct": "^3.0.0",
"spdx-expression-parse": "^3.0.0"
}
},
"node_modules/vite": { "node_modules/vite": {
"version": "6.3.5", "version": "6.3.5",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",

View File

@ -21,7 +21,7 @@
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/micromatch": "^4.0.9", "@types/micromatch": "^4.0.9",
"@types/mock-fs": "^4.13.1", "@types/mock-fs": "^4.13.1",
"@types/node": "^22.15.21", "@types/node": "^22.15.29",
"@vitest/coverage-v8": "^3.0.0", "@vitest/coverage-v8": "^3.0.0",
"byte-size": "^9.0.0", "byte-size": "^9.0.0",
"cli-progress": "^3.12.0", "cli-progress": "^3.12.0",
@ -29,7 +29,7 @@
"eslint": "^9.14.0", "eslint": "^9.14.0",
"eslint-config-prettier": "^10.0.0", "eslint-config-prettier": "^10.0.0",
"eslint-plugin-prettier": "^5.1.3", "eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-unicorn": "^57.0.0", "eslint-plugin-unicorn": "^59.0.0",
"globals": "^16.0.0", "globals": "^16.0.0",
"mock-fs": "^5.2.0", "mock-fs": "^5.2.0",
"prettier": "^3.2.5", "prettier": "^3.2.5",

View File

@ -82,7 +82,7 @@ services:
container_name: immich_prometheus container_name: immich_prometheus
ports: ports:
- 9090:9090 - 9090:9090
image: prom/prometheus@sha256:78ed1f9050eb9eaf766af6e580230b1c4965728650e332cd1ee918c0c4699775 image: prom/prometheus@sha256:9abc6cf6aea7710d163dbb28d8eeb7dc5baef01e38fa4cd146a406dd9f07f70d
volumes: volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml - ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus - prometheus-data:/prometheus
@ -94,7 +94,7 @@ services:
command: [ './run.sh', '-disable-reporting' ] command: [ './run.sh', '-disable-reporting' ]
ports: ports:
- 3000:3000 - 3000:3000
image: grafana/grafana:11.6.1-ubuntu@sha256:6fc273288470ef499dd3c6b36aeade093170d4f608f864c5dd3a7fabeae77b50 image: grafana/grafana:12.0.1-ubuntu@sha256:65575bb9c761335e2ff30e364f21d38632e3b2e75f5f81d83cc92f44b9bbc055
volumes: volumes:
- grafana-data:/var/lib/grafana - grafana-data:/var/lib/grafana

790
e2e/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -25,8 +25,8 @@
"@immich/sdk": "file:../open-api/typescript-sdk", "@immich/sdk": "file:../open-api/typescript-sdk",
"@playwright/test": "^1.44.1", "@playwright/test": "^1.44.1",
"@types/luxon": "^3.4.2", "@types/luxon": "^3.4.2",
"@types/node": "^22.15.21", "@types/node": "^22.15.29",
"@types/oidc-provider": "^8.5.1", "@types/oidc-provider": "^9.0.0",
"@types/pg": "^8.15.1", "@types/pg": "^8.15.1",
"@types/pngjs": "^6.0.4", "@types/pngjs": "^6.0.4",
"@types/supertest": "^6.0.2", "@types/supertest": "^6.0.2",
@ -34,12 +34,12 @@
"eslint": "^9.14.0", "eslint": "^9.14.0",
"eslint-config-prettier": "^10.0.0", "eslint-config-prettier": "^10.0.0",
"eslint-plugin-prettier": "^5.1.3", "eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-unicorn": "^57.0.0", "eslint-plugin-unicorn": "^59.0.0",
"exiftool-vendored": "^28.3.1", "exiftool-vendored": "^28.3.1",
"globals": "^16.0.0", "globals": "^16.0.0",
"jose": "^5.6.3", "jose": "^5.6.3",
"luxon": "^3.4.4", "luxon": "^3.4.4",
"oidc-provider": "^8.5.1", "oidc-provider": "^9.0.0",
"pg": "^8.11.3", "pg": "^8.11.3",
"pngjs": "^7.0.0", "pngjs": "^7.0.0",
"prettier": "^3.2.5", "prettier": "^3.2.5",

View File

@ -22,6 +22,7 @@
"add_partner": "Add partner", "add_partner": "Add partner",
"add_path": "Add path", "add_path": "Add path",
"add_photos": "Add photos", "add_photos": "Add photos",
"add_tag": "Add tag",
"add_to": "Add to…", "add_to": "Add to…",
"add_to_album": "Add to album", "add_to_album": "Add to album",
"add_to_album_bottom_sheet_added": "Added to {album}", "add_to_album_bottom_sheet_added": "Added to {album}",
@ -169,7 +170,7 @@
"note_apply_storage_label_previous_assets": "Note: To apply the Storage Label to previously uploaded assets, run the", "note_apply_storage_label_previous_assets": "Note: To apply the Storage Label to previously uploaded assets, run the",
"note_cannot_be_changed_later": "NOTE: This cannot be changed later!", "note_cannot_be_changed_later": "NOTE: This cannot be changed later!",
"notification_email_from_address": "From address", "notification_email_from_address": "From address",
"notification_email_from_address_description": "Sender email address, for example: \"Immich Photo Server <noreply@example.com>\"", "notification_email_from_address_description": "Sender email address, for example: \"Immich Photo Server <noreply@example.com>\". Make sure to use an address you're allowed to send emails from.",
"notification_email_host_description": "Host of the email server (e.g. smtp.immich.app)", "notification_email_host_description": "Host of the email server (e.g. smtp.immich.app)",
"notification_email_ignore_certificate_errors": "Ignore certificate errors", "notification_email_ignore_certificate_errors": "Ignore certificate errors",
"notification_email_ignore_certificate_errors_description": "Ignore TLS certificate validation errors (not recommended)", "notification_email_ignore_certificate_errors_description": "Ignore TLS certificate validation errors (not recommended)",
@ -648,6 +649,7 @@
"confirm_password": "Confirm password", "confirm_password": "Confirm password",
"confirm_tag_face": "Do you want to tag this face as {name}?", "confirm_tag_face": "Do you want to tag this face as {name}?",
"confirm_tag_face_unnamed": "Do you want to tag this face?", "confirm_tag_face_unnamed": "Do you want to tag this face?",
"connected_device": "Connected device",
"connected_to": "Connected to", "connected_to": "Connected to",
"contain": "Contain", "contain": "Contain",
"context": "Context", "context": "Context",
@ -748,6 +750,7 @@
"disallow_edits": "Disallow edits", "disallow_edits": "Disallow edits",
"discord": "Discord", "discord": "Discord",
"discover": "Discover", "discover": "Discover",
"discovered_devices": "Discovered devices",
"dismiss_all_errors": "Dismiss all errors", "dismiss_all_errors": "Dismiss all errors",
"dismiss_error": "Dismiss error", "dismiss_error": "Dismiss error",
"display_options": "Display options", "display_options": "Display options",
@ -1132,6 +1135,7 @@
"list": "List", "list": "List",
"loading": "Loading", "loading": "Loading",
"loading_search_results_failed": "Loading search results failed", "loading_search_results_failed": "Loading search results failed",
"local_asset_cast_failed": "Unable to cast an asset that is not uploaded to the server",
"local_network": "Local network", "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", "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", "location_permission": "Location permission",
@ -1272,6 +1276,7 @@
"no_archived_assets_message": "Archive photos and videos to hide them from your Photos view", "no_archived_assets_message": "Archive photos and videos to hide them from your Photos view",
"no_assets_message": "CLICK TO UPLOAD YOUR FIRST PHOTO", "no_assets_message": "CLICK TO UPLOAD YOUR FIRST PHOTO",
"no_assets_to_show": "No assets to show", "no_assets_to_show": "No assets to show",
"no_cast_devices_found": "No cast devices found",
"no_duplicates_found": "No duplicates were found.", "no_duplicates_found": "No duplicates were found.",
"no_exif_info_available": "No exif info available", "no_exif_info_available": "No exif info available",
"no_explore_results_message": "Upload more photos to explore your collection.", "no_explore_results_message": "Upload more photos to explore your collection.",
@ -1630,6 +1635,7 @@
"set_date_of_birth": "Set date of birth", "set_date_of_birth": "Set date of birth",
"set_profile_picture": "Set profile picture", "set_profile_picture": "Set profile picture",
"set_slideshow_to_fullscreen": "Set Slideshow to fullscreen", "set_slideshow_to_fullscreen": "Set Slideshow to fullscreen",
"set_stack_primary_asset": "Set as primary asset",
"setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).", "setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).",
"setting_image_viewer_original_subtitle": "Enable to load the original full-resolution image (large!). Disable to reduce data usage (both network and on device cache).", "setting_image_viewer_original_subtitle": "Enable to load the original full-resolution image (large!). Disable to reduce data usage (both network and on device cache).",
"setting_image_viewer_original_title": "Load original image", "setting_image_viewer_original_title": "Load original image",
@ -1767,6 +1773,7 @@
"start_date": "Start date", "start_date": "Start date",
"state": "State", "state": "State",
"status": "Status", "status": "Status",
"stop_casting": "Stop casting",
"stop_motion_photo": "Stop Motion Photo", "stop_motion_photo": "Stop Motion Photo",
"stop_photo_sharing": "Stop sharing your photos?", "stop_photo_sharing": "Stop sharing your photos?",
"stop_photo_sharing_description": "{partner} will no longer be able to access your photos.", "stop_photo_sharing_description": "{partner} will no longer be able to access your photos.",

View File

@ -1,6 +1,6 @@
ARG DEVICE=cpu ARG DEVICE=cpu
FROM python:3.11-bookworm@sha256:ab60e444e04215a62671149f24c59cc2893b49cb5dad26f9d139077a86be760e AS builder-cpu FROM python:3.11-bookworm@sha256:d2621a9f74d31a8a60af19f97b09cc3ac54382c8680b6544018713a12ef6c048 AS builder-cpu
FROM builder-cpu AS builder-openvino FROM builder-cpu AS builder-openvino
@ -59,7 +59,7 @@ ENV PYTHONDONTWRITEBYTECODE=1 \
RUN apt-get update && apt-get install -y --no-install-recommends g++ RUN apt-get update && apt-get install -y --no-install-recommends g++
COPY --from=ghcr.io/astral-sh/uv:latest@sha256:4a6c9444b126bd325fba904bff796bf91fb777bf6148d60109c4cb1de2ffc497 /uv /uvx /bin/ COPY --from=ghcr.io/astral-sh/uv:latest@sha256:4faec156e35a5f345d57804d8858c6ba1cf6352ce5f4bffc11b7fdebdef46a38 /uv /uvx /bin/
RUN --mount=type=cache,target=/root/.cache/uv \ RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \ --mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
@ -68,11 +68,11 @@ RUN if [ "$DEVICE" = "rocm" ]; then \
uv pip install /opt/onnxruntime_rocm-*.whl; \ uv pip install /opt/onnxruntime_rocm-*.whl; \
fi fi
FROM python:3.11-slim-bookworm@sha256:97ef3198ec8c78690587167bb6a4905d00ffe053900687bdae93ad667e507cbb AS prod-cpu FROM python:3.11-slim-bookworm@sha256:7a3ed1226224bcc1fe5443262363d42f48cf832a540c1836ba8ccbeaadf8637c AS prod-cpu
ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2 ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2
FROM python:3.11-slim-bookworm@sha256:97ef3198ec8c78690587167bb6a4905d00ffe053900687bdae93ad667e507cbb AS prod-openvino FROM python:3.11-slim-bookworm@sha256:7a3ed1226224bcc1fe5443262363d42f48cf832a540c1836ba8ccbeaadf8637c AS prod-openvino
RUN apt-get update && \ RUN apt-get update && \
apt-get install --no-install-recommends -yqq ocl-icd-libopencl1 wget && \ apt-get install --no-install-recommends -yqq ocl-icd-libopencl1 wget && \

170
machine-learning/uv.lock generated
View File

@ -346,11 +346,11 @@ wheels = [
[[package]] [[package]]
name = "configargparse" name = "configargparse"
version = "1.7" version = "1.7.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/70/8a/73f1008adfad01cb923255b924b1528727b8270e67cb4ef41eabdc7d783e/ConfigArgParse-1.7.tar.gz", hash = "sha256:e7067471884de5478c58a511e529f0f9bd1c66bfef1dea90935438d6c23306d1", size = 43817, upload-time = "2023-07-23T16:20:04.95Z" } sdist = { url = "https://files.pythonhosted.org/packages/85/4d/6c9ef746dfcc2a32e26f3860bb4a011c008c392b83eabdfb598d1a8bbe5d/configargparse-1.7.1.tar.gz", hash = "sha256:79c2ddae836a1e5914b71d58e4b9adbd9f7779d4e6351a637b7d2d9b6c46d3d9", size = 43958, upload-time = "2025-05-23T14:26:17.369Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/6f/b3/b4ac838711fd74a2b4e6f746703cf9dd2cf5462d17dac07e349234e21b97/ConfigArgParse-1.7-py3-none-any.whl", hash = "sha256:d249da6591465c6c26df64a9f73d2536e743be2f244eb3ebe61114af2f94f86b", size = 25489, upload-time = "2023-07-23T16:20:03.27Z" }, { url = "https://files.pythonhosted.org/packages/31/28/d28211d29bcc3620b1fece85a65ce5bb22f18670a03cd28ea4b75ede270c/configargparse-1.7.1-py3-none-any.whl", hash = "sha256:8b586a31f9d873abd1ca527ffbe58863c99f36d896e2829779803125e83be4b6", size = 25607, upload-time = "2025-05-23T14:26:15.923Z" },
] ]
[[package]] [[package]]
@ -900,7 +900,7 @@ wheels = [
[[package]] [[package]]
name = "huggingface-hub" name = "huggingface-hub"
version = "0.32.2" version = "0.32.4"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "filelock" }, { name = "filelock" },
@ -912,9 +912,9 @@ dependencies = [
{ name = "tqdm" }, { name = "tqdm" },
{ name = "typing-extensions" }, { name = "typing-extensions" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/d0/76/44f7025d1b3f29336aeb7324a57dd7c19f7c69f6612b7637b39ac7c17302/huggingface_hub-0.32.2.tar.gz", hash = "sha256:64a288b1eadad6b60bbfd50f0e52fd6cfa2ef77ab13c3e8a834a038ae929de54", size = 422847, upload-time = "2025-05-27T09:23:00.306Z" } sdist = { url = "https://files.pythonhosted.org/packages/60/c8/4f7d270285c46324fd66f62159eb16739aa5696f422dba57678a8c6b78e9/huggingface_hub-0.32.4.tar.gz", hash = "sha256:f61d45cd338736f59fb0e97550b74c24ee771bcc92c05ae0766b9116abe720be", size = 424494, upload-time = "2025-06-03T09:59:46.105Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/32/30/532fe57467a6cc7ff2e39f088db1cb6d6bf522f724a4a5c7beda1282d5a6/huggingface_hub-0.32.2-py3-none-any.whl", hash = "sha256:f8fcf14603237eadf96dbe577d30b330f8c27b4a0a31e8f6c94fdc25e021fdb8", size = 509968, upload-time = "2025-05-27T09:22:57.967Z" }, { url = "https://files.pythonhosted.org/packages/67/8b/222140f3cfb6f17b0dd8c4b9a0b36bd4ebefe9fb0098ba35d6960abcda0f/huggingface_hub-0.32.4-py3-none-any.whl", hash = "sha256:37abf8826b38d971f60d3625229221c36e53fe58060286db9baf619cfbf39767", size = 512101, upload-time = "2025-06-03T09:59:44.099Z" },
] ]
[[package]] [[package]]
@ -1225,7 +1225,7 @@ wheels = [
[[package]] [[package]]
name = "locust" name = "locust"
version = "2.37.5" version = "2.37.9"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "configargparse" }, { name = "configargparse" },
@ -1245,25 +1245,26 @@ dependencies = [
{ name = "typing-extensions", marker = "python_full_version < '3.11'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" },
{ name = "werkzeug" }, { name = "werkzeug" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/d2/d1/60d5fddac2baa47314c091636868b50178a38fc71ce39d68afd847448028/locust-2.37.5.tar.gz", hash = "sha256:c90824c4cb6a01cdede220684c7c8381253fcca47fc689dbca4f6c46d740c99f", size = 2252000, upload-time = "2025-05-22T08:54:58.676Z" } sdist = { url = "https://files.pythonhosted.org/packages/90/05/2bfdf19756c6a12f6f9513f75340ecf0595d83cab4d9fc91162225908e3d/locust-2.37.9.tar.gz", hash = "sha256:e43673b594ec5ecde4f9ba6e0d5c66c00d7c0ae93591951abe83e8d186c67175", size = 2252507, upload-time = "2025-06-05T09:26:58.186Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/e0/a0/32a51fb48f96b0de6bb6ea7308f68b7ae1bae53e6b975672f8c4ef7f8c08/locust-2.37.5-py3-none-any.whl", hash = "sha256:9922a2718b42f1c57a05c822e47b66555b3c61292694ec5edaf7a166fac6d112", size = 2268626, upload-time = "2025-05-22T08:54:55.938Z" }, { url = "https://files.pythonhosted.org/packages/33/1c/0ece4176231c578e819d970ec08d124492833e50aafd171c582bcc414446/locust-2.37.9-py3-none-any.whl", hash = "sha256:e17da439f3a252d1fb6d4c34daf00d7e8b87e99d833a32e8a79f4f8ebb07767d", size = 2269084, upload-time = "2025-06-05T09:26:56.257Z" },
] ]
[[package]] [[package]]
name = "locust-cloud" name = "locust-cloud"
version = "1.21.8" version = "1.23.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "configargparse" }, { name = "configargparse" },
{ name = "gevent" }, { name = "gevent" },
{ name = "platformdirs" }, { name = "platformdirs" },
{ name = "python-engineio" },
{ name = "python-socketio", extra = ["client"] }, { name = "python-socketio", extra = ["client"] },
{ name = "tomli", marker = "python_full_version < '3.11'" }, { name = "tomli", marker = "python_full_version < '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/09/d4/64a169b4831d26ab9dceacb192ea30c749501d87b4958e628cf1f7ef45c4/locust_cloud-1.21.8.tar.gz", hash = "sha256:e8bde0da013c8731a45cc834cdf9fec2fc21738a5f2807d93c2c5eeb3008a80e", size = 450414, upload-time = "2025-05-22T08:30:27.458Z" } sdist = { url = "https://files.pythonhosted.org/packages/bd/7c/d9cbbd051490aeedfbd6ddda8ad48f77dd848ee490f6ebd166d20db5911e/locust_cloud-1.23.1.tar.gz", hash = "sha256:a09161752b8c9a9205e97cef5223ee3ad967bc2d91c52d61952aaa3da6802a55", size = 450937, upload-time = "2025-06-05T06:07:53.773Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/06/76/aa8b2f73bdf7de5ee344e5d0c4749e8d62ff38257b41d9df37b0b7ac84e2/locust_cloud-1.21.8-py3-none-any.whl", hash = "sha256:4f06b5d8a26ba91840a768008f4870965b13cc71481de9797409556de2edc007", size = 407879, upload-time = "2025-05-22T08:30:25.512Z" }, { url = "https://files.pythonhosted.org/packages/7e/01/5af43edee540e38ba0ee0a2e3beb72c50073e0f646bb543a8b34650315e3/locust_cloud-1.23.1-py3-none-any.whl", hash = "sha256:11677895c6ed6d0beef1b425a6f04f10ea2cfcaeaefbf00a97fb3c9134296e54", size = 408323, upload-time = "2025-06-05T06:07:51.947Z" },
] ]
[[package]] [[package]]
@ -1414,40 +1415,41 @@ wheels = [
[[package]] [[package]]
name = "mypy" name = "mypy"
version = "1.15.0" version = "1.16.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "mypy-extensions" }, { name = "mypy-extensions" },
{ name = "pathspec" },
{ name = "tomli", marker = "python_full_version < '3.11'" }, { name = "tomli", marker = "python_full_version < '3.11'" },
{ name = "typing-extensions" }, { name = "typing-extensions" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717, upload-time = "2025-02-05T03:50:34.655Z" } sdist = { url = "https://files.pythonhosted.org/packages/d4/38/13c2f1abae94d5ea0354e146b95a1be9b2137a0d506728e0da037c4276f6/mypy-1.16.0.tar.gz", hash = "sha256:84b94283f817e2aa6350a14b4a8fb2a35a53c286f97c9d30f53b63620e7af8ab", size = 3323139, upload-time = "2025-05-29T13:46:12.532Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/68/f8/65a7ce8d0e09b6329ad0c8d40330d100ea343bd4dd04c4f8ae26462d0a17/mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13", size = 10738433, upload-time = "2025-02-05T03:49:29.145Z" }, { url = "https://files.pythonhosted.org/packages/64/5e/a0485f0608a3d67029d3d73cec209278b025e3493a3acfda3ef3a88540fd/mypy-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7909541fef256527e5ee9c0a7e2aeed78b6cda72ba44298d1334fe7881b05c5c", size = 10967416, upload-time = "2025-05-29T13:34:17.783Z" },
{ url = "https://files.pythonhosted.org/packages/b4/95/9c0ecb8eacfe048583706249439ff52105b3f552ea9c4024166c03224270/mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559", size = 9861472, upload-time = "2025-02-05T03:49:16.986Z" }, { url = "https://files.pythonhosted.org/packages/4b/53/5837c221f74c0d53a4bfc3003296f8179c3a2a7f336d7de7bbafbe96b688/mypy-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e71d6f0090c2256c713ed3d52711d01859c82608b5d68d4fa01a3fe30df95571", size = 10087654, upload-time = "2025-05-29T13:32:37.878Z" },
{ url = "https://files.pythonhosted.org/packages/84/09/9ec95e982e282e20c0d5407bc65031dfd0f0f8ecc66b69538296e06fcbee/mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b", size = 11611424, upload-time = "2025-02-05T03:49:46.908Z" }, { url = "https://files.pythonhosted.org/packages/29/59/5fd2400352c3093bed4c09017fe671d26bc5bb7e6ef2d4bf85f2a2488104/mypy-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:936ccfdd749af4766be824268bfe22d1db9eb2f34a3ea1d00ffbe5b5265f5491", size = 11875192, upload-time = "2025-05-29T13:34:54.281Z" },
{ url = "https://files.pythonhosted.org/packages/78/13/f7d14e55865036a1e6a0a69580c240f43bc1f37407fe9235c0d4ef25ffb0/mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3", size = 12365450, upload-time = "2025-02-05T03:50:05.89Z" }, { url = "https://files.pythonhosted.org/packages/ad/3e/4bfec74663a64c2012f3e278dbc29ffe82b121bc551758590d1b6449ec0c/mypy-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4086883a73166631307fdd330c4a9080ce24913d4f4c5ec596c601b3a4bdd777", size = 12612939, upload-time = "2025-05-29T13:33:14.766Z" },
{ url = "https://files.pythonhosted.org/packages/48/e1/301a73852d40c241e915ac6d7bcd7fedd47d519246db2d7b86b9d7e7a0cb/mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b", size = 12551765, upload-time = "2025-02-05T03:49:33.56Z" }, { url = "https://files.pythonhosted.org/packages/88/1f/fecbe3dcba4bf2ca34c26ca016383a9676711907f8db4da8354925cbb08f/mypy-1.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:feec38097f71797da0231997e0de3a58108c51845399669ebc532c815f93866b", size = 12874719, upload-time = "2025-05-29T13:21:52.09Z" },
{ url = "https://files.pythonhosted.org/packages/77/ba/c37bc323ae5fe7f3f15a28e06ab012cd0b7552886118943e90b15af31195/mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828", size = 9274701, upload-time = "2025-02-05T03:49:38.981Z" }, { url = "https://files.pythonhosted.org/packages/f3/51/c2d280601cd816c43dfa512a759270d5a5ef638d7ac9bea9134c8305a12f/mypy-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:09a8da6a0ee9a9770b8ff61b39c0bb07971cda90e7297f4213741b48a0cc8d93", size = 9487053, upload-time = "2025-05-29T13:33:29.797Z" },
{ url = "https://files.pythonhosted.org/packages/03/bc/f6339726c627bd7ca1ce0fa56c9ae2d0144604a319e0e339bdadafbbb599/mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f", size = 10662338, upload-time = "2025-02-05T03:50:17.287Z" }, { url = "https://files.pythonhosted.org/packages/24/c4/ff2f79db7075c274fe85b5fff8797d29c6b61b8854c39e3b7feb556aa377/mypy-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9f826aaa7ff8443bac6a494cf743f591488ea940dd360e7dd330e30dd772a5ab", size = 10884498, upload-time = "2025-05-29T13:18:54.066Z" },
{ url = "https://files.pythonhosted.org/packages/e2/90/8dcf506ca1a09b0d17555cc00cd69aee402c203911410136cd716559efe7/mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5", size = 9787540, upload-time = "2025-02-05T03:49:51.21Z" }, { url = "https://files.pythonhosted.org/packages/02/07/12198e83006235f10f6a7808917376b5d6240a2fd5dce740fe5d2ebf3247/mypy-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:82d056e6faa508501af333a6af192c700b33e15865bda49611e3d7d8358ebea2", size = 10011755, upload-time = "2025-05-29T13:34:00.851Z" },
{ url = "https://files.pythonhosted.org/packages/05/05/a10f9479681e5da09ef2f9426f650d7b550d4bafbef683b69aad1ba87457/mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e", size = 11538051, upload-time = "2025-02-05T03:50:20.885Z" }, { url = "https://files.pythonhosted.org/packages/f1/9b/5fd5801a72b5d6fb6ec0105ea1d0e01ab2d4971893076e558d4b6d6b5f80/mypy-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:089bedc02307c2548eb51f426e085546db1fa7dd87fbb7c9fa561575cf6eb1ff", size = 11800138, upload-time = "2025-05-29T13:32:55.082Z" },
{ url = "https://files.pythonhosted.org/packages/e9/9a/1f7d18b30edd57441a6411fcbc0c6869448d1a4bacbaee60656ac0fc29c8/mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c", size = 12286751, upload-time = "2025-02-05T03:49:42.408Z" }, { url = "https://files.pythonhosted.org/packages/2e/81/a117441ea5dfc3746431e51d78a4aca569c677aa225bca2cc05a7c239b61/mypy-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a2322896003ba66bbd1318c10d3afdfe24e78ef12ea10e2acd985e9d684a666", size = 12533156, upload-time = "2025-05-29T13:19:12.963Z" },
{ url = "https://files.pythonhosted.org/packages/72/af/19ff499b6f1dafcaf56f9881f7a965ac2f474f69f6f618b5175b044299f5/mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f", size = 12421783, upload-time = "2025-02-05T03:49:07.707Z" }, { url = "https://files.pythonhosted.org/packages/3f/38/88ec57c6c86014d3f06251e00f397b5a7daa6888884d0abf187e4f5f587f/mypy-1.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:021a68568082c5b36e977d54e8f1de978baf401a33884ffcea09bd8e88a98f4c", size = 12742426, upload-time = "2025-05-29T13:20:22.72Z" },
{ url = "https://files.pythonhosted.org/packages/96/39/11b57431a1f686c1aed54bf794870efe0f6aeca11aca281a0bd87a5ad42c/mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f", size = 9265618, upload-time = "2025-02-05T03:49:54.581Z" }, { url = "https://files.pythonhosted.org/packages/bd/53/7e9d528433d56e6f6f77ccf24af6ce570986c2d98a5839e4c2009ef47283/mypy-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:54066fed302d83bf5128632d05b4ec68412e1f03ef2c300434057d66866cea4b", size = 9478319, upload-time = "2025-05-29T13:21:17.582Z" },
{ url = "https://files.pythonhosted.org/packages/98/3a/03c74331c5eb8bd025734e04c9840532226775c47a2c39b56a0c8d4f128d/mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", size = 10793981, upload-time = "2025-02-05T03:50:28.25Z" }, { url = "https://files.pythonhosted.org/packages/70/cf/158e5055e60ca2be23aec54a3010f89dcffd788732634b344fc9cb1e85a0/mypy-1.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5436d11e89a3ad16ce8afe752f0f373ae9620841c50883dc96f8b8805620b13", size = 11062927, upload-time = "2025-05-29T13:35:52.328Z" },
{ url = "https://files.pythonhosted.org/packages/f0/1a/41759b18f2cfd568848a37c89030aeb03534411eef981df621d8fad08a1d/mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", size = 9749175, upload-time = "2025-02-05T03:50:13.411Z" }, { url = "https://files.pythonhosted.org/packages/94/34/cfff7a56be1609f5d10ef386342ce3494158e4d506516890142007e6472c/mypy-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f2622af30bf01d8fc36466231bdd203d120d7a599a6d88fb22bdcb9dbff84090", size = 10083082, upload-time = "2025-05-29T13:35:33.378Z" },
{ url = "https://files.pythonhosted.org/packages/12/7e/873481abf1ef112c582db832740f4c11b2bfa510e829d6da29b0ab8c3f9c/mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", size = 11455675, upload-time = "2025-02-05T03:50:31.421Z" }, { url = "https://files.pythonhosted.org/packages/b3/7f/7242062ec6288c33d8ad89574df87c3903d394870e5e6ba1699317a65075/mypy-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d045d33c284e10a038f5e29faca055b90eee87da3fc63b8889085744ebabb5a1", size = 11828306, upload-time = "2025-05-29T13:21:02.164Z" },
{ url = "https://files.pythonhosted.org/packages/b3/d0/92ae4cde706923a2d3f2d6c39629134063ff64b9dedca9c1388363da072d/mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", size = 12410020, upload-time = "2025-02-05T03:48:48.705Z" }, { url = "https://files.pythonhosted.org/packages/6f/5f/b392f7b4f659f5b619ce5994c5c43caab3d80df2296ae54fa888b3d17f5a/mypy-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b4968f14f44c62e2ec4a038c8797a87315be8df7740dc3ee8d3bfe1c6bf5dba8", size = 12702764, upload-time = "2025-05-29T13:20:42.826Z" },
{ url = "https://files.pythonhosted.org/packages/46/8b/df49974b337cce35f828ba6fda228152d6db45fed4c86ba56ffe442434fd/mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", size = 12498582, upload-time = "2025-02-05T03:49:03.628Z" }, { url = "https://files.pythonhosted.org/packages/9b/c0/7646ef3a00fa39ac9bc0938626d9ff29d19d733011be929cfea59d82d136/mypy-1.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb14a4a871bb8efb1e4a50360d4e3c8d6c601e7a31028a2c79f9bb659b63d730", size = 12896233, upload-time = "2025-05-29T13:18:37.446Z" },
{ url = "https://files.pythonhosted.org/packages/13/50/da5203fcf6c53044a0b699939f31075c45ae8a4cadf538a9069b165c1050/mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", size = 9366614, upload-time = "2025-02-05T03:50:00.313Z" }, { url = "https://files.pythonhosted.org/packages/6d/38/52f4b808b3fef7f0ef840ee8ff6ce5b5d77381e65425758d515cdd4f5bb5/mypy-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:bd4e1ebe126152a7bbaa4daedd781c90c8f9643c79b9748caa270ad542f12bec", size = 9565547, upload-time = "2025-05-29T13:20:02.836Z" },
{ url = "https://files.pythonhosted.org/packages/6a/9b/fd2e05d6ffff24d912f150b87db9e364fa8282045c875654ce7e32fffa66/mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445", size = 10788592, upload-time = "2025-02-05T03:48:55.789Z" }, { url = "https://files.pythonhosted.org/packages/97/9c/ca03bdbefbaa03b264b9318a98950a9c683e06472226b55472f96ebbc53d/mypy-1.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a9e056237c89f1587a3be1a3a70a06a698d25e2479b9a2f57325ddaaffc3567b", size = 11059753, upload-time = "2025-05-29T13:18:18.167Z" },
{ url = "https://files.pythonhosted.org/packages/74/37/b246d711c28a03ead1fd906bbc7106659aed7c089d55fe40dd58db812628/mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d", size = 9753611, upload-time = "2025-02-05T03:48:44.581Z" }, { url = "https://files.pythonhosted.org/packages/36/92/79a969b8302cfe316027c88f7dc6fee70129490a370b3f6eb11d777749d0/mypy-1.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b07e107affb9ee6ce1f342c07f51552d126c32cd62955f59a7db94a51ad12c0", size = 10073338, upload-time = "2025-05-29T13:19:48.079Z" },
{ url = "https://files.pythonhosted.org/packages/a6/ac/395808a92e10cfdac8003c3de9a2ab6dc7cde6c0d2a4df3df1b815ffd067/mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5", size = 11438443, upload-time = "2025-02-05T03:49:25.514Z" }, { url = "https://files.pythonhosted.org/packages/14/9b/a943f09319167da0552d5cd722104096a9c99270719b1afeea60d11610aa/mypy-1.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6fb60cbd85dc65d4d63d37cb5c86f4e3a301ec605f606ae3a9173e5cf34997b", size = 11827764, upload-time = "2025-05-29T13:46:04.47Z" },
{ url = "https://files.pythonhosted.org/packages/d2/8b/801aa06445d2de3895f59e476f38f3f8d610ef5d6908245f07d002676cbf/mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036", size = 12402541, upload-time = "2025-02-05T03:49:57.623Z" }, { url = "https://files.pythonhosted.org/packages/ec/64/ff75e71c65a0cb6ee737287c7913ea155845a556c64144c65b811afdb9c7/mypy-1.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7e32297a437cc915599e0578fa6bc68ae6a8dc059c9e009c628e1c47f91495d", size = 12701356, upload-time = "2025-05-29T13:35:13.553Z" },
{ url = "https://files.pythonhosted.org/packages/c7/67/5a4268782eb77344cc613a4cf23540928e41f018a9a1ec4c6882baf20ab8/mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357", size = 12494348, upload-time = "2025-02-05T03:48:52.361Z" }, { url = "https://files.pythonhosted.org/packages/0a/ad/0e93c18987a1182c350f7a5fab70550852f9fabe30ecb63bfbe51b602074/mypy-1.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:afe420c9380ccec31e744e8baff0d406c846683681025db3531b32db56962d52", size = 12900745, upload-time = "2025-05-29T13:17:24.409Z" },
{ url = "https://files.pythonhosted.org/packages/83/3e/57bb447f7bbbfaabf1712d96f9df142624a386d98fb026a761532526057e/mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf", size = 9373648, upload-time = "2025-02-05T03:49:11.395Z" }, { url = "https://files.pythonhosted.org/packages/28/5d/036c278d7a013e97e33f08c047fe5583ab4f1fc47c9a49f985f1cdd2a2d7/mypy-1.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:55f9076c6ce55dd3f8cd0c6fff26a008ca8e5131b89d5ba6d86bd3f47e736eeb", size = 9572200, upload-time = "2025-05-29T13:33:44.92Z" },
{ url = "https://files.pythonhosted.org/packages/09/4e/a7d65c7322c510de2c409ff3828b03354a7c43f5a8ed458a7a131b41c7b9/mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", size = 2221777, upload-time = "2025-02-05T03:50:08.348Z" }, { url = "https://files.pythonhosted.org/packages/99/a3/6ed10530dec8e0fdc890d81361260c9ef1f5e5c217ad8c9b21ecb2b8366b/mypy-1.16.0-py3-none-any.whl", hash = "sha256:29e1499864a3888bca5c1542f2d7232c6e586295183320caa95758fc84034031", size = 2265773, upload-time = "2025-05-29T13:35:18.762Z" },
] ]
[[package]] [[package]]
@ -1975,7 +1977,7 @@ wheels = [
[[package]] [[package]]
name = "pytest" name = "pytest"
version = "8.3.5" version = "8.4.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" }, { name = "colorama", marker = "sys_platform == 'win32'" },
@ -1983,23 +1985,24 @@ dependencies = [
{ name = "iniconfig" }, { name = "iniconfig" },
{ name = "packaging" }, { name = "packaging" },
{ name = "pluggy" }, { name = "pluggy" },
{ name = "pygments" },
{ name = "tomli", marker = "python_full_version < '3.11'" }, { name = "tomli", marker = "python_full_version < '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891, upload-time = "2025-03-02T12:54:54.503Z" } sdist = { url = "https://files.pythonhosted.org/packages/fb/aa/405082ce2749be5398045152251ac69c0f3578c7077efc53431303af97ce/pytest-8.4.0.tar.gz", hash = "sha256:14d920b48472ea0dbf68e45b96cd1ffda4705f33307dcc86c676c1b5104838a6", size = 1515232, upload-time = "2025-06-02T17:36:30.03Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634, upload-time = "2025-03-02T12:54:52.069Z" }, { url = "https://files.pythonhosted.org/packages/2f/de/afa024cbe022b1b318a3d224125aa24939e99b4ff6f22e0ba639a2eaee47/pytest-8.4.0-py3-none-any.whl", hash = "sha256:f40f825768ad76c0977cbacdf1fd37c6f7a468e460ea6a0636078f8972d4517e", size = 363797, upload-time = "2025-06-02T17:36:27.859Z" },
] ]
[[package]] [[package]]
name = "pytest-asyncio" name = "pytest-asyncio"
version = "0.26.0" version = "1.0.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "pytest" }, { name = "pytest" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/8e/c4/453c52c659521066969523e87d85d54139bbd17b78f09532fb8eb8cdb58e/pytest_asyncio-0.26.0.tar.gz", hash = "sha256:c4df2a697648241ff39e7f0e4a73050b03f123f760673956cf0d72a4990e312f", size = 54156, upload-time = "2025-03-25T06:22:28.883Z" } sdist = { url = "https://files.pythonhosted.org/packages/d0/d4/14f53324cb1a6381bef29d698987625d80052bb33932d8e7cbf9b337b17c/pytest_asyncio-1.0.0.tar.gz", hash = "sha256:d15463d13f4456e1ead2594520216b225a16f781e144f8fdf6c5bb4667c48b3f", size = 46960, upload-time = "2025-05-26T04:54:40.484Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/20/7f/338843f449ace853647ace35870874f69a764d251872ed1b4de9f234822c/pytest_asyncio-0.26.0-py3-none-any.whl", hash = "sha256:7b51ed894f4fbea1340262bdae5135797ebbe21d8638978e35d31c6d19f72fb0", size = 19694, upload-time = "2025-03-25T06:22:27.807Z" }, { url = "https://files.pythonhosted.org/packages/30/05/ce271016e351fddc8399e546f6e23761967ee09c8c568bbfbecb0c150171/pytest_asyncio-1.0.0-py3-none-any.whl", hash = "sha256:4f024da9f1ef945e680dc68610b52550e36590a67fd31bb3b4943979a1f90ef3", size = 15976, upload-time = "2025-05-26T04:54:39.035Z" },
] ]
[[package]] [[package]]
@ -2017,14 +2020,14 @@ wheels = [
[[package]] [[package]]
name = "pytest-mock" name = "pytest-mock"
version = "3.14.0" version = "3.14.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "pytest" }, { name = "pytest" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/c6/90/a955c3ab35ccd41ad4de556596fa86685bf4fc5ffcc62d22d856cfd4e29a/pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0", size = 32814, upload-time = "2024-03-21T22:14:04.964Z" } sdist = { url = "https://files.pythonhosted.org/packages/71/28/67172c96ba684058a4d24ffe144d64783d2a270d0af0d9e792737bddc75c/pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e", size = 33241, upload-time = "2025-05-26T13:58:45.167Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863, upload-time = "2024-03-21T22:14:02.694Z" }, { url = "https://files.pythonhosted.org/packages/b2/05/77b60e520511c53d1c1ca75f1930c7dd8e971d0c4379b7f4b3f9644685ba/pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0", size = 9923, upload-time = "2025-05-26T13:58:43.487Z" },
] ]
[[package]] [[package]]
@ -2050,14 +2053,14 @@ wheels = [
[[package]] [[package]]
name = "python-engineio" name = "python-engineio"
version = "4.12.0" version = "4.12.2"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "simple-websocket" }, { name = "simple-websocket" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/f7/e1/eee1129544b7f78fa2afa9fa0fce153cdcb21015b9b331d1b8adf90f45cb/python_engineio-4.12.0.tar.gz", hash = "sha256:f42a36a868d7063aa10ddccf6bd6117a169b6bd00d7ca53999772093b62014f9", size = 91503, upload-time = "2025-04-12T15:30:23.905Z" } sdist = { url = "https://files.pythonhosted.org/packages/ba/0b/67295279b66835f9fa7a491650efcd78b20321c127036eef62c11a31e028/python_engineio-4.12.2.tar.gz", hash = "sha256:e7e712ffe1be1f6a05ee5f951e72d434854a32fcfc7f6e4d9d3cae24ec70defa", size = 91677, upload-time = "2025-06-04T19:22:18.789Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/2b/f7/0aeea75424c47633c1d98557a2323be23bed31fa950f00161b34a5150d06/python_engineio-4.12.0-py3-none-any.whl", hash = "sha256:a0c47c129c39777e8ebc6d18011efd50db2144e4e8f08983acae8a3614626535", size = 59319, upload-time = "2025-04-12T15:30:22.325Z" }, { url = "https://files.pythonhosted.org/packages/0c/fa/df59acedf7bbb937f69174d00f921a7b93aa5a5f5c17d05296c814fff6fc/python_engineio-4.12.2-py3-none-any.whl", hash = "sha256:8218ab66950e179dfec4b4bbb30aecf3f5d86f5e58e6fc1aa7fde2c698b2804f", size = 59536, upload-time = "2025-06-04T19:22:16.916Z" },
] ]
[[package]] [[package]]
@ -2215,16 +2218,16 @@ wheels = [
[[package]] [[package]]
name = "rich" name = "rich"
version = "13.9.4" version = "14.0.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "markdown-it-py" }, { name = "markdown-it-py" },
{ name = "pygments" }, { name = "pygments" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149, upload-time = "2024-11-01T16:43:57.873Z" } sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078, upload-time = "2025-03-30T14:15:14.23Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424, upload-time = "2024-11-01T16:43:55.817Z" }, { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229, upload-time = "2025-03-30T14:15:12.283Z" },
] ]
[[package]] [[package]]
@ -2300,27 +2303,27 @@ wheels = [
[[package]] [[package]]
name = "ruff" name = "ruff"
version = "0.11.11" version = "0.11.13"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b2/53/ae4857030d59286924a8bdb30d213d6ff22d8f0957e738d0289990091dd8/ruff-0.11.11.tar.gz", hash = "sha256:7774173cc7c1980e6bf67569ebb7085989a78a103922fb83ef3dfe230cd0687d", size = 4186707, upload-time = "2025-05-22T19:19:34.363Z" } sdist = { url = "https://files.pythonhosted.org/packages/ed/da/9c6f995903b4d9474b39da91d2d626659af3ff1eeb43e9ae7c119349dba6/ruff-0.11.13.tar.gz", hash = "sha256:26fa247dc68d1d4e72c179e08889a25ac0c7ba4d78aecfc835d49cbfd60bf514", size = 4282054, upload-time = "2025-06-05T21:00:15.721Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/b1/14/f2326676197bab099e2a24473158c21656fbf6a207c65f596ae15acb32b9/ruff-0.11.11-py3-none-linux_armv6l.whl", hash = "sha256:9924e5ae54125ed8958a4f7de320dab7380f6e9fa3195e3dc3b137c6842a0092", size = 10229049, upload-time = "2025-05-22T19:18:45.516Z" }, { url = "https://files.pythonhosted.org/packages/7d/ce/a11d381192966e0b4290842cc8d4fac7dc9214ddf627c11c1afff87da29b/ruff-0.11.13-py3-none-linux_armv6l.whl", hash = "sha256:4bdfbf1240533f40042ec00c9e09a3aade6f8c10b6414cf11b519488d2635d46", size = 10292516, upload-time = "2025-06-05T20:59:32.944Z" },
{ url = "https://files.pythonhosted.org/packages/9a/f3/bff7c92dd66c959e711688b2e0768e486bbca46b2f35ac319bb6cce04447/ruff-0.11.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c8a93276393d91e952f790148eb226658dd275cddfde96c6ca304873f11d2ae4", size = 11053601, upload-time = "2025-05-22T19:18:49.269Z" }, { url = "https://files.pythonhosted.org/packages/78/db/87c3b59b0d4e753e40b6a3b4a2642dfd1dcaefbff121ddc64d6c8b47ba00/ruff-0.11.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aef9c9ed1b5ca28bb15c7eac83b8670cf3b20b478195bd49c8d756ba0a36cf48", size = 11106083, upload-time = "2025-06-05T20:59:37.03Z" },
{ url = "https://files.pythonhosted.org/packages/e2/38/8e1a3efd0ef9d8259346f986b77de0f62c7a5ff4a76563b6b39b68f793b9/ruff-0.11.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d6e333dbe2e6ae84cdedefa943dfd6434753ad321764fd937eef9d6b62022bcd", size = 10367421, upload-time = "2025-05-22T19:18:51.754Z" }, { url = "https://files.pythonhosted.org/packages/77/79/d8cec175856ff810a19825d09ce700265f905c643c69f45d2b737e4a470a/ruff-0.11.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:53b15a9dfdce029c842e9a5aebc3855e9ab7771395979ff85b7c1dedb53ddc2b", size = 10436024, upload-time = "2025-06-05T20:59:39.741Z" },
{ url = "https://files.pythonhosted.org/packages/b4/50/557ad9dd4fb9d0bf524ec83a090a3932d284d1a8b48b5906b13b72800e5f/ruff-0.11.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7885d9a5e4c77b24e8c88aba8c80be9255fa22ab326019dac2356cff42089fc6", size = 10581980, upload-time = "2025-05-22T19:18:54.011Z" }, { url = "https://files.pythonhosted.org/packages/8b/5b/f6d94f2980fa1ee854b41568368a2e1252681b9238ab2895e133d303538f/ruff-0.11.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab153241400789138d13f362c43f7edecc0edfffce2afa6a68434000ecd8f69a", size = 10646324, upload-time = "2025-06-05T20:59:42.185Z" },
{ url = "https://files.pythonhosted.org/packages/c4/b2/e2ed82d6e2739ece94f1bdbbd1d81b712d3cdaf69f0a1d1f1a116b33f9ad/ruff-0.11.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1b5ab797fcc09121ed82e9b12b6f27e34859e4227080a42d090881be888755d4", size = 10089241, upload-time = "2025-05-22T19:18:56.041Z" }, { url = "https://files.pythonhosted.org/packages/6c/9c/b4c2acf24ea4426016d511dfdc787f4ce1ceb835f3c5fbdbcb32b1c63bda/ruff-0.11.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c51f93029d54a910d3d24f7dd0bb909e31b6cd989a5e4ac513f4eb41629f0dc", size = 10174416, upload-time = "2025-06-05T20:59:44.319Z" },
{ url = "https://files.pythonhosted.org/packages/3d/9f/b4539f037a5302c450d7c695c82f80e98e48d0d667ecc250e6bdeb49b5c3/ruff-0.11.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e231ff3132c1119ece836487a02785f099a43992b95c2f62847d29bace3c75ac", size = 11699398, upload-time = "2025-05-22T19:18:58.248Z" }, { url = "https://files.pythonhosted.org/packages/f3/10/e2e62f77c65ede8cd032c2ca39c41f48feabedb6e282bfd6073d81bb671d/ruff-0.11.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1808b3ed53e1a777c2ef733aca9051dc9bf7c99b26ece15cb59a0320fbdbd629", size = 11724197, upload-time = "2025-06-05T20:59:46.935Z" },
{ url = "https://files.pythonhosted.org/packages/61/fb/32e029d2c0b17df65e6eaa5ce7aea5fbeaed22dddd9fcfbbf5fe37c6e44e/ruff-0.11.11-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a97c9babe1d4081037a90289986925726b802d180cca784ac8da2bbbc335f709", size = 12427955, upload-time = "2025-05-22T19:19:00.981Z" }, { url = "https://files.pythonhosted.org/packages/bb/f0/466fe8469b85c561e081d798c45f8a1d21e0b4a5ef795a1d7f1a9a9ec182/ruff-0.11.13-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d28ce58b5ecf0f43c1b71edffabe6ed7f245d5336b17805803312ec9bc665933", size = 12511615, upload-time = "2025-06-05T20:59:49.534Z" },
{ url = "https://files.pythonhosted.org/packages/6e/e3/160488dbb11f18c8121cfd588e38095ba779ae208292765972f7732bfd95/ruff-0.11.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8c4ddcbe8a19f59f57fd814b8b117d4fcea9bee7c0492e6cf5fdc22cfa563c8", size = 12069803, upload-time = "2025-05-22T19:19:03.258Z" }, { url = "https://files.pythonhosted.org/packages/17/0e/cefe778b46dbd0cbcb03a839946c8f80a06f7968eb298aa4d1a4293f3448/ruff-0.11.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55e4bc3a77842da33c16d55b32c6cac1ec5fb0fbec9c8c513bdce76c4f922165", size = 12117080, upload-time = "2025-06-05T20:59:51.654Z" },
{ url = "https://files.pythonhosted.org/packages/ff/16/3b006a875f84b3d0bff24bef26b8b3591454903f6f754b3f0a318589dcc3/ruff-0.11.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6224076c344a7694c6fbbb70d4f2a7b730f6d47d2a9dc1e7f9d9bb583faf390b", size = 11242630, upload-time = "2025-05-22T19:19:05.871Z" }, { url = "https://files.pythonhosted.org/packages/5d/2c/caaeda564cbe103bed145ea557cb86795b18651b0f6b3ff6a10e84e5a33f/ruff-0.11.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:633bf2c6f35678c56ec73189ba6fa19ff1c5e4807a78bf60ef487b9dd272cc71", size = 11326315, upload-time = "2025-06-05T20:59:54.469Z" },
{ url = "https://files.pythonhosted.org/packages/65/0d/0338bb8ac0b97175c2d533e9c8cdc127166de7eb16d028a43c5ab9e75abd/ruff-0.11.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:882821fcdf7ae8db7a951df1903d9cb032bbe838852e5fc3c2b6c3ab54e39875", size = 11507310, upload-time = "2025-05-22T19:19:08.584Z" }, { url = "https://files.pythonhosted.org/packages/75/f0/782e7d681d660eda8c536962920c41309e6dd4ebcea9a2714ed5127d44bd/ruff-0.11.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ffbc82d70424b275b089166310448051afdc6e914fdab90e08df66c43bb5ca9", size = 11555640, upload-time = "2025-06-05T20:59:56.986Z" },
{ url = "https://files.pythonhosted.org/packages/6f/bf/d7130eb26174ce9b02348b9f86d5874eafbf9f68e5152e15e8e0a392e4a3/ruff-0.11.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:dcec2d50756463d9df075a26a85a6affbc1b0148873da3997286caf1ce03cae1", size = 10441144, upload-time = "2025-05-22T19:19:13.621Z" }, { url = "https://files.pythonhosted.org/packages/5d/d4/3d580c616316c7f07fb3c99dbecfe01fbaea7b6fd9a82b801e72e5de742a/ruff-0.11.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a9ddd3ec62a9a89578c85842b836e4ac832d4a2e0bfaad3b02243f930ceafcc", size = 10507364, upload-time = "2025-06-05T20:59:59.154Z" },
{ url = "https://files.pythonhosted.org/packages/b3/f3/4be2453b258c092ff7b1761987cf0749e70ca1340cd1bfb4def08a70e8d8/ruff-0.11.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:99c28505ecbaeb6594701a74e395b187ee083ee26478c1a795d35084d53ebd81", size = 10081987, upload-time = "2025-05-22T19:19:15.821Z" }, { url = "https://files.pythonhosted.org/packages/5a/dc/195e6f17d7b3ea6b12dc4f3e9de575db7983db187c378d44606e5d503319/ruff-0.11.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d237a496e0778d719efb05058c64d28b757c77824e04ffe8796c7436e26712b7", size = 10141462, upload-time = "2025-06-05T21:00:01.481Z" },
{ url = "https://files.pythonhosted.org/packages/6c/6e/dfa4d2030c5b5c13db158219f2ec67bf333e8a7748dccf34cfa2a6ab9ebc/ruff-0.11.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9263f9e5aa4ff1dec765e99810f1cc53f0c868c5329b69f13845f699fe74f639", size = 11073922, upload-time = "2025-05-22T19:19:18.104Z" }, { url = "https://files.pythonhosted.org/packages/f4/8e/39a094af6967faa57ecdeacb91bedfb232474ff8c3d20f16a5514e6b3534/ruff-0.11.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:26816a218ca6ef02142343fd24c70f7cd8c5aa6c203bca284407adf675984432", size = 11121028, upload-time = "2025-06-05T21:00:04.06Z" },
{ url = "https://files.pythonhosted.org/packages/ff/f4/f7b0b0c3d32b593a20ed8010fa2c1a01f2ce91e79dda6119fcc51d26c67b/ruff-0.11.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:64ac6f885e3ecb2fdbb71de2701d4e34526651f1e8503af8fb30d4915a3fe345", size = 11568537, upload-time = "2025-05-22T19:19:20.889Z" }, { url = "https://files.pythonhosted.org/packages/5a/c0/b0b508193b0e8a1654ec683ebab18d309861f8bd64e3a2f9648b80d392cb/ruff-0.11.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:51c3f95abd9331dc5b87c47ac7f376db5616041173826dfd556cfe3d4977f492", size = 11602992, upload-time = "2025-06-05T21:00:06.249Z" },
{ url = "https://files.pythonhosted.org/packages/d2/46/0e892064d0adc18bcc81deed9aaa9942a27fd2cd9b1b7791111ce468c25f/ruff-0.11.11-py3-none-win32.whl", hash = "sha256:1adcb9a18802268aaa891ffb67b1c94cd70578f126637118e8099b8e4adcf112", size = 10536492, upload-time = "2025-05-22T19:19:23.642Z" }, { url = "https://files.pythonhosted.org/packages/7c/91/263e33ab93ab09ca06ce4f8f8547a858cc198072f873ebc9be7466790bae/ruff-0.11.13-py3-none-win32.whl", hash = "sha256:96c27935418e4e8e77a26bb05962817f28b8ef3843a6c6cc49d8783b5507f250", size = 10474944, upload-time = "2025-06-05T21:00:08.459Z" },
{ url = "https://files.pythonhosted.org/packages/1b/d9/232e79459850b9f327e9f1dc9c047a2a38a6f9689e1ec30024841fc4416c/ruff-0.11.11-py3-none-win_amd64.whl", hash = "sha256:748b4bb245f11e91a04a4ff0f96e386711df0a30412b9fe0c74d5bdc0e4a531f", size = 11612562, upload-time = "2025-05-22T19:19:27.013Z" }, { url = "https://files.pythonhosted.org/packages/46/f4/7c27734ac2073aae8efb0119cae6931b6fb48017adf048fdf85c19337afc/ruff-0.11.13-py3-none-win_amd64.whl", hash = "sha256:29c3189895a8a6a657b7af4e97d330c8a3afd2c9c8f46c81e2fc5a31866517e3", size = 11548669, upload-time = "2025-06-05T21:00:11.147Z" },
{ url = "https://files.pythonhosted.org/packages/ce/eb/09c132cff3cc30b2e7244191dcce69437352d6d6709c0adf374f3e6f476e/ruff-0.11.11-py3-none-win_arm64.whl", hash = "sha256:6c51f136c0364ab1b774767aa8b86331bd8e9d414e2d107db7a2189f35ea1f7b", size = 10735951, upload-time = "2025-05-22T19:19:30.043Z" }, { url = "https://files.pythonhosted.org/packages/ec/bf/b273dd11673fed8a6bd46032c0ea2a04b2ac9bfa9c628756a5856ba113b0/ruff-0.11.13-py3-none-win_arm64.whl", hash = "sha256:b4385285e9179d608ff1d2fb9922062663c658605819a6876d8beef0c30b7f3b", size = 10683928, upload-time = "2025-06-05T21:00:13.758Z" },
] ]
[[package]] [[package]]
@ -2555,26 +2558,23 @@ wheels = [
[[package]] [[package]]
name = "types-requests" name = "types-requests"
version = "2.32.0.20250515" version = "2.32.0.20250602"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "urllib3" }, { name = "urllib3" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/06/c1/cdc4f9b8cfd9130fbe6276db574f114541f4231fcc6fb29648289e6e3390/types_requests-2.32.0.20250515.tar.gz", hash = "sha256:09c8b63c11318cb2460813871aaa48b671002e59fda67ca909e9883777787581", size = 23012, upload-time = "2025-05-15T03:04:31.817Z" } sdist = { url = "https://files.pythonhosted.org/packages/48/b0/5321e6eeba5d59e4347fcf9bf06a5052f085c3aa0f4876230566d6a4dc97/types_requests-2.32.0.20250602.tar.gz", hash = "sha256:ee603aeefec42051195ae62ca7667cd909a2f8128fdf8aad9e8a5219ecfab3bf", size = 23042, upload-time = "2025-06-02T03:15:02.958Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/fe/0f/68a997c73a129287785f418c1ebb6004f81e46b53b3caba88c0e03fcd04a/types_requests-2.32.0.20250515-py3-none-any.whl", hash = "sha256:f8eba93b3a892beee32643ff836993f15a785816acca21ea0ffa006f05ef0fb2", size = 20635, upload-time = "2025-05-15T03:04:30.5Z" }, { url = "https://files.pythonhosted.org/packages/da/18/9b782980e575c6581d5c0c1c99f4c6f89a1d7173dad072ee96b2756c02e6/types_requests-2.32.0.20250602-py3-none-any.whl", hash = "sha256:f4f335f87779b47ce10b8b8597b409130299f6971ead27fead4fe7ba6ea3e726", size = 20638, upload-time = "2025-06-02T03:15:01.959Z" },
] ]
[[package]] [[package]]
name = "types-setuptools" name = "types-setuptools"
version = "76.0.0.20250313" version = "80.9.0.20250529"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ sdist = { url = "https://files.pythonhosted.org/packages/79/66/1b276526aad4696a9519919e637801f2c103419d2c248a6feb2729e034d1/types_setuptools-80.9.0.20250529.tar.gz", hash = "sha256:79e088ba0cba2186c8d6499cbd3e143abb142d28a44b042c28d3148b1e353c91", size = 41337, upload-time = "2025-05-29T03:07:34.487Z" }
{ name = "setuptools" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b8/0f/2d1d000c2be3919bcdea15e5da48456bf1e55c18d02c5509ea59dade1408/types_setuptools-76.0.0.20250313.tar.gz", hash = "sha256:b2be66f550f95f3cad2a7d46177b273c7e9c80df7d257fa57addbbcfc8126a9e", size = 43627, upload-time = "2025-03-13T02:51:28.3Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/ca/89/ea9669a0a76b160ffb312d0b02b15bad053c1bc81d2a54e42e3a402ca754/types_setuptools-76.0.0.20250313-py3-none-any.whl", hash = "sha256:bf454b2a49b8cfd7ebcf5844d4dd5fe4c8666782df1e3663c5866fd51a47460e", size = 65845, upload-time = "2025-03-13T02:51:27.055Z" }, { url = "https://files.pythonhosted.org/packages/1b/d8/83790d67ec771bf029a45ff1bd1aedbb738d8aa58c09dd0cc3033eea0e69/types_setuptools-80.9.0.20250529-py3-none-any.whl", hash = "sha256:00dfcedd73e333a430e10db096e4d46af93faf9314f832f13b6bbe3d6757e95f", size = 63263, upload-time = "2025-05-29T03:07:33.064Z" },
] ]
[[package]] [[package]]
@ -2627,16 +2627,16 @@ wheels = [
[[package]] [[package]]
name = "uvicorn" name = "uvicorn"
version = "0.34.2" version = "0.34.3"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "click" }, { name = "click" },
{ name = "h11" }, { name = "h11" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/a6/ae/9bbb19b9e1c450cf9ecaef06463e40234d98d95bf572fab11b4f19ae5ded/uvicorn-0.34.2.tar.gz", hash = "sha256:0e929828f6186353a80b58ea719861d2629d766293b6d19baf086ba31d4f3328", size = 76815, upload-time = "2025-04-19T06:02:50.101Z" } sdist = { url = "https://files.pythonhosted.org/packages/de/ad/713be230bcda622eaa35c28f0d328c3675c371238470abdea52417f17a8e/uvicorn-0.34.3.tar.gz", hash = "sha256:35919a9a979d7a59334b6b10e05d77c1d0d574c50e0fc98b8b1a0f165708b55a", size = 76631, upload-time = "2025-06-01T07:48:17.531Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/b1/4b/4cef6ce21a2aaca9d852a6e84ef4f135d99fcd74fa75105e2fc0c8308acd/uvicorn-0.34.2-py3-none-any.whl", hash = "sha256:deb49af569084536d269fe0a6d67e3754f104cf03aba7c11c40f01aadf33c403", size = 62483, upload-time = "2025-04-19T06:02:48.42Z" }, { url = "https://files.pythonhosted.org/packages/6d/0d/8adfeaa62945f90d19ddc461c55f4a50c258af7662d34b6a3d5d1f8646f6/uvicorn-0.34.3-py3-none-any.whl", hash = "sha256:16246631db62bdfbf069b0645177d6e8a77ba950cfedbfd093acef9444e4d885", size = 62431, upload-time = "2025-06-01T07:48:15.664Z" },
] ]
[package.optional-dependencies] [package.optional-dependencies]

2
mobile/.gitignore vendored
View File

@ -59,4 +59,4 @@ libisar.so
.fvm/ .fvm/
# Translation file # Translation file
lib/generated/codegen_loader.g.dart lib/generated/

View File

@ -17,6 +17,27 @@ To add a new translation text, enter the key-value pair in the `i18n/en.json` in
make translation make translation
``` ```
## Static Analysis
The following checks of static analysis must pass for a contribution to the mobile app to be valid:
```bash
dart format lib
dart analyze
dart run custom_lint
dcm analyze lib
```
[DCM](https://dcm.dev/) is a vendor tool that needs to be downloaded manually to run locally.
Immich was provided an open source license.
To use it, it is important that you do not have an active free tier license (can be verified with `dcm license`).
If you have write-access to the Immich repository directly, running dcm in your clone should just work.
If you are working on a clone of a fork, you need to connect to the main Immich repository as remote first:
```bash
git remote add immich git@github.com:immich-app/immich.git
```
## Immich-Flutter Directory Structure ## Immich-Flutter Directory Structure
Below are the directory inside the `lib` directory: Below are the directory inside the `lib` directory:

View File

@ -128,82 +128,169 @@ custom_lint:
- test/**.dart - test/**.dart
dart_code_metrics: dart_code_metrics:
extends:
- recommended
rules: rules:
# Common # All rules from "recommended" preset
- arguments-ordering: # Show potential errors
last: # - avoid-cascade-after-if-null
- child # - avoid-collection-methods-with-unrelated-types
- children # - avoid-duplicate-exports
- avoid-accessing-other-classes-private-members # - avoid-dynamic
- avoid-assigning-to-static-field # - avoid-missing-enum-constant-in-map
- avoid-assignments-as-conditions # - avoid-passing-async-when-sync-expected
- avoid-async-call-in-sync-function # - avoid-throw-in-catch-block
- avoid-collapsible-if - avoid-unused-parameters
- avoid-collection-equality-checks # - avoid-unnecessary-type-assertions
- avoid-complex-loop-conditions # - avoid-unnecessary-type-casts
- avoid-declaring-call-method # - avoid-unrelated-type-assertions
- avoid-extensions-on-records # - avoid-unrelated-type-casts
- avoid-function-type-in-records # - no-empty-block
- avoid-future-ignore # - no-equal-then-else
- avoid-global-state # - prefer-correct-test-file-name
- avoid-inverted-boolean-checks # - prefer-match-file-name
- avoid-late-final-reassignment # - prefer-return-await
- avoid-local-functions: # - avoid-self-assignment
exclude: # - avoid-self-compare
- test/**.dart # - avoid-shadowing
- avoid-negated-conditions # - prefer-iterable-of
- avoid-nested-streams-and-futures # - no-equal-switch-case
- avoid-referencing-subclasses # - no-equal-conditions
- avoid-unnecessary-continue # - avoid-equal-expressions
- avoid-unnecessary-nullable-return-type: false # - avoid-missed-calls
- binary-expression-operand-order # - avoid-unnecessary-negations
- pattern-fields-ordering # - avoid-unused-generics
- prefer-abstract-final-static-class # - function-always-returns-null
- prefer-commenting-future-delayed # - avoid-throw-objects-without-tostring
- prefer-early-return # - avoid-unsafe-collection-methods
- prefer-first # - prefer-wildcard-pattern
- prefer-immediate-return # - no-equal-switch-expression-cases
- prefer-last # - avoid-future-tostring
- prefer-simpler-boolean-expressions # - avoid-unassigned-late-fields
- prefer-switch-expression # - avoid-nested-futures
- prefer-type-over-var # - avoid-generics-shadowing
- use-existing-destructuring # - prefer-parentheses-with-if-null
- use-existing-variable # - no-equal-nested-conditions
# Flutter # - avoid-shadowed-extension-methods
- avoid-border-all # - avoid-unnecessary-conditionals
- avoid-complex-arithmetic-expressions # - avoid-double-slash-imports
- avoid-expanded-as-spacer # - avoid-map-keys-contains
- avoid-if-with-many-branches # - prefer-correct-json-casts
- avoid-inherited-widget-in-initstate # - avoid-duplicate-mixins
- avoid-late-context # - avoid-nullable-interpolation
- avoid-returning-widgets # - avoid-unused-instances
- avoid-shrink-wrap-in-lists # - prefer-correct-for-loop-increment
- avoid-single-child-column-or-row # - prefer-public-exception-classes
- avoid-stateless-widget-initialized-fields # - avoid-uncaught-future-errors
- avoid-wrapping-in-padding # - always-remove-listener
- prefer-align-over-container # - avoid-unnecessary-setstate
- prefer-const-border-radius # - check-for-equals-in-render-object-setters
- prefer-correct-callback-field-name: false # - consistent-update-render-object
- prefer-correct-edge-insets-constructor # - use-setstate-synchronously
- prefer-define-hero-tag # - avoid-incomplete-copy-with
- prefer-extracting-callbacks # - proper-super-calls
- prefer-for-loop-in-children # - dispose-fields
- prefer-match-file-name: false # - avoid-empty-setstate
- prefer-sliver-prefix # - avoid-state-constructors
- prefer-spacing # - avoid-recursive-widget-calls
- prefer-text-rich # - avoid-missing-image-alt
- prefer-transform-over-container # - avoid-passing-self-as-argument
- prefer-using-list-view # - avoid-unnecessary-if
- prefer-widget-private-members: # - avoid-unconditional-break
ignore-static: true # - avoid-referencing-discarded-variables
- use-closest-build-context # - avoid-unnecessary-local-late
# riverpod # - avoid-wildcard-cases-with-enums
- avoid-calling-notifier-members-inside-build # - match-getter-setter-field-names
- avoid-notifier-constructors # - avoid-accessing-collections-by-constant-index
- avoid-ref-read-inside-build # - prefer-unique-test-names
- avoid-ref-watch-outside-build # - avoid-duplicate-cascades
- avoid-unnecessary-consumer-widgets # - prefer-specific-cases-first
- dispose-provided-instances # - avoid-duplicate-switch-case-conditions
- use-ref-read-synchronously # - prefer-explicit-function-type
# - avoid-misused-test-matchers
# - avoid-duplicate-test-assertions
# - prefer-switch-with-enums
# - prefer-any-or-every
# - avoid-duplicate-map-keys
# - avoid-nullable-tostring
# - avoid-undisposed-instances
# - avoid-duplicate-initializers
# - avoid-unassigned-stream-subscriptions
# - avoid-empty-test-groups
# - avoid-not-encodable-in-to-json
# - avoid-contradictory-expressions
# - avoid-excessive-expressions
# - prefer-private-extension-type-field
# - avoid-renaming-representation-getters
# - avoid-empty-spread
# - avoid-unnecessary-gesture-detector
# - avoid-missing-completer-stack-trace
# - avoid-casting-to-extension-type
# - prefer-overriding-parent-equality
# - avoid-missing-controller
# - avoid-unknown-pragma
# - avoid-conditions-with-boolean-literals
# - avoid-multi-assignment
# - avoid-collection-equality-checks
# - avoid-only-rethrow
# - avoid-incorrect-image-opacity
# - avoid-misused-set-literals
# - dispose-class-fields
# - avoid-suspicious-super-overrides
# - avoid-assignments-as-conditions
# - avoid-unused-assignment
# - avoid-unnecessary-overrides
# - avoid-implicitly-nullable-extension-types
# Enable with the next release
# - avoid-late-final-reassignment
# - avoid-duplicate-constant-values
# - function-always-returns-same-value
# - avoid-flexible-outside-flex
# - avoid-unnecessary-patterns
# - use-closest-build-context
# - avoid-commented-out-code
# - avoid-recursive-tostring
# - avoid-enum-values-by-index
# - avoid-constant-assert-conditions
# - avoid-inconsistent-digit-separators
# - pass-existing-future-to-future-builder
# - pass-existing-stream-to-stream-builder
# Code simplification
# - avoid-redundant-async
# - avoid-redundant-else
# - avoid-unnecessary-nullable-return-type
# - avoid-redundant-pragma-inline
# - avoid-nested-records
# - avoid-redundant-positional-field-name
# - avoid-explicit-pattern-field-name
# - prefer-simpler-patterns-null-check
# - avoid-unnecessary-return
# - avoid-duplicate-patterns
# - avoid-keywords-in-wildcard-pattern
# - avoid-unnecessary-futures
# - avoid-unnecessary-reassignment
# - avoid-unnecessary-call
# - avoid-unnecessary-stateful-widgets
# - prefer-dedicated-media-query-methods
# - avoid-unnecessary-overrides-in-state
# - move-variable-closer-to-its-usage
# - avoid-nullable-parameters-with-default-values
# - prefer-null-aware-spread
# - avoid-inferrable-type-arguments
# - avoid-unnecessary-super
# - avoid-unnecessary-collections
# - avoid-unnecessary-extends
# - avoid-unnecessary-enum-arguments
# - prefer-contains
# Enable with the next release
# - prefer-simpler-boolean-expressions
# - prefer-spacing
# - avoid-unnecessary-continue
# - avoid-unnecessary-compare-to
# Style
# - prefer-trailing-comma
# - unnecessary-trailing-comma
# - prefer-declaring-const-constructor
# - prefer-single-widget-per-file
# - prefer-prefixed-global-constants
# - prefer-correct-callback-field-name

View File

@ -29,6 +29,7 @@ if (keystorePropertiesFile.exists()) {
android { android {
compileSdkVersion 35 compileSdkVersion 35
ndkVersion = "28.1.13356709"
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_17 sourceCompatibility JavaVersion.VERSION_17

View File

@ -85,6 +85,8 @@ data class PlatformAsset (
val type: Long, val type: Long,
val createdAt: Long? = null, val createdAt: Long? = null,
val updatedAt: Long? = null, val updatedAt: Long? = null,
val width: Long? = null,
val height: Long? = null,
val durationInSeconds: Long val durationInSeconds: Long
) )
{ {
@ -95,8 +97,10 @@ data class PlatformAsset (
val type = pigeonVar_list[2] as Long val type = pigeonVar_list[2] as Long
val createdAt = pigeonVar_list[3] as Long? val createdAt = pigeonVar_list[3] as Long?
val updatedAt = pigeonVar_list[4] as Long? val updatedAt = pigeonVar_list[4] as Long?
val durationInSeconds = pigeonVar_list[5] as Long val width = pigeonVar_list[5] as Long?
return PlatformAsset(id, name, type, createdAt, updatedAt, durationInSeconds) val height = pigeonVar_list[6] as Long?
val durationInSeconds = pigeonVar_list[7] as Long
return PlatformAsset(id, name, type, createdAt, updatedAt, width, height, durationInSeconds)
} }
} }
fun toList(): List<Any?> { fun toList(): List<Any?> {
@ -106,6 +110,8 @@ data class PlatformAsset (
type, type,
createdAt, createdAt,
updatedAt, updatedAt,
width,
height,
durationInSeconds, durationInSeconds,
) )
} }

View File

@ -37,6 +37,8 @@ open class NativeSyncApiImplBase(context: Context) {
MediaStore.MediaColumns.DATE_MODIFIED, MediaStore.MediaColumns.DATE_MODIFIED,
MediaStore.Files.FileColumns.MEDIA_TYPE, MediaStore.Files.FileColumns.MEDIA_TYPE,
MediaStore.MediaColumns.BUCKET_ID, MediaStore.MediaColumns.BUCKET_ID,
MediaStore.MediaColumns.WIDTH,
MediaStore.MediaColumns.HEIGHT,
MediaStore.MediaColumns.DURATION MediaStore.MediaColumns.DURATION
) )
@ -68,6 +70,8 @@ open class NativeSyncApiImplBase(context: Context) {
val dateModifiedColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_MODIFIED) val dateModifiedColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_MODIFIED)
val mediaTypeColumn = c.getColumnIndexOrThrow(MediaStore.Files.FileColumns.MEDIA_TYPE) val mediaTypeColumn = c.getColumnIndexOrThrow(MediaStore.Files.FileColumns.MEDIA_TYPE)
val bucketIdColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.BUCKET_ID) val bucketIdColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.BUCKET_ID)
val widthColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.WIDTH)
val heightColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.HEIGHT)
val durationColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.DURATION) val durationColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.DURATION)
while (c.moveToNext()) { while (c.moveToNext()) {
@ -86,12 +90,23 @@ open class NativeSyncApiImplBase(context: Context) {
?: c.getLong(dateAddedColumn) ?: c.getLong(dateAddedColumn)
// Date modified is seconds since epoch // Date modified is seconds since epoch
val modifiedAt = c.getLong(dateModifiedColumn) val modifiedAt = c.getLong(dateModifiedColumn)
val width = c.getInt(widthColumn).toLong()
val height = c.getInt(heightColumn).toLong()
// Duration is milliseconds // Duration is milliseconds
val duration = if (mediaType == MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE) 0 val duration = if (mediaType == MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE) 0
else c.getLong(durationColumn) / 1000 else c.getLong(durationColumn) / 1000
val bucketId = c.getString(bucketIdColumn) val bucketId = c.getString(bucketIdColumn)
val asset = PlatformAsset(id, name, mediaType.toLong(), createdAt, modifiedAt, duration) val asset = PlatformAsset(
id,
name,
mediaType.toLong(),
createdAt,
modifiedAt,
width,
height,
duration
)
yield(AssetResult.ValidAsset(asset, bucketId)) yield(AssetResult.ValidAsset(asset, bucketId))
} }
} }

View File

@ -0,0 +1,63 @@
// ignore_for_file: avoid_print
import 'dart:convert';
import 'dart:io';
const _kReservedWords = ['continue'];
void main() async {
final sourceFile = File('../i18n/en.json');
if (!await sourceFile.exists()) {
stderr.writeln('Source file does not exist');
return;
}
final outputDir = Directory('lib/generated');
await outputDir.create(recursive: true);
final outputFile = File('lib/generated/intl_keys.g.dart');
await _generate(sourceFile, outputFile);
print('Generated ${outputFile.path}');
}
Future<void> _generate(File source, File output) async {
final content = await source.readAsString();
final translations = json.decode(content) as Map<String, dynamic>;
final buffer = StringBuffer('''
// DO NOT EDIT. This is code generated via generate_keys.dart
abstract class IntlKeys {
''');
_writeKeys(buffer, translations);
buffer.writeln('}');
await output.writeAsString(buffer.toString());
}
void _writeKeys(
StringBuffer buffer,
Map<String, dynamic> map, [
String prefix = '',
]) {
for (final entry in map.entries) {
final key = entry.key;
final value = entry.value;
if (value is Map<String, dynamic>) {
_writeKeys(buffer, value, prefix.isEmpty ? key : '${prefix}_$key');
} else {
final name = _cleanName(prefix.isEmpty ? key : '${prefix}_$key');
final path = prefix.isEmpty ? key : '$prefix.$key'.replaceAll('_', '.');
buffer.writeln(' static const $name = \'$path\';');
}
}
}
String _cleanName(String name) {
name = name.replaceAll(RegExp(r'[^a-zA-Z0-9_]'), '_');
if (RegExp(r'^[0-9]').hasMatch(name)) name = 'k_$name';
if (_kReservedWords.contains(name)) name = '${name}_';
return name;
}

1
mobile/dcm_global.yaml Normal file
View File

@ -0,0 +1 @@
version: '>=1.29.0 <1.30.0'

File diff suppressed because one or more lines are too long

View File

@ -189,10 +189,10 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: lints name: lints
sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.1.1" version: "6.0.0"
logging: logging:
dependency: transitive dependency: transitive
description: description:
@ -362,4 +362,4 @@ packages:
source: hosted source: hosted
version: "3.1.3" version: "3.1.3"
sdks: sdks:
dart: ">=3.7.0 <4.0.0" dart: ">=3.8.0-0 <4.0.0"

View File

@ -11,4 +11,4 @@ dependencies:
glob: ^2.1.2 glob: ^2.1.2
dev_dependencies: dev_dependencies:
lints: ^5.0.0 lints: ^6.0.0

View File

@ -1,6 +1,9 @@
PODS: PODS:
- background_downloader (0.0.1): - background_downloader (0.0.1):
- Flutter - Flutter
- bonsoir_darwin (0.0.1):
- Flutter
- FlutterMacOS
- connectivity_plus (0.0.1): - connectivity_plus (0.0.1):
- Flutter - Flutter
- device_info_plus (0.0.1): - device_info_plus (0.0.1):
@ -64,10 +67,10 @@ PODS:
- local_auth_darwin (0.0.1): - local_auth_darwin (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
- MapLibre (6.5.0) - MapLibre (6.14.0)
- maplibre_gl (0.0.1): - maplibre_gl (0.0.1):
- Flutter - Flutter
- MapLibre (= 6.5.0) - MapLibre (= 6.14.0)
- native_video_player (1.0.0): - native_video_player (1.0.0):
- Flutter - Flutter
- network_info_plus (0.0.1): - network_info_plus (0.0.1):
@ -129,6 +132,7 @@ PODS:
DEPENDENCIES: DEPENDENCIES:
- background_downloader (from `.symlinks/plugins/background_downloader/ios`) - background_downloader (from `.symlinks/plugins/background_downloader/ios`)
- bonsoir_darwin (from `.symlinks/plugins/bonsoir_darwin/darwin`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`)
@ -173,6 +177,8 @@ SPEC REPOS:
EXTERNAL SOURCES: EXTERNAL SOURCES:
background_downloader: background_downloader:
:path: ".symlinks/plugins/background_downloader/ios" :path: ".symlinks/plugins/background_downloader/ios"
bonsoir_darwin:
:path: ".symlinks/plugins/bonsoir_darwin/darwin"
connectivity_plus: connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios" :path: ".symlinks/plugins/connectivity_plus/ios"
device_info_plus: device_info_plus:
@ -236,6 +242,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
background_downloader: 50e91d979067b82081aba359d7d916b3ba5fadad background_downloader: 50e91d979067b82081aba359d7d916b3ba5fadad
bonsoir_darwin: 29c7ccf356646118844721f36e1de4b61f6cbd0e
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
@ -253,8 +260,8 @@ SPEC CHECKSUMS:
integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
isar_flutter_libs: bc909e72c3d756c2759f14c8776c13b5b0556e26 isar_flutter_libs: bc909e72c3d756c2759f14c8776c13b5b0556e26
local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391 local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391
MapLibre: 0ebfa9329d313cec8bf0a5ba5a336a1dc903785e MapLibre: 69e572367f4ef6287e18246cfafc39c80cdcabcd
maplibre_gl: eab61cca6e1cfa9187249bacd3f08b51e8cd8ae9 maplibre_gl: 3c924e44725147b03dda33430ad216005b40555f
native_video_player: b65c58951ede2f93d103a25366bdebca95081265 native_video_player: b65c58951ede2f93d103a25366bdebca95081265
network_info_plus: cf61925ab5205dce05a4f0895989afdb6aade5fc network_info_plus: cf61925ab5205dce05a4f0895989afdb6aade5fc
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499 package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499

View File

@ -113,6 +113,11 @@
<key>NSAllowsArbitraryLoads</key> <key>NSAllowsArbitraryLoads</key>
<true /> <true />
</dict> </dict>
<key>NSBonjourServices</key>
<array>
<string>_googlecast._tcp</string>
<string>_CC1AD845._googlecast._tcp</string>
</array>
<key>NSCameraUsageDescription</key> <key>NSCameraUsageDescription</key>
<string>We need to access the camera to let you take beautiful video using this app</string> <string>We need to access the camera to let you take beautiful video using this app</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key> <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
@ -164,4 +169,4 @@
<key>NSFaceIDUsageDescription</key> <key>NSFaceIDUsageDescription</key>
<string>We need to use FaceID to allow access to your locked folder</string> <string>We need to use FaceID to allow access to your locked folder</string>
</dict> </dict>
</plist> </plist>

View File

@ -135,6 +135,8 @@ struct PlatformAsset: Hashable {
var type: Int64 var type: Int64
var createdAt: Int64? = nil var createdAt: Int64? = nil
var updatedAt: Int64? = nil var updatedAt: Int64? = nil
var width: Int64? = nil
var height: Int64? = nil
var durationInSeconds: Int64 var durationInSeconds: Int64
@ -145,7 +147,9 @@ struct PlatformAsset: Hashable {
let type = pigeonVar_list[2] as! Int64 let type = pigeonVar_list[2] as! Int64
let createdAt: Int64? = nilOrValue(pigeonVar_list[3]) let createdAt: Int64? = nilOrValue(pigeonVar_list[3])
let updatedAt: Int64? = nilOrValue(pigeonVar_list[4]) let updatedAt: Int64? = nilOrValue(pigeonVar_list[4])
let durationInSeconds = pigeonVar_list[5] as! Int64 let width: Int64? = nilOrValue(pigeonVar_list[5])
let height: Int64? = nilOrValue(pigeonVar_list[6])
let durationInSeconds = pigeonVar_list[7] as! Int64
return PlatformAsset( return PlatformAsset(
id: id, id: id,
@ -153,6 +157,8 @@ struct PlatformAsset: Hashable {
type: type, type: type,
createdAt: createdAt, createdAt: createdAt,
updatedAt: updatedAt, updatedAt: updatedAt,
width: width,
height: height,
durationInSeconds: durationInSeconds durationInSeconds: durationInSeconds
) )
} }
@ -163,6 +169,8 @@ struct PlatformAsset: Hashable {
type, type,
createdAt, createdAt,
updatedAt, updatedAt,
width,
height,
durationInSeconds, durationInSeconds,
] ]
} }

View File

@ -25,6 +25,8 @@ extension PHAsset {
type: Int64(mediaType.rawValue), type: Int64(mediaType.rawValue),
createdAt: creationDate.map { Int64($0.timeIntervalSince1970) }, createdAt: creationDate.map { Int64($0.timeIntervalSince1970) },
updatedAt: modificationDate.map { Int64($0.timeIntervalSince1970) }, updatedAt: modificationDate.map { Int64($0.timeIntervalSince1970) },
width: Int64(pixelWidth),
height: Int64(pixelHeight),
durationInSeconds: Int64(duration) durationInSeconds: Int64(duration)
) )
} }
@ -156,8 +158,6 @@ class NativeSyncApiImpl: NativeSyncApi {
id: asset.localIdentifier, id: asset.localIdentifier,
name: "", name: "",
type: 0, type: 0,
createdAt: nil,
updatedAt: nil,
durationInSeconds: 0 durationInSeconds: 0
) )
if (updatedAssets.contains(AssetWrapper(with: predicate))) { if (updatedAssets.contains(AssetWrapper(with: predicate))) {

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
List<ColorFilter> filters = [ const List<ColorFilter> filters = [
//Original //Original
const ColorFilter.matrix([ ColorFilter.matrix([
1, 1,
0, 0,
0, 0,
@ -25,7 +25,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Vintage //Vintage
const ColorFilter.matrix([ ColorFilter.matrix([
0.8, 0.8,
0.1, 0.1,
0.1, 0.1,
@ -48,7 +48,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Mood //Mood
const ColorFilter.matrix([ ColorFilter.matrix([
1.2, 1.2,
0.1, 0.1,
0.1, 0.1,
@ -71,7 +71,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Crisp //Crisp
const ColorFilter.matrix([ ColorFilter.matrix([
1.2, 1.2,
0, 0,
0, 0,
@ -94,7 +94,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Cool //Cool
const ColorFilter.matrix([ ColorFilter.matrix([
0.9, 0.9,
0, 0,
0.2, 0.2,
@ -117,7 +117,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Blush //Blush
const ColorFilter.matrix([ ColorFilter.matrix([
1.1, 1.1,
0.1, 0.1,
0.1, 0.1,
@ -140,7 +140,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Sunkissed //Sunkissed
const ColorFilter.matrix([ ColorFilter.matrix([
1.3, 1.3,
0, 0,
0.1, 0.1,
@ -163,7 +163,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Fresh //Fresh
const ColorFilter.matrix([ ColorFilter.matrix([
1.2, 1.2,
0, 0,
0, 0,
@ -186,7 +186,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Classic //Classic
const ColorFilter.matrix([ ColorFilter.matrix([
1.1, 1.1,
0, 0,
-0.1, -0.1,
@ -209,7 +209,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Lomo-ish //Lomo-ish
const ColorFilter.matrix([ ColorFilter.matrix([
1.5, 1.5,
0, 0,
0.1, 0.1,
@ -232,7 +232,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Nashville //Nashville
const ColorFilter.matrix([ ColorFilter.matrix([
1.2, 1.2,
0.15, 0.15,
-0.15, -0.15,
@ -255,7 +255,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Valencia //Valencia
const ColorFilter.matrix([ ColorFilter.matrix([
1.15, 1.15,
0.1, 0.1,
0.1, 0.1,
@ -278,7 +278,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Clarendon //Clarendon
const ColorFilter.matrix([ ColorFilter.matrix([
1.2, 1.2,
0, 0,
0, 0,
@ -301,7 +301,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Moon //Moon
const ColorFilter.matrix([ ColorFilter.matrix([
0.33, 0.33,
0.33, 0.33,
0.33, 0.33,
@ -324,7 +324,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Willow //Willow
const ColorFilter.matrix([ ColorFilter.matrix([
0.5, 0.5,
0.5, 0.5,
0.5, 0.5,
@ -347,7 +347,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Kodak //Kodak
const ColorFilter.matrix([ ColorFilter.matrix([
1.3, 1.3,
0.1, 0.1,
-0.1, -0.1,
@ -370,7 +370,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Frost //Frost
const ColorFilter.matrix([ ColorFilter.matrix([
0.8, 0.8,
0.2, 0.2,
0.1, 0.1,
@ -393,7 +393,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Night Vision //Night Vision
const ColorFilter.matrix([ ColorFilter.matrix([
0.1, 0.1,
0.95, 0.95,
0.2, 0.2,
@ -416,7 +416,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Sunset //Sunset
const ColorFilter.matrix([ ColorFilter.matrix([
1.5, 1.5,
0.2, 0.2,
0, 0,
@ -439,7 +439,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Noir //Noir
const ColorFilter.matrix([ ColorFilter.matrix([
1.3, 1.3,
-0.3, -0.3,
0.1, 0.1,
@ -462,7 +462,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Dreamy //Dreamy
const ColorFilter.matrix([ ColorFilter.matrix([
1.1, 1.1,
0.1, 0.1,
0.1, 0.1,
@ -485,7 +485,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Sepia //Sepia
const ColorFilter.matrix([ ColorFilter.matrix([
0.393, 0.393,
0.769, 0.769,
0.189, 0.189,
@ -508,7 +508,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Radium //Radium
const ColorFilter.matrix([ ColorFilter.matrix([
1.438, 1.438,
-0.062, -0.062,
-0.062, -0.062,
@ -531,7 +531,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Aqua //Aqua
const ColorFilter.matrix([ ColorFilter.matrix([
0.2126, 0.2126,
0.7152, 0.7152,
0.0722, 0.0722,
@ -554,7 +554,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Purple Haze //Purple Haze
const ColorFilter.matrix([ ColorFilter.matrix([
1.3, 1.3,
0, 0,
1.2, 1.2,
@ -577,7 +577,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Lemonade //Lemonade
const ColorFilter.matrix([ ColorFilter.matrix([
1.2, 1.2,
0.1, 0.1,
0, 0,
@ -600,7 +600,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Caramel //Caramel
const ColorFilter.matrix([ ColorFilter.matrix([
1.6, 1.6,
0.2, 0.2,
0, 0,
@ -623,7 +623,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Peachy //Peachy
const ColorFilter.matrix([ ColorFilter.matrix([
1.3, 1.3,
0.5, 0.5,
0, 0,
@ -646,7 +646,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Neon //Neon
const ColorFilter.matrix([ ColorFilter.matrix([
1, 1,
0, 0,
1, 1,
@ -669,7 +669,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Cold Morning //Cold Morning
const ColorFilter.matrix([ ColorFilter.matrix([
0.9, 0.9,
0.1, 0.1,
0.2, 0.2,
@ -692,7 +692,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Lush //Lush
const ColorFilter.matrix([ ColorFilter.matrix([
0.9, 0.9,
0.2, 0.2,
0, 0,
@ -715,7 +715,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Urban Neon //Urban Neon
const ColorFilter.matrix([ ColorFilter.matrix([
1.1, 1.1,
0, 0,
0.3, 0.3,
@ -738,7 +738,7 @@ List<ColorFilter> filters = [
0, 0,
]), ]),
//Monochrome //Monochrome
const ColorFilter.matrix([ ColorFilter.matrix([
0.6, 0.6,
0.2, 0.2,
0.2, 0.2,

View File

@ -3,11 +3,11 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/local_album.model.dart'; import 'package:immich_mobile/domain/models/local_album.model.dart';
abstract interface class ILocalAlbumRepository implements IDatabaseRepository { abstract interface class ILocalAlbumRepository implements IDatabaseRepository {
Future<List<LocalAlbum>> getAll({SortLocalAlbumsBy? sortBy}); Future<List<LocalAlbum>> getAll({Set<SortLocalAlbumsBy> sortBy = const {}});
Future<List<LocalAsset>> getAssetsForAlbum(String albumId); Future<List<LocalAsset>> getAssets(String albumId);
Future<List<String>> getAssetIdsForAlbum(String albumId); Future<List<String>> getAssetIds(String albumId);
Future<void> upsert( Future<void> upsert(
LocalAlbum album, { LocalAlbum album, {
@ -26,12 +26,9 @@ abstract interface class ILocalAlbumRepository implements IDatabaseRepository {
required Map<String, List<String>> assetAlbums, required Map<String, List<String>> assetAlbums,
}); });
Future<void> syncAlbumDeletes( Future<void> syncDeletes(String albumId, Iterable<String> assetIdsToKeep);
String albumId,
Iterable<String> assetIdsToKeep,
);
Future<List<LocalAsset>> getAssetsToHash(String albumId); Future<List<LocalAsset>> getAssetsToHash(String albumId);
} }
enum SortLocalAlbumsBy { id } enum SortLocalAlbumsBy { id, backupSelection, isIosSharedAlbum }

View File

@ -6,9 +6,11 @@ abstract interface class IStoreRepository implements IDatabaseRepository {
Future<T?> tryGet<T>(StoreKey<T> key); Future<T?> tryGet<T>(StoreKey<T> key);
Future<List<StoreDto<Object>>> getAll();
Stream<T?> watch<T>(StoreKey<T> key); Stream<T?> watch<T>(StoreKey<T> key);
Stream<StoreUpdateEvent> watchAll(); Stream<StoreDto<Object>> watchAll();
Future<bool> update<T>(StoreKey<T> key, T value); Future<bool> update<T>(StoreKey<T> key, T value);

View File

@ -15,4 +15,13 @@ abstract interface class ISyncStreamRepository implements IDatabaseRepository {
Future<void> updatePartnerAssetsV1(Iterable<SyncAssetV1> data); Future<void> updatePartnerAssetsV1(Iterable<SyncAssetV1> data);
Future<void> deletePartnerAssetsV1(Iterable<SyncAssetDeleteV1> data); Future<void> deletePartnerAssetsV1(Iterable<SyncAssetDeleteV1> data);
Future<void> updatePartnerAssetsExifV1(Iterable<SyncAssetExifV1> data); Future<void> updatePartnerAssetsExifV1(Iterable<SyncAssetExifV1> data);
Future<void> updateAlbumsV1(Iterable<SyncAlbumV1> data);
Future<void> deleteAlbumsV1(Iterable<SyncAlbumDeleteV1> data);
// Future<void> updateAlbumAssetsV1(Iterable<SyncAlbumAssetV1> data);
// Future<void> deleteAlbumAssetsV1(Iterable<SyncAlbumAssetV1> data);
Future<void> updateAlbumUsersV1(Iterable<SyncAlbumUserV1> data);
Future<void> deleteAlbumUsersV1(Iterable<SyncAlbumUserDeleteV1> data);
} }

View File

@ -0,0 +1,68 @@
enum AssetOrder {
// do not change this order!
asc,
desc,
}
// Model for an album stored in the server
class Album {
final String id;
final String name;
final String description;
final DateTime createdAt;
final DateTime updatedAt;
final String? thumbnailAssetId;
final bool isActivityEnabled;
final AssetOrder order;
const Album({
required this.id,
required this.name,
required this.description,
required this.createdAt,
required this.updatedAt,
this.thumbnailAssetId,
required this.isActivityEnabled,
required this.order,
});
@override
String toString() {
return '''Album {
id: $id,
name: $name,
description: $description,
createdAt: $createdAt,
updatedAt: $updatedAt,
isActivityEnabled: $isActivityEnabled,
order: $order,
thumbnailAssetId: ${thumbnailAssetId ?? "<NA>"}
}''';
}
@override
bool operator ==(Object other) {
if (other is! Album) return false;
if (identical(this, other)) return true;
return id == other.id &&
name == other.name &&
description == other.description &&
createdAt == other.createdAt &&
updatedAt == other.updatedAt &&
thumbnailAssetId == other.thumbnailAssetId &&
isActivityEnabled == other.isActivityEnabled &&
order == other.order;
}
@override
int get hashCode {
return id.hashCode ^
name.hashCode ^
description.hashCode ^
createdAt.hashCode ^
updatedAt.hashCode ^
thumbnailAssetId.hashCode ^
isActivityEnabled.hashCode ^
order.hashCode;
}
}

View File

@ -0,0 +1,5 @@
enum AlbumUserRole {
// do not change this order!
editor,
viewer,
}

View File

@ -1,12 +1,10 @@
enum BackupSelection { enum BackupSelection {
none._(1),
selected._(0),
excluded._(2);
// Used to sort albums based on the backupSelection // Used to sort albums based on the backupSelection
// selected -> none -> excluded // selected -> none -> excluded
final int sortOrder; // Do not change the order of these values
const BackupSelection._(this.sortOrder); selected,
none,
excluded,
} }
class LocalAlbum { class LocalAlbum {

View File

@ -76,23 +76,23 @@ enum StoreKey<T> {
Type get type => T; Type get type => T;
} }
class StoreUpdateEvent<T> { class StoreDto<T> {
final StoreKey<T> key; final StoreKey<T> key;
final T? value; final T? value;
const StoreUpdateEvent(this.key, this.value); const StoreDto(this.key, this.value);
@override @override
String toString() { String toString() {
return ''' return '''
StoreUpdateEvent: { StoreDto: {
key: $key, key: $key,
value: ${value ?? '<NA>'}, value: ${value ?? '<NA>'},
}'''; }''';
} }
@override @override
bool operator ==(covariant StoreUpdateEvent<T> other) { bool operator ==(covariant StoreDto<T> other) {
if (identical(this, other)) return true; if (identical(this, other)) return true;
return other.key == key && other.value == value; return other.key == key && other.value == value;

View File

@ -2,8 +2,7 @@ import 'package:openapi/api.dart';
class SyncEvent { class SyncEvent {
final SyncEntityType type; final SyncEntityType type;
// ignore: avoid-dynamic final Object data;
final dynamic data;
final String ack; final String ack;
const SyncEvent({required this.type, required this.data, required this.ack}); const SyncEvent({required this.type, required this.data, required this.ack});

View File

@ -33,18 +33,12 @@ class HashService {
Future<void> hashAssets() async { Future<void> hashAssets() async {
final Stopwatch stopwatch = Stopwatch()..start(); final Stopwatch stopwatch = Stopwatch()..start();
// Sorted by backupSelection followed by isCloud // Sorted by backupSelection followed by isCloud
final localAlbums = await _localAlbumRepository.getAll(); final localAlbums = await _localAlbumRepository.getAll(
localAlbums.sort((a, b) { sortBy: {
final backupComparison = SortLocalAlbumsBy.backupSelection,
a.backupSelection.sortOrder.compareTo(b.backupSelection.sortOrder); SortLocalAlbumsBy.isIosSharedAlbum,
},
if (backupComparison != 0) { );
return backupComparison;
}
// Local albums come before iCloud albums
return (a.isIosSharedAlbum ? 1 : 0).compareTo(b.isIosSharedAlbum ? 1 : 0);
});
for (final album in localAlbums) { for (final album in localAlbums) {
final assetsToHash = final assetsToHash =
@ -96,13 +90,18 @@ class HashService {
final hashed = <LocalAsset>[]; final hashed = <LocalAsset>[];
final hashes = final hashes =
await _nativeSyncApi.hashPaths(toHash.map((e) => e.path).toList()); 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}",
);
for (final (index, hash) in hashes.indexed) { for (int i = 0; i < hashes.length; i++) {
final asset = toHash[index].asset; final hash = hashes[i];
final asset = toHash[i].asset;
if (hash?.length == 20) { if (hash?.length == 20) {
hashed.add(asset.copyWith(checksum: base64.encode(hash!))); hashed.add(asset.copyWith(checksum: base64.encode(hash!)));
} else { } else {
_log.warning("Failed to hash file ${asset.id}"); _log.warning("Failed to hash file for ${asset.id}");
} }
} }

View File

@ -66,7 +66,7 @@ class LocalSyncService {
if (_platform.isAndroid) { if (_platform.isAndroid) {
for (final album in dbAlbums) { for (final album in dbAlbums) {
final deviceIds = await _nativeSyncApi.getAssetIdsForAlbum(album.id); final deviceIds = await _nativeSyncApi.getAssetIdsForAlbum(album.id);
await _localAlbumRepository.syncAlbumDeletes(album.id, deviceIds); await _localAlbumRepository.syncDeletes(album.id, deviceIds);
} }
} }
@ -113,7 +113,7 @@ class LocalSyncService {
} }
final dbAlbums = final dbAlbums =
await _localAlbumRepository.getAll(sortBy: SortLocalAlbumsBy.id); await _localAlbumRepository.getAll(sortBy: {SortLocalAlbumsBy.id});
await diffSortedLists( await diffSortedLists(
dbAlbums, dbAlbums,
@ -252,7 +252,7 @@ class LocalSyncService {
.then((a) => a.toLocalAssets()) .then((a) => a.toLocalAssets())
: <LocalAsset>[]; : <LocalAsset>[];
final assetsInDb = dbAlbum.assetCount > 0 final assetsInDb = dbAlbum.assetCount > 0
? await _localAlbumRepository.getAssetsForAlbum(dbAlbum.id) ? await _localAlbumRepository.getAssets(dbAlbum.id)
: <LocalAsset>[]; : <LocalAsset>[];
if (deviceAlbum.assetCount == 0) { if (deviceAlbum.assetCount == 0) {
@ -373,6 +373,8 @@ extension on Iterable<PlatformAsset> {
updatedAt: e.updatedAt == null updatedAt: e.updatedAt == null
? DateTime.now() ? DateTime.now()
: DateTime.fromMillisecondsSinceEpoch(e.updatedAt! * 1000), : DateTime.fromMillisecondsSinceEpoch(e.updatedAt! * 1000),
width: e.width,
height: e.height,
durationInSeconds: e.durationInSeconds, durationInSeconds: e.durationInSeconds,
), ),
).toList(); ).toList();

View File

@ -8,6 +8,11 @@ import 'package:immich_mobile/domain/models/log.model.dart';
import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
/// Service responsible for handling application logging.
///
/// It listens to Dart's [Logger.root], buffers logs in memory (optionally),
/// writes them to a persistent [ILogRepository], and manages log levels
/// via [IStoreRepository]
class LogService { class LogService {
final ILogRepository _logRepository; final ILogRepository _logRepository;
final IStoreRepository _storeRepository; final IStoreRepository _storeRepository;
@ -18,19 +23,11 @@ class LogService {
/// This is useful when logging in quick succession, as it increases performance /// This is useful when logging in quick succession, as it increases performance
/// and reduces NAND wear. However, it may cause the logs to be lost in case of a crash / in isolates. /// and reduces NAND wear. However, it may cause the logs to be lost in case of a crash / in isolates.
final bool _shouldBuffer; final bool _shouldBuffer;
Timer? _flushTimer; Timer? _flushTimer;
late final StreamSubscription<LogRecord> _logSubscription; late final StreamSubscription<LogRecord> _logSubscription;
LogService._(
this._logRepository,
this._storeRepository,
this._shouldBuffer,
) {
// Listen to log messages and write them to the database
_logSubscription = Logger.root.onRecord.listen(_writeLogToDatabase);
}
static LogService? _instance; static LogService? _instance;
static LogService get I { static LogService get I {
if (_instance == null) { if (_instance == null) {
@ -44,10 +41,7 @@ class LogService {
required IStoreRepository storeRepository, required IStoreRepository storeRepository,
bool shouldBuffer = true, bool shouldBuffer = true,
}) async { }) async {
if (_instance != null) { _instance ??= await create(
return _instance!;
}
_instance = await create(
logRepository: logRepository, logRepository: logRepository,
storeRepository: storeRepository, storeRepository: storeRepository,
shouldBuffer: shouldBuffer, shouldBuffer: shouldBuffer,
@ -61,55 +55,28 @@ class LogService {
bool shouldBuffer = true, bool shouldBuffer = true,
}) async { }) async {
final instance = LogService._(logRepository, storeRepository, shouldBuffer); final instance = LogService._(logRepository, storeRepository, shouldBuffer);
// Truncate logs to 250
await logRepository.truncate(limit: kLogTruncateLimit); await logRepository.truncate(limit: kLogTruncateLimit);
// Get log level from store final level = await instance._storeRepository.tryGet(StoreKey.logLevel) ??
final level = await instance._storeRepository.tryGet(StoreKey.logLevel); LogLevel.info.index;
if (level != null) { Logger.root.level = Level.LEVELS.elementAtOrNull(level) ?? Level.INFO;
Logger.root.level = Level.LEVELS.elementAtOrNull(level) ?? Level.INFO;
}
return instance; return instance;
} }
Future<void> setlogLevel(LogLevel level) async { LogService._(
await _storeRepository.insert(StoreKey.logLevel, level.index); this._logRepository,
Logger.root.level = level.toLevel(); this._storeRepository,
this._shouldBuffer,
) {
_logSubscription = Logger.root.onRecord.listen(_handleLogRecord);
} }
Future<List<LogMessage>> getMessages() async { void _handleLogRecord(LogRecord r) {
final logsFromDb = await _logRepository.getAll();
if (_msgBuffer.isNotEmpty) {
return [..._msgBuffer.reversed, ...logsFromDb];
}
return logsFromDb;
}
Future<void> clearLogs() async {
_flushTimer?.cancel();
_flushTimer = null;
_msgBuffer.clear();
await _logRepository.deleteAll();
}
/// Flush pending log messages to persistent storage
void flush() {
if (_flushTimer == null) {
return;
}
_flushTimer!.cancel();
// TODO: Rename enable this after moving to sqlite - #16504
// await _flushBufferToDatabase();
}
Future<void> dispose() {
_flushTimer?.cancel();
_logSubscription.cancel();
return _flushBufferToDatabase();
}
void _writeLogToDatabase(LogRecord r) {
if (kDebugMode) { if (kDebugMode) {
debugPrint('[${r.level.name}] [${r.time}] ${r.message}'); debugPrint(
'[${r.level.name}] [${r.time}] [${r.loggerName}] ${r.message}'
'${r.error == null ? '' : '\nError: ${r.error}'}'
'${r.stackTrace == null ? '' : '\nStack: ${r.stackTrace}'}',
);
} }
final record = LogMessage( final record = LogMessage(
@ -125,14 +92,44 @@ class LogService {
_msgBuffer.add(record); _msgBuffer.add(record);
_flushTimer ??= Timer( _flushTimer ??= Timer(
const Duration(seconds: 5), const Duration(seconds: 5),
() => unawaited(_flushBufferToDatabase()), () => unawaited(flushBuffer()),
); );
} else { } else {
unawaited(_logRepository.insert(record)); unawaited(_logRepository.insert(record));
} }
} }
Future<void> _flushBufferToDatabase() async { Future<void> setLogLevel(LogLevel level) async {
await _storeRepository.insert(StoreKey.logLevel, level.index);
Logger.root.level = level.toLevel();
}
Future<List<LogMessage>> getMessages() async {
final logsFromDb = await _logRepository.getAll();
return [..._msgBuffer.reversed, ...logsFromDb];
}
Future<void> clearLogs() async {
_flushTimer?.cancel();
_flushTimer = null;
_msgBuffer.clear();
await _logRepository.deleteAll();
}
void flush() {
_flushTimer?.cancel();
// TODO: Rename enable this after moving to sqlite - #16504
// await _flushBufferToDatabase();
}
Future<void> dispose() {
_flushTimer?.cancel();
_logSubscription.cancel();
return flushBuffer();
}
// TOOD: Move this to private once Isar is removed
Future<void> flushBuffer() async {
_flushTimer = null; _flushTimer = null;
final buffer = [..._msgBuffer]; final buffer = [..._msgBuffer];
_msgBuffer.clear(); _msgBuffer.clear();

View File

@ -3,15 +3,17 @@ import 'dart:async';
import 'package:immich_mobile/domain/interfaces/store.interface.dart'; import 'package:immich_mobile/domain/interfaces/store.interface.dart';
import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/models/store.model.dart';
/// Provides access to a persistent key-value store with an in-memory cache.
/// Listens for repository changes to keep the cache updated.
class StoreService { class StoreService {
final IStoreRepository _storeRepository; final IStoreRepository _storeRepository;
final Map<int, dynamic> _cache = {}; /// In-memory cache. Keys are [StoreKey.id]
late final StreamSubscription<StoreUpdateEvent> _storeUpdateSubscription; final Map<int, Object?> _cache = {};
late final StreamSubscription<StoreDto> _storeUpdateSubscription;
StoreService._({ StoreService._({required IStoreRepository storeRepository})
required IStoreRepository storeRepository, : _storeRepository = storeRepository;
}) : _storeRepository = storeRepository;
// TODO: Temporary typedef to make minimal changes. Remove this and make the presentation layer access store through a provider // TODO: Temporary typedef to make minimal changes. Remove this and make the presentation layer access store through a provider
static StoreService? _instance; static StoreService? _instance;
@ -23,7 +25,6 @@ class StoreService {
} }
// TODO: Replace the implementation with the one from create after removing the typedef // TODO: Replace the implementation with the one from create after removing the typedef
/// Initializes the store with the given [storeRepository]
static Future<StoreService> init({ static Future<StoreService> init({
required IStoreRepository storeRepository, required IStoreRepository storeRepository,
}) async { }) async {
@ -31,7 +32,6 @@ class StoreService {
return _instance!; return _instance!;
} }
/// Initializes the store with the given [storeRepository]
static Future<StoreService> create({ static Future<StoreService> create({
required IStoreRepository storeRepository, required IStoreRepository storeRepository,
}) async { }) async {
@ -41,16 +41,14 @@ class StoreService {
return instance; return instance;
} }
/// Fills the cache with the values from the DB
Future<void> _populateCache() async { Future<void> _populateCache() async {
for (StoreKey key in StoreKey.values) { final storeValues = await _storeRepository.getAll();
final storeValue = await _storeRepository.tryGet(key); for (StoreDto storeValue in storeValues) {
_cache[key.id] = storeValue; _cache[storeValue.key.id] = storeValue.value;
} }
} }
/// Listens for changes in the DB and updates the cache StreamSubscription<StoreDto> _listenForChange() =>
StreamSubscription<StoreUpdateEvent> _listenForChange() =>
_storeRepository.watchAll().listen((event) { _storeRepository.watchAll().listen((event) {
_cache[event.key.id] = event.value; _cache[event.key.id] = event.value;
}); });
@ -61,11 +59,11 @@ class StoreService {
_cache.clear(); _cache.clear();
} }
/// Returns the stored value for the given key (possibly null) /// Returns the cached value for [key], or `null`
T? tryGet<T>(StoreKey<T> key) => _cache[key.id]; T? tryGet<T>(StoreKey<T> key) => _cache[key.id] as T?;
/// Returns the stored value for the given key or if null the [defaultValue] /// Returns the stored value for [key] or [defaultValue].
/// Throws a [StoreKeyNotFoundException] if both are null /// Throws [StoreKeyNotFoundException] if value and [defaultValue] are null.
T get<T>(StoreKey<T> key, [T? defaultValue]) { T get<T>(StoreKey<T> key, [T? defaultValue]) {
final value = tryGet(key) ?? defaultValue; final value = tryGet(key) ?? defaultValue;
if (value == null) { if (value == null) {
@ -74,23 +72,23 @@ class StoreService {
return value; return value;
} }
/// Asynchronously stores the value in the Store /// Stores the [value] for the [key]. Skips write if value hasn't changed.
Future<void> put<U extends StoreKey<T>, T>(U key, T value) async { Future<void> put<U extends StoreKey<T>, T>(U key, T value) async {
if (_cache[key.id] == value) return; if (_cache[key.id] == value) return;
await _storeRepository.insert(key, value); await _storeRepository.insert(key, value);
_cache[key.id] = value; _cache[key.id] = value;
} }
/// Watches a specific key for changes /// Returns a stream that emits the value for [key] on change.
Stream<T?> watch<T>(StoreKey<T> key) => _storeRepository.watch(key); Stream<T?> watch<T>(StoreKey<T> key) => _storeRepository.watch(key);
/// Removes the value asynchronously from the Store /// Removes the value for [key]
Future<void> delete<T>(StoreKey<T> key) async { Future<void> delete<T>(StoreKey<T> key) async {
await _storeRepository.delete(key); await _storeRepository.delete(key);
_cache.remove(key.id); _cache.remove(key.id);
} }
/// Clears all values from this store (cache and DB) /// Clears all values from thw store (cache and DB)
Future<void> clear() async { Future<void> clear() async {
await _storeRepository.deleteAll(); await _storeRepository.deleteAll();
_cache.clear(); _cache.clear();

View File

@ -1,5 +1,3 @@
// ignore_for_file: avoid-passing-async-when-sync-expected
import 'dart:async'; import 'dart:async';
import 'package:immich_mobile/domain/interfaces/sync_api.interface.dart'; import 'package:immich_mobile/domain/interfaces/sync_api.interface.dart';
@ -59,8 +57,7 @@ class SyncStreamService {
Future<void> _handleSyncData( Future<void> _handleSyncData(
SyncEntityType type, SyncEntityType type,
// ignore: avoid-dynamic Iterable<Object> data,
Iterable<dynamic> data,
) async { ) async {
_logger.fine("Processing sync data for $type of length ${data.length}"); _logger.fine("Processing sync data for $type of length ${data.length}");
switch (type) { switch (type) {
@ -84,6 +81,18 @@ class SyncStreamService {
return _syncStreamRepository.deletePartnerAssetsV1(data.cast()); return _syncStreamRepository.deletePartnerAssetsV1(data.cast());
case SyncEntityType.partnerAssetExifV1: case SyncEntityType.partnerAssetExifV1:
return _syncStreamRepository.updatePartnerAssetsExifV1(data.cast()); return _syncStreamRepository.updatePartnerAssetsExifV1(data.cast());
case SyncEntityType.albumV1:
return _syncStreamRepository.updateAlbumsV1(data.cast());
case SyncEntityType.albumDeleteV1:
return _syncStreamRepository.deleteAlbumsV1(data.cast());
// case SyncEntityType.albumAssetV1:
// return _syncStreamRepository.updateAlbumAssetsV1(data.cast());
// case SyncEntityType.albumAssetDeleteV1:
// return _syncStreamRepository.deleteAlbumAssetsV1(data.cast());
case SyncEntityType.albumUserV1:
return _syncStreamRepository.updateAlbumUsersV1(data.cast());
case SyncEntityType.albumUserDeleteV1:
return _syncStreamRepository.deleteAlbumUsersV1(data.cast());
default: default:
_logger.warning("Unknown sync data type: $type"); _logger.warning("Unknown sync data type: $type");
} }

View File

@ -0,0 +1,20 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/album_user.model.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
class AlbumUserEntity extends Table with DriftDefaultsMixin {
const AlbumUserEntity();
TextColumn get albumId =>
text().references(RemoteAlbumEntity, #id, onDelete: KeyAction.cascade)();
TextColumn get userId =>
text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
IntColumn get role => intEnum<AlbumUserRole>()();
@override
Set<Column> get primaryKey => {albumId, userId};
}

View File

@ -0,0 +1,602 @@
// dart format width=80
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:immich_mobile/infrastructure/entities/album_user.entity.drift.dart'
as i1;
import 'package:immich_mobile/domain/models/album_user.model.dart' as i2;
import 'package:immich_mobile/infrastructure/entities/album_user.entity.dart'
as i3;
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
as i4;
import 'package:drift/internal/modular.dart' as i5;
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
as i6;
typedef $$AlbumUserEntityTableCreateCompanionBuilder
= i1.AlbumUserEntityCompanion Function({
required String albumId,
required String userId,
required i2.AlbumUserRole role,
});
typedef $$AlbumUserEntityTableUpdateCompanionBuilder
= i1.AlbumUserEntityCompanion Function({
i0.Value<String> albumId,
i0.Value<String> userId,
i0.Value<i2.AlbumUserRole> role,
});
final class $$AlbumUserEntityTableReferences extends i0.BaseReferences<
i0.GeneratedDatabase, i1.$AlbumUserEntityTable, i1.AlbumUserEntityData> {
$$AlbumUserEntityTableReferences(
super.$_db, super.$_table, super.$_typedResult);
static i4.$RemoteAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) =>
i5.ReadDatabaseContainer(db)
.resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity')
.createAlias(i0.$_aliasNameGenerator(
i5.ReadDatabaseContainer(db)
.resultSet<i1.$AlbumUserEntityTable>('album_user_entity')
.albumId,
i5.ReadDatabaseContainer(db)
.resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity')
.id));
i4.$$RemoteAlbumEntityTableProcessedTableManager get albumId {
final $_column = $_itemColumn<String>('album_id')!;
final manager = i4
.$$RemoteAlbumEntityTableTableManager(
$_db,
i5.ReadDatabaseContainer($_db)
.resultSet<i4.$RemoteAlbumEntityTable>('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]));
}
static i6.$UserEntityTable _userIdTable(i0.GeneratedDatabase db) =>
i5.ReadDatabaseContainer(db)
.resultSet<i6.$UserEntityTable>('user_entity')
.createAlias(i0.$_aliasNameGenerator(
i5.ReadDatabaseContainer(db)
.resultSet<i1.$AlbumUserEntityTable>('album_user_entity')
.userId,
i5.ReadDatabaseContainer(db)
.resultSet<i6.$UserEntityTable>('user_entity')
.id));
i6.$$UserEntityTableProcessedTableManager get userId {
final $_column = $_itemColumn<String>('user_id')!;
final manager = i6
.$$UserEntityTableTableManager(
$_db,
i5.ReadDatabaseContainer($_db)
.resultSet<i6.$UserEntityTable>('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]));
}
}
class $$AlbumUserEntityTableFilterComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$AlbumUserEntityTable> {
$$AlbumUserEntityTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnWithTypeConverterFilters<i2.AlbumUserRole, i2.AlbumUserRole, int>
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<i4.$RemoteAlbumEntityTable>('remote_album_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i4.$$RemoteAlbumEntityTableFilterComposer(
$db: $db,
$table: i5.ReadDatabaseContainer($db)
.resultSet<i4.$RemoteAlbumEntityTable>('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<i6.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i6.$$UserEntityTableFilterComposer(
$db: $db,
$table: i5.ReadDatabaseContainer($db)
.resultSet<i6.$UserEntityTable>('user_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
}
class $$AlbumUserEntityTableOrderingComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$AlbumUserEntityTable> {
$$AlbumUserEntityTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnOrderings<int> get role => $composableBuilder(
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<i4.$RemoteAlbumEntityTable>('remote_album_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i4.$$RemoteAlbumEntityTableOrderingComposer(
$db: $db,
$table: i5.ReadDatabaseContainer($db)
.resultSet<i4.$RemoteAlbumEntityTable>(
'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<i6.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i6.$$UserEntityTableOrderingComposer(
$db: $db,
$table: i5.ReadDatabaseContainer($db)
.resultSet<i6.$UserEntityTable>('user_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
}
class $$AlbumUserEntityTableAnnotationComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$AlbumUserEntityTable> {
$$AlbumUserEntityTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.GeneratedColumnWithTypeConverter<i2.AlbumUserRole, int> get role =>
$composableBuilder(column: $table.role, builder: (column) => column);
i4.$$RemoteAlbumEntityTableAnnotationComposer get albumId {
final i4.$$RemoteAlbumEntityTableAnnotationComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.albumId,
referencedTable: i5.ReadDatabaseContainer($db)
.resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i4.$$RemoteAlbumEntityTableAnnotationComposer(
$db: $db,
$table: i5.ReadDatabaseContainer($db)
.resultSet<i4.$RemoteAlbumEntityTable>(
'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<i6.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i6.$$UserEntityTableAnnotationComposer(
$db: $db,
$table: i5.ReadDatabaseContainer($db)
.resultSet<i6.$UserEntityTable>('user_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
}
class $$AlbumUserEntityTableTableManager extends i0.RootTableManager<
i0.GeneratedDatabase,
i1.$AlbumUserEntityTable,
i1.AlbumUserEntityData,
i1.$$AlbumUserEntityTableFilterComposer,
i1.$$AlbumUserEntityTableOrderingComposer,
i1.$$AlbumUserEntityTableAnnotationComposer,
$$AlbumUserEntityTableCreateCompanionBuilder,
$$AlbumUserEntityTableUpdateCompanionBuilder,
(i1.AlbumUserEntityData, i1.$$AlbumUserEntityTableReferences),
i1.AlbumUserEntityData,
i0.PrefetchHooks Function({bool albumId, bool userId})> {
$$AlbumUserEntityTableTableManager(
i0.GeneratedDatabase db, i1.$AlbumUserEntityTable table)
: super(i0.TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
i1.$$AlbumUserEntityTableFilterComposer($db: db, $table: table),
createOrderingComposer: () =>
i1.$$AlbumUserEntityTableOrderingComposer($db: db, $table: table),
createComputedFieldComposer: () => i1
.$$AlbumUserEntityTableAnnotationComposer($db: db, $table: table),
updateCompanionCallback: ({
i0.Value<String> albumId = const i0.Value.absent(),
i0.Value<String> userId = const i0.Value.absent(),
i0.Value<i2.AlbumUserRole> role = const i0.Value.absent(),
}) =>
i1.AlbumUserEntityCompanion(
albumId: albumId,
userId: userId,
role: role,
),
createCompanionCallback: ({
required String albumId,
required String userId,
required i2.AlbumUserRole role,
}) =>
i1.AlbumUserEntityCompanion.insert(
albumId: albumId,
userId: userId,
role: role,
),
withReferenceMapper: (p0) => p0
.map((e) => (
e.readTable(table),
i1.$$AlbumUserEntityTableReferences(db, table, e)
))
.toList(),
prefetchHooksCallback: ({albumId = false, userId = 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 (albumId) {
state = state.withJoin(
currentTable: table,
currentColumn: table.albumId,
referencedTable:
i1.$$AlbumUserEntityTableReferences._albumIdTable(db),
referencedColumn: i1.$$AlbumUserEntityTableReferences
._albumIdTable(db)
.id,
) as T;
}
if (userId) {
state = state.withJoin(
currentTable: table,
currentColumn: table.userId,
referencedTable:
i1.$$AlbumUserEntityTableReferences._userIdTable(db),
referencedColumn:
i1.$$AlbumUserEntityTableReferences._userIdTable(db).id,
) as T;
}
return state;
},
getPrefetchedDataCallback: (items) async {
return [];
},
);
},
));
}
typedef $$AlbumUserEntityTableProcessedTableManager = i0.ProcessedTableManager<
i0.GeneratedDatabase,
i1.$AlbumUserEntityTable,
i1.AlbumUserEntityData,
i1.$$AlbumUserEntityTableFilterComposer,
i1.$$AlbumUserEntityTableOrderingComposer,
i1.$$AlbumUserEntityTableAnnotationComposer,
$$AlbumUserEntityTableCreateCompanionBuilder,
$$AlbumUserEntityTableUpdateCompanionBuilder,
(i1.AlbumUserEntityData, i1.$$AlbumUserEntityTableReferences),
i1.AlbumUserEntityData,
i0.PrefetchHooks Function({bool albumId, bool userId})>;
class $AlbumUserEntityTable extends i3.AlbumUserEntity
with i0.TableInfo<$AlbumUserEntityTable, i1.AlbumUserEntityData> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
$AlbumUserEntityTable(this.attachedDatabase, [this._alias]);
static const i0.VerificationMeta _albumIdMeta =
const i0.VerificationMeta('albumId');
@override
late final i0.GeneratedColumn<String> albumId = i0.GeneratedColumn<String>(
'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<String> userId = i0.GeneratedColumn<String>(
'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<i2.AlbumUserRole, int> role =
i0.GeneratedColumn<int>('role', aliasedName, false,
type: i0.DriftSqlType.int, requiredDuringInsert: true)
.withConverter<i2.AlbumUserRole>(
i1.$AlbumUserEntityTable.$converterrole);
@override
List<i0.GeneratedColumn> get $columns => [albumId, userId, role];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'album_user_entity';
@override
i0.VerificationContext validateIntegrity(
i0.Insertable<i1.AlbumUserEntityData> 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));
} else if (isInserting) {
context.missing(_albumIdMeta);
}
if (data.containsKey('user_id')) {
context.handle(_userIdMeta,
userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta));
} else if (isInserting) {
context.missing(_userIdMeta);
}
return context;
}
@override
Set<i0.GeneratedColumn> get $primaryKey => {albumId, userId};
@override
i1.AlbumUserEntityData map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.AlbumUserEntityData(
albumId: attachedDatabase.typeMapping
.read(i0.DriftSqlType.string, data['${effectivePrefix}album_id'])!,
userId: attachedDatabase.typeMapping
.read(i0.DriftSqlType.string, data['${effectivePrefix}user_id'])!,
role: i1.$AlbumUserEntityTable.$converterrole.fromSql(attachedDatabase
.typeMapping
.read(i0.DriftSqlType.int, data['${effectivePrefix}role'])!),
);
}
@override
$AlbumUserEntityTable createAlias(String alias) {
return $AlbumUserEntityTable(attachedDatabase, alias);
}
static i0.JsonTypeConverter2<i2.AlbumUserRole, int, int> $converterrole =
const i0.EnumIndexConverter<i2.AlbumUserRole>(i2.AlbumUserRole.values);
@override
bool get withoutRowId => true;
@override
bool get isStrict => true;
}
class AlbumUserEntityData extends i0.DataClass
implements i0.Insertable<i1.AlbumUserEntityData> {
final String albumId;
final String userId;
final i2.AlbumUserRole role;
const AlbumUserEntityData(
{required this.albumId, required this.userId, required this.role});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['album_id'] = i0.Variable<String>(albumId);
map['user_id'] = i0.Variable<String>(userId);
{
map['role'] =
i0.Variable<int>(i1.$AlbumUserEntityTable.$converterrole.toSql(role));
}
return map;
}
factory AlbumUserEntityData.fromJson(Map<String, dynamic> json,
{i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return AlbumUserEntityData(
albumId: serializer.fromJson<String>(json['albumId']),
userId: serializer.fromJson<String>(json['userId']),
role: i1.$AlbumUserEntityTable.$converterrole
.fromJson(serializer.fromJson<int>(json['role'])),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'albumId': serializer.toJson<String>(albumId),
'userId': serializer.toJson<String>(userId),
'role': serializer
.toJson<int>(i1.$AlbumUserEntityTable.$converterrole.toJson(role)),
};
}
i1.AlbumUserEntityData copyWith(
{String? albumId, String? userId, i2.AlbumUserRole? role}) =>
i1.AlbumUserEntityData(
albumId: albumId ?? this.albumId,
userId: userId ?? this.userId,
role: role ?? this.role,
);
AlbumUserEntityData copyWithCompanion(i1.AlbumUserEntityCompanion data) {
return AlbumUserEntityData(
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('AlbumUserEntityData(')
..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 i1.AlbumUserEntityData &&
other.albumId == this.albumId &&
other.userId == this.userId &&
other.role == this.role);
}
class AlbumUserEntityCompanion
extends i0.UpdateCompanion<i1.AlbumUserEntityData> {
final i0.Value<String> albumId;
final i0.Value<String> userId;
final i0.Value<i2.AlbumUserRole> role;
const AlbumUserEntityCompanion({
this.albumId = const i0.Value.absent(),
this.userId = const i0.Value.absent(),
this.role = const i0.Value.absent(),
});
AlbumUserEntityCompanion.insert({
required String albumId,
required String userId,
required i2.AlbumUserRole role,
}) : albumId = i0.Value(albumId),
userId = i0.Value(userId),
role = i0.Value(role);
static i0.Insertable<i1.AlbumUserEntityData> custom({
i0.Expression<String>? albumId,
i0.Expression<String>? userId,
i0.Expression<int>? role,
}) {
return i0.RawValuesInsertable({
if (albumId != null) 'album_id': albumId,
if (userId != null) 'user_id': userId,
if (role != null) 'role': role,
});
}
i1.AlbumUserEntityCompanion copyWith(
{i0.Value<String>? albumId,
i0.Value<String>? userId,
i0.Value<i2.AlbumUserRole>? role}) {
return i1.AlbumUserEntityCompanion(
albumId: albumId ?? this.albumId,
userId: userId ?? this.userId,
role: role ?? this.role,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (albumId.present) {
map['album_id'] = i0.Variable<String>(albumId.value);
}
if (userId.present) {
map['user_id'] = i0.Variable<String>(userId.value);
}
if (role.present) {
map['role'] = i0.Variable<int>(
i1.$AlbumUserEntityTable.$converterrole.toSql(role.value));
}
return map;
}
@override
String toString() {
return (StringBuffer('AlbumUserEntityCompanion(')
..write('albumId: $albumId, ')
..write('userId: $userId, ')
..write('role: $role')
..write(')'))
.toString();
}
}

View File

@ -14,6 +14,8 @@ typedef $$LocalAssetEntityTableCreateCompanionBuilder
required i2.AssetType type, required i2.AssetType type,
i0.Value<DateTime> createdAt, i0.Value<DateTime> createdAt,
i0.Value<DateTime> updatedAt, i0.Value<DateTime> updatedAt,
i0.Value<int?> width,
i0.Value<int?> height,
i0.Value<int?> durationInSeconds, i0.Value<int?> durationInSeconds,
required String id, required String id,
i0.Value<String?> checksum, i0.Value<String?> checksum,
@ -25,6 +27,8 @@ typedef $$LocalAssetEntityTableUpdateCompanionBuilder
i0.Value<i2.AssetType> type, i0.Value<i2.AssetType> type,
i0.Value<DateTime> createdAt, i0.Value<DateTime> createdAt,
i0.Value<DateTime> updatedAt, i0.Value<DateTime> updatedAt,
i0.Value<int?> width,
i0.Value<int?> height,
i0.Value<int?> durationInSeconds, i0.Value<int?> durationInSeconds,
i0.Value<String> id, i0.Value<String> id,
i0.Value<String?> checksum, i0.Value<String?> checksum,
@ -54,6 +58,12 @@ class $$LocalAssetEntityTableFilterComposer
i0.ColumnFilters<DateTime> get updatedAt => $composableBuilder( i0.ColumnFilters<DateTime> get updatedAt => $composableBuilder(
column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<int> get width => $composableBuilder(
column: $table.width, builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<int> get height => $composableBuilder(
column: $table.height, builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<int> get durationInSeconds => $composableBuilder( i0.ColumnFilters<int> get durationInSeconds => $composableBuilder(
column: $table.durationInSeconds, column: $table.durationInSeconds,
builder: (column) => i0.ColumnFilters(column)); builder: (column) => i0.ColumnFilters(column));
@ -91,6 +101,12 @@ class $$LocalAssetEntityTableOrderingComposer
column: $table.updatedAt, column: $table.updatedAt,
builder: (column) => i0.ColumnOrderings(column)); builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<int> get width => $composableBuilder(
column: $table.width, builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<int> get height => $composableBuilder(
column: $table.height, builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<int> get durationInSeconds => $composableBuilder( i0.ColumnOrderings<int> get durationInSeconds => $composableBuilder(
column: $table.durationInSeconds, column: $table.durationInSeconds,
builder: (column) => i0.ColumnOrderings(column)); builder: (column) => i0.ColumnOrderings(column));
@ -127,6 +143,12 @@ class $$LocalAssetEntityTableAnnotationComposer
i0.GeneratedColumn<DateTime> get updatedAt => i0.GeneratedColumn<DateTime> get updatedAt =>
$composableBuilder(column: $table.updatedAt, builder: (column) => column); $composableBuilder(column: $table.updatedAt, builder: (column) => column);
i0.GeneratedColumn<int> get width =>
$composableBuilder(column: $table.width, builder: (column) => column);
i0.GeneratedColumn<int> get height =>
$composableBuilder(column: $table.height, builder: (column) => column);
i0.GeneratedColumn<int> get durationInSeconds => $composableBuilder( i0.GeneratedColumn<int> get durationInSeconds => $composableBuilder(
column: $table.durationInSeconds, builder: (column) => column); column: $table.durationInSeconds, builder: (column) => column);
@ -173,6 +195,8 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
i0.Value<i2.AssetType> type = const i0.Value.absent(), i0.Value<i2.AssetType> type = const i0.Value.absent(),
i0.Value<DateTime> createdAt = const i0.Value.absent(), i0.Value<DateTime> createdAt = const i0.Value.absent(),
i0.Value<DateTime> updatedAt = const i0.Value.absent(), i0.Value<DateTime> updatedAt = const i0.Value.absent(),
i0.Value<int?> width = const i0.Value.absent(),
i0.Value<int?> height = const i0.Value.absent(),
i0.Value<int?> durationInSeconds = const i0.Value.absent(), i0.Value<int?> durationInSeconds = const i0.Value.absent(),
i0.Value<String> id = const i0.Value.absent(), i0.Value<String> id = const i0.Value.absent(),
i0.Value<String?> checksum = const i0.Value.absent(), i0.Value<String?> checksum = const i0.Value.absent(),
@ -183,6 +207,8 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
type: type, type: type,
createdAt: createdAt, createdAt: createdAt,
updatedAt: updatedAt, updatedAt: updatedAt,
width: width,
height: height,
durationInSeconds: durationInSeconds, durationInSeconds: durationInSeconds,
id: id, id: id,
checksum: checksum, checksum: checksum,
@ -193,6 +219,8 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
required i2.AssetType type, required i2.AssetType type,
i0.Value<DateTime> createdAt = const i0.Value.absent(), i0.Value<DateTime> createdAt = const i0.Value.absent(),
i0.Value<DateTime> updatedAt = const i0.Value.absent(), i0.Value<DateTime> updatedAt = const i0.Value.absent(),
i0.Value<int?> width = const i0.Value.absent(),
i0.Value<int?> height = const i0.Value.absent(),
i0.Value<int?> durationInSeconds = const i0.Value.absent(), i0.Value<int?> durationInSeconds = const i0.Value.absent(),
required String id, required String id,
i0.Value<String?> checksum = const i0.Value.absent(), i0.Value<String?> checksum = const i0.Value.absent(),
@ -203,6 +231,8 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
type: type, type: type,
createdAt: createdAt, createdAt: createdAt,
updatedAt: updatedAt, updatedAt: updatedAt,
width: width,
height: height,
durationInSeconds: durationInSeconds, durationInSeconds: durationInSeconds,
id: id, id: id,
checksum: checksum, checksum: checksum,
@ -268,6 +298,18 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
type: i0.DriftSqlType.dateTime, type: i0.DriftSqlType.dateTime,
requiredDuringInsert: false, requiredDuringInsert: false,
defaultValue: i4.currentDateAndTime); defaultValue: i4.currentDateAndTime);
static const i0.VerificationMeta _widthMeta =
const i0.VerificationMeta('width');
@override
late final i0.GeneratedColumn<int> width = i0.GeneratedColumn<int>(
'width', aliasedName, true,
type: i0.DriftSqlType.int, requiredDuringInsert: false);
static const i0.VerificationMeta _heightMeta =
const i0.VerificationMeta('height');
@override
late final i0.GeneratedColumn<int> height = i0.GeneratedColumn<int>(
'height', aliasedName, true,
type: i0.DriftSqlType.int, requiredDuringInsert: false);
static const i0.VerificationMeta _durationInSecondsMeta = static const i0.VerificationMeta _durationInSecondsMeta =
const i0.VerificationMeta('durationInSeconds'); const i0.VerificationMeta('durationInSeconds');
@override @override
@ -301,6 +343,8 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
type, type,
createdAt, createdAt,
updatedAt, updatedAt,
width,
height,
durationInSeconds, durationInSeconds,
id, id,
checksum, checksum,
@ -331,6 +375,14 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
context.handle(_updatedAtMeta, context.handle(_updatedAtMeta,
updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta));
} }
if (data.containsKey('width')) {
context.handle(
_widthMeta, width.isAcceptableOrUnknown(data['width']!, _widthMeta));
}
if (data.containsKey('height')) {
context.handle(_heightMeta,
height.isAcceptableOrUnknown(data['height']!, _heightMeta));
}
if (data.containsKey('duration_in_seconds')) { if (data.containsKey('duration_in_seconds')) {
context.handle( context.handle(
_durationInSecondsMeta, _durationInSecondsMeta,
@ -371,6 +423,10 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
updatedAt: attachedDatabase.typeMapping.read( updatedAt: attachedDatabase.typeMapping.read(
i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, 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( durationInSeconds: attachedDatabase.typeMapping.read(
i0.DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), i0.DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']),
id: attachedDatabase.typeMapping id: attachedDatabase.typeMapping
@ -401,6 +457,8 @@ class LocalAssetEntityData extends i0.DataClass
final i2.AssetType type; final i2.AssetType type;
final DateTime createdAt; final DateTime createdAt;
final DateTime updatedAt; final DateTime updatedAt;
final int? width;
final int? height;
final int? durationInSeconds; final int? durationInSeconds;
final String id; final String id;
final String? checksum; final String? checksum;
@ -410,6 +468,8 @@ class LocalAssetEntityData extends i0.DataClass
required this.type, required this.type,
required this.createdAt, required this.createdAt,
required this.updatedAt, required this.updatedAt,
this.width,
this.height,
this.durationInSeconds, this.durationInSeconds,
required this.id, required this.id,
this.checksum, this.checksum,
@ -424,6 +484,12 @@ class LocalAssetEntityData extends i0.DataClass
} }
map['created_at'] = i0.Variable<DateTime>(createdAt); map['created_at'] = i0.Variable<DateTime>(createdAt);
map['updated_at'] = i0.Variable<DateTime>(updatedAt); map['updated_at'] = i0.Variable<DateTime>(updatedAt);
if (!nullToAbsent || width != null) {
map['width'] = i0.Variable<int>(width);
}
if (!nullToAbsent || height != null) {
map['height'] = i0.Variable<int>(height);
}
if (!nullToAbsent || durationInSeconds != null) { if (!nullToAbsent || durationInSeconds != null) {
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds); map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds);
} }
@ -444,6 +510,8 @@ class LocalAssetEntityData extends i0.DataClass
.fromJson(serializer.fromJson<int>(json['type'])), .fromJson(serializer.fromJson<int>(json['type'])),
createdAt: serializer.fromJson<DateTime>(json['createdAt']), createdAt: serializer.fromJson<DateTime>(json['createdAt']),
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']), updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
width: serializer.fromJson<int?>(json['width']),
height: serializer.fromJson<int?>(json['height']),
durationInSeconds: serializer.fromJson<int?>(json['durationInSeconds']), durationInSeconds: serializer.fromJson<int?>(json['durationInSeconds']),
id: serializer.fromJson<String>(json['id']), id: serializer.fromJson<String>(json['id']),
checksum: serializer.fromJson<String?>(json['checksum']), checksum: serializer.fromJson<String?>(json['checksum']),
@ -459,6 +527,8 @@ class LocalAssetEntityData extends i0.DataClass
.toJson<int>(i1.$LocalAssetEntityTable.$convertertype.toJson(type)), .toJson<int>(i1.$LocalAssetEntityTable.$convertertype.toJson(type)),
'createdAt': serializer.toJson<DateTime>(createdAt), 'createdAt': serializer.toJson<DateTime>(createdAt),
'updatedAt': serializer.toJson<DateTime>(updatedAt), 'updatedAt': serializer.toJson<DateTime>(updatedAt),
'width': serializer.toJson<int?>(width),
'height': serializer.toJson<int?>(height),
'durationInSeconds': serializer.toJson<int?>(durationInSeconds), 'durationInSeconds': serializer.toJson<int?>(durationInSeconds),
'id': serializer.toJson<String>(id), 'id': serializer.toJson<String>(id),
'checksum': serializer.toJson<String?>(checksum), 'checksum': serializer.toJson<String?>(checksum),
@ -471,6 +541,8 @@ class LocalAssetEntityData extends i0.DataClass
i2.AssetType? type, i2.AssetType? type,
DateTime? createdAt, DateTime? createdAt,
DateTime? updatedAt, DateTime? updatedAt,
i0.Value<int?> width = const i0.Value.absent(),
i0.Value<int?> height = const i0.Value.absent(),
i0.Value<int?> durationInSeconds = const i0.Value.absent(), i0.Value<int?> durationInSeconds = const i0.Value.absent(),
String? id, String? id,
i0.Value<String?> checksum = const i0.Value.absent(), i0.Value<String?> checksum = const i0.Value.absent(),
@ -480,6 +552,8 @@ class LocalAssetEntityData extends i0.DataClass
type: type ?? this.type, type: type ?? this.type,
createdAt: createdAt ?? this.createdAt, createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt, updatedAt: updatedAt ?? this.updatedAt,
width: width.present ? width.value : this.width,
height: height.present ? height.value : this.height,
durationInSeconds: durationInSeconds.present durationInSeconds: durationInSeconds.present
? durationInSeconds.value ? durationInSeconds.value
: this.durationInSeconds, : this.durationInSeconds,
@ -493,6 +567,8 @@ class LocalAssetEntityData extends i0.DataClass
type: data.type.present ? data.type.value : this.type, type: data.type.present ? data.type.value : this.type,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
width: data.width.present ? data.width.value : this.width,
height: data.height.present ? data.height.value : this.height,
durationInSeconds: data.durationInSeconds.present durationInSeconds: data.durationInSeconds.present
? data.durationInSeconds.value ? data.durationInSeconds.value
: this.durationInSeconds, : this.durationInSeconds,
@ -510,6 +586,8 @@ class LocalAssetEntityData extends i0.DataClass
..write('type: $type, ') ..write('type: $type, ')
..write('createdAt: $createdAt, ') ..write('createdAt: $createdAt, ')
..write('updatedAt: $updatedAt, ') ..write('updatedAt: $updatedAt, ')
..write('width: $width, ')
..write('height: $height, ')
..write('durationInSeconds: $durationInSeconds, ') ..write('durationInSeconds: $durationInSeconds, ')
..write('id: $id, ') ..write('id: $id, ')
..write('checksum: $checksum, ') ..write('checksum: $checksum, ')
@ -519,8 +597,8 @@ class LocalAssetEntityData extends i0.DataClass
} }
@override @override
int get hashCode => Object.hash(name, type, createdAt, updatedAt, int get hashCode => Object.hash(name, type, createdAt, updatedAt, width,
durationInSeconds, id, checksum, isFavorite); height, durationInSeconds, id, checksum, isFavorite);
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
@ -529,6 +607,8 @@ class LocalAssetEntityData extends i0.DataClass
other.type == this.type && other.type == this.type &&
other.createdAt == this.createdAt && other.createdAt == this.createdAt &&
other.updatedAt == this.updatedAt && other.updatedAt == this.updatedAt &&
other.width == this.width &&
other.height == this.height &&
other.durationInSeconds == this.durationInSeconds && other.durationInSeconds == this.durationInSeconds &&
other.id == this.id && other.id == this.id &&
other.checksum == this.checksum && other.checksum == this.checksum &&
@ -541,6 +621,8 @@ class LocalAssetEntityCompanion
final i0.Value<i2.AssetType> type; final i0.Value<i2.AssetType> type;
final i0.Value<DateTime> createdAt; final i0.Value<DateTime> createdAt;
final i0.Value<DateTime> updatedAt; final i0.Value<DateTime> updatedAt;
final i0.Value<int?> width;
final i0.Value<int?> height;
final i0.Value<int?> durationInSeconds; final i0.Value<int?> durationInSeconds;
final i0.Value<String> id; final i0.Value<String> id;
final i0.Value<String?> checksum; final i0.Value<String?> checksum;
@ -550,6 +632,8 @@ class LocalAssetEntityCompanion
this.type = const i0.Value.absent(), this.type = const i0.Value.absent(),
this.createdAt = const i0.Value.absent(), this.createdAt = const i0.Value.absent(),
this.updatedAt = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(),
this.width = const i0.Value.absent(),
this.height = const i0.Value.absent(),
this.durationInSeconds = const i0.Value.absent(), this.durationInSeconds = const i0.Value.absent(),
this.id = const i0.Value.absent(), this.id = const i0.Value.absent(),
this.checksum = const i0.Value.absent(), this.checksum = const i0.Value.absent(),
@ -560,6 +644,8 @@ class LocalAssetEntityCompanion
required i2.AssetType type, required i2.AssetType type,
this.createdAt = const i0.Value.absent(), this.createdAt = const i0.Value.absent(),
this.updatedAt = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(),
this.width = const i0.Value.absent(),
this.height = const i0.Value.absent(),
this.durationInSeconds = const i0.Value.absent(), this.durationInSeconds = const i0.Value.absent(),
required String id, required String id,
this.checksum = const i0.Value.absent(), this.checksum = const i0.Value.absent(),
@ -572,6 +658,8 @@ class LocalAssetEntityCompanion
i0.Expression<int>? type, i0.Expression<int>? type,
i0.Expression<DateTime>? createdAt, i0.Expression<DateTime>? createdAt,
i0.Expression<DateTime>? updatedAt, i0.Expression<DateTime>? updatedAt,
i0.Expression<int>? width,
i0.Expression<int>? height,
i0.Expression<int>? durationInSeconds, i0.Expression<int>? durationInSeconds,
i0.Expression<String>? id, i0.Expression<String>? id,
i0.Expression<String>? checksum, i0.Expression<String>? checksum,
@ -582,6 +670,8 @@ class LocalAssetEntityCompanion
if (type != null) 'type': type, if (type != null) 'type': type,
if (createdAt != null) 'created_at': createdAt, if (createdAt != null) 'created_at': createdAt,
if (updatedAt != null) 'updated_at': updatedAt, if (updatedAt != null) 'updated_at': updatedAt,
if (width != null) 'width': width,
if (height != null) 'height': height,
if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds,
if (id != null) 'id': id, if (id != null) 'id': id,
if (checksum != null) 'checksum': checksum, if (checksum != null) 'checksum': checksum,
@ -594,6 +684,8 @@ class LocalAssetEntityCompanion
i0.Value<i2.AssetType>? type, i0.Value<i2.AssetType>? type,
i0.Value<DateTime>? createdAt, i0.Value<DateTime>? createdAt,
i0.Value<DateTime>? updatedAt, i0.Value<DateTime>? updatedAt,
i0.Value<int?>? width,
i0.Value<int?>? height,
i0.Value<int?>? durationInSeconds, i0.Value<int?>? durationInSeconds,
i0.Value<String>? id, i0.Value<String>? id,
i0.Value<String?>? checksum, i0.Value<String?>? checksum,
@ -603,6 +695,8 @@ class LocalAssetEntityCompanion
type: type ?? this.type, type: type ?? this.type,
createdAt: createdAt ?? this.createdAt, createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt, updatedAt: updatedAt ?? this.updatedAt,
width: width ?? this.width,
height: height ?? this.height,
durationInSeconds: durationInSeconds ?? this.durationInSeconds, durationInSeconds: durationInSeconds ?? this.durationInSeconds,
id: id ?? this.id, id: id ?? this.id,
checksum: checksum ?? this.checksum, checksum: checksum ?? this.checksum,
@ -626,6 +720,12 @@ class LocalAssetEntityCompanion
if (updatedAt.present) { if (updatedAt.present) {
map['updated_at'] = i0.Variable<DateTime>(updatedAt.value); map['updated_at'] = i0.Variable<DateTime>(updatedAt.value);
} }
if (width.present) {
map['width'] = i0.Variable<int>(width.value);
}
if (height.present) {
map['height'] = i0.Variable<int>(height.value);
}
if (durationInSeconds.present) { if (durationInSeconds.present) {
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds.value); map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds.value);
} }
@ -648,6 +748,8 @@ class LocalAssetEntityCompanion
..write('type: $type, ') ..write('type: $type, ')
..write('createdAt: $createdAt, ') ..write('createdAt: $createdAt, ')
..write('updatedAt: $updatedAt, ') ..write('updatedAt: $updatedAt, ')
..write('width: $width, ')
..write('height: $height, ')
..write('durationInSeconds: $durationInSeconds, ') ..write('durationInSeconds: $durationInSeconds, ')
..write('id: $id, ') ..write('id: $id, ')
..write('checksum: $checksum, ') ..write('checksum: $checksum, ')

View File

@ -0,0 +1,34 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
class RemoteAlbumEntity extends Table with DriftDefaultsMixin {
const RemoteAlbumEntity();
TextColumn get id => text()();
TextColumn get name => text()();
TextColumn get description => 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 thumbnailAssetId => text()
.references(RemoteAssetEntity, #id, onDelete: KeyAction.setNull)
.nullable()();
BoolColumn get isActivityEnabled =>
boolean().withDefault(const Constant(true))();
IntColumn get order => intEnum<AssetOrder>()();
@override
Set<Column> get primaryKey => {id};
}

View File

@ -0,0 +1,946 @@
// dart format width=80
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
as i1;
import 'package:immich_mobile/domain/models/album/album.model.dart' as i2;
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart'
as i3;
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4;
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
as i5;
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,
required String description,
i0.Value<DateTime> createdAt,
i0.Value<DateTime> updatedAt,
required String ownerId,
i0.Value<String?> thumbnailAssetId,
i0.Value<bool> isActivityEnabled,
required i2.AssetOrder order,
});
typedef $$RemoteAlbumEntityTableUpdateCompanionBuilder
= i1.RemoteAlbumEntityCompanion Function({
i0.Value<String> id,
i0.Value<String> name,
i0.Value<String> description,
i0.Value<DateTime> createdAt,
i0.Value<DateTime> updatedAt,
i0.Value<String> ownerId,
i0.Value<String?> thumbnailAssetId,
i0.Value<bool> isActivityEnabled,
i0.Value<i2.AssetOrder> order,
});
final class $$RemoteAlbumEntityTableReferences extends i0.BaseReferences<
i0.GeneratedDatabase,
i1.$RemoteAlbumEntityTable,
i1.RemoteAlbumEntityData> {
$$RemoteAlbumEntityTableReferences(
super.$_db, super.$_table, super.$_typedResult);
static i5.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) =>
i6.ReadDatabaseContainer(db)
.resultSet<i5.$UserEntityTable>('user_entity')
.createAlias(i0.$_aliasNameGenerator(
i6.ReadDatabaseContainer(db)
.resultSet<i1.$RemoteAlbumEntityTable>('remote_album_entity')
.ownerId,
i6.ReadDatabaseContainer(db)
.resultSet<i5.$UserEntityTable>('user_entity')
.id));
i5.$$UserEntityTableProcessedTableManager get ownerId {
final $_column = $_itemColumn<String>('owner_id')!;
final manager = i5
.$$UserEntityTableTableManager(
$_db,
i6.ReadDatabaseContainer($_db)
.resultSet<i5.$UserEntityTable>('user_entity'))
.filter((f) => f.id.sqlEquals($_column));
final item = $_typedResult.readTableOrNull(_ownerIdTable($_db));
if (item == null) return manager;
return i0.ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item]));
}
static i7.$RemoteAssetEntityTable _thumbnailAssetIdTable(
i0.GeneratedDatabase db) =>
i6.ReadDatabaseContainer(db)
.resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity')
.createAlias(i0.$_aliasNameGenerator(
i6.ReadDatabaseContainer(db)
.resultSet<i1.$RemoteAlbumEntityTable>('remote_album_entity')
.thumbnailAssetId,
i6.ReadDatabaseContainer(db)
.resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity')
.id));
i7.$$RemoteAssetEntityTableProcessedTableManager? get thumbnailAssetId {
final $_column = $_itemColumn<String>('thumbnail_asset_id');
if ($_column == null) return null;
final manager = i7
.$$RemoteAssetEntityTableTableManager(
$_db,
i6.ReadDatabaseContainer($_db)
.resultSet<i7.$RemoteAssetEntityTable>('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]));
}
}
class $$RemoteAlbumEntityTableFilterComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumEntityTable> {
$$RemoteAlbumEntityTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnFilters<String> get id => $composableBuilder(
column: $table.id, builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<String> get name => $composableBuilder(
column: $table.name, builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<String> get description => $composableBuilder(
column: $table.description,
builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt, builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<DateTime> get updatedAt => $composableBuilder(
column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<bool> get isActivityEnabled => $composableBuilder(
column: $table.isActivityEnabled,
builder: (column) => i0.ColumnFilters(column));
i0.ColumnWithTypeConverterFilters<i2.AssetOrder, i2.AssetOrder, int>
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<i5.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i5.$$UserEntityTableFilterComposer(
$db: $db,
$table: i6.ReadDatabaseContainer($db)
.resultSet<i5.$UserEntityTable>('user_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
i7.$$RemoteAssetEntityTableFilterComposer get thumbnailAssetId {
final i7.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.thumbnailAssetId,
referencedTable: i6.ReadDatabaseContainer($db)
.resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i7.$$RemoteAssetEntityTableFilterComposer(
$db: $db,
$table: i6.ReadDatabaseContainer($db)
.resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
}
class $$RemoteAlbumEntityTableOrderingComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumEntityTable> {
$$RemoteAlbumEntityTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnOrderings<String> get id => $composableBuilder(
column: $table.id, builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<String> get name => $composableBuilder(
column: $table.name, builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<String> get description => $composableBuilder(
column: $table.description,
builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt,
builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<DateTime> get updatedAt => $composableBuilder(
column: $table.updatedAt,
builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<bool> get isActivityEnabled => $composableBuilder(
column: $table.isActivityEnabled,
builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<int> get order => $composableBuilder(
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<i5.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i5.$$UserEntityTableOrderingComposer(
$db: $db,
$table: i6.ReadDatabaseContainer($db)
.resultSet<i5.$UserEntityTable>('user_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
i7.$$RemoteAssetEntityTableOrderingComposer get thumbnailAssetId {
final i7.$$RemoteAssetEntityTableOrderingComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.thumbnailAssetId,
referencedTable: i6.ReadDatabaseContainer($db)
.resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i7.$$RemoteAssetEntityTableOrderingComposer(
$db: $db,
$table: i6.ReadDatabaseContainer($db)
.resultSet<i7.$RemoteAssetEntityTable>(
'remote_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
}
class $$RemoteAlbumEntityTableAnnotationComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumEntityTable> {
$$RemoteAlbumEntityTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.GeneratedColumn<String> get id =>
$composableBuilder(column: $table.id, builder: (column) => column);
i0.GeneratedColumn<String> get name =>
$composableBuilder(column: $table.name, builder: (column) => column);
i0.GeneratedColumn<String> get description => $composableBuilder(
column: $table.description, builder: (column) => column);
i0.GeneratedColumn<DateTime> get createdAt =>
$composableBuilder(column: $table.createdAt, builder: (column) => column);
i0.GeneratedColumn<DateTime> get updatedAt =>
$composableBuilder(column: $table.updatedAt, builder: (column) => column);
i0.GeneratedColumn<bool> get isActivityEnabled => $composableBuilder(
column: $table.isActivityEnabled, builder: (column) => column);
i0.GeneratedColumnWithTypeConverter<i2.AssetOrder, int> 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<i5.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i5.$$UserEntityTableAnnotationComposer(
$db: $db,
$table: i6.ReadDatabaseContainer($db)
.resultSet<i5.$UserEntityTable>('user_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
i7.$$RemoteAssetEntityTableAnnotationComposer get thumbnailAssetId {
final i7.$$RemoteAssetEntityTableAnnotationComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.thumbnailAssetId,
referencedTable: i6.ReadDatabaseContainer($db)
.resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i7.$$RemoteAssetEntityTableAnnotationComposer(
$db: $db,
$table: i6.ReadDatabaseContainer($db)
.resultSet<i7.$RemoteAssetEntityTable>(
'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})> {
$$RemoteAlbumEntityTableTableManager(
i0.GeneratedDatabase db, i1.$RemoteAlbumEntityTable table)
: super(i0.TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
i1.$$RemoteAlbumEntityTableFilterComposer($db: db, $table: table),
createOrderingComposer: () => i1
.$$RemoteAlbumEntityTableOrderingComposer($db: db, $table: table),
createComputedFieldComposer: () =>
i1.$$RemoteAlbumEntityTableAnnotationComposer(
$db: db, $table: table),
updateCompanionCallback: ({
i0.Value<String> id = const i0.Value.absent(),
i0.Value<String> name = const i0.Value.absent(),
i0.Value<String> description = const i0.Value.absent(),
i0.Value<DateTime> createdAt = const i0.Value.absent(),
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
i0.Value<String> ownerId = const i0.Value.absent(),
i0.Value<String?> thumbnailAssetId = const i0.Value.absent(),
i0.Value<bool> isActivityEnabled = const i0.Value.absent(),
i0.Value<i2.AssetOrder> 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,
required String description,
i0.Value<DateTime> createdAt = const i0.Value.absent(),
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
required String ownerId,
i0.Value<String?> thumbnailAssetId = const i0.Value.absent(),
i0.Value<bool> isActivityEnabled = const i0.Value.absent(),
required i2.AssetOrder 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)
))
.toList(),
prefetchHooksCallback: ({ownerId = false, thumbnailAssetId = 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.$$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;
},
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})>;
class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity
with i0.TableInfo<$RemoteAlbumEntityTable, i1.RemoteAlbumEntityData> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
$RemoteAlbumEntityTable(this.attachedDatabase, [this._alias]);
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
@override
late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
'id', aliasedName, false,
type: i0.DriftSqlType.string, requiredDuringInsert: true);
static const i0.VerificationMeta _nameMeta =
const i0.VerificationMeta('name');
@override
late final i0.GeneratedColumn<String> name = i0.GeneratedColumn<String>(
'name', aliasedName, false,
type: i0.DriftSqlType.string, requiredDuringInsert: true);
static const i0.VerificationMeta _descriptionMeta =
const i0.VerificationMeta('description');
@override
late final i0.GeneratedColumn<String> description =
i0.GeneratedColumn<String>('description', aliasedName, false,
type: i0.DriftSqlType.string, requiredDuringInsert: true);
static const i0.VerificationMeta _createdAtMeta =
const i0.VerificationMeta('createdAt');
@override
late final i0.GeneratedColumn<DateTime> createdAt =
i0.GeneratedColumn<DateTime>('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<DateTime> updatedAt =
i0.GeneratedColumn<DateTime>('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<String> ownerId = i0.GeneratedColumn<String>(
'owner_id', aliasedName, false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'REFERENCES user_entity (id) ON DELETE CASCADE'));
static const i0.VerificationMeta _thumbnailAssetIdMeta =
const i0.VerificationMeta('thumbnailAssetId');
@override
late final i0.GeneratedColumn<String> thumbnailAssetId =
i0.GeneratedColumn<String>('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<bool> isActivityEnabled =
i0.GeneratedColumn<bool>('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<i2.AssetOrder, int> order =
i0.GeneratedColumn<int>('order', aliasedName, false,
type: i0.DriftSqlType.int, requiredDuringInsert: true)
.withConverter<i2.AssetOrder>(
i1.$RemoteAlbumEntityTable.$converterorder);
@override
List<i0.GeneratedColumn> 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
i0.VerificationContext validateIntegrity(
i0.Insertable<i1.RemoteAlbumEntityData> 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('name')) {
context.handle(
_nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta));
} else if (isInserting) {
context.missing(_nameMeta);
}
if (data.containsKey('description')) {
context.handle(
_descriptionMeta,
description.isAcceptableOrUnknown(
data['description']!, _descriptionMeta));
} else if (isInserting) {
context.missing(_descriptionMeta);
}
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('thumbnail_asset_id')) {
context.handle(
_thumbnailAssetIdMeta,
thumbnailAssetId.isAcceptableOrUnknown(
data['thumbnail_asset_id']!, _thumbnailAssetIdMeta));
}
if (data.containsKey('is_activity_enabled')) {
context.handle(
_isActivityEnabledMeta,
isActivityEnabled.isAcceptableOrUnknown(
data['is_activity_enabled']!, _isActivityEnabledMeta));
}
return context;
}
@override
Set<i0.GeneratedColumn> get $primaryKey => {id};
@override
i1.RemoteAlbumEntityData map(Map<String, dynamic> 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'])!,
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'])!,
thumbnailAssetId: attachedDatabase.typeMapping.read(
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'])!),
);
}
@override
$RemoteAlbumEntityTable createAlias(String alias) {
return $RemoteAlbumEntityTable(attachedDatabase, alias);
}
static i0.JsonTypeConverter2<i2.AssetOrder, int, int> $converterorder =
const i0.EnumIndexConverter<i2.AssetOrder>(i2.AssetOrder.values);
@override
bool get withoutRowId => true;
@override
bool get isStrict => true;
}
class RemoteAlbumEntityData extends i0.DataClass
implements i0.Insertable<i1.RemoteAlbumEntityData> {
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 i2.AssetOrder 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<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['id'] = i0.Variable<String>(id);
map['name'] = i0.Variable<String>(name);
map['description'] = i0.Variable<String>(description);
map['created_at'] = i0.Variable<DateTime>(createdAt);
map['updated_at'] = i0.Variable<DateTime>(updatedAt);
map['owner_id'] = i0.Variable<String>(ownerId);
if (!nullToAbsent || thumbnailAssetId != null) {
map['thumbnail_asset_id'] = i0.Variable<String>(thumbnailAssetId);
}
map['is_activity_enabled'] = i0.Variable<bool>(isActivityEnabled);
{
map['order'] = i0.Variable<int>(
i1.$RemoteAlbumEntityTable.$converterorder.toSql(order));
}
return map;
}
factory RemoteAlbumEntityData.fromJson(Map<String, dynamic> json,
{i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return RemoteAlbumEntityData(
id: serializer.fromJson<String>(json['id']),
name: serializer.fromJson<String>(json['name']),
description: serializer.fromJson<String>(json['description']),
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
ownerId: serializer.fromJson<String>(json['ownerId']),
thumbnailAssetId: serializer.fromJson<String?>(json['thumbnailAssetId']),
isActivityEnabled: serializer.fromJson<bool>(json['isActivityEnabled']),
order: i1.$RemoteAlbumEntityTable.$converterorder
.fromJson(serializer.fromJson<int>(json['order'])),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<String>(id),
'name': serializer.toJson<String>(name),
'description': serializer.toJson<String>(description),
'createdAt': serializer.toJson<DateTime>(createdAt),
'updatedAt': serializer.toJson<DateTime>(updatedAt),
'ownerId': serializer.toJson<String>(ownerId),
'thumbnailAssetId': serializer.toJson<String?>(thumbnailAssetId),
'isActivityEnabled': serializer.toJson<bool>(isActivityEnabled),
'order': serializer.toJson<int>(
i1.$RemoteAlbumEntityTable.$converterorder.toJson(order)),
};
}
i1.RemoteAlbumEntityData copyWith(
{String? id,
String? name,
String? description,
DateTime? createdAt,
DateTime? updatedAt,
String? ownerId,
i0.Value<String?> thumbnailAssetId = const i0.Value.absent(),
bool? isActivityEnabled,
i2.AssetOrder? 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,
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 i1.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 i0.UpdateCompanion<i1.RemoteAlbumEntityData> {
final i0.Value<String> id;
final i0.Value<String> name;
final i0.Value<String> description;
final i0.Value<DateTime> createdAt;
final i0.Value<DateTime> updatedAt;
final i0.Value<String> ownerId;
final i0.Value<String?> thumbnailAssetId;
final i0.Value<bool> isActivityEnabled;
final i0.Value<i2.AssetOrder> order;
const RemoteAlbumEntityCompanion({
this.id = const i0.Value.absent(),
this.name = const i0.Value.absent(),
this.description = const i0.Value.absent(),
this.createdAt = const i0.Value.absent(),
this.updatedAt = const i0.Value.absent(),
this.ownerId = const i0.Value.absent(),
this.thumbnailAssetId = const i0.Value.absent(),
this.isActivityEnabled = const i0.Value.absent(),
this.order = const i0.Value.absent(),
});
RemoteAlbumEntityCompanion.insert({
required String id,
required String name,
required String description,
this.createdAt = const i0.Value.absent(),
this.updatedAt = const i0.Value.absent(),
required String ownerId,
this.thumbnailAssetId = const i0.Value.absent(),
this.isActivityEnabled = const i0.Value.absent(),
required i2.AssetOrder order,
}) : id = i0.Value(id),
name = i0.Value(name),
description = i0.Value(description),
ownerId = i0.Value(ownerId),
order = i0.Value(order);
static i0.Insertable<i1.RemoteAlbumEntityData> custom({
i0.Expression<String>? id,
i0.Expression<String>? name,
i0.Expression<String>? description,
i0.Expression<DateTime>? createdAt,
i0.Expression<DateTime>? updatedAt,
i0.Expression<String>? ownerId,
i0.Expression<String>? thumbnailAssetId,
i0.Expression<bool>? isActivityEnabled,
i0.Expression<int>? order,
}) {
return i0.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,
});
}
i1.RemoteAlbumEntityCompanion copyWith(
{i0.Value<String>? id,
i0.Value<String>? name,
i0.Value<String>? description,
i0.Value<DateTime>? createdAt,
i0.Value<DateTime>? updatedAt,
i0.Value<String>? ownerId,
i0.Value<String?>? thumbnailAssetId,
i0.Value<bool>? isActivityEnabled,
i0.Value<i2.AssetOrder>? order}) {
return i1.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<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (id.present) {
map['id'] = i0.Variable<String>(id.value);
}
if (name.present) {
map['name'] = i0.Variable<String>(name.value);
}
if (description.present) {
map['description'] = i0.Variable<String>(description.value);
}
if (createdAt.present) {
map['created_at'] = i0.Variable<DateTime>(createdAt.value);
}
if (updatedAt.present) {
map['updated_at'] = i0.Variable<DateTime>(updatedAt.value);
}
if (ownerId.present) {
map['owner_id'] = i0.Variable<String>(ownerId.value);
}
if (thumbnailAssetId.present) {
map['thumbnail_asset_id'] = i0.Variable<String>(thumbnailAssetId.value);
}
if (isActivityEnabled.present) {
map['is_activity_enabled'] = i0.Variable<bool>(isActivityEnabled.value);
}
if (order.present) {
map['order'] = i0.Variable<int>(
i1.$RemoteAlbumEntityTable.$converterorder.toSql(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();
}
}

View File

@ -0,0 +1,17 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
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 albumId =>
text().references(RemoteAlbumEntity, #id, onDelete: KeyAction.cascade)();
@override
Set<Column> get primaryKey => {assetId, albumId};
}

View File

@ -0,0 +1,565 @@
// dart format width=80
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart'
as i1;
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.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/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<String> assetId,
i0.Value<String> albumId,
});
final class $$RemoteAlbumAssetEntityTableReferences extends i0.BaseReferences<
i0.GeneratedDatabase,
i1.$RemoteAlbumAssetEntityTable,
i1.RemoteAlbumAssetEntityData> {
$$RemoteAlbumAssetEntityTableReferences(
super.$_db, super.$_table, super.$_typedResult);
static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) =>
i4.ReadDatabaseContainer(db)
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity')
.createAlias(i0.$_aliasNameGenerator(
i4.ReadDatabaseContainer(db)
.resultSet<i1.$RemoteAlbumAssetEntityTable>(
'remote_album_asset_entity')
.assetId,
i4.ReadDatabaseContainer(db)
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity')
.id));
i3.$$RemoteAssetEntityTableProcessedTableManager get assetId {
final $_column = $_itemColumn<String>('asset_id')!;
final manager = i3
.$$RemoteAssetEntityTableTableManager(
$_db,
i4.ReadDatabaseContainer($_db)
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'))
.filter((f) => f.id.sqlEquals($_column));
final item = $_typedResult.readTableOrNull(_assetIdTable($_db));
if (item == null) return manager;
return i0.ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item]));
}
static i5.$RemoteAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) =>
i4.ReadDatabaseContainer(db)
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity')
.createAlias(i0.$_aliasNameGenerator(
i4.ReadDatabaseContainer(db)
.resultSet<i1.$RemoteAlbumAssetEntityTable>(
'remote_album_asset_entity')
.albumId,
i4.ReadDatabaseContainer(db)
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity')
.id));
i5.$$RemoteAlbumEntityTableProcessedTableManager get albumId {
final $_column = $_itemColumn<String>('album_id')!;
final manager = i5
.$$RemoteAlbumEntityTableTableManager(
$_db,
i4.ReadDatabaseContainer($_db)
.resultSet<i5.$RemoteAlbumEntityTable>('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]));
}
}
class $$RemoteAlbumAssetEntityTableFilterComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumAssetEntityTable> {
$$RemoteAlbumAssetEntityTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i3.$$RemoteAssetEntityTableFilterComposer get assetId {
final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.assetId,
referencedTable: i4.ReadDatabaseContainer($db)
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i3.$$RemoteAssetEntityTableFilterComposer(
$db: $db,
$table: i4.ReadDatabaseContainer($db)
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
i5.$$RemoteAlbumEntityTableFilterComposer get albumId {
final i5.$$RemoteAlbumEntityTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.albumId,
referencedTable: i4.ReadDatabaseContainer($db)
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i5.$$RemoteAlbumEntityTableFilterComposer(
$db: $db,
$table: i4.ReadDatabaseContainer($db)
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
}
class $$RemoteAlbumAssetEntityTableOrderingComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumAssetEntityTable> {
$$RemoteAlbumAssetEntityTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i3.$$RemoteAssetEntityTableOrderingComposer get assetId {
final i3.$$RemoteAssetEntityTableOrderingComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.assetId,
referencedTable: i4.ReadDatabaseContainer($db)
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i3.$$RemoteAssetEntityTableOrderingComposer(
$db: $db,
$table: i4.ReadDatabaseContainer($db)
.resultSet<i3.$RemoteAssetEntityTable>(
'remote_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
i5.$$RemoteAlbumEntityTableOrderingComposer get albumId {
final i5.$$RemoteAlbumEntityTableOrderingComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.albumId,
referencedTable: i4.ReadDatabaseContainer($db)
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i5.$$RemoteAlbumEntityTableOrderingComposer(
$db: $db,
$table: i4.ReadDatabaseContainer($db)
.resultSet<i5.$RemoteAlbumEntityTable>(
'remote_album_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
}
class $$RemoteAlbumAssetEntityTableAnnotationComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumAssetEntityTable> {
$$RemoteAlbumAssetEntityTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i3.$$RemoteAssetEntityTableAnnotationComposer get assetId {
final i3.$$RemoteAssetEntityTableAnnotationComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.assetId,
referencedTable: i4.ReadDatabaseContainer($db)
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i3.$$RemoteAssetEntityTableAnnotationComposer(
$db: $db,
$table: i4.ReadDatabaseContainer($db)
.resultSet<i3.$RemoteAssetEntityTable>(
'remote_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
i5.$$RemoteAlbumEntityTableAnnotationComposer get albumId {
final i5.$$RemoteAlbumEntityTableAnnotationComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.albumId,
referencedTable: i4.ReadDatabaseContainer($db)
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
i5.$$RemoteAlbumEntityTableAnnotationComposer(
$db: $db,
$table: i4.ReadDatabaseContainer($db)
.resultSet<i5.$RemoteAlbumEntityTable>(
'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})> {
$$RemoteAlbumAssetEntityTableTableManager(
i0.GeneratedDatabase db, i1.$RemoteAlbumAssetEntityTable table)
: super(i0.TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
i1.$$RemoteAlbumAssetEntityTableFilterComposer(
$db: db, $table: table),
createOrderingComposer: () =>
i1.$$RemoteAlbumAssetEntityTableOrderingComposer(
$db: db, $table: table),
createComputedFieldComposer: () =>
i1.$$RemoteAlbumAssetEntityTableAnnotationComposer(
$db: db, $table: table),
updateCompanionCallback: ({
i0.Value<String> assetId = const i0.Value.absent(),
i0.Value<String> 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)
))
.toList(),
prefetchHooksCallback: ({assetId = false, albumId = 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.$$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;
},
getPrefetchedDataCallback: (items) async {
return [];
},
);
},
));
}
typedef $$RemoteAlbumAssetEntityTableProcessedTableManager
= i0.ProcessedTableManager<
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 $RemoteAlbumAssetEntityTable extends i2.RemoteAlbumAssetEntity
with
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');
@override
late final i0.GeneratedColumn<String> assetId = i0.GeneratedColumn<String>(
'asset_id', aliasedName, false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'REFERENCES remote_asset_entity (id) ON DELETE CASCADE'));
static const i0.VerificationMeta _albumIdMeta =
const i0.VerificationMeta('albumId');
@override
late final i0.GeneratedColumn<String> albumId = i0.GeneratedColumn<String>(
'album_id', aliasedName, false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'REFERENCES remote_album_entity (id) ON DELETE CASCADE'));
@override
List<i0.GeneratedColumn> get $columns => [assetId, albumId];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'remote_album_asset_entity';
@override
i0.VerificationContext validateIntegrity(
i0.Insertable<i1.RemoteAlbumAssetEntityData> instance,
{bool isInserting = false}) {
final context = i0.VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('asset_id')) {
context.handle(_assetIdMeta,
assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta));
} else if (isInserting) {
context.missing(_assetIdMeta);
}
if (data.containsKey('album_id')) {
context.handle(_albumIdMeta,
albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta));
} else if (isInserting) {
context.missing(_albumIdMeta);
}
return context;
}
@override
Set<i0.GeneratedColumn> get $primaryKey => {assetId, albumId};
@override
i1.RemoteAlbumAssetEntityData map(Map<String, dynamic> 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'])!,
);
}
@override
$RemoteAlbumAssetEntityTable createAlias(String alias) {
return $RemoteAlbumAssetEntityTable(attachedDatabase, alias);
}
@override
bool get withoutRowId => true;
@override
bool get isStrict => true;
}
class RemoteAlbumAssetEntityData extends i0.DataClass
implements i0.Insertable<i1.RemoteAlbumAssetEntityData> {
final String assetId;
final String albumId;
const RemoteAlbumAssetEntityData(
{required this.assetId, required this.albumId});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['asset_id'] = i0.Variable<String>(assetId);
map['album_id'] = i0.Variable<String>(albumId);
return map;
}
factory RemoteAlbumAssetEntityData.fromJson(Map<String, dynamic> json,
{i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return RemoteAlbumAssetEntityData(
assetId: serializer.fromJson<String>(json['assetId']),
albumId: serializer.fromJson<String>(json['albumId']),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'assetId': serializer.toJson<String>(assetId),
'albumId': serializer.toJson<String>(albumId),
};
}
i1.RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) =>
i1.RemoteAlbumAssetEntityData(
assetId: assetId ?? this.assetId,
albumId: albumId ?? this.albumId,
);
RemoteAlbumAssetEntityData copyWithCompanion(
i1.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 i1.RemoteAlbumAssetEntityData &&
other.assetId == this.assetId &&
other.albumId == this.albumId);
}
class RemoteAlbumAssetEntityCompanion
extends i0.UpdateCompanion<i1.RemoteAlbumAssetEntityData> {
final i0.Value<String> assetId;
final i0.Value<String> albumId;
const RemoteAlbumAssetEntityCompanion({
this.assetId = const i0.Value.absent(),
this.albumId = const i0.Value.absent(),
});
RemoteAlbumAssetEntityCompanion.insert({
required String assetId,
required String albumId,
}) : assetId = i0.Value(assetId),
albumId = i0.Value(albumId);
static i0.Insertable<i1.RemoteAlbumAssetEntityData> custom({
i0.Expression<String>? assetId,
i0.Expression<String>? albumId,
}) {
return i0.RawValuesInsertable({
if (assetId != null) 'asset_id': assetId,
if (albumId != null) 'album_id': albumId,
});
}
i1.RemoteAlbumAssetEntityCompanion copyWith(
{i0.Value<String>? assetId, i0.Value<String>? albumId}) {
return i1.RemoteAlbumAssetEntityCompanion(
assetId: assetId ?? this.assetId,
albumId: albumId ?? this.albumId,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (assetId.present) {
map['asset_id'] = i0.Variable<String>(assetId.value);
}
if (albumId.present) {
map['album_id'] = i0.Variable<String>(albumId.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('RemoteAlbumAssetEntityCompanion(')
..write('assetId: $assetId, ')
..write('albumId: $albumId')
..write(')'))
.toString();
}
}

View File

@ -17,6 +17,8 @@ typedef $$RemoteAssetEntityTableCreateCompanionBuilder
required i2.AssetType type, required i2.AssetType type,
i0.Value<DateTime> createdAt, i0.Value<DateTime> createdAt,
i0.Value<DateTime> updatedAt, i0.Value<DateTime> updatedAt,
i0.Value<int?> width,
i0.Value<int?> height,
i0.Value<int?> durationInSeconds, i0.Value<int?> durationInSeconds,
required String id, required String id,
required String checksum, required String checksum,
@ -33,6 +35,8 @@ typedef $$RemoteAssetEntityTableUpdateCompanionBuilder
i0.Value<i2.AssetType> type, i0.Value<i2.AssetType> type,
i0.Value<DateTime> createdAt, i0.Value<DateTime> createdAt,
i0.Value<DateTime> updatedAt, i0.Value<DateTime> updatedAt,
i0.Value<int?> width,
i0.Value<int?> height,
i0.Value<int?> durationInSeconds, i0.Value<int?> durationInSeconds,
i0.Value<String> id, i0.Value<String> id,
i0.Value<String> checksum, i0.Value<String> checksum,
@ -101,6 +105,12 @@ class $$RemoteAssetEntityTableFilterComposer
i0.ColumnFilters<DateTime> get updatedAt => $composableBuilder( i0.ColumnFilters<DateTime> get updatedAt => $composableBuilder(
column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<int> get width => $composableBuilder(
column: $table.width, builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<int> get height => $composableBuilder(
column: $table.height, builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<int> get durationInSeconds => $composableBuilder( i0.ColumnFilters<int> get durationInSeconds => $composableBuilder(
column: $table.durationInSeconds, column: $table.durationInSeconds,
builder: (column) => i0.ColumnFilters(column)); builder: (column) => i0.ColumnFilters(column));
@ -175,6 +185,12 @@ class $$RemoteAssetEntityTableOrderingComposer
column: $table.updatedAt, column: $table.updatedAt,
builder: (column) => i0.ColumnOrderings(column)); builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<int> get width => $composableBuilder(
column: $table.width, builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<int> get height => $composableBuilder(
column: $table.height, builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<int> get durationInSeconds => $composableBuilder( i0.ColumnOrderings<int> get durationInSeconds => $composableBuilder(
column: $table.durationInSeconds, column: $table.durationInSeconds,
builder: (column) => i0.ColumnOrderings(column)); builder: (column) => i0.ColumnOrderings(column));
@ -249,6 +265,12 @@ class $$RemoteAssetEntityTableAnnotationComposer
i0.GeneratedColumn<DateTime> get updatedAt => i0.GeneratedColumn<DateTime> get updatedAt =>
$composableBuilder(column: $table.updatedAt, builder: (column) => column); $composableBuilder(column: $table.updatedAt, builder: (column) => column);
i0.GeneratedColumn<int> get width =>
$composableBuilder(column: $table.width, builder: (column) => column);
i0.GeneratedColumn<int> get height =>
$composableBuilder(column: $table.height, builder: (column) => column);
i0.GeneratedColumn<int> get durationInSeconds => $composableBuilder( i0.GeneratedColumn<int> get durationInSeconds => $composableBuilder(
column: $table.durationInSeconds, builder: (column) => column); column: $table.durationInSeconds, builder: (column) => column);
@ -326,6 +348,8 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
i0.Value<i2.AssetType> type = const i0.Value.absent(), i0.Value<i2.AssetType> type = const i0.Value.absent(),
i0.Value<DateTime> createdAt = const i0.Value.absent(), i0.Value<DateTime> createdAt = const i0.Value.absent(),
i0.Value<DateTime> updatedAt = const i0.Value.absent(), i0.Value<DateTime> updatedAt = const i0.Value.absent(),
i0.Value<int?> width = const i0.Value.absent(),
i0.Value<int?> height = const i0.Value.absent(),
i0.Value<int?> durationInSeconds = const i0.Value.absent(), i0.Value<int?> durationInSeconds = const i0.Value.absent(),
i0.Value<String> id = const i0.Value.absent(), i0.Value<String> id = const i0.Value.absent(),
i0.Value<String> checksum = const i0.Value.absent(), i0.Value<String> checksum = const i0.Value.absent(),
@ -341,6 +365,8 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
type: type, type: type,
createdAt: createdAt, createdAt: createdAt,
updatedAt: updatedAt, updatedAt: updatedAt,
width: width,
height: height,
durationInSeconds: durationInSeconds, durationInSeconds: durationInSeconds,
id: id, id: id,
checksum: checksum, checksum: checksum,
@ -356,6 +382,8 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
required i2.AssetType type, required i2.AssetType type,
i0.Value<DateTime> createdAt = const i0.Value.absent(), i0.Value<DateTime> createdAt = const i0.Value.absent(),
i0.Value<DateTime> updatedAt = const i0.Value.absent(), i0.Value<DateTime> updatedAt = const i0.Value.absent(),
i0.Value<int?> width = const i0.Value.absent(),
i0.Value<int?> height = const i0.Value.absent(),
i0.Value<int?> durationInSeconds = const i0.Value.absent(), i0.Value<int?> durationInSeconds = const i0.Value.absent(),
required String id, required String id,
required String checksum, required String checksum,
@ -371,6 +399,8 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
type: type, type: type,
createdAt: createdAt, createdAt: createdAt,
updatedAt: updatedAt, updatedAt: updatedAt,
width: width,
height: height,
durationInSeconds: durationInSeconds, durationInSeconds: durationInSeconds,
id: id, id: id,
checksum: checksum, checksum: checksum,
@ -477,6 +507,18 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
type: i0.DriftSqlType.dateTime, type: i0.DriftSqlType.dateTime,
requiredDuringInsert: false, requiredDuringInsert: false,
defaultValue: i4.currentDateAndTime); defaultValue: i4.currentDateAndTime);
static const i0.VerificationMeta _widthMeta =
const i0.VerificationMeta('width');
@override
late final i0.GeneratedColumn<int> width = i0.GeneratedColumn<int>(
'width', aliasedName, true,
type: i0.DriftSqlType.int, requiredDuringInsert: false);
static const i0.VerificationMeta _heightMeta =
const i0.VerificationMeta('height');
@override
late final i0.GeneratedColumn<int> height = i0.GeneratedColumn<int>(
'height', aliasedName, true,
type: i0.DriftSqlType.int, requiredDuringInsert: false);
static const i0.VerificationMeta _durationInSecondsMeta = static const i0.VerificationMeta _durationInSecondsMeta =
const i0.VerificationMeta('durationInSeconds'); const i0.VerificationMeta('durationInSeconds');
@override @override
@ -543,6 +585,8 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
type, type,
createdAt, createdAt,
updatedAt, updatedAt,
width,
height,
durationInSeconds, durationInSeconds,
id, id,
checksum, checksum,
@ -578,6 +622,14 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
context.handle(_updatedAtMeta, context.handle(_updatedAtMeta,
updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta));
} }
if (data.containsKey('width')) {
context.handle(
_widthMeta, width.isAcceptableOrUnknown(data['width']!, _widthMeta));
}
if (data.containsKey('height')) {
context.handle(_heightMeta,
height.isAcceptableOrUnknown(data['height']!, _heightMeta));
}
if (data.containsKey('duration_in_seconds')) { if (data.containsKey('duration_in_seconds')) {
context.handle( context.handle(
_durationInSecondsMeta, _durationInSecondsMeta,
@ -640,6 +692,10 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
updatedAt: attachedDatabase.typeMapping.read( updatedAt: attachedDatabase.typeMapping.read(
i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, 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( durationInSeconds: attachedDatabase.typeMapping.read(
i0.DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), i0.DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']),
id: attachedDatabase.typeMapping id: attachedDatabase.typeMapping
@ -684,6 +740,8 @@ class RemoteAssetEntityData extends i0.DataClass
final i2.AssetType type; final i2.AssetType type;
final DateTime createdAt; final DateTime createdAt;
final DateTime updatedAt; final DateTime updatedAt;
final int? width;
final int? height;
final int? durationInSeconds; final int? durationInSeconds;
final String id; final String id;
final String checksum; final String checksum;
@ -698,6 +756,8 @@ class RemoteAssetEntityData extends i0.DataClass
required this.type, required this.type,
required this.createdAt, required this.createdAt,
required this.updatedAt, required this.updatedAt,
this.width,
this.height,
this.durationInSeconds, this.durationInSeconds,
required this.id, required this.id,
required this.checksum, required this.checksum,
@ -717,6 +777,12 @@ class RemoteAssetEntityData extends i0.DataClass
} }
map['created_at'] = i0.Variable<DateTime>(createdAt); map['created_at'] = i0.Variable<DateTime>(createdAt);
map['updated_at'] = i0.Variable<DateTime>(updatedAt); map['updated_at'] = i0.Variable<DateTime>(updatedAt);
if (!nullToAbsent || width != null) {
map['width'] = i0.Variable<int>(width);
}
if (!nullToAbsent || height != null) {
map['height'] = i0.Variable<int>(height);
}
if (!nullToAbsent || durationInSeconds != null) { if (!nullToAbsent || durationInSeconds != null) {
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds); map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds);
} }
@ -749,6 +815,8 @@ class RemoteAssetEntityData extends i0.DataClass
.fromJson(serializer.fromJson<int>(json['type'])), .fromJson(serializer.fromJson<int>(json['type'])),
createdAt: serializer.fromJson<DateTime>(json['createdAt']), createdAt: serializer.fromJson<DateTime>(json['createdAt']),
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']), updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
width: serializer.fromJson<int?>(json['width']),
height: serializer.fromJson<int?>(json['height']),
durationInSeconds: serializer.fromJson<int?>(json['durationInSeconds']), durationInSeconds: serializer.fromJson<int?>(json['durationInSeconds']),
id: serializer.fromJson<String>(json['id']), id: serializer.fromJson<String>(json['id']),
checksum: serializer.fromJson<String>(json['checksum']), checksum: serializer.fromJson<String>(json['checksum']),
@ -770,6 +838,8 @@ class RemoteAssetEntityData extends i0.DataClass
.toJson<int>(i1.$RemoteAssetEntityTable.$convertertype.toJson(type)), .toJson<int>(i1.$RemoteAssetEntityTable.$convertertype.toJson(type)),
'createdAt': serializer.toJson<DateTime>(createdAt), 'createdAt': serializer.toJson<DateTime>(createdAt),
'updatedAt': serializer.toJson<DateTime>(updatedAt), 'updatedAt': serializer.toJson<DateTime>(updatedAt),
'width': serializer.toJson<int?>(width),
'height': serializer.toJson<int?>(height),
'durationInSeconds': serializer.toJson<int?>(durationInSeconds), 'durationInSeconds': serializer.toJson<int?>(durationInSeconds),
'id': serializer.toJson<String>(id), 'id': serializer.toJson<String>(id),
'checksum': serializer.toJson<String>(checksum), 'checksum': serializer.toJson<String>(checksum),
@ -788,6 +858,8 @@ class RemoteAssetEntityData extends i0.DataClass
i2.AssetType? type, i2.AssetType? type,
DateTime? createdAt, DateTime? createdAt,
DateTime? updatedAt, DateTime? updatedAt,
i0.Value<int?> width = const i0.Value.absent(),
i0.Value<int?> height = const i0.Value.absent(),
i0.Value<int?> durationInSeconds = const i0.Value.absent(), i0.Value<int?> durationInSeconds = const i0.Value.absent(),
String? id, String? id,
String? checksum, String? checksum,
@ -802,6 +874,8 @@ class RemoteAssetEntityData extends i0.DataClass
type: type ?? this.type, type: type ?? this.type,
createdAt: createdAt ?? this.createdAt, createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt, updatedAt: updatedAt ?? this.updatedAt,
width: width.present ? width.value : this.width,
height: height.present ? height.value : this.height,
durationInSeconds: durationInSeconds.present durationInSeconds: durationInSeconds.present
? durationInSeconds.value ? durationInSeconds.value
: this.durationInSeconds, : this.durationInSeconds,
@ -821,6 +895,8 @@ class RemoteAssetEntityData extends i0.DataClass
type: data.type.present ? data.type.value : this.type, type: data.type.present ? data.type.value : this.type,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
width: data.width.present ? data.width.value : this.width,
height: data.height.present ? data.height.value : this.height,
durationInSeconds: data.durationInSeconds.present durationInSeconds: data.durationInSeconds.present
? data.durationInSeconds.value ? data.durationInSeconds.value
: this.durationInSeconds, : this.durationInSeconds,
@ -846,6 +922,8 @@ class RemoteAssetEntityData extends i0.DataClass
..write('type: $type, ') ..write('type: $type, ')
..write('createdAt: $createdAt, ') ..write('createdAt: $createdAt, ')
..write('updatedAt: $updatedAt, ') ..write('updatedAt: $updatedAt, ')
..write('width: $width, ')
..write('height: $height, ')
..write('durationInSeconds: $durationInSeconds, ') ..write('durationInSeconds: $durationInSeconds, ')
..write('id: $id, ') ..write('id: $id, ')
..write('checksum: $checksum, ') ..write('checksum: $checksum, ')
@ -865,6 +943,8 @@ class RemoteAssetEntityData extends i0.DataClass
type, type,
createdAt, createdAt,
updatedAt, updatedAt,
width,
height,
durationInSeconds, durationInSeconds,
id, id,
checksum, checksum,
@ -882,6 +962,8 @@ class RemoteAssetEntityData extends i0.DataClass
other.type == this.type && other.type == this.type &&
other.createdAt == this.createdAt && other.createdAt == this.createdAt &&
other.updatedAt == this.updatedAt && other.updatedAt == this.updatedAt &&
other.width == this.width &&
other.height == this.height &&
other.durationInSeconds == this.durationInSeconds && other.durationInSeconds == this.durationInSeconds &&
other.id == this.id && other.id == this.id &&
other.checksum == this.checksum && other.checksum == this.checksum &&
@ -899,6 +981,8 @@ class RemoteAssetEntityCompanion
final i0.Value<i2.AssetType> type; final i0.Value<i2.AssetType> type;
final i0.Value<DateTime> createdAt; final i0.Value<DateTime> createdAt;
final i0.Value<DateTime> updatedAt; final i0.Value<DateTime> updatedAt;
final i0.Value<int?> width;
final i0.Value<int?> height;
final i0.Value<int?> durationInSeconds; final i0.Value<int?> durationInSeconds;
final i0.Value<String> id; final i0.Value<String> id;
final i0.Value<String> checksum; final i0.Value<String> checksum;
@ -913,6 +997,8 @@ class RemoteAssetEntityCompanion
this.type = const i0.Value.absent(), this.type = const i0.Value.absent(),
this.createdAt = const i0.Value.absent(), this.createdAt = const i0.Value.absent(),
this.updatedAt = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(),
this.width = const i0.Value.absent(),
this.height = const i0.Value.absent(),
this.durationInSeconds = const i0.Value.absent(), this.durationInSeconds = const i0.Value.absent(),
this.id = const i0.Value.absent(), this.id = const i0.Value.absent(),
this.checksum = const i0.Value.absent(), this.checksum = const i0.Value.absent(),
@ -928,6 +1014,8 @@ class RemoteAssetEntityCompanion
required i2.AssetType type, required i2.AssetType type,
this.createdAt = const i0.Value.absent(), this.createdAt = const i0.Value.absent(),
this.updatedAt = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(),
this.width = const i0.Value.absent(),
this.height = const i0.Value.absent(),
this.durationInSeconds = const i0.Value.absent(), this.durationInSeconds = const i0.Value.absent(),
required String id, required String id,
required String checksum, required String checksum,
@ -948,6 +1036,8 @@ class RemoteAssetEntityCompanion
i0.Expression<int>? type, i0.Expression<int>? type,
i0.Expression<DateTime>? createdAt, i0.Expression<DateTime>? createdAt,
i0.Expression<DateTime>? updatedAt, i0.Expression<DateTime>? updatedAt,
i0.Expression<int>? width,
i0.Expression<int>? height,
i0.Expression<int>? durationInSeconds, i0.Expression<int>? durationInSeconds,
i0.Expression<String>? id, i0.Expression<String>? id,
i0.Expression<String>? checksum, i0.Expression<String>? checksum,
@ -963,6 +1053,8 @@ class RemoteAssetEntityCompanion
if (type != null) 'type': type, if (type != null) 'type': type,
if (createdAt != null) 'created_at': createdAt, if (createdAt != null) 'created_at': createdAt,
if (updatedAt != null) 'updated_at': updatedAt, if (updatedAt != null) 'updated_at': updatedAt,
if (width != null) 'width': width,
if (height != null) 'height': height,
if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds,
if (id != null) 'id': id, if (id != null) 'id': id,
if (checksum != null) 'checksum': checksum, if (checksum != null) 'checksum': checksum,
@ -980,6 +1072,8 @@ class RemoteAssetEntityCompanion
i0.Value<i2.AssetType>? type, i0.Value<i2.AssetType>? type,
i0.Value<DateTime>? createdAt, i0.Value<DateTime>? createdAt,
i0.Value<DateTime>? updatedAt, i0.Value<DateTime>? updatedAt,
i0.Value<int?>? width,
i0.Value<int?>? height,
i0.Value<int?>? durationInSeconds, i0.Value<int?>? durationInSeconds,
i0.Value<String>? id, i0.Value<String>? id,
i0.Value<String>? checksum, i0.Value<String>? checksum,
@ -994,6 +1088,8 @@ class RemoteAssetEntityCompanion
type: type ?? this.type, type: type ?? this.type,
createdAt: createdAt ?? this.createdAt, createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt, updatedAt: updatedAt ?? this.updatedAt,
width: width ?? this.width,
height: height ?? this.height,
durationInSeconds: durationInSeconds ?? this.durationInSeconds, durationInSeconds: durationInSeconds ?? this.durationInSeconds,
id: id ?? this.id, id: id ?? this.id,
checksum: checksum ?? this.checksum, checksum: checksum ?? this.checksum,
@ -1022,6 +1118,12 @@ class RemoteAssetEntityCompanion
if (updatedAt.present) { if (updatedAt.present) {
map['updated_at'] = i0.Variable<DateTime>(updatedAt.value); map['updated_at'] = i0.Variable<DateTime>(updatedAt.value);
} }
if (width.present) {
map['width'] = i0.Variable<int>(width.value);
}
if (height.present) {
map['height'] = i0.Variable<int>(height.value);
}
if (durationInSeconds.present) { if (durationInSeconds.present) {
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds.value); map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds.value);
} }
@ -1061,6 +1163,8 @@ class RemoteAssetEntityCompanion
..write('type: $type, ') ..write('type: $type, ')
..write('createdAt: $createdAt, ') ..write('createdAt: $createdAt, ')
..write('updatedAt: $updatedAt, ') ..write('updatedAt: $updatedAt, ')
..write('width: $width, ')
..write('height: $height, ')
..write('durationInSeconds: $durationInSeconds, ') ..write('durationInSeconds: $durationInSeconds, ')
..write('id: $id, ') ..write('id: $id, ')
..write('checksum: $checksum, ') ..write('checksum: $checksum, ')

View File

@ -3,12 +3,15 @@ import 'dart:async';
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:drift_flutter/drift_flutter.dart'; import 'package:drift_flutter/drift_flutter.dart';
import 'package:immich_mobile/domain/interfaces/db.interface.dart'; import 'package:immich_mobile/domain/interfaces/db.interface.dart';
import 'package:immich_mobile/infrastructure/entities/album_user.entity.dart';
import 'package:immich_mobile/infrastructure/entities/exif.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.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/partner.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_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/partner.entity.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart';
import 'package:isar/isar.dart'; import 'package:isar/isar.dart';
@ -38,8 +41,11 @@ class IsarDatabaseRepository implements IDatabaseRepository {
LocalAlbumEntity, LocalAlbumEntity,
LocalAssetEntity, LocalAssetEntity,
LocalAlbumAssetEntity, LocalAlbumAssetEntity,
RemoteAssetEntity,
RemoteExifEntity, RemoteExifEntity,
RemoteAssetEntity,
RemoteAlbumEntity,
RemoteAlbumAssetEntity,
AlbumUserEntity,
], ],
) )
class Drift extends $Drift implements IDatabaseRepository { class Drift extends $Drift implements IDatabaseRepository {

View File

@ -17,6 +17,12 @@ import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.
as i7; as i7;
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart' import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'
as i8; as i8;
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
as i9;
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart'
as i10;
import 'package:immich_mobile/infrastructure/entities/album_user.entity.drift.dart'
as i11;
abstract class $Drift extends i0.GeneratedDatabase { abstract class $Drift extends i0.GeneratedDatabase {
$Drift(i0.QueryExecutor e) : super(e); $Drift(i0.QueryExecutor e) : super(e);
@ -36,6 +42,12 @@ abstract class $Drift extends i0.GeneratedDatabase {
i7.$RemoteAssetEntityTable(this); i7.$RemoteAssetEntityTable(this);
late final i8.$RemoteExifEntityTable remoteExifEntity = late final i8.$RemoteExifEntityTable remoteExifEntity =
i8.$RemoteExifEntityTable(this); i8.$RemoteExifEntityTable(this);
late final i9.$RemoteAlbumEntityTable remoteAlbumEntity =
i9.$RemoteAlbumEntityTable(this);
late final i10.$RemoteAlbumAssetEntityTable remoteAlbumAssetEntity =
i10.$RemoteAlbumAssetEntityTable(this);
late final i11.$AlbumUserEntityTable albumUserEntity =
i11.$AlbumUserEntityTable(this);
@override @override
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables => Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>(); allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
@ -49,6 +61,9 @@ abstract class $Drift extends i0.GeneratedDatabase {
localAlbumAssetEntity, localAlbumAssetEntity,
remoteAssetEntity, remoteAssetEntity,
remoteExifEntity, remoteExifEntity,
remoteAlbumEntity,
remoteAlbumAssetEntity,
albumUserEntity,
i5.idxLocalAssetChecksum, i5.idxLocalAssetChecksum,
i7.uQRemoteAssetOwnerChecksum i7.uQRemoteAssetOwnerChecksum
]; ];
@ -108,6 +123,50 @@ abstract class $Drift extends i0.GeneratedDatabase {
i0.TableUpdate('remote_exif_entity', kind: i0.UpdateKind.delete), i0.TableUpdate('remote_exif_entity', kind: i0.UpdateKind.delete),
], ],
), ),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName('user_entity',
limitUpdateKind: i0.UpdateKind.delete),
result: [
i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.delete),
],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName('remote_asset_entity',
limitUpdateKind: i0.UpdateKind.delete),
result: [
i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.update),
],
),
i0.WritePropagation(
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('album_user_entity', kind: i0.UpdateKind.delete),
],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName('user_entity',
limitUpdateKind: i0.UpdateKind.delete),
result: [
i0.TableUpdate('album_user_entity', kind: i0.UpdateKind.delete),
],
),
], ],
); );
@override @override
@ -134,4 +193,11 @@ class $DriftManager {
i7.$$RemoteAssetEntityTableTableManager(_db, _db.remoteAssetEntity); i7.$$RemoteAssetEntityTableTableManager(_db, _db.remoteAssetEntity);
i8.$$RemoteExifEntityTableTableManager get remoteExifEntity => i8.$$RemoteExifEntityTableTableManager get remoteExifEntity =>
i8.$$RemoteExifEntityTableTableManager(_db, _db.remoteExifEntity); i8.$$RemoteExifEntityTableTableManager(_db, _db.remoteExifEntity);
i9.$$RemoteAlbumEntityTableTableManager get remoteAlbumEntity =>
i9.$$RemoteAlbumEntityTableTableManager(_db, _db.remoteAlbumEntity);
i10.$$RemoteAlbumAssetEntityTableTableManager get remoteAlbumAssetEntity =>
i10.$$RemoteAlbumAssetEntityTableTableManager(
_db, _db.remoteAlbumAssetEntity);
i11.$$AlbumUserEntityTableTableManager get albumUserEntity =>
i11.$$AlbumUserEntityTableTableManager(_db, _db.albumUserEntity);
} }

View File

@ -17,7 +17,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
super(_db); super(_db);
@override @override
Future<List<LocalAlbum>> getAll({SortLocalAlbumsBy? sortBy}) { Future<List<LocalAlbum>> getAll({Set<SortLocalAlbumsBy> sortBy = const {}}) {
final assetCount = _db.localAlbumAssetEntity.assetId.count(); final assetCount = _db.localAlbumAssetEntity.assetId.count();
final query = _db.localAlbumEntity.select().join([ final query = _db.localAlbumEntity.select().join([
@ -30,9 +30,23 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
query query
..addColumns([assetCount]) ..addColumns([assetCount])
..groupBy([_db.localAlbumEntity.id]); ..groupBy([_db.localAlbumEntity.id]);
if (sortBy == SortLocalAlbumsBy.id) {
query.orderBy([OrderingTerm.asc(_db.localAlbumEntity.id)]); if (sortBy.isNotEmpty) {
final orderings = <OrderingTerm>[];
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),
},
);
}
query.orderBy(orderings);
} }
return query return query
.map( .map(
(row) => row (row) => row
@ -49,7 +63,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
// That is not the case on Android since asset <-> album has one:one mapping // That is not the case on Android since asset <-> album has one:one mapping
final assetsToDelete = _platform.isIOS final assetsToDelete = _platform.isIOS
? await _getUniqueAssetsInAlbum(albumId) ? await _getUniqueAssetsInAlbum(albumId)
: await getAssetIdsForAlbum(albumId); : await getAssetIds(albumId);
await _deleteAssets(assetsToDelete); await _deleteAssets(assetsToDelete);
// All the other assets that are still associated will be unlinked automatically on-cascade // All the other assets that are still associated will be unlinked automatically on-cascade
@ -59,7 +73,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
}); });
@override @override
Future<void> syncAlbumDeletes( Future<void> syncDeletes(
String albumId, String albumId,
Iterable<String> assetIdsToKeep, Iterable<String> assetIdsToKeep,
) async { ) async {
@ -172,7 +186,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
} }
@override @override
Future<List<LocalAsset>> getAssetsForAlbum(String albumId) { Future<List<LocalAsset>> getAssets(String albumId) {
final query = _db.localAlbumAssetEntity.select().join( final query = _db.localAlbumAssetEntity.select().join(
[ [
innerJoin( innerJoin(
@ -189,7 +203,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
} }
@override @override
Future<List<String>> getAssetIdsForAlbum(String albumId) { Future<List<String>> getAssetIds(String albumId) {
final query = _db.localAlbumAssetEntity.selectOnly() final query = _db.localAlbumAssetEntity.selectOnly()
..addColumns([_db.localAlbumAssetEntity.assetId]) ..addColumns([_db.localAlbumAssetEntity.assetId])
..where(_db.localAlbumAssetEntity.albumId.equals(albumId)); ..where(_db.localAlbumAssetEntity.albumId.equals(albumId));
@ -272,7 +286,9 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
type: asset.type, type: asset.type,
createdAt: Value(asset.createdAt), createdAt: Value(asset.createdAt),
updatedAt: Value(asset.updatedAt), updatedAt: Value(asset.updatedAt),
durationInSeconds: Value.absentIfNull(asset.durationInSeconds), width: Value(asset.width),
height: Value(asset.height),
durationInSeconds: Value(asset.durationInSeconds),
id: asset.id, id: asset.id,
checksum: const Value(null), checksum: const Value(null),
); );

View File

@ -22,7 +22,7 @@ class IsarStoreRepository extends IsarDatabaseRepository
} }
@override @override
Stream<StoreUpdateEvent> watchAll() { Stream<StoreDto<Object>> watchAll() {
return _db.storeValues return _db.storeValues
.filter() .filter()
.anyOf(validStoreKeys, (query, id) => query.idEqualTo(id)) .anyOf(validStoreKeys, (query, id) => query.idEqualTo(id))
@ -71,10 +71,11 @@ class IsarStoreRepository extends IsarDatabaseRepository
.asyncMap((e) async => e == null ? null : await _toValue(key, e)); .asyncMap((e) async => e == null ? null : await _toValue(key, e));
} }
Future<StoreUpdateEvent> _toUpdateEvent(StoreValue entity) async { Future<StoreDto<Object>> _toUpdateEvent(StoreValue entity) async {
final key = StoreKey.values.firstWhere((e) => e.id == entity.id); final key = StoreKey.values.firstWhere((e) => e.id == entity.id)
as StoreKey<Object>;
final value = await _toValue(key, entity); final value = await _toValue(key, entity);
return StoreUpdateEvent(key, value); return StoreDto(key, value);
} }
Future<T?> _toValue<T>(StoreKey<T> key, StoreValue entity) async => Future<T?> _toValue<T>(StoreKey<T> key, StoreValue entity) async =>
@ -107,4 +108,13 @@ class IsarStoreRepository extends IsarDatabaseRepository
}; };
return StoreValue(key.id, intValue: intValue, strValue: strValue); return StoreValue(key.id, intValue: intValue, strValue: strValue);
} }
@override
Future<List<StoreDto<Object>>> getAll() async {
final entities = await _db.storeValues
.filter()
.anyOf(validStoreKeys, (query, id) => query.idEqualTo(id))
.findAll();
return Future.wait(entities.map((e) => _toUpdateEvent(e)).toList());
}
} }

View File

@ -50,6 +50,9 @@ class SyncApiRepository implements ISyncApiRepository {
SyncRequestType.partnerAssetsV1, SyncRequestType.partnerAssetsV1,
SyncRequestType.assetExifsV1, SyncRequestType.assetExifsV1,
SyncRequestType.partnerAssetExifsV1, SyncRequestType.partnerAssetExifsV1,
SyncRequestType.albumsV1,
// SyncRequestType.albumAssetsV1,
SyncRequestType.albumUsersV1,
], ],
).toJson(), ).toJson(),
); );
@ -65,8 +68,7 @@ class SyncApiRepository implements ISyncApiRepository {
} }
try { try {
final response = final response = await client.send(request);
await client.send(request).timeout(const Duration(seconds: 20));
if (response.statusCode != 200) { if (response.statusCode != 200) {
final errorBody = await response.stream.bytesToString(); final errorBody = await response.stream.bytesToString();
@ -130,8 +132,7 @@ class SyncApiRepository implements ISyncApiRepository {
} }
} }
// ignore: avoid-dynamic const _kResponseMap = <SyncEntityType, Function(Object)>{
const _kResponseMap = <SyncEntityType, Function(dynamic)>{
SyncEntityType.userV1: SyncUserV1.fromJson, SyncEntityType.userV1: SyncUserV1.fromJson,
SyncEntityType.userDeleteV1: SyncUserDeleteV1.fromJson, SyncEntityType.userDeleteV1: SyncUserDeleteV1.fromJson,
SyncEntityType.partnerV1: SyncPartnerV1.fromJson, SyncEntityType.partnerV1: SyncPartnerV1.fromJson,
@ -142,4 +143,10 @@ const _kResponseMap = <SyncEntityType, Function(dynamic)>{
SyncEntityType.partnerAssetV1: SyncAssetV1.fromJson, SyncEntityType.partnerAssetV1: SyncAssetV1.fromJson,
SyncEntityType.partnerAssetDeleteV1: SyncAssetDeleteV1.fromJson, SyncEntityType.partnerAssetDeleteV1: SyncAssetDeleteV1.fromJson,
SyncEntityType.partnerAssetExifV1: SyncAssetExifV1.fromJson, SyncEntityType.partnerAssetExifV1: SyncAssetExifV1.fromJson,
SyncEntityType.albumV1: SyncAlbumV1.fromJson,
SyncEntityType.albumDeleteV1: SyncAlbumDeleteV1.fromJson,
// SyncEntityType.albumAssetV1: SyncAlbumAssetV1.fromJson,
// SyncEntityType.albumAssetDeleteV1: SyncAlbumAssetDeleteV1.fromJson,
SyncEntityType.albumUserV1: SyncAlbumUserV1.fromJson,
SyncEntityType.albumUserDeleteV1: SyncAlbumUserDeleteV1.fromJson,
}; };

View File

@ -3,12 +3,19 @@ import 'package:immich_mobile/domain/interfaces/sync_stream.interface.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/domain/models/album_user.model.dart';
import 'package:immich_mobile/infrastructure/entities/album_user.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_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:openapi/api.dart' as api show AssetVisibility; import 'package:openapi/api.dart' as api
import 'package:openapi/api.dart' hide AssetVisibility; show AssetVisibility, AssetOrder, AlbumUserRole;
import 'package:openapi/api.dart'
hide AssetVisibility, AssetOrder, AlbumUserRole;
class DriftSyncStreamRepository extends DriftDatabaseRepository class DriftSyncStreamRepository extends DriftDatabaseRepository
implements ISyncStreamRepository { implements ISyncStreamRepository {
@ -161,6 +168,135 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
} }
} }
@override
Future<void> updateAlbumsV1(Iterable<SyncAlbumV1> data) async {
try {
await _db.batch((batch) {
for (final album in data) {
final companion = RemoteAlbumEntityCompanion(
name: Value(album.name),
description: Value(album.description),
ownerId: Value(album.ownerId),
thumbnailAssetId: Value(album.thumbnailAssetId),
createdAt: Value(album.createdAt),
updatedAt: Value(album.updatedAt),
isActivityEnabled: Value(album.isActivityEnabled),
order: Value(album.order.toAssetOrder()),
);
batch.insert(
_db.remoteAlbumEntity,
companion.copyWith(id: Value(album.id)),
onConflict: DoUpdate((_) => companion),
);
}
});
} catch (e, s) {
_logger.severe('Error while processing updateAlbumsV1', e, s);
rethrow;
}
}
@override
Future<void> deleteAlbumsV1(Iterable<SyncAlbumDeleteV1> data) async {
try {
_db.batch((batch) {
for (final album in data) {
batch.delete(
_db.remoteAlbumEntity,
RemoteAlbumEntityCompanion(id: Value(album.albumId)),
);
}
});
} catch (e, s) {
_logger.severe('Error while processing deleteAlbumsV1', e, s);
rethrow;
}
}
// @override
// Future<void> updateAlbumAssetsV1(Iterable<SyncAlbumAssetV1> data) async {
// try {
// await _db.remoteAlbumAssetEntity.insertAll(
// data.map(
// (albumAsset) => RemoteAlbumAssetEntityCompanion.insert(
// albumId: albumAsset.albumId,
// assetId: albumAsset.assetId,
// ),
// ),
// mode: InsertMode.insertOrIgnore,
// );
// } catch (e, s) {
// _logger.severe('Error while processing updateAlbumAssetsV1', e, s);
// rethrow;
// }
// }
// @override
// Future<void> deleteAlbumAssetsV1(Iterable<SyncAlbumAssetDeleteV1> data) async {
// try {
// await _db.batch((batch) {
// for (final albumAsset in data) {
// batch.delete(
// _db.remoteAlbumAssetEntity,
// RemoteAlbumAssetEntityCompanion(
// albumId: Value(albumAsset.albumId),
// assetId: Value(albumAsset.assetId),
// ),
// );
// }
// });
// } catch (e, s) {
// _logger.severe('Error while processing deleteAlbumAssetsV1', e, s);
// rethrow;
// }
// }
@override
Future<void> updateAlbumUsersV1(Iterable<SyncAlbumUserV1> data) async {
try {
await _db.batch((batch) {
for (final albumUser in data) {
final companion = AlbumUserEntityCompanion(
role: Value(albumUser.role.toAlbumUserRole()),
);
batch.insert(
_db.albumUserEntity,
companion.copyWith(
albumId: Value(albumUser.albumId),
userId: Value(albumUser.userId),
),
onConflict: DoUpdate((_) => companion),
);
}
});
} catch (e, s) {
_logger.severe('Error while processing updateAlbumUsersV1', e, s);
rethrow;
}
}
@override
Future<void> deleteAlbumUsersV1(Iterable<SyncAlbumUserDeleteV1> data) async {
try {
await _db.batch((batch) {
for (final albumUser in data) {
batch.delete(
_db.albumUserEntity,
AlbumUserEntityCompanion(
albumId: Value(albumUser.albumId),
userId: Value(albumUser.userId),
),
);
}
});
} catch (e, s) {
_logger.severe('Error while processing deleteAlbumUsersV1', e, s);
rethrow;
}
}
Future<void> _updateAssetsV1(Iterable<SyncAssetV1> data) => Future<void> _updateAssetsV1(Iterable<SyncAssetV1> data) =>
_db.batch((batch) { _db.batch((batch) {
for (final asset in data) { for (final asset in data) {
@ -251,3 +387,19 @@ extension on api.AssetVisibility {
_ => throw Exception('Unknown AssetVisibility value: $this'), _ => throw Exception('Unknown AssetVisibility value: $this'),
}; };
} }
extension on api.AssetOrder {
AssetOrder toAssetOrder() => switch (this) {
api.AssetOrder.asc => AssetOrder.asc,
api.AssetOrder.desc => AssetOrder.desc,
_ => throw Exception('Unknown AssetOrder 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'),
};
}

View File

@ -6,5 +6,7 @@ mixin AssetEntityMixin on Table {
IntColumn get type => intEnum<AssetType>()(); IntColumn get type => intEnum<AssetType>()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
IntColumn get width => integer().nullable()();
IntColumn get height => integer().nullable()();
IntColumn get durationInSeconds => integer().nullable()(); IntColumn get durationInSeconds => integer().nullable()();
} }

View File

@ -21,4 +21,6 @@ abstract interface class IAssetApiRepository {
List<String> list, List<String> list,
AssetVisibilityEnum visibility, AssetVisibilityEnum visibility,
); );
Future<String?> getAssetMIMEType(String id);
} }

View File

@ -0,0 +1,27 @@
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/models/cast/cast_manager_state.dart';
abstract interface class ICastDestinationService {
Future<bool> initialize();
CastDestinationType getType();
void Function(bool)? onConnectionState;
void Function(Duration)? onCurrentTime;
void Function(Duration)? onDuration;
void Function(String)? onReceiverName;
void Function(CastState)? onCastState;
Future<void> connect(dynamic device);
void loadMedia(Asset asset, bool reload);
void play();
void pause();
void seekTo(Duration position);
void stop();
Future<void> disconnect();
Future<List<(String, CastDestinationType, dynamic)>> getDevices();
}

View File

@ -0,0 +1,9 @@
import 'package:immich_mobile/models/sessions/session_create_response.model.dart';
abstract interface class ISessionAPIRepository {
Future<SessionCreateResponse> createSession(
String deviceName,
String deviceOS, {
int? duration,
});
}

View File

@ -205,34 +205,30 @@ class ImmichAppState extends ConsumerState<ImmichApp>
overrides: [ overrides: [
localeProvider.overrideWithValue(context.locale), localeProvider.overrideWithValue(context.locale),
], ],
child: MaterialApp( child: MaterialApp.router(
title: 'Immich',
debugShowCheckedModeBanner: true,
localizationsDelegates: context.localizationDelegates, localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales, supportedLocales: context.supportedLocales,
locale: context.locale, locale: context.locale,
debugShowCheckedModeBanner: true, themeMode: ref.watch(immichThemeModeProvider),
home: MaterialApp.router( darkTheme: getThemeData(
title: 'Immich', colorScheme: immichTheme.dark,
debugShowCheckedModeBanner: false, locale: context.locale,
themeMode: ref.watch(immichThemeModeProvider), ),
darkTheme: getThemeData( theme: getThemeData(
colorScheme: immichTheme.dark, colorScheme: immichTheme.light,
locale: context.locale, locale: context.locale,
), ),
theme: getThemeData( routeInformationParser: router.defaultRouteParser(),
colorScheme: immichTheme.light, routerDelegate: router.delegate(
locale: context.locale, navigatorObservers: () => [AppNavigationObserver(ref: ref)],
),
routeInformationParser: router.defaultRouteParser(),
routerDelegate: router.delegate(
navigatorObservers: () => [AppNavigationObserver(ref: ref)],
),
), ),
), ),
); );
} }
} }
// ignore: prefer-single-widget-per-file
class MainWidget extends StatelessWidget { class MainWidget extends StatelessWidget {
const MainWidget({super.key}); const MainWidget({super.key});

View File

@ -1,5 +1,3 @@
import 'dart:typed_data';
import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/album.entity.dart';
class AvailableAlbum { class AvailableAlbum {
@ -16,7 +14,6 @@ class AvailableAlbum {
Album? album, Album? album,
int? assetCount, int? assetCount,
DateTime? lastBackup, DateTime? lastBackup,
Uint8List? thumbnailData,
}) { }) {
return AvailableAlbum( return AvailableAlbum(
album: album ?? this.album, album: album ?? this.album,

View File

@ -0,0 +1,88 @@
import 'dart:convert';
enum CastDestinationType { googleCast }
enum CastState { idle, playing, paused, buffering }
class CastManagerState {
final bool isCasting;
final String receiverName;
final CastState castState;
final Duration currentTime;
final Duration duration;
const CastManagerState({
required this.isCasting,
required this.receiverName,
required this.castState,
required this.currentTime,
required this.duration,
});
CastManagerState copyWith({
bool? isCasting,
String? receiverName,
CastState? castState,
Duration? currentTime,
Duration? duration,
}) {
return CastManagerState(
isCasting: isCasting ?? this.isCasting,
receiverName: receiverName ?? this.receiverName,
castState: castState ?? this.castState,
currentTime: currentTime ?? this.currentTime,
duration: duration ?? this.duration,
);
}
Map<String, dynamic> toMap() {
final result = <String, dynamic>{};
result.addAll({'isCasting': isCasting});
result.addAll({'receiverName': receiverName});
result.addAll({'castState': castState});
result.addAll({'currentTime': currentTime.inSeconds});
result.addAll({'duration': duration.inSeconds});
return result;
}
factory CastManagerState.fromMap(Map<String, dynamic> map) {
return CastManagerState(
isCasting: map['isCasting'] ?? false,
receiverName: map['receiverName'] ?? '',
castState: map['castState'] ?? CastState.idle,
currentTime: Duration(seconds: map['currentTime']?.toInt() ?? 0),
duration: Duration(seconds: map['duration']?.toInt() ?? 0),
);
}
String toJson() => json.encode(toMap());
factory CastManagerState.fromJson(String source) =>
CastManagerState.fromMap(json.decode(source));
@override
String toString() =>
'CastManagerState(isCasting: $isCasting, receiverName: $receiverName, castState: $castState, currentTime: $currentTime, duration: $duration)';
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is CastManagerState &&
other.isCasting == isCasting &&
other.receiverName == receiverName &&
other.castState == castState &&
other.currentTime == currentTime &&
other.duration == duration;
}
@override
int get hashCode =>
isCasting.hashCode ^
receiverName.hashCode ^
castState.hashCode ^
currentTime.hashCode ^
duration.hashCode;
}

View File

@ -1,5 +1,3 @@
// ignore_for_file: add-copy-with
sealed class MapEvent { sealed class MapEvent {
const MapEvent(); const MapEvent();
} }

View File

@ -0,0 +1,26 @@
class SessionCreateResponse {
final String createdAt;
final bool current;
final String deviceOS;
final String deviceType;
final String? expiresAt;
final String id;
final String token;
final String updatedAt;
const SessionCreateResponse({
required this.createdAt,
required this.current,
required this.deviceOS,
required this.deviceType,
this.expiresAt,
required this.id,
required this.token,
required this.updatedAt,
});
@override
String toString() {
return 'SessionCreateResponse[createdAt=$createdAt, current=$current, deviceOS=$deviceOS, deviceType=$deviceType, expiresAt=$expiresAt, id=$id, token=$token, updatedAt=$updatedAt]';
}
}

View File

@ -4,6 +4,7 @@ import 'dart:math';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart' hide Store; import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
@ -20,6 +21,7 @@ import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
import 'package:immich_mobile/providers/asset_viewer/show_controls.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/show_controls.provider.dart';
import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart'; import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart';
import 'package:immich_mobile/providers/cast.provider.dart';
import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/services/app_settings.service.dart';
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
@ -62,6 +64,7 @@ class GalleryViewerPage extends HookConsumerWidget {
final currentIndex = useValueNotifier(initialIndex); final currentIndex = useValueNotifier(initialIndex);
final loadAsset = renderList.loadAsset; final loadAsset = renderList.loadAsset;
final isPlayingMotionVideo = ref.watch(isPlayingMotionVideoProvider); final isPlayingMotionVideo = ref.watch(isPlayingMotionVideoProvider);
final isCasting = ref.watch(castProvider.select((c) => c.isCasting));
final videoPlayerKeys = useRef<Map<int, GlobalKey>>({}); final videoPlayerKeys = useRef<Map<int, GlobalKey>>({});
@ -118,6 +121,36 @@ class GalleryViewerPage extends HookConsumerWidget {
const [], const [],
); );
useEffect(() {
final asset = loadAsset(currentIndex.value);
if (asset.isRemote) {
ref.read(castProvider.notifier).loadMedia(asset, false);
} else {
if (isCasting) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (context.mounted) {
ref.read(castProvider.notifier).stop();
context.scaffoldMessenger.showSnackBar(
SnackBar(
duration: const Duration(seconds: 1),
content: Text(
"local_asset_cast_failed".tr(),
style: context.textTheme.bodyLarge?.copyWith(
color: context.primaryColor,
),
),
),
);
}
});
}
}
return null;
}, [
ref.watch(castProvider).isCasting,
]);
void showInfo() { void showInfo() {
final asset = ref.read(currentAssetProvider); final asset = ref.read(currentAssetProvider);
if (asset == null) { if (asset == null) {
@ -203,7 +236,7 @@ class GalleryViewerPage extends HookConsumerWidget {
}); });
}); });
PhotoViewGalleryPageOptions buildImage(BuildContext context, Asset asset) { PhotoViewGalleryPageOptions buildImage(Asset asset) {
return PhotoViewGalleryPageOptions( return PhotoViewGalleryPageOptions(
onDragStart: (_, details, __) { onDragStart: (_, details, __) {
localPosition.value = details.localPosition; localPosition.value = details.localPosition;
@ -279,7 +312,7 @@ class GalleryViewerPage extends HookConsumerWidget {
} }
if (newAsset.isImage && !isPlayingMotionVideo) { if (newAsset.isImage && !isPlayingMotionVideo) {
return buildImage(context, newAsset); return buildImage(newAsset);
} }
return buildVideo(context, newAsset); return buildVideo(context, newAsset);
} }
@ -356,6 +389,30 @@ class GalleryViewerPage extends HookConsumerWidget {
Timer(const Duration(milliseconds: 400), () { Timer(const Duration(milliseconds: 400), () {
precacheNextImage(next); precacheNextImage(next);
}); });
context.scaffoldMessenger.hideCurrentSnackBar();
// send image to casting if the server has it
if (newAsset.isRemote) {
ref.read(castProvider.notifier).loadMedia(newAsset, false);
} else {
context.scaffoldMessenger.clearSnackBars();
if (isCasting) {
ref.read(castProvider.notifier).stop();
context.scaffoldMessenger.showSnackBar(
SnackBar(
duration: const Duration(seconds: 2),
content: Text(
"local_asset_cast_failed".tr(),
style: context.textTheme.bodyLarge?.copyWith(
color: context.primaryColor,
),
),
),
);
}
}
}, },
builder: buildAsset, builder: buildAsset,
), ),

View File

@ -13,6 +13,7 @@ import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provider.dart'; import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provider.dart';
import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart'; import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart';
import 'package:immich_mobile/providers/cast.provider.dart';
import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/services/api.service.dart';
import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/services/app_settings.service.dart';
import 'package:immich_mobile/services/asset.service.dart'; import 'package:immich_mobile/services/asset.service.dart';
@ -60,6 +61,8 @@ class NativeVideoViewerPage extends HookConsumerWidget {
final log = Logger('NativeVideoViewerPage'); final log = Logger('NativeVideoViewerPage');
final isCasting = ref.watch(castProvider.select((c) => c.isCasting));
Future<VideoSource?> createSource() async { Future<VideoSource?> createSource() async {
if (!context.mounted) { if (!context.mounted) {
return null; return null;
@ -391,7 +394,7 @@ class NativeVideoViewerPage extends HookConsumerWidget {
// This remains under the video to avoid flickering // This remains under the video to avoid flickering
// For motion videos, this is the image portion of the asset // For motion videos, this is the image portion of the asset
Center(key: ValueKey(asset.id), child: image), Center(key: ValueKey(asset.id), child: image),
if (aspectRatio.value != null) if (aspectRatio.value != null && !isCasting)
Visibility.maintain( Visibility.maintain(
key: ValueKey(asset), key: ValueKey(asset),
visible: isVisible.value, visible: isVisible.value,

View File

@ -5,6 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/generated/intl_keys.g.dart';
import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/album/album.provider.dart';
import 'package:immich_mobile/providers/partner.provider.dart'; import 'package:immich_mobile/providers/partner.provider.dart';
import 'package:immich_mobile/providers/search/people.provider.dart'; import 'package:immich_mobile/providers/search/people.provider.dart';
@ -41,13 +42,13 @@ class LibraryPage extends ConsumerWidget {
ActionButton( ActionButton(
onPressed: () => context.pushRoute(const FavoritesRoute()), onPressed: () => context.pushRoute(const FavoritesRoute()),
icon: Icons.favorite_outline_rounded, icon: Icons.favorite_outline_rounded,
label: 'favorites'.tr(), label: IntlKeys.favorites.tr(),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
ActionButton( ActionButton(
onPressed: () => context.pushRoute(const ArchiveRoute()), onPressed: () => context.pushRoute(const ArchiveRoute()),
icon: Icons.archive_outlined, icon: Icons.archive_outlined,
label: 'archived'.tr(), label: IntlKeys.archived.tr(),
), ),
], ],
), ),
@ -58,14 +59,14 @@ class LibraryPage extends ConsumerWidget {
ActionButton( ActionButton(
onPressed: () => context.pushRoute(const SharedLinkRoute()), onPressed: () => context.pushRoute(const SharedLinkRoute()),
icon: Icons.link_outlined, icon: Icons.link_outlined,
label: 'shared_links'.tr(), label: IntlKeys.shared_links.tr(),
), ),
SizedBox(width: trashEnabled ? 8 : 0), SizedBox(width: trashEnabled ? 8 : 0),
trashEnabled trashEnabled
? ActionButton( ? ActionButton(
onPressed: () => context.pushRoute(const TrashRoute()), onPressed: () => context.pushRoute(const TrashRoute()),
icon: Icons.delete_outline_rounded, icon: Icons.delete_outline_rounded,
label: 'trash'.tr(), label: IntlKeys.trash.tr(),
) )
: const SizedBox.shrink(), : const SizedBox.shrink(),
], ],
@ -133,7 +134,7 @@ class QuickAccessButtons extends ConsumerWidget {
size: 26, size: 26,
), ),
title: Text( title: Text(
'folders'.tr(), IntlKeys.folders.tr(),
style: context.textTheme.titleSmall?.copyWith( style: context.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
@ -146,7 +147,7 @@ class QuickAccessButtons extends ConsumerWidget {
size: 26, size: 26,
), ),
title: Text( title: Text(
'locked_folder'.tr(), IntlKeys.locked_folder.tr(),
style: context.textTheme.titleSmall?.copyWith( style: context.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
@ -159,7 +160,7 @@ class QuickAccessButtons extends ConsumerWidget {
size: 26, size: 26,
), ),
title: Text( title: Text(
'partners'.tr(), IntlKeys.partners.tr(),
style: context.textTheme.titleSmall?.copyWith( style: context.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
@ -275,7 +276,7 @@ class PeopleCollectionCard extends ConsumerWidget {
Padding( Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Text( child: Text(
'people'.tr(), IntlKeys.people.tr(),
style: context.textTheme.titleSmall?.copyWith( style: context.textTheme.titleSmall?.copyWith(
color: context.colorScheme.onSurface, color: context.colorScheme.onSurface,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -343,7 +344,7 @@ class LocalAlbumsCollectionCard extends HookConsumerWidget {
Padding( Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Text( child: Text(
'on_this_device'.tr(), IntlKeys.on_this_device.tr(),
style: context.textTheme.titleSmall?.copyWith( style: context.textTheme.titleSmall?.copyWith(
color: context.colorScheme.onSurface, color: context.colorScheme.onSurface,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -404,7 +405,7 @@ class PlacesCollectionCard extends StatelessWidget {
Padding( Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Text( child: Text(
'places'.tr(), IntlKeys.places.tr(),
style: context.textTheme.titleSmall?.copyWith( style: context.textTheme.titleSmall?.copyWith(
color: context.colorScheme.onSurface, color: context.colorScheme.onSurface,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,

View File

@ -27,6 +27,7 @@ class PeopleCollectionPage extends HookConsumerWidget {
) { ) {
return showDialog( return showDialog(
context: context, context: context,
useRootNavigator: false,
builder: (BuildContext context) { builder: (BuildContext context) {
return PersonNameEditForm(personId: personId, personName: personName); return PersonNameEditForm(personId: personId, personName: personName);
}, },
@ -60,80 +61,84 @@ class PeopleCollectionPage extends HookConsumerWidget {
), ),
], ],
), ),
body: people.when( body: SafeArea(
data: (people) { child: people.when(
if (search.value != null) { data: (people) {
people = people.where((person) { if (search.value != null) {
return person.name people = people.where((person) {
.toLowerCase() return person.name
.contains(search.value!.toLowerCase()); .toLowerCase()
}).toList(); .contains(search.value!.toLowerCase());
} }).toList();
return GridView.builder( }
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( return GridView.builder(
crossAxisCount: isTablet ? 6 : 3, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
childAspectRatio: 0.85, crossAxisCount: isTablet ? 6 : 3,
mainAxisSpacing: isPortrait && isTablet ? 36 : 0, childAspectRatio: 0.85,
), mainAxisSpacing: isPortrait && isTablet ? 36 : 0,
padding: const EdgeInsets.symmetric(vertical: 32), ),
itemCount: people.length, padding: const EdgeInsets.symmetric(vertical: 32),
itemBuilder: (context, index) { itemCount: people.length,
final person = people[index]; itemBuilder: (context, index) {
final person = people[index];
return Column( return Column(
children: [ children: [
GestureDetector( GestureDetector(
onTap: () { onTap: () {
context.pushRoute( context.pushRoute(
PersonResultRoute( PersonResultRoute(
personId: person.id, personId: person.id,
personName: person.name, personName: person.name,
), ),
); );
}, },
child: Material( child: Material(
shape: const CircleBorder(side: BorderSide.none), shape: const CircleBorder(side: BorderSide.none),
elevation: 3, elevation: 3,
child: CircleAvatar( child: CircleAvatar(
maxRadius: isTablet ? 120 / 2 : 96 / 2, maxRadius: isTablet ? 120 / 2 : 96 / 2,
backgroundImage: NetworkImage( backgroundImage: NetworkImage(
getFaceThumbnailUrl(person.id), getFaceThumbnailUrl(person.id),
headers: headers, headers: headers,
),
), ),
), ),
), ),
), const SizedBox(height: 12),
const SizedBox(height: 12), GestureDetector(
GestureDetector( onTap: () =>
onTap: () => showNameEditModel(person.id, person.name), showNameEditModel(person.id, person.name),
child: person.name.isEmpty child: person.name.isEmpty
? Text( ? Text(
'add_a_name'.tr(), '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( style: context.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.w500, 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"),
error: (error, stack) => const Text("error"), loading: () => const Center(child: CircularProgressIndicator()),
loading: () => const Center(child: CircularProgressIndicator()), ),
), ),
); );
}, },

View File

@ -28,6 +28,7 @@ class PersonResultPage extends HookConsumerWidget {
showEditNameDialog() { showEditNameDialog() {
showDialog( showDialog(
context: context, context: context,
useRootNavigator: false,
builder: (BuildContext context) { builder: (BuildContext context) {
return PersonNameEditForm( return PersonNameEditForm(
personId: personId, personId: personId,

View File

@ -37,6 +37,8 @@ class PlatformAsset {
required this.type, required this.type,
this.createdAt, this.createdAt,
this.updatedAt, this.updatedAt,
this.width,
this.height,
required this.durationInSeconds, required this.durationInSeconds,
}); });
@ -50,6 +52,10 @@ class PlatformAsset {
int? updatedAt; int? updatedAt;
int? width;
int? height;
int durationInSeconds; int durationInSeconds;
List<Object?> _toList() { List<Object?> _toList() {
@ -59,6 +65,8 @@ class PlatformAsset {
type, type,
createdAt, createdAt,
updatedAt, updatedAt,
width,
height,
durationInSeconds, durationInSeconds,
]; ];
} }
@ -75,7 +83,9 @@ class PlatformAsset {
type: result[2]! as int, type: result[2]! as int,
createdAt: result[3] as int?, createdAt: result[3] as int?,
updatedAt: result[4] as int?, updatedAt: result[4] as int?,
durationInSeconds: result[5]! as int, width: result[5] as int?,
height: result[6] as int?,
durationInSeconds: result[7]! as int,
); );
} }

View File

@ -1,5 +1,3 @@
// ignore_for_file: avoid-local-functions
import 'dart:async'; import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
@ -63,8 +61,10 @@ final _features = [
icon: Icons.delete_sweep_rounded, icon: Icons.delete_sweep_rounded,
onTap: (_, ref) async { onTap: (_, ref) async {
final db = ref.read(driftProvider); final db = ref.read(driftProvider);
await db.remoteAssetEntity.deleteAll();
await db.remoteExifEntity.deleteAll(); await db.remoteExifEntity.deleteAll();
await db.remoteAssetEntity.deleteAll();
await db.remoteAlbumEntity.deleteAll();
await db.remoteAlbumAssetEntity.deleteAll();
}, },
), ),
_Feature( _Feature(
@ -142,7 +142,6 @@ class _Feature {
final Future<void> Function(BuildContext, WidgetRef _) onTap; final Future<void> Function(BuildContext, WidgetRef _) onTap;
} }
// ignore: prefer-single-widget-per-file
class _DevLogs extends StatelessWidget { class _DevLogs extends StatelessWidget {
const _DevLogs(); const _DevLogs();
@ -170,7 +169,6 @@ class _DevLogs extends StatelessWidget {
builder: (_, logMessages) { builder: (_, logMessages) {
return ListView.separated( return ListView.separated(
itemBuilder: (ctx, index) { itemBuilder: (ctx, index) {
// ignore: avoid-unsafe-collection-methods
final logMessage = logMessages.data![index]; final logMessage = logMessages.data![index];
return ListTile( return ListTile(
title: Text( title: Text(

View File

@ -1,5 +1,3 @@
// ignore_for_file: prefer-single-widget-per-file
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -132,6 +130,10 @@ final _remoteStats = [
name: 'Exif Entities', name: 'Exif Entities',
load: (db) => db.managers.remoteExifEntity.count(), load: (db) => db.managers.remoteExifEntity.count(),
), ),
_Stat(
name: 'Remote Albums',
load: (db) => db.managers.remoteAlbumEntity.count(),
),
]; ];
@RoutePage() @RoutePage()

View File

@ -5,4 +5,4 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'api.provider.g.dart'; part 'api.provider.g.dart';
@Riverpod(keepAlive: true) @Riverpod(keepAlive: true)
ApiService apiService(Ref ref) => ApiService(); ApiService apiService(Ref _) => ApiService();

View File

@ -6,7 +6,7 @@ part of 'api.provider.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$apiServiceHash() => r'93a7e3b4d3004741abc3061c4688239c3a72f9c4'; String _$apiServiceHash() => r'187a7de59b064fab1104c23717f18ce0ae3e426c';
/// See also [apiService]. /// See also [apiService].
@ProviderFor(apiService) @ProviderFor(apiService)

View File

@ -5,4 +5,4 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'app_settings.provider.g.dart'; part 'app_settings.provider.g.dart';
@Riverpod(keepAlive: true) @Riverpod(keepAlive: true)
AppSettingsService appSettingsService(Ref ref) => AppSettingsService(); AppSettingsService appSettingsService(Ref _) => AppSettingsService();

View File

@ -7,7 +7,7 @@ part of 'app_settings.provider.dart';
// ************************************************************************** // **************************************************************************
String _$appSettingsServiceHash() => String _$appSettingsServiceHash() =>
r'3736e0d384ec7b1f896938589656dd6eb1552d60'; r'2aa16d76a8df869c39486325efc1d08b2d2c284c';
/// See also [appSettingsService]. /// See also [appSettingsService].
@ProviderFor(appSettingsService) @ProviderFor(appSettingsService)

View File

@ -39,6 +39,6 @@ final assetStackStateProvider = StateNotifierProvider.autoDispose
); );
@riverpod @riverpod
int assetStackIndex(Ref ref, Asset asset) { int assetStackIndex(Ref _) {
return -1; return -1;
} }

View File

@ -6,155 +6,22 @@ part of 'asset_stack.provider.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$assetStackIndexHash() => r'38b4b0116e3e4592620b118ae01cf89b77da9cfe'; String _$assetStackIndexHash() => r'086ddb782e3eb38b80d755666fe35be8fe7322d7';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
/// See also [assetStackIndex]. /// See also [assetStackIndex].
@ProviderFor(assetStackIndex) @ProviderFor(assetStackIndex)
const assetStackIndexProvider = AssetStackIndexFamily(); final assetStackIndexProvider = AutoDisposeProvider<int>.internal(
assetStackIndex,
/// See also [assetStackIndex]. name: r'assetStackIndexProvider',
class AssetStackIndexFamily extends Family<int> { debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
/// See also [assetStackIndex]. ? null
const AssetStackIndexFamily(); : _$assetStackIndexHash,
dependencies: null,
/// See also [assetStackIndex]. allTransitiveDependencies: null,
AssetStackIndexProvider call( );
Asset asset,
) {
return AssetStackIndexProvider(
asset,
);
}
@override
AssetStackIndexProvider getProviderOverride(
covariant AssetStackIndexProvider provider,
) {
return call(
provider.asset,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'assetStackIndexProvider';
}
/// See also [assetStackIndex].
class AssetStackIndexProvider extends AutoDisposeProvider<int> {
/// See also [assetStackIndex].
AssetStackIndexProvider(
Asset asset,
) : this._internal(
(ref) => assetStackIndex(
ref as AssetStackIndexRef,
asset,
),
from: assetStackIndexProvider,
name: r'assetStackIndexProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$assetStackIndexHash,
dependencies: AssetStackIndexFamily._dependencies,
allTransitiveDependencies:
AssetStackIndexFamily._allTransitiveDependencies,
asset: asset,
);
AssetStackIndexProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.asset,
}) : super.internal();
final Asset asset;
@override
Override overrideWith(
int Function(AssetStackIndexRef provider) create,
) {
return ProviderOverride(
origin: this,
override: AssetStackIndexProvider._internal(
(ref) => create(ref as AssetStackIndexRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
asset: asset,
),
);
}
@override
AutoDisposeProviderElement<int> createElement() {
return _AssetStackIndexProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is AssetStackIndexProvider && other.asset == asset;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, asset.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead') @Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element // ignore: unused_element
mixin AssetStackIndexRef on AutoDisposeProviderRef<int> { typedef AssetStackIndexRef = AutoDisposeProviderRef<int>;
/// The parameter `asset` of this provider.
Asset get asset;
}
class _AssetStackIndexProviderElement extends AutoDisposeProviderElement<int>
with AssetStackIndexRef {
_AssetStackIndexProviderElement(super.provider);
@override
Asset get asset => (origin as AssetStackIndexProvider).asset;
}
// ignore_for_file: type=lint // 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 // 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

View File

@ -144,7 +144,7 @@ class DownloadStateNotifier extends StateNotifier<DownloadState> {
return await _downloadService.downloadAll(assets); return await _downloadService.downloadAll(assets);
} }
void downloadAsset(Asset asset, BuildContext context) async { void downloadAsset(Asset asset) async {
await _downloadService.download(asset); await _downloadService.download(asset);
} }
@ -186,6 +186,7 @@ class DownloadStateNotifier extends StateNotifier<DownloadState> {
return const ShareDialog(); return const ShareDialog();
}, },
barrierDismissible: false, barrierDismissible: false,
useRootNavigator: false,
); );
} }
} }

View File

@ -0,0 +1,93 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/interfaces/cast_destination_service.interface.dart';
import 'package:immich_mobile/models/cast/cast_manager_state.dart';
import 'package:immich_mobile/services/gcast.service.dart';
final castProvider = StateNotifierProvider<CastNotifier, CastManagerState>(
(ref) => CastNotifier(ref.watch(gCastServiceProvider)),
);
class CastNotifier extends StateNotifier<CastManagerState> {
// more cast providers can be added here (ie Fcast)
final ICastDestinationService _gCastService;
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,
),
) {
_gCastService.onConnectionState = _onConnectionState;
_gCastService.onCurrentTime = _onCurrentTime;
_gCastService.onDuration = _onDuration;
_gCastService.onReceiverName = _onReceiverName;
_gCastService.onCastState = _onCastState;
}
void _onConnectionState(bool isCasting) {
state = state.copyWith(isCasting: isCasting);
}
void _onCurrentTime(Duration currentTime) {
state = state.copyWith(currentTime: currentTime);
}
void _onDuration(Duration duration) {
state = state.copyWith(duration: duration);
}
void _onReceiverName(String receiverName) {
state = state.copyWith(receiverName: receiverName);
}
void _onCastState(CastState castState) {
state = state.copyWith(castState: castState);
}
void loadMedia(Asset asset, bool reload) {
_gCastService.loadMedia(asset, reload);
}
Future<void> connect(CastDestinationType type, dynamic device) async {
switch (type) {
case CastDestinationType.googleCast:
await _gCastService.connect(device);
break;
}
}
Future<List<(String, CastDestinationType, dynamic)>> getDevices() async {
if (discovered.isEmpty) {
discovered = await _gCastService.getDevices();
}
return discovered;
}
void play() {
_gCastService.play();
}
void pause() {
_gCastService.pause();
}
void seekTo(Duration position) {
_gCastService.seekTo(position);
}
void stop() {
_gCastService.stop();
}
Future<void> disconnect() async {
await _gCastService.disconnect();
}
}

View File

@ -7,7 +7,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'immich_logo_provider.g.dart'; part 'immich_logo_provider.g.dart';
@riverpod @riverpod
Future<Uint8List> immichLogo(Ref ref) async { Future<Uint8List> immichLogo(Ref _) async {
final json = await rootBundle.loadString('assets/immich-logo.json'); final json = await rootBundle.loadString('assets/immich-logo.json');
final j = jsonDecode(json); final j = jsonDecode(json);
return base64Decode(j['content']); return base64Decode(j['content']);

View File

@ -6,7 +6,7 @@ part of 'immich_logo_provider.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$immichLogoHash() => r'6f23d217c44279537b7edee1ca80ebf47f69a4d0'; String _$immichLogoHash() => r'6de7fcca1ef9acef6ab7398eb0c664080747e0ea';
/// See also [immichLogo]. /// See also [immichLogo].
@ProviderFor(immichLogo) @ProviderFor(immichLogo)

View File

@ -72,4 +72,12 @@ class AssetApiRepository extends ApiRepository implements IAssetApiRepository {
return AssetVisibility.archive; return AssetVisibility.archive;
} }
} }
@override
Future<String?> getAssetMIMEType(String assetId) async {
final response = await checkNull(_api.getAssetInfo(assetId));
// we need to get the MIME of the thumbnail once that gets added to the API
return response.originalMimeType;
}
} }

View File

@ -35,8 +35,10 @@ class AuthRepository extends DatabaseRepository implements IAuthRepository {
db.albums.clear(), db.albums.clear(),
db.eTags.clear(), db.eTags.clear(),
db.users.clear(), db.users.clear(),
_drift.remoteAssetEntity.deleteAll(),
_drift.remoteExifEntity.deleteAll(), _drift.remoteExifEntity.deleteAll(),
_drift.remoteAssetEntity.deleteAll(),
_drift.remoteAlbumEntity.deleteAll(),
_drift.remoteAlbumAssetEntity.deleteAll(),
]); ]);
}); });
} }

View File

@ -0,0 +1,75 @@
import 'package:cast/device.dart';
import 'package:cast/session.dart';
import 'package:cast/session_manager.dart';
import 'package:cast/discovery_service.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
final gCastRepositoryProvider = Provider((_) {
return GCastRepository();
});
class GCastRepository {
CastSession? _castSession;
void Function(CastSessionState)? onCastStatus;
void Function(Map<String, dynamic>)? onCastMessage;
Map<String, dynamic>? _receiverStatus;
GCastRepository();
Future<void> connect(CastDevice device) async {
_castSession = await CastSessionManager().startSession(device);
_castSession?.stateStream.listen((state) {
onCastStatus?.call(state);
});
_castSession?.messageStream.listen((message) {
onCastMessage?.call(message);
if (message['type'] == 'RECEIVER_STATUS') {
_receiverStatus = message;
}
});
// open the default receiver
sendMessage(CastSession.kNamespaceReceiver, {
'type': 'LAUNCH',
'appId': 'CC1AD845',
});
}
Future<void> disconnect() async {
final sessionID = getSessionId();
sendMessage(CastSession.kNamespaceReceiver, {
'type': "STOP",
"sessionId": sessionID,
});
// wait 500ms to ensure the stop command is processed
await Future.delayed(const Duration(milliseconds: 500));
await _castSession?.close();
}
String? getSessionId() {
if (_receiverStatus == null) {
return null;
}
return _receiverStatus!['status']['applications'][0]['sessionId'];
}
void sendMessage(String namespace, Map<String, dynamic> message) {
if (_castSession == null) {
throw Exception("Cast session is not established");
}
_castSession!.sendMessage(namespace, message);
}
Future<List<CastDevice>> listDestinations() async {
return await CastDiscoveryService()
.search(timeout: const Duration(seconds: 3));
}
}

View File

@ -0,0 +1,47 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/interfaces/sessions_api.interface.dart';
import 'package:immich_mobile/models/sessions/session_create_response.model.dart';
import 'package:immich_mobile/providers/api.provider.dart';
import 'package:immich_mobile/repositories/api.repository.dart';
import 'package:openapi/api.dart';
final sessionsAPIRepositoryProvider = Provider(
(ref) => SessionsAPIRepository(
ref.watch(apiServiceProvider).sessionsApi,
),
);
class SessionsAPIRepository extends ApiRepository
implements ISessionAPIRepository {
final SessionsApi _api;
SessionsAPIRepository(this._api);
@override
Future<SessionCreateResponse> createSession(
String deviceType,
String deviceOS, {
int? duration,
}) async {
final dto = await checkNull(
_api.createSession(
SessionCreateDto(
deviceType: deviceType,
deviceOS: deviceOS,
duration: duration,
),
),
);
return SessionCreateResponse(
id: dto.id,
current: dto.current,
deviceType: deviceType,
deviceOS: deviceOS,
expiresAt: dto.expiresAt,
createdAt: dto.createdAt,
updatedAt: dto.updatedAt,
token: dto.token,
);
}
}

View File

@ -34,6 +34,7 @@ class ApiService implements Authentication {
late StacksApi stacksApi; late StacksApi stacksApi;
late ViewApi viewApi; late ViewApi viewApi;
late MemoriesApi memoriesApi; late MemoriesApi memoriesApi;
late SessionsApi sessionsApi;
ApiService() { ApiService() {
// The below line ensures that the api clients are initialized when the service is instantiated // The below line ensures that the api clients are initialized when the service is instantiated
@ -72,6 +73,7 @@ class ApiService implements Authentication {
stacksApi = StacksApi(_apiClient); stacksApi = StacksApi(_apiClient);
viewApi = ViewApi(_apiClient); viewApi = ViewApi(_apiClient);
memoriesApi = MemoriesApi(_apiClient); memoriesApi = MemoriesApi(_apiClient);
sessionsApi = SessionsApi(_apiClient);
} }
Future<void> _setUserAgentHeader() async { Future<void> _setUserAgentHeader() async {

View File

@ -18,7 +18,6 @@ import 'package:immich_mobile/interfaces/backup_album.interface.dart';
import 'package:immich_mobile/models/backup/backup_candidate.model.dart'; import 'package:immich_mobile/models/backup/backup_candidate.model.dart';
import 'package:immich_mobile/models/backup/current_upload_asset.model.dart'; import 'package:immich_mobile/models/backup/current_upload_asset.model.dart';
import 'package:immich_mobile/models/backup/error_upload_asset.model.dart'; import 'package:immich_mobile/models/backup/error_upload_asset.model.dart';
import 'package:immich_mobile/models/backup/success_upload_asset.model.dart';
import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/providers/api.provider.dart';
import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart';
import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/providers/db.provider.dart';
@ -489,7 +488,6 @@ class BackgroundService {
_cancellationToken!, _cancellationToken!,
pmProgressHandler: pmProgressHandler, pmProgressHandler: pmProgressHandler,
onSuccess: (result) => _onAssetUploaded( onSuccess: (result) => _onAssetUploaded(
result: result,
shouldNotify: notifyTotalProgress, shouldNotify: notifyTotalProgress,
), ),
onProgress: (bytes, totalBytes) => onProgress: (bytes, totalBytes) =>
@ -511,7 +509,6 @@ class BackgroundService {
} }
void _onAssetUploaded({ void _onAssetUploaded({
required SuccessUploadAsset result,
bool shouldNotify = false, bool shouldNotify = false,
}) async { }) async {
if (!shouldNotify) { if (!shouldNotify) {

View File

@ -184,10 +184,10 @@ class BackupVerificationService {
// for images: make sure they are pixel-wise identical // for images: make sure they are pixel-wise identical
// (skip first few KBs containing metadata) // (skip first few KBs containing metadata)
final Uint64List localImage = final Uint64List localImage =
_fakeDecodeImg(local, await file.readAsBytes()); _fakeDecodeImg(await file.readAsBytes());
final res = await apiService.assetsApi final res = await apiService.assetsApi
.downloadAssetWithHttpInfo(remote.remoteId!); .downloadAssetWithHttpInfo(remote.remoteId!);
final Uint64List remoteImage = _fakeDecodeImg(remote, res.bodyBytes); final Uint64List remoteImage = _fakeDecodeImg(res.bodyBytes);
final eq = const ListEquality().equals(remoteImage, localImage); final eq = const ListEquality().equals(remoteImage, localImage);
return eq; return eq;
@ -198,7 +198,7 @@ class BackupVerificationService {
return false; return false;
} }
static Uint64List _fakeDecodeImg(Asset asset, Uint8List bytes) { static Uint64List _fakeDecodeImg(Uint8List bytes) {
const headerLength = 131072; // assume header is at most 128 KB const headerLength = 131072; // assume header is at most 128 KB
final start = bytes.length < headerLength * 2 final start = bytes.length < headerLength * 2
? (bytes.length ~/ (4 * 8)) * 8 ? (bytes.length ~/ (4 * 8)) * 8

View File

@ -0,0 +1,295 @@
import 'dart:async';
import 'package:cast/session.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/interfaces/cast_destination_service.interface.dart';
import 'package:immich_mobile/models/cast/cast_manager_state.dart';
import 'package:immich_mobile/models/sessions/session_create_response.model.dart';
import 'package:immich_mobile/repositories/asset_api.repository.dart';
import 'package:immich_mobile/repositories/gcast.repository.dart';
import 'package:immich_mobile/repositories/sessions_api.repository.dart';
import 'package:immich_mobile/utils/image_url_builder.dart';
// ignore: import_rule_openapi, we are only using the AssetMediaSize enum
import 'package:openapi/api.dart';
final gCastServiceProvider = Provider(
(ref) => GCastService(
ref.watch(gCastRepositoryProvider),
ref.watch(sessionsAPIRepositoryProvider),
ref.watch(assetApiRepositoryProvider),
),
);
class GCastService implements ICastDestinationService {
final GCastRepository _gCastRepository;
final SessionsAPIRepository _sessionsApiService;
final AssetApiRepository _assetApiRepository;
SessionCreateResponse? sessionKey;
String? currentAssetId;
bool isConnected = false;
int? _sessionId;
Timer? _mediaStatusPollingTimer;
@override
void Function(bool)? onConnectionState;
@override
void Function(Duration)? onCurrentTime;
@override
void Function(Duration)? onDuration;
@override
void Function(String)? onReceiverName;
@override
void Function(CastState)? onCastState;
GCastService(
this._gCastRepository,
this._sessionsApiService,
this._assetApiRepository,
) {
_gCastRepository.onCastStatus = _onCastStatusCallback;
_gCastRepository.onCastMessage = _onCastMessageCallback;
}
void _onCastStatusCallback(CastSessionState state) {
if (state == CastSessionState.connected) {
onConnectionState?.call(true);
isConnected = true;
} else if (state == CastSessionState.closed) {
onConnectionState?.call(false);
isConnected = false;
onReceiverName?.call("");
currentAssetId = null;
}
}
void _onCastMessageCallback(Map<String, dynamic> message) {
switch (message['type']) {
case "MEDIA_STATUS":
_handleMediaStatus(message);
break;
}
}
void _handleMediaStatus(Map<String, dynamic> message) {
final statusList =
(message['status'] as List).whereType<Map<String, dynamic>>().toList();
if (statusList.isEmpty) {
return;
}
final status = statusList[0];
switch (status['playerState']) {
case "PLAYING":
onCastState?.call(CastState.playing);
break;
case "PAUSED":
onCastState?.call(CastState.paused);
break;
case "BUFFERING":
onCastState?.call(CastState.buffering);
break;
case "IDLE":
onCastState?.call(CastState.idle);
// stop polling for media status if the video finished playing
if (status["idleReason"] == "FINISHED") {
_mediaStatusPollingTimer?.cancel();
}
break;
}
if (status["media"] != null && status["media"]["duration"] != null) {
final duration = Duration(
milliseconds: (status["media"]["duration"] * 1000 ?? 0).toInt(),
);
onDuration?.call(duration);
}
if (status["mediaSessionId"] != null) {
_sessionId = status["mediaSessionId"];
}
if (status["currentTime"] != null) {
final currentTime =
Duration(milliseconds: (status["currentTime"] * 1000 ?? 0).toInt());
onCurrentTime?.call(currentTime);
}
}
@override
Future<void> connect(dynamic device) async {
await _gCastRepository.connect(device);
onReceiverName?.call(device.extras["fn"] ?? "Google Cast");
}
@override
CastDestinationType getType() {
return CastDestinationType.googleCast;
}
@override
Future<bool> initialize() async {
// there is nothing blocking us from using Google Cast that we can check for
return true;
}
@override
Future<void> disconnect() async {
onReceiverName?.call("");
currentAssetId = null;
await _gCastRepository.disconnect();
}
bool isSessionValid() {
// check if we already have a session token
// we should always have a expiration date
if (sessionKey == null || sessionKey?.expiresAt == null) {
return false;
}
final tokenExpiration = DateTime.parse(sessionKey!.expiresAt!);
// 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));
return bufferedExpiration.isAfter(DateTime.now());
}
@override
void loadMedia(Asset asset, bool reload) async {
if (!isConnected) {
return;
} else if (asset.remoteId == null) {
return;
} else if (asset.remoteId == currentAssetId && !reload) {
return;
}
// create a session key
if (!isSessionValid()) {
sessionKey = await _sessionsApiService.createSession(
"Cast",
"Google Cast",
duration: const Duration(minutes: 15).inSeconds,
);
}
final unauthenticatedUrl = asset.isVideo
? getPlaybackUrlForRemoteId(
asset.remoteId!,
)
: getThumbnailUrlForRemoteId(
asset.remoteId!,
type: AssetMediaSize.fullsize,
);
final authenticatedURL =
"$unauthenticatedUrl&sessionKey=${sessionKey?.token}";
// get image mime type
final mimeType =
await _assetApiRepository.getAssetMIMEType(asset.remoteId!);
if (mimeType == null) {
return;
}
_gCastRepository.sendMessage(CastSession.kNamespaceMedia, {
"type": "LOAD",
"media": {
"contentId": authenticatedURL,
"streamType": "BUFFERED",
"contentType": mimeType,
"contentUrl": authenticatedURL,
},
"autoplay": true,
});
currentAssetId = asset.remoteId;
// we need to poll for media status since the cast device does not
// send a message when the media is loaded for whatever reason
// only do this on videos
_mediaStatusPollingTimer?.cancel();
if (asset.isVideo) {
_mediaStatusPollingTimer =
Timer.periodic(const Duration(milliseconds: 500), (timer) {
if (isConnected) {
_gCastRepository.sendMessage(CastSession.kNamespaceMedia, {
"type": "GET_STATUS",
"mediaSessionId": _sessionId,
});
} else {
timer.cancel();
}
});
}
}
@override
void play() {
_gCastRepository.sendMessage(CastSession.kNamespaceMedia, {
"type": "PLAY",
"mediaSessionId": _sessionId,
});
}
@override
void pause() {
_gCastRepository.sendMessage(CastSession.kNamespaceMedia, {
"type": "PAUSE",
"mediaSessionId": _sessionId,
});
}
@override
void seekTo(Duration position) {
_gCastRepository.sendMessage(CastSession.kNamespaceMedia, {
"type": "SEEK",
"mediaSessionId": _sessionId,
"currentTime": position.inSeconds,
});
}
@override
void stop() {
_gCastRepository.sendMessage(CastSession.kNamespaceMedia, {
"type": "STOP",
"mediaSessionId": _sessionId,
});
_mediaStatusPollingTimer?.cancel();
currentAssetId = null;
}
// 0x01 is display capability bitmask
bool isDisplay(int ca) => (ca & 0x01) != 0;
@override
Future<List<(String, CastDestinationType, dynamic)>> getDevices() async {
final dests = await _gCastRepository.listDestinations();
return dests
.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;
return isDisplay(caNumber);
}).toList(growable: false);
}
}

View File

@ -1,5 +1,3 @@
// ignore_for_file: avoid-unsafe-collection-methods
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';

View File

@ -73,6 +73,10 @@ String getThumbnailUrlForRemoteId(
return '${Store.get(StoreKey.serverEndpoint)}/assets/$id/thumbnail?size=${type.value}'; return '${Store.get(StoreKey.serverEndpoint)}/assets/$id/thumbnail?size=${type.value}';
} }
String getPlaybackUrlForRemoteId(final String id) {
return '${Store.get(StoreKey.serverEndpoint)}/assets/$id/video/playback?';
}
String getFaceThumbnailUrl(final String personId) { String getFaceThumbnailUrl(final String personId) {
return '${Store.get(StoreKey.serverEndpoint)}/people/$personId/thumbnail'; return '${Store.get(StoreKey.serverEndpoint)}/people/$personId/thumbnail';
} }

View File

@ -3,6 +3,7 @@ import 'dart:ui';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/services/log.service.dart';
import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/providers/db.provider.dart';
import 'package:immich_mobile/providers/infrastructure/cancel.provider.dart'; import 'package:immich_mobile/providers/infrastructure/cancel.provider.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
@ -58,9 +59,7 @@ Cancelable<T?> runInIsolateGentle<T>({
stack, stack,
); );
} finally { } finally {
// Wait for the logs to flush await LogService.I.flushBuffer();
await Future.delayed(const Duration(seconds: 2));
// Always close the new db connection on Isolate end
ref.read(driftProvider).close(); ref.read(driftProvider).close();
ref.read(isarProvider).close(); ref.read(isarProvider).close();
} }

View File

@ -1,5 +1,3 @@
// ignore_for_file: avoid-unsafe-collection-methods
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';

View File

@ -43,6 +43,7 @@ void handleShareAssets(
return const ShareDialog(); return const ShareDialog();
}, },
barrierDismissible: false, barrierDismissible: false,
useRootNavigator: false,
); );
} }
@ -93,7 +94,7 @@ Future<void> handleFavoriteAssets(
ImmichToast.show( ImmichToast.show(
context: context, context: context,
msg: toastMessage, msg: toastMessage,
gravity: ToastGravity.BOTTOM, gravity: toastGravity,
); );
} }
} }
@ -163,9 +164,8 @@ Future<void> handleSetAssetsVisibility(
WidgetRef ref, WidgetRef ref,
BuildContext context, BuildContext context,
AssetVisibilityEnum visibility, AssetVisibilityEnum visibility,
List<Asset> selection, { List<Asset> selection,
ToastGravity toastGravity = ToastGravity.BOTTOM, ) async {
}) async {
if (selection.isNotEmpty) { if (selection.isNotEmpty) {
await ref await ref
.watch(assetProvider.notifier) .watch(assetProvider.notifier)

View File

@ -1,4 +1,3 @@
// ignore_for_file: library_private_types_in_public_api
// Based on https://stackoverflow.com/a/52625182 // Based on https://stackoverflow.com/a/52625182
import 'dart:async'; import 'dart:async';
@ -164,7 +163,6 @@ class _CustomLongPressGestureRecognizer extends LongPressGestureRecognizer {
} }
} }
// ignore: prefer-single-widget-per-file
class AssetIndexWrapper extends SingleChildRenderObjectWidget { class AssetIndexWrapper extends SingleChildRenderObjectWidget {
final int rowIndex; final int rowIndex;
final int sectionIndex; final int sectionIndex;
@ -177,6 +175,7 @@ class AssetIndexWrapper extends SingleChildRenderObjectWidget {
}); });
@override @override
// ignore: library_private_types_in_public_api
_AssetIndexProxy createRenderObject(BuildContext context) { _AssetIndexProxy createRenderObject(BuildContext context) {
return _AssetIndexProxy( return _AssetIndexProxy(
index: AssetIndex(rowIndex: rowIndex, sectionIndex: sectionIndex), index: AssetIndex(rowIndex: rowIndex, sectionIndex: sectionIndex),
@ -186,6 +185,7 @@ class AssetIndexWrapper extends SingleChildRenderObjectWidget {
@override @override
void updateRenderObject( void updateRenderObject(
BuildContext context, BuildContext context,
// ignore: library_private_types_in_public_api
_AssetIndexProxy renderObject, _AssetIndexProxy renderObject,
) { ) {
renderObject.index = renderObject.index =

View File

@ -1,5 +1,3 @@
// ignore_for_file: prefer-single-widget-per-file
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart';

View File

@ -80,8 +80,7 @@ class DraggableScrollbar extends StatefulWidget {
this.labelTextBuilder, this.labelTextBuilder,
this.labelConstraints, this.labelConstraints,
}) : assert(child.scrollDirection == Axis.vertical), }) : assert(child.scrollDirection == Axis.vertical),
scrollThumbBuilder = scrollThumbBuilder = _thumbRRectBuilder(alwaysVisibleScrollThumb);
_thumbRRectBuilder(scrollThumbKey, alwaysVisibleScrollThumb);
DraggableScrollbar.arrows({ DraggableScrollbar.arrows({
super.key, super.key,
@ -97,8 +96,7 @@ class DraggableScrollbar extends StatefulWidget {
this.labelTextBuilder, this.labelTextBuilder,
this.labelConstraints, this.labelConstraints,
}) : assert(child.scrollDirection == Axis.vertical), }) : assert(child.scrollDirection == Axis.vertical),
scrollThumbBuilder = scrollThumbBuilder = _thumbArrowBuilder(alwaysVisibleScrollThumb);
_thumbArrowBuilder(scrollThumbKey, alwaysVisibleScrollThumb);
DraggableScrollbar.semicircle({ DraggableScrollbar.semicircle({
super.key, super.key,
@ -201,7 +199,6 @@ class DraggableScrollbar extends StatefulWidget {
} }
static ScrollThumbBuilder _thumbArrowBuilder( static ScrollThumbBuilder _thumbArrowBuilder(
Key? scrollThumbKey,
bool alwaysVisibleScrollThumb, bool alwaysVisibleScrollThumb,
) { ) {
return ( return (
@ -239,7 +236,6 @@ class DraggableScrollbar extends StatefulWidget {
} }
static ScrollThumbBuilder _thumbRRectBuilder( static ScrollThumbBuilder _thumbRRectBuilder(
Key? scrollThumbKey,
bool alwaysVisibleScrollThumb, bool alwaysVisibleScrollThumb,
) { ) {
return ( return (

View File

@ -48,7 +48,7 @@ class ThumbnailImage extends ConsumerWidget {
// Assets from response DTOs do not have an isar id, querying which would give us the default autoIncrement id // Assets from response DTOs do not have an isar id, querying which would give us the default autoIncrement id
final isFromDto = asset.id == noDbId; final isFromDto = asset.id == noDbId;
Widget buildSelectionIcon(Asset asset) { Widget buildSelectionIcon() {
if (isSelected) { if (isSelected) {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
@ -233,7 +233,7 @@ class ThumbnailImage extends ConsumerWidget {
padding: const EdgeInsets.all(3.0), padding: const EdgeInsets.all(3.0),
child: Align( child: Align(
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
child: buildSelectionIcon(asset), child: buildSelectionIcon(),
), ),
), ),
], ],

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