mirror of
https://github.com/immich-app/immich.git
synced 2025-07-09 03:04:16 -04:00
Merge branch 'main' of github.com:immich-app/immich into new-upload
This commit is contained in:
commit
2b6672293b
142
cli/package-lock.json
generated
142
cli/package-lock.json
generated
@ -1370,17 +1370,17 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.33.0.tgz",
|
||||
"integrity": "sha512-CACyQuqSHt7ma3Ns601xykeBK/rDeZa3w6IS6UtMQbixO5DWy+8TilKkviGDH6jtWCo8FGRKEK5cLLkPvEammQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz",
|
||||
"integrity": "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.33.0",
|
||||
"@typescript-eslint/type-utils": "8.33.0",
|
||||
"@typescript-eslint/utils": "8.33.0",
|
||||
"@typescript-eslint/visitor-keys": "8.33.0",
|
||||
"@typescript-eslint/scope-manager": "8.32.1",
|
||||
"@typescript-eslint/type-utils": "8.32.1",
|
||||
"@typescript-eslint/utils": "8.32.1",
|
||||
"@typescript-eslint/visitor-keys": "8.32.1",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^7.0.0",
|
||||
"natural-compare": "^1.4.0",
|
||||
@ -1394,7 +1394,7 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.33.0",
|
||||
"@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
|
||||
"eslint": "^8.57.0 || ^9.0.0",
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
@ -1410,16 +1410,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.33.0.tgz",
|
||||
"integrity": "sha512-JaehZvf6m0yqYp34+RVnihBAChkqeH+tqqhS0GuX1qgPpwLvmTPheKEs6OeCK6hVJgXZHJ2vbjnC9j119auStQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz",
|
||||
"integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.33.0",
|
||||
"@typescript-eslint/types": "8.33.0",
|
||||
"@typescript-eslint/typescript-estree": "8.33.0",
|
||||
"@typescript-eslint/visitor-keys": "8.33.0",
|
||||
"@typescript-eslint/scope-manager": "8.32.1",
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/typescript-estree": "8.32.1",
|
||||
"@typescript-eslint/visitor-keys": "8.32.1",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@ -1434,34 +1434,15 @@
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.33.0.tgz",
|
||||
"integrity": "sha512-d1hz0u9l6N+u/gcrk6s6gYdl7/+pp8yHheRTqP6X5hVDKALEaTn8WfGiit7G511yueBEL3OpOEpD+3/MBdoN+A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.33.0",
|
||||
"@typescript-eslint/types": "^8.33.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.33.0.tgz",
|
||||
"integrity": "sha512-LMi/oqrzpqxyO72ltP+dBSP6V0xiUb4saY7WLtxSfiNEBI8m321LLVFU9/QDJxjDQG9/tjSqKz/E3380TEqSTw==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz",
|
||||
"integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.33.0",
|
||||
"@typescript-eslint/visitor-keys": "8.33.0"
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/visitor-keys": "8.32.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -1471,32 +1452,15 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.33.0.tgz",
|
||||
"integrity": "sha512-sTkETlbqhEoiFmGr1gsdq5HyVbSOF0145SYDJ/EQmXHtKViCaGvnyLqWFFHtEXoS0J1yU8Wyou2UGmgW88fEug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.33.0.tgz",
|
||||
"integrity": "sha512-lScnHNCBqL1QayuSrWeqAL5GmqNdVUQAAMTaCwdYEdWfIrSrOGzyLGRCHXcCixa5NK6i5l0AfSO2oBSjCjf4XQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz",
|
||||
"integrity": "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "8.33.0",
|
||||
"@typescript-eslint/utils": "8.33.0",
|
||||
"@typescript-eslint/typescript-estree": "8.32.1",
|
||||
"@typescript-eslint/utils": "8.32.1",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
},
|
||||
@ -1513,9 +1477,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.33.0.tgz",
|
||||
"integrity": "sha512-DKuXOKpM5IDT1FA2g9x9x1Ug81YuKrzf4mYX8FAVSNu5Wo/LELHWQyM1pQaDkI42bX15PWl0vNPt1uGiIFUOpg==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz",
|
||||
"integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -1527,16 +1491,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.33.0.tgz",
|
||||
"integrity": "sha512-vegY4FQoB6jL97Tu/lWRsAiUUp8qJTqzAmENH2k59SJhw0Th1oszb9Idq/FyyONLuNqT1OADJPXfyUNOR8SzAQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz",
|
||||
"integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.33.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.33.0",
|
||||
"@typescript-eslint/types": "8.33.0",
|
||||
"@typescript-eslint/visitor-keys": "8.33.0",
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/visitor-keys": "8.32.1",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
@ -1582,16 +1544,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.33.0.tgz",
|
||||
"integrity": "sha512-lPFuQaLA9aSNa7D5u2EpRiqdAUhzShwGg/nhpBlc4GR6kcTABttCuyjFs8BcEZ8VWrjCBof/bePhP3Q3fS+Yrw==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz",
|
||||
"integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.7.0",
|
||||
"@typescript-eslint/scope-manager": "8.33.0",
|
||||
"@typescript-eslint/types": "8.33.0",
|
||||
"@typescript-eslint/typescript-estree": "8.33.0"
|
||||
"@typescript-eslint/scope-manager": "8.32.1",
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/typescript-estree": "8.32.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -1606,13 +1568,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.33.0.tgz",
|
||||
"integrity": "sha512-7RW7CMYoskiz5OOGAWjJFxgb7c5UNjTG292gYhWeOAcFmYCtVCSqjqSBj5zMhxbXo2JOW95YYrUWJfU0zrpaGQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz",
|
||||
"integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.33.0",
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"eslint-visitor-keys": "^4.2.0"
|
||||
},
|
||||
"engines": {
|
||||
@ -2794,9 +2756,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-16.2.0.tgz",
|
||||
"integrity": "sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg==",
|
||||
"version": "16.1.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz",
|
||||
"integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -4238,15 +4200,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.33.0.tgz",
|
||||
"integrity": "sha512-5YmNhF24ylCsvdNW2oJwMzTbaeO4bg90KeGtMjUw0AGtHksgEPLRTUil+coHwCfiu4QjVJFnjp94DmU6zV7DhQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.32.1.tgz",
|
||||
"integrity": "sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "8.33.0",
|
||||
"@typescript-eslint/parser": "8.33.0",
|
||||
"@typescript-eslint/utils": "8.33.0"
|
||||
"@typescript-eslint/eslint-plugin": "8.32.1",
|
||||
"@typescript-eslint/parser": "8.32.1",
|
||||
"@typescript-eslint/utils": "8.32.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
994
docs/package-lock.json
generated
994
docs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -16,8 +16,8 @@
|
||||
"write-heading-ids": "docusaurus write-heading-ids"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "~3.8.0",
|
||||
"@docusaurus/preset-classic": "~3.8.0",
|
||||
"@docusaurus/core": "~3.7.0",
|
||||
"@docusaurus/preset-classic": "~3.7.0",
|
||||
"@mdi/js": "^7.3.67",
|
||||
"@mdi/react": "^1.6.1",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
@ -35,7 +35,7 @@
|
||||
"url": "^0.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "~3.8.0",
|
||||
"@docusaurus/module-type-aliases": "~3.7.0",
|
||||
"@docusaurus/tsconfig": "^3.7.0",
|
||||
"@docusaurus/types": "^3.7.0",
|
||||
"prettier": "^3.2.4",
|
||||
|
142
e2e/package-lock.json
generated
142
e2e/package-lock.json
generated
@ -1709,17 +1709,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.33.0.tgz",
|
||||
"integrity": "sha512-CACyQuqSHt7ma3Ns601xykeBK/rDeZa3w6IS6UtMQbixO5DWy+8TilKkviGDH6jtWCo8FGRKEK5cLLkPvEammQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz",
|
||||
"integrity": "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.33.0",
|
||||
"@typescript-eslint/type-utils": "8.33.0",
|
||||
"@typescript-eslint/utils": "8.33.0",
|
||||
"@typescript-eslint/visitor-keys": "8.33.0",
|
||||
"@typescript-eslint/scope-manager": "8.32.1",
|
||||
"@typescript-eslint/type-utils": "8.32.1",
|
||||
"@typescript-eslint/utils": "8.32.1",
|
||||
"@typescript-eslint/visitor-keys": "8.32.1",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^7.0.0",
|
||||
"natural-compare": "^1.4.0",
|
||||
@ -1733,7 +1733,7 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.33.0",
|
||||
"@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
|
||||
"eslint": "^8.57.0 || ^9.0.0",
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
@ -1749,16 +1749,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.33.0.tgz",
|
||||
"integrity": "sha512-JaehZvf6m0yqYp34+RVnihBAChkqeH+tqqhS0GuX1qgPpwLvmTPheKEs6OeCK6hVJgXZHJ2vbjnC9j119auStQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz",
|
||||
"integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.33.0",
|
||||
"@typescript-eslint/types": "8.33.0",
|
||||
"@typescript-eslint/typescript-estree": "8.33.0",
|
||||
"@typescript-eslint/visitor-keys": "8.33.0",
|
||||
"@typescript-eslint/scope-manager": "8.32.1",
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/typescript-estree": "8.32.1",
|
||||
"@typescript-eslint/visitor-keys": "8.32.1",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@ -1773,34 +1773,15 @@
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.33.0.tgz",
|
||||
"integrity": "sha512-d1hz0u9l6N+u/gcrk6s6gYdl7/+pp8yHheRTqP6X5hVDKALEaTn8WfGiit7G511yueBEL3OpOEpD+3/MBdoN+A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.33.0",
|
||||
"@typescript-eslint/types": "^8.33.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.33.0.tgz",
|
||||
"integrity": "sha512-LMi/oqrzpqxyO72ltP+dBSP6V0xiUb4saY7WLtxSfiNEBI8m321LLVFU9/QDJxjDQG9/tjSqKz/E3380TEqSTw==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz",
|
||||
"integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.33.0",
|
||||
"@typescript-eslint/visitor-keys": "8.33.0"
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/visitor-keys": "8.32.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -1810,32 +1791,15 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.33.0.tgz",
|
||||
"integrity": "sha512-sTkETlbqhEoiFmGr1gsdq5HyVbSOF0145SYDJ/EQmXHtKViCaGvnyLqWFFHtEXoS0J1yU8Wyou2UGmgW88fEug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.33.0.tgz",
|
||||
"integrity": "sha512-lScnHNCBqL1QayuSrWeqAL5GmqNdVUQAAMTaCwdYEdWfIrSrOGzyLGRCHXcCixa5NK6i5l0AfSO2oBSjCjf4XQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz",
|
||||
"integrity": "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "8.33.0",
|
||||
"@typescript-eslint/utils": "8.33.0",
|
||||
"@typescript-eslint/typescript-estree": "8.32.1",
|
||||
"@typescript-eslint/utils": "8.32.1",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
},
|
||||
@ -1852,9 +1816,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.33.0.tgz",
|
||||
"integrity": "sha512-DKuXOKpM5IDT1FA2g9x9x1Ug81YuKrzf4mYX8FAVSNu5Wo/LELHWQyM1pQaDkI42bX15PWl0vNPt1uGiIFUOpg==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz",
|
||||
"integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -1866,16 +1830,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.33.0.tgz",
|
||||
"integrity": "sha512-vegY4FQoB6jL97Tu/lWRsAiUUp8qJTqzAmENH2k59SJhw0Th1oszb9Idq/FyyONLuNqT1OADJPXfyUNOR8SzAQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz",
|
||||
"integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.33.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.33.0",
|
||||
"@typescript-eslint/types": "8.33.0",
|
||||
"@typescript-eslint/visitor-keys": "8.33.0",
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/visitor-keys": "8.32.1",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
@ -1921,16 +1883,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.33.0.tgz",
|
||||
"integrity": "sha512-lPFuQaLA9aSNa7D5u2EpRiqdAUhzShwGg/nhpBlc4GR6kcTABttCuyjFs8BcEZ8VWrjCBof/bePhP3Q3fS+Yrw==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz",
|
||||
"integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.7.0",
|
||||
"@typescript-eslint/scope-manager": "8.33.0",
|
||||
"@typescript-eslint/types": "8.33.0",
|
||||
"@typescript-eslint/typescript-estree": "8.33.0"
|
||||
"@typescript-eslint/scope-manager": "8.32.1",
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/typescript-estree": "8.32.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -1945,13 +1907,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.33.0.tgz",
|
||||
"integrity": "sha512-7RW7CMYoskiz5OOGAWjJFxgb7c5UNjTG292gYhWeOAcFmYCtVCSqjqSBj5zMhxbXo2JOW95YYrUWJfU0zrpaGQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz",
|
||||
"integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.33.0",
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"eslint-visitor-keys": "^4.2.0"
|
||||
},
|
||||
"engines": {
|
||||
@ -3872,9 +3834,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-16.2.0.tgz",
|
||||
"integrity": "sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg==",
|
||||
"version": "16.1.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz",
|
||||
"integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -6817,15 +6779,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.33.0.tgz",
|
||||
"integrity": "sha512-5YmNhF24ylCsvdNW2oJwMzTbaeO4bg90KeGtMjUw0AGtHksgEPLRTUil+coHwCfiu4QjVJFnjp94DmU6zV7DhQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.32.1.tgz",
|
||||
"integrity": "sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "8.33.0",
|
||||
"@typescript-eslint/parser": "8.33.0",
|
||||
"@typescript-eslint/utils": "8.33.0"
|
||||
"@typescript-eslint/eslint-plugin": "8.32.1",
|
||||
"@typescript-eslint/parser": "8.32.1",
|
||||
"@typescript-eslint/utils": "8.32.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
@ -463,6 +463,8 @@
|
||||
"assets_count": "{count, plural, one {# asset} other {# assets}}",
|
||||
"assets_deleted_permanently": "{count} asset(s) deleted permanently",
|
||||
"assets_deleted_permanently_from_server": "{count} asset(s) deleted permanently from the Immich server",
|
||||
"assets_downloaded_failed": "{count, plural, one {Downloaded # file - {error} file failed} other {Downloaded # files - {error} files failed}}",
|
||||
"assets_downloaded_successfully": "{count, plural, one {Downloaded # file successfully} other {Downloaded # files successfully}}",
|
||||
"assets_moved_to_trash_count": "Moved {count, plural, one {# asset} other {# assets}} to trash",
|
||||
"assets_permanently_deleted_count": "Permanently deleted {count, plural, one {# asset} other {# assets}}",
|
||||
"assets_removed_count": "Removed {count, plural, one {# asset} other {# assets}}",
|
||||
@ -694,6 +696,7 @@
|
||||
"daily_title_text_date": "E, MMM dd",
|
||||
"daily_title_text_date_year": "E, MMM dd, yyyy",
|
||||
"dark": "Dark",
|
||||
"darkTheme": "Toggle dark theme",
|
||||
"date_after": "Date after",
|
||||
"date_and_time": "Date and Time",
|
||||
"date_before": "Date before",
|
||||
@ -1169,7 +1172,7 @@
|
||||
"look": "Look",
|
||||
"loop_videos": "Loop videos",
|
||||
"loop_videos_description": "Enable to automatically loop a video in the detail viewer.",
|
||||
"main_branch_warning": "You’re using a development version; we strongly recommend using a release version!",
|
||||
"main_branch_warning": "You're using a development version; we strongly recommend using a release version!",
|
||||
"main_menu": "Main menu",
|
||||
"make": "Make",
|
||||
"manage_shared_links": "Manage shared links",
|
||||
@ -1434,7 +1437,7 @@
|
||||
"purchase_lifetime_description": "Lifetime purchase",
|
||||
"purchase_option_title": "PURCHASE OPTIONS",
|
||||
"purchase_panel_info_1": "Building Immich takes a lot of time and effort, and we have full-time engineers working on it to make it as good as we possibly can. Our mission is for open-source software and ethical business practices to become a sustainable income source for developers and to create a privacy-respecting ecosystem with real alternatives to exploitative cloud services.",
|
||||
"purchase_panel_info_2": "As we’re committed not to add paywalls, this purchase will not grant you any additional features in Immich. We rely on users like you to support Immich’s ongoing development.",
|
||||
"purchase_panel_info_2": "As we're committed not to add paywalls, this purchase will not grant you any additional features in Immich. We rely on users like you to support Immich's ongoing development.",
|
||||
"purchase_panel_title": "Support the project",
|
||||
"purchase_per_server": "Per server",
|
||||
"purchase_per_user": "Per user",
|
||||
@ -1817,7 +1820,6 @@
|
||||
"to_parent": "Go to parent",
|
||||
"to_trash": "Trash",
|
||||
"toggle_settings": "Toggle settings",
|
||||
"toggle_theme": "Toggle dark theme",
|
||||
"total": "Total",
|
||||
"total_usage": "Total usage",
|
||||
"trash": "Trash",
|
||||
@ -1839,6 +1841,7 @@
|
||||
"unable_to_setup_pin_code": "Unable to setup PIN code",
|
||||
"unarchive": "Unarchive",
|
||||
"unarchived_count": "{count, plural, other {Unarchived #}}",
|
||||
"undo": "Undo",
|
||||
"unfavorite": "Unfavorite",
|
||||
"unhide_person": "Unhide person",
|
||||
"unknown": "Unknown",
|
||||
|
@ -19,6 +19,7 @@ class Album {
|
||||
required this.name,
|
||||
required this.createdAt,
|
||||
required this.modifiedAt,
|
||||
this.description,
|
||||
this.startDate,
|
||||
this.endDate,
|
||||
this.lastModifiedAssetTimestamp,
|
||||
@ -34,6 +35,7 @@ class Album {
|
||||
@Index(unique: false, replace: false, type: IndexType.hash)
|
||||
String? localId;
|
||||
String name;
|
||||
String? description;
|
||||
DateTime createdAt;
|
||||
DateTime modifiedAt;
|
||||
DateTime? startDate;
|
||||
@ -108,6 +110,7 @@ class Album {
|
||||
remoteId == other.remoteId &&
|
||||
localId == other.localId &&
|
||||
name == other.name &&
|
||||
description == other.description &&
|
||||
createdAt.isAtSameMomentAs(other.createdAt) &&
|
||||
modifiedAt.isAtSameMomentAs(other.modifiedAt) &&
|
||||
isAtSameMomentAs(startDate, other.startDate) &&
|
||||
@ -135,6 +138,7 @@ class Album {
|
||||
modifiedAt.hashCode ^
|
||||
startDate.hashCode ^
|
||||
endDate.hashCode ^
|
||||
description.hashCode ^
|
||||
lastModifiedAssetTimestamp.hashCode ^
|
||||
shared.hashCode ^
|
||||
activityEnabled.hashCode ^
|
||||
@ -150,6 +154,7 @@ class Album {
|
||||
name: dto.albumName,
|
||||
createdAt: dto.createdAt,
|
||||
modifiedAt: dto.updatedAt,
|
||||
description: dto.description,
|
||||
lastModifiedAssetTimestamp: dto.lastModifiedAssetTimestamp,
|
||||
shared: dto.shared,
|
||||
startDate: dto.startDate,
|
||||
@ -184,7 +189,8 @@ class Album {
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => name;
|
||||
String toString() =>
|
||||
'remoteId: $remoteId name: $name description: $description';
|
||||
}
|
||||
|
||||
extension AssetsHelper on IsarCollection<Album> {
|
||||
|
269
mobile/lib/entities/album.entity.g.dart
generated
269
mobile/lib/entities/album.entity.g.dart
generated
@ -27,49 +27,54 @@ const AlbumSchema = CollectionSchema(
|
||||
name: r'createdAt',
|
||||
type: IsarType.dateTime,
|
||||
),
|
||||
r'endDate': PropertySchema(
|
||||
r'description': PropertySchema(
|
||||
id: 2,
|
||||
name: r'description',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'endDate': PropertySchema(
|
||||
id: 3,
|
||||
name: r'endDate',
|
||||
type: IsarType.dateTime,
|
||||
),
|
||||
r'lastModifiedAssetTimestamp': PropertySchema(
|
||||
id: 3,
|
||||
id: 4,
|
||||
name: r'lastModifiedAssetTimestamp',
|
||||
type: IsarType.dateTime,
|
||||
),
|
||||
r'localId': PropertySchema(
|
||||
id: 4,
|
||||
id: 5,
|
||||
name: r'localId',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'modifiedAt': PropertySchema(
|
||||
id: 5,
|
||||
id: 6,
|
||||
name: r'modifiedAt',
|
||||
type: IsarType.dateTime,
|
||||
),
|
||||
r'name': PropertySchema(
|
||||
id: 6,
|
||||
id: 7,
|
||||
name: r'name',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'remoteId': PropertySchema(
|
||||
id: 7,
|
||||
id: 8,
|
||||
name: r'remoteId',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'shared': PropertySchema(
|
||||
id: 8,
|
||||
id: 9,
|
||||
name: r'shared',
|
||||
type: IsarType.bool,
|
||||
),
|
||||
r'sortOrder': PropertySchema(
|
||||
id: 9,
|
||||
id: 10,
|
||||
name: r'sortOrder',
|
||||
type: IsarType.byte,
|
||||
enumMap: _AlbumsortOrderEnumValueMap,
|
||||
),
|
||||
r'startDate': PropertySchema(
|
||||
id: 10,
|
||||
id: 11,
|
||||
name: r'startDate',
|
||||
type: IsarType.dateTime,
|
||||
)
|
||||
@ -146,6 +151,12 @@ int _albumEstimateSize(
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
var bytesCount = offsets.last;
|
||||
{
|
||||
final value = object.description;
|
||||
if (value != null) {
|
||||
bytesCount += 3 + value.length * 3;
|
||||
}
|
||||
}
|
||||
{
|
||||
final value = object.localId;
|
||||
if (value != null) {
|
||||
@ -170,15 +181,16 @@ void _albumSerialize(
|
||||
) {
|
||||
writer.writeBool(offsets[0], object.activityEnabled);
|
||||
writer.writeDateTime(offsets[1], object.createdAt);
|
||||
writer.writeDateTime(offsets[2], object.endDate);
|
||||
writer.writeDateTime(offsets[3], object.lastModifiedAssetTimestamp);
|
||||
writer.writeString(offsets[4], object.localId);
|
||||
writer.writeDateTime(offsets[5], object.modifiedAt);
|
||||
writer.writeString(offsets[6], object.name);
|
||||
writer.writeString(offsets[7], object.remoteId);
|
||||
writer.writeBool(offsets[8], object.shared);
|
||||
writer.writeByte(offsets[9], object.sortOrder.index);
|
||||
writer.writeDateTime(offsets[10], object.startDate);
|
||||
writer.writeString(offsets[2], object.description);
|
||||
writer.writeDateTime(offsets[3], object.endDate);
|
||||
writer.writeDateTime(offsets[4], object.lastModifiedAssetTimestamp);
|
||||
writer.writeString(offsets[5], object.localId);
|
||||
writer.writeDateTime(offsets[6], object.modifiedAt);
|
||||
writer.writeString(offsets[7], object.name);
|
||||
writer.writeString(offsets[8], object.remoteId);
|
||||
writer.writeBool(offsets[9], object.shared);
|
||||
writer.writeByte(offsets[10], object.sortOrder.index);
|
||||
writer.writeDateTime(offsets[11], object.startDate);
|
||||
}
|
||||
|
||||
Album _albumDeserialize(
|
||||
@ -190,16 +202,18 @@ Album _albumDeserialize(
|
||||
final object = Album(
|
||||
activityEnabled: reader.readBool(offsets[0]),
|
||||
createdAt: reader.readDateTime(offsets[1]),
|
||||
endDate: reader.readDateTimeOrNull(offsets[2]),
|
||||
lastModifiedAssetTimestamp: reader.readDateTimeOrNull(offsets[3]),
|
||||
localId: reader.readStringOrNull(offsets[4]),
|
||||
modifiedAt: reader.readDateTime(offsets[5]),
|
||||
name: reader.readString(offsets[6]),
|
||||
remoteId: reader.readStringOrNull(offsets[7]),
|
||||
shared: reader.readBool(offsets[8]),
|
||||
sortOrder: _AlbumsortOrderValueEnumMap[reader.readByteOrNull(offsets[9])] ??
|
||||
description: reader.readStringOrNull(offsets[2]),
|
||||
endDate: reader.readDateTimeOrNull(offsets[3]),
|
||||
lastModifiedAssetTimestamp: reader.readDateTimeOrNull(offsets[4]),
|
||||
localId: reader.readStringOrNull(offsets[5]),
|
||||
modifiedAt: reader.readDateTime(offsets[6]),
|
||||
name: reader.readString(offsets[7]),
|
||||
remoteId: reader.readStringOrNull(offsets[8]),
|
||||
shared: reader.readBool(offsets[9]),
|
||||
sortOrder:
|
||||
_AlbumsortOrderValueEnumMap[reader.readByteOrNull(offsets[10])] ??
|
||||
SortOrder.desc,
|
||||
startDate: reader.readDateTimeOrNull(offsets[10]),
|
||||
startDate: reader.readDateTimeOrNull(offsets[11]),
|
||||
);
|
||||
object.id = id;
|
||||
return object;
|
||||
@ -217,23 +231,25 @@ P _albumDeserializeProp<P>(
|
||||
case 1:
|
||||
return (reader.readDateTime(offset)) as P;
|
||||
case 2:
|
||||
return (reader.readDateTimeOrNull(offset)) as P;
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 3:
|
||||
return (reader.readDateTimeOrNull(offset)) as P;
|
||||
case 4:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
return (reader.readDateTimeOrNull(offset)) as P;
|
||||
case 5:
|
||||
return (reader.readDateTime(offset)) as P;
|
||||
case 6:
|
||||
return (reader.readString(offset)) as P;
|
||||
case 7:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 6:
|
||||
return (reader.readDateTime(offset)) as P;
|
||||
case 7:
|
||||
return (reader.readString(offset)) as P;
|
||||
case 8:
|
||||
return (reader.readBool(offset)) as P;
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 9:
|
||||
return (reader.readBool(offset)) as P;
|
||||
case 10:
|
||||
return (_AlbumsortOrderValueEnumMap[reader.readByteOrNull(offset)] ??
|
||||
SortOrder.desc) as P;
|
||||
case 10:
|
||||
case 11:
|
||||
return (reader.readDateTimeOrNull(offset)) as P;
|
||||
default:
|
||||
throw IsarError('Unknown property with id $propertyId');
|
||||
@ -535,6 +551,152 @@ extension AlbumQueryFilter on QueryBuilder<Album, Album, QFilterCondition> {
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNull(
|
||||
property: r'description',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNotNull(
|
||||
property: r'description',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionEqualTo(
|
||||
String? value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'description',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionGreaterThan(
|
||||
String? value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'description',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionLessThan(
|
||||
String? value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'description',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionBetween(
|
||||
String? lower,
|
||||
String? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.between(
|
||||
property: r'description',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionStartsWith(
|
||||
String value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.startsWith(
|
||||
property: r'description',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionEndsWith(
|
||||
String value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.endsWith(
|
||||
property: r'description',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionContains(
|
||||
String value,
|
||||
{bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.contains(
|
||||
property: r'description',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionMatches(
|
||||
String pattern,
|
||||
{bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.matches(
|
||||
property: r'description',
|
||||
wildcard: pattern,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionIsEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'description',
|
||||
value: '',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionIsNotEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
property: r'description',
|
||||
value: '',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> endDateIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNull(
|
||||
@ -1502,6 +1664,18 @@ extension AlbumQuerySortBy on QueryBuilder<Album, Album, QSortBy> {
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterSortBy> sortByDescription() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'description', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterSortBy> sortByDescriptionDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'description', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterSortBy> sortByEndDate() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'endDate', Sort.asc);
|
||||
@ -1637,6 +1811,18 @@ extension AlbumQuerySortThenBy on QueryBuilder<Album, Album, QSortThenBy> {
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterSortBy> thenByDescription() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'description', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterSortBy> thenByDescriptionDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'description', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterSortBy> thenByEndDate() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'endDate', Sort.asc);
|
||||
@ -1772,6 +1958,13 @@ extension AlbumQueryWhereDistinct on QueryBuilder<Album, Album, QDistinct> {
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QDistinct> distinctByDescription(
|
||||
{bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'description', caseSensitive: caseSensitive);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QDistinct> distinctByEndDate() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'endDate');
|
||||
@ -1849,6 +2042,12 @@ extension AlbumQueryProperty on QueryBuilder<Album, Album, QQueryProperty> {
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, String?, QQueryOperations> descriptionProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'description');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, DateTime?, QQueryOperations> endDateProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'endDate');
|
||||
|
@ -7,6 +7,7 @@ import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:openapi/api.dart' as api show AssetVisibility;
|
||||
import 'package:openapi/api.dart' hide AssetVisibility;
|
||||
|
||||
class DriftSyncStreamRepository extends DriftDatabaseRepository
|
||||
@ -231,22 +232,22 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
||||
});
|
||||
}
|
||||
|
||||
extension on SyncAssetV1TypeEnum {
|
||||
extension on AssetTypeEnum {
|
||||
AssetType toAssetType() => switch (this) {
|
||||
SyncAssetV1TypeEnum.IMAGE => AssetType.image,
|
||||
SyncAssetV1TypeEnum.VIDEO => AssetType.video,
|
||||
SyncAssetV1TypeEnum.AUDIO => AssetType.audio,
|
||||
SyncAssetV1TypeEnum.OTHER => AssetType.other,
|
||||
_ => throw Exception('Unknown SyncAssetV1TypeEnum value: $this'),
|
||||
AssetTypeEnum.IMAGE => AssetType.image,
|
||||
AssetTypeEnum.VIDEO => AssetType.video,
|
||||
AssetTypeEnum.AUDIO => AssetType.audio,
|
||||
AssetTypeEnum.OTHER => AssetType.other,
|
||||
_ => throw Exception('Unknown AssetType value: $this'),
|
||||
};
|
||||
}
|
||||
|
||||
extension on SyncAssetV1VisibilityEnum {
|
||||
extension on api.AssetVisibility {
|
||||
AssetVisibility toAssetVisibility() => switch (this) {
|
||||
SyncAssetV1VisibilityEnum.timeline => AssetVisibility.timeline,
|
||||
SyncAssetV1VisibilityEnum.hidden => AssetVisibility.hidden,
|
||||
SyncAssetV1VisibilityEnum.archive => AssetVisibility.archive,
|
||||
SyncAssetV1VisibilityEnum.locked => AssetVisibility.locked,
|
||||
_ => throw Exception('Unknown SyncAssetV1VisibilityEnum value: $this'),
|
||||
api.AssetVisibility.timeline => AssetVisibility.timeline,
|
||||
api.AssetVisibility.hidden => AssetVisibility.hidden,
|
||||
api.AssetVisibility.archive => AssetVisibility.archive,
|
||||
api.AssetVisibility.locked => AssetVisibility.locked,
|
||||
_ => throw Exception('Unknown AssetVisibility value: $this'),
|
||||
};
|
||||
}
|
||||
|
@ -7,7 +7,8 @@ abstract interface class IDownloadRepository {
|
||||
void Function(TaskProgressUpdate)? onTaskProgress;
|
||||
|
||||
Future<List<TaskRecord>> getLiveVideoTasks();
|
||||
Future<bool> download(DownloadTask task);
|
||||
Future<List<bool>> downloadAll(List<DownloadTask> tasks);
|
||||
|
||||
Future<bool> cancel(String id);
|
||||
Future<void> deleteAllTrackingRecords();
|
||||
Future<void> deleteRecordsWithIds(List<String> id);
|
||||
|
@ -3,18 +3,23 @@ import 'dart:convert';
|
||||
class AlbumViewerPageState {
|
||||
final bool isEditAlbum;
|
||||
final String editTitleText;
|
||||
final String editDescriptionText;
|
||||
|
||||
AlbumViewerPageState({
|
||||
required this.isEditAlbum,
|
||||
required this.editTitleText,
|
||||
required this.editDescriptionText,
|
||||
});
|
||||
|
||||
AlbumViewerPageState copyWith({
|
||||
bool? isEditAlbum,
|
||||
String? editTitleText,
|
||||
String? editDescriptionText,
|
||||
}) {
|
||||
return AlbumViewerPageState(
|
||||
isEditAlbum: isEditAlbum ?? this.isEditAlbum,
|
||||
editTitleText: editTitleText ?? this.editTitleText,
|
||||
editDescriptionText: editDescriptionText ?? this.editDescriptionText,
|
||||
);
|
||||
}
|
||||
|
||||
@ -23,6 +28,7 @@ class AlbumViewerPageState {
|
||||
|
||||
result.addAll({'isEditAlbum': isEditAlbum});
|
||||
result.addAll({'editTitleText': editTitleText});
|
||||
result.addAll({'editDescriptionText': editDescriptionText});
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -31,6 +37,7 @@ class AlbumViewerPageState {
|
||||
return AlbumViewerPageState(
|
||||
isEditAlbum: map['isEditAlbum'] ?? false,
|
||||
editTitleText: map['editTitleText'] ?? '',
|
||||
editDescriptionText: map['editDescriptionText'] ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
@ -41,7 +48,7 @@ class AlbumViewerPageState {
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'AlbumViewerPageState(isEditAlbum: $isEditAlbum, editTitleText: $editTitleText)';
|
||||
'AlbumViewerPageState(isEditAlbum: $isEditAlbum, editTitleText: $editTitleText, editDescriptionText: $editDescriptionText)';
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
@ -49,9 +56,13 @@ class AlbumViewerPageState {
|
||||
|
||||
return other is AlbumViewerPageState &&
|
||||
other.isEditAlbum == isEditAlbum &&
|
||||
other.editTitleText == editTitleText;
|
||||
other.editTitleText == editTitleText &&
|
||||
other.editDescriptionText == editDescriptionText;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => isEditAlbum.hashCode ^ editTitleText.hashCode;
|
||||
int get hashCode =>
|
||||
isEditAlbum.hashCode ^
|
||||
editTitleText.hashCode ^
|
||||
editDescriptionText.hashCode;
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ class AssetSelectionState {
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'SelectionAssetState(hasRemote: $hasRemote, hasMerged: $hasMerged, hasMerged: $hasMerged, selectedCount: $selectedCount)';
|
||||
'SelectionAssetState(hasRemote: $hasRemote, hasLocal: $hasLocal, hasMerged: $hasMerged, selectedCount: $selectedCount)';
|
||||
|
||||
@override
|
||||
bool operator ==(covariant AssetSelectionState other) {
|
||||
|
@ -26,9 +26,9 @@ class AlbumControlButton extends ConsumerWidget {
|
||||
);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0, top: 8, bottom: 16),
|
||||
padding: const EdgeInsets.only(left: 16.0),
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
height: 36,
|
||||
child: ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
children: [
|
||||
|
@ -30,15 +30,12 @@ class AlbumDateRange extends ConsumerWidget {
|
||||
final (startDate, endDate, shared) = data;
|
||||
|
||||
return Padding(
|
||||
padding: shared
|
||||
? const EdgeInsets.only(
|
||||
left: 16.0,
|
||||
bottom: 0.0,
|
||||
)
|
||||
: const EdgeInsets.only(left: 16.0, bottom: 8.0),
|
||||
padding: const EdgeInsets.only(left: 16.0),
|
||||
child: Text(
|
||||
_getDateRangeText(startDate, endDate),
|
||||
style: context.textTheme.labelLarge,
|
||||
style: context.textTheme.labelLarge?.copyWith(
|
||||
color: context.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
45
mobile/lib/pages/album/album_description.dart
Normal file
45
mobile/lib/pages/album/album_description.dart
Normal file
@ -0,0 +1,45 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/providers/album/current_album.provider.dart';
|
||||
import 'package:immich_mobile/widgets/album/album_viewer_editable_description.dart';
|
||||
import 'package:immich_mobile/providers/auth.provider.dart';
|
||||
|
||||
class AlbumDescription extends ConsumerWidget {
|
||||
const AlbumDescription({super.key, required this.descriptionFocusNode});
|
||||
|
||||
final FocusNode descriptionFocusNode;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final userId = ref.watch(authProvider).userId;
|
||||
final (isOwner, isRemote, albumDescription) = ref.watch(
|
||||
currentAlbumProvider.select((album) {
|
||||
if (album == null) {
|
||||
return const (false, false, '');
|
||||
}
|
||||
|
||||
return (album.ownerId == userId, album.isRemote, album.description);
|
||||
}),
|
||||
);
|
||||
|
||||
if (isOwner && isRemote) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 8, right: 8),
|
||||
child: AlbumViewerEditableDescription(
|
||||
albumDescription: albumDescription ?? 'add_a_description'.tr(),
|
||||
descriptionFocusNode: descriptionFocusNode,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 16, right: 8),
|
||||
child: Text(
|
||||
albumDescription ?? 'add_a_description'.tr(),
|
||||
style: context.textTheme.bodyLarge,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -36,7 +36,7 @@ class AlbumSharedUserIcons extends HookConsumerWidget {
|
||||
child: SizedBox(
|
||||
height: 50,
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
padding: const EdgeInsets.only(left: 16, bottom: 8),
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: ((context, index) {
|
||||
return Padding(
|
||||
|
@ -19,7 +19,11 @@ class AlbumTitle extends ConsumerWidget {
|
||||
return const (false, false, '');
|
||||
}
|
||||
|
||||
return (album.ownerId == userId, album.isRemote, album.name);
|
||||
return (
|
||||
album.ownerId == userId,
|
||||
album.isRemote,
|
||||
album.name,
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
@ -35,7 +39,12 @@ class AlbumTitle extends ConsumerWidget {
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 16, right: 8),
|
||||
child: Text(albumName, style: context.textTheme.headlineMedium),
|
||||
child: Text(
|
||||
albumName,
|
||||
style: context.textTheme.headlineLarge?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/models/albums/asset_selection_page_result.model.dart';
|
||||
import 'package:immich_mobile/pages/album/album_control_button.dart';
|
||||
import 'package:immich_mobile/pages/album/album_date_range.dart';
|
||||
import 'package:immich_mobile/pages/album/album_description.dart';
|
||||
import 'package:immich_mobile/pages/album/album_shared_user_icons.dart';
|
||||
import 'package:immich_mobile/pages/album/album_title.dart';
|
||||
import 'package:immich_mobile/providers/album/album.provider.dart';
|
||||
@ -36,6 +37,7 @@ class AlbumViewer extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
final titleFocusNode = useFocusNode();
|
||||
final descriptionFocusNode = useFocusNode();
|
||||
final userId = ref.watch(authProvider).userId;
|
||||
final isMultiselecting = ref.watch(multiselectProvider);
|
||||
final isProcessing = useProcessingOverlay();
|
||||
@ -106,15 +108,34 @@ class AlbumViewer extends HookConsumerWidget {
|
||||
MultiselectGrid(
|
||||
key: const ValueKey("albumViewerMultiselectGrid"),
|
||||
renderListProvider: albumTimelineProvider(album.id),
|
||||
topWidget: Column(
|
||||
topWidget: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
context.primaryColor.withValues(alpha: 0.04),
|
||||
context.primaryColor.withValues(alpha: 0.02),
|
||||
Colors.orange.withValues(alpha: 0.02),
|
||||
Colors.transparent,
|
||||
],
|
||||
stops: const [0.0, 0.3, 0.7, 1.0],
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 32),
|
||||
const AlbumDateRange(),
|
||||
AlbumTitle(
|
||||
key: const ValueKey("albumTitle"),
|
||||
titleFocusNode: titleFocusNode,
|
||||
),
|
||||
const AlbumDateRange(),
|
||||
AlbumDescription(
|
||||
key: const ValueKey("albumDescription"),
|
||||
descriptionFocusNode: descriptionFocusNode,
|
||||
),
|
||||
const AlbumSharedUserIcons(),
|
||||
if (album.isRemote)
|
||||
AlbumControlButton(
|
||||
@ -122,8 +143,10 @@ class AlbumViewer extends HookConsumerWidget {
|
||||
onAddPhotosPressed: onAddPhotosPressed,
|
||||
onAddUsersPressed: onAddUsersPressed,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
),
|
||||
),
|
||||
onRemoveFromAlbum: onRemoveFromAlbumPressed,
|
||||
editEnabled: album.ownerId == userId,
|
||||
),
|
||||
@ -136,6 +159,7 @@ class AlbumViewer extends HookConsumerWidget {
|
||||
child: AlbumViewerAppbar(
|
||||
key: const ValueKey("albumViewerAppbar"),
|
||||
titleFocusNode: titleFocusNode,
|
||||
descriptionFocusNode: descriptionFocusNode,
|
||||
userId: userId,
|
||||
onAddPhotos: onAddPhotosPressed,
|
||||
onAddUsers: onAddUsersPressed,
|
||||
|
@ -8,9 +8,11 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/models/albums/asset_selection_page_result.model.dart';
|
||||
import 'package:immich_mobile/providers/album/album.provider.dart';
|
||||
import 'package:immich_mobile/providers/album/album_title.provider.dart';
|
||||
import 'package:immich_mobile/providers/album/album_viewer.provider.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/widgets/album/album_action_filled_button.dart';
|
||||
import 'package:immich_mobile/widgets/album/album_title_text_field.dart';
|
||||
import 'package:immich_mobile/widgets/album/album_viewer_editable_description.dart';
|
||||
import 'package:immich_mobile/widgets/album/shared_album_thumbnail_image.dart';
|
||||
|
||||
@RoutePage()
|
||||
@ -28,6 +30,7 @@ class CreateAlbumPage extends HookConsumerWidget {
|
||||
final albumTitleController =
|
||||
useTextEditingController.fromValue(TextEditingValue.empty);
|
||||
final albumTitleTextFieldFocusNode = useFocusNode();
|
||||
final albumDescriptionTextFieldFocusNode = useFocusNode();
|
||||
final isAlbumTitleTextFieldFocus = useState(false);
|
||||
final isAlbumTitleEmpty = useState(true);
|
||||
final selectedAssets = useState<Set<Asset>>(
|
||||
@ -36,6 +39,7 @@ class CreateAlbumPage extends HookConsumerWidget {
|
||||
|
||||
void onBackgroundTapped() {
|
||||
albumTitleTextFieldFocusNode.unfocus();
|
||||
albumDescriptionTextFieldFocusNode.unfocus();
|
||||
isAlbumTitleTextFieldFocus.value = false;
|
||||
|
||||
if (albumTitleController.text.isEmpty) {
|
||||
@ -77,6 +81,19 @@ class CreateAlbumPage extends HookConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
buildDescriptionInputField() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 10,
|
||||
left: 10,
|
||||
),
|
||||
child: AlbumViewerEditableDescription(
|
||||
albumDescription: '',
|
||||
descriptionFocusNode: albumDescriptionTextFieldFocusNode,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
buildTitle() {
|
||||
if (selectedAssets.value.isEmpty) {
|
||||
return SliverToBoxAdapter(
|
||||
@ -178,18 +195,18 @@ class CreateAlbumPage extends HookConsumerWidget {
|
||||
return const SliverToBoxAdapter();
|
||||
}
|
||||
|
||||
createNonSharedAlbum() async {
|
||||
Future<void> createAlbum() async {
|
||||
onBackgroundTapped();
|
||||
var newAlbum = await ref.watch(albumProvider.notifier).createAlbum(
|
||||
ref.watch(albumTitleProvider),
|
||||
ref.read(albumTitleProvider),
|
||||
selectedAssets.value,
|
||||
);
|
||||
|
||||
if (newAlbum != null) {
|
||||
ref.watch(albumProvider.notifier).refreshRemoteAlbums();
|
||||
ref.read(albumProvider.notifier).refreshRemoteAlbums();
|
||||
selectedAssets.value = {};
|
||||
ref.watch(albumTitleProvider.notifier).clearAlbumTitle();
|
||||
|
||||
ref.read(albumTitleProvider.notifier).clearAlbumTitle();
|
||||
ref.read(albumViewerProvider.notifier).disableEditAlbum();
|
||||
context.replaceRoute(AlbumViewerRoute(albumId: newAlbum.id));
|
||||
}
|
||||
}
|
||||
@ -211,9 +228,8 @@ class CreateAlbumPage extends HookConsumerWidget {
|
||||
).tr(),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: albumTitleController.text.isNotEmpty
|
||||
? createNonSharedAlbum
|
||||
: null,
|
||||
onPressed:
|
||||
albumTitleController.text.isNotEmpty ? createAlbum : null,
|
||||
child: Text(
|
||||
'create'.tr(),
|
||||
style: TextStyle(
|
||||
@ -237,10 +253,11 @@ class CreateAlbumPage extends HookConsumerWidget {
|
||||
pinned: true,
|
||||
floating: false,
|
||||
bottom: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(96.0),
|
||||
preferredSize: const Size.fromHeight(125.0),
|
||||
child: Column(
|
||||
children: [
|
||||
buildTitleInputField(),
|
||||
buildDescriptionInputField(),
|
||||
if (selectedAssets.value.isNotEmpty) buildControlButton(),
|
||||
],
|
||||
),
|
||||
|
@ -72,7 +72,9 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.router.current.name != ShareIntentRoute.name) {
|
||||
context.replaceRoute(const TabControllerRoute());
|
||||
}
|
||||
|
||||
final hasPermission =
|
||||
await ref.read(galleryPermissionNotifier.notifier).hasPermission;
|
||||
|
@ -7,6 +7,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart';
|
||||
import 'package:immich_mobile/pages/common/large_leading_tile.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/share_intent_upload.provider.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/utils/url_helper.dart';
|
||||
|
||||
@RoutePage()
|
||||
@ -20,6 +21,11 @@ class ShareIntentPage extends HookConsumerWidget {
|
||||
final currentEndpoint = getServerUrl() ?? '--';
|
||||
final candidates = ref.watch(shareIntentUploadProvider);
|
||||
final isUploaded = useState(false);
|
||||
useOnAppLifecycleStateChange((previous, current) {
|
||||
if (current == AppLifecycleState.resumed) {
|
||||
isUploaded.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
void removeAttachment(ShareIntentAttachment attachment) {
|
||||
ref.read(shareIntentUploadProvider.notifier).removeAttachment(attachment);
|
||||
@ -66,6 +72,14 @@ class ShareIntentPage extends HookConsumerWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
leading: IconButton(
|
||||
onPressed: () {
|
||||
context.navigateTo(
|
||||
const TabControllerRoute(),
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
),
|
||||
),
|
||||
body: ListView.builder(
|
||||
itemCount: attachments.length,
|
||||
|
@ -5,7 +5,13 @@ import 'package:immich_mobile/entities/album.entity.dart';
|
||||
|
||||
class AlbumViewerNotifier extends StateNotifier<AlbumViewerPageState> {
|
||||
AlbumViewerNotifier(this.ref)
|
||||
: super(AlbumViewerPageState(editTitleText: "", isEditAlbum: false));
|
||||
: super(
|
||||
AlbumViewerPageState(
|
||||
editTitleText: "",
|
||||
isEditAlbum: false,
|
||||
editDescriptionText: "",
|
||||
),
|
||||
);
|
||||
|
||||
final Ref ref;
|
||||
|
||||
@ -21,12 +27,24 @@ class AlbumViewerNotifier extends StateNotifier<AlbumViewerPageState> {
|
||||
state = state.copyWith(editTitleText: newTitle);
|
||||
}
|
||||
|
||||
void setEditDescriptionText(String newDescription) {
|
||||
state = state.copyWith(editDescriptionText: newDescription);
|
||||
}
|
||||
|
||||
void remoteEditTitleText() {
|
||||
state = state.copyWith(editTitleText: "");
|
||||
}
|
||||
|
||||
void remoteEditDescriptionText() {
|
||||
state = state.copyWith(editDescriptionText: "");
|
||||
}
|
||||
|
||||
void resetState() {
|
||||
state = state.copyWith(editTitleText: "", isEditAlbum: false);
|
||||
state = state.copyWith(
|
||||
editTitleText: "",
|
||||
isEditAlbum: false,
|
||||
editDescriptionText: "",
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> changeAlbumTitle(
|
||||
@ -46,6 +64,28 @@ class AlbumViewerNotifier extends StateNotifier<AlbumViewerPageState> {
|
||||
state = state.copyWith(editTitleText: "", isEditAlbum: false);
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<bool> changeAlbumDescription(
|
||||
Album album,
|
||||
String newAlbumDescription,
|
||||
) async {
|
||||
AlbumService service = ref.watch(albumServiceProvider);
|
||||
|
||||
bool isSuccess = await service.changeDescriptionAlbum(
|
||||
album,
|
||||
newAlbumDescription,
|
||||
);
|
||||
|
||||
if (isSuccess) {
|
||||
state = state.copyWith(editDescriptionText: "", isEditAlbum: false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
state = state.copyWith(editDescriptionText: "", isEditAlbum: false);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
final albumViewerProvider =
|
||||
|
@ -140,6 +140,10 @@ class DownloadStateNotifier extends StateNotifier<DownloadState> {
|
||||
});
|
||||
}
|
||||
|
||||
Future<List<bool>> downloadAllAsset(List<Asset> assets) async {
|
||||
return await _downloadService.downloadAll(assets);
|
||||
}
|
||||
|
||||
void downloadAsset(Asset asset, BuildContext context) async {
|
||||
await _downloadService.download(asset);
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository {
|
||||
String name, {
|
||||
required Iterable<String> assetIds,
|
||||
Iterable<String> sharedUserIds = const [],
|
||||
String? description,
|
||||
}) async {
|
||||
final users = sharedUserIds.map(
|
||||
(id) => AlbumUserCreateDto(userId: id, role: AlbumUserRole.editor),
|
||||
@ -44,6 +45,7 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository {
|
||||
_api.createAlbum(
|
||||
CreateAlbumDto(
|
||||
albumName: name,
|
||||
description: description,
|
||||
assetIds: assetIds.toList(),
|
||||
albumUsers: users.toList(),
|
||||
),
|
||||
@ -161,6 +163,7 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository {
|
||||
lastModifiedAssetTimestamp: dto.lastModifiedAssetTimestamp,
|
||||
shared: dto.shared,
|
||||
startDate: dto.startDate,
|
||||
description: dto.description,
|
||||
endDate: dto.endDate,
|
||||
activityEnabled: dto.isActivityEnabled,
|
||||
sortOrder: dto.order == AssetOrder.asc ? SortOrder.asc : SortOrder.desc,
|
||||
@ -174,6 +177,7 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository {
|
||||
album.sharedUsers.addAll(users.map(entity.User.fromDto));
|
||||
final assets = dto.assets.map(Asset.remote).toList();
|
||||
album.assets.addAll(assets);
|
||||
|
||||
return album;
|
||||
}
|
||||
}
|
||||
|
@ -39,8 +39,8 @@ class DownloadRepository implements IDownloadRepository {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> download(DownloadTask task) {
|
||||
return FileDownloader().enqueue(task);
|
||||
Future<List<bool>> downloadAll(List<DownloadTask> tasks) {
|
||||
return FileDownloader().enqueueAll(tasks);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -422,6 +422,25 @@ class AlbumService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> changeDescriptionAlbum(
|
||||
Album album,
|
||||
String newAlbumDescription,
|
||||
) async {
|
||||
try {
|
||||
final updatedAlbum = await _albumApiRepository.update(
|
||||
album.remoteId!,
|
||||
description: newAlbumDescription,
|
||||
);
|
||||
|
||||
album.description = updatedAlbum.description;
|
||||
await _albumRepository.update(album);
|
||||
return true;
|
||||
} catch (e) {
|
||||
debugPrint("Error changeDescriptionAlbum ${e.toString()}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<Album?> getAlbumByName(
|
||||
String name, {
|
||||
bool? remote,
|
||||
|
@ -159,9 +159,19 @@ class DownloadService {
|
||||
return await FileDownloader().cancelTaskWithId(id);
|
||||
}
|
||||
|
||||
Future<List<bool>> downloadAll(List<Asset> assets) async {
|
||||
return await _downloadRepository
|
||||
.downloadAll(assets.expand(_createDownloadTasks).toList());
|
||||
}
|
||||
|
||||
Future<void> download(Asset asset) async {
|
||||
final tasks = _createDownloadTasks(asset);
|
||||
await _downloadRepository.downloadAll(tasks);
|
||||
}
|
||||
|
||||
List<DownloadTask> _createDownloadTasks(Asset asset) {
|
||||
if (asset.isImage && asset.livePhotoVideoId != null && Platform.isIOS) {
|
||||
await _downloadRepository.download(
|
||||
return [
|
||||
_buildDownloadTask(
|
||||
asset.remoteId!,
|
||||
asset.fileName,
|
||||
@ -171,9 +181,6 @@ class DownloadService {
|
||||
id: asset.remoteId!,
|
||||
).toJson(),
|
||||
),
|
||||
);
|
||||
|
||||
await _downloadRepository.download(
|
||||
_buildDownloadTask(
|
||||
asset.livePhotoVideoId!,
|
||||
asset.fileName
|
||||
@ -185,16 +192,20 @@ class DownloadService {
|
||||
id: asset.remoteId!,
|
||||
).toJson(),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
await _downloadRepository.download(
|
||||
];
|
||||
}
|
||||
|
||||
if (asset.remoteId == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
_buildDownloadTask(
|
||||
asset.remoteId!,
|
||||
asset.fileName,
|
||||
group: asset.isImage ? downloadGroupImage : downloadGroupVideo,
|
||||
),
|
||||
);
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
DownloadTask _buildDownloadTask(
|
||||
|
@ -451,6 +451,7 @@ class SyncService {
|
||||
final usersToLink = await _userRepository.getByUserIds(userIdsToAdd);
|
||||
|
||||
album.name = dto.name;
|
||||
album.description = dto.description;
|
||||
album.shared = dto.shared;
|
||||
album.createdAt = dto.createdAt;
|
||||
album.modifiedAt = dto.modifiedAt;
|
||||
@ -643,6 +644,7 @@ class SyncService {
|
||||
toUpdate.isEmpty &&
|
||||
toDelete.isEmpty &&
|
||||
dbAlbum.name == deviceAlbum.name &&
|
||||
dbAlbum.description == deviceAlbum.description &&
|
||||
dbAlbum.modifiedAt.isAtSameMomentAs(deviceAlbum.modifiedAt)) {
|
||||
// changes only affeted excluded albums
|
||||
_log.info(
|
||||
@ -670,6 +672,7 @@ class SyncService {
|
||||
deleteCandidates.addAll(toDelete);
|
||||
existing.addAll(existingInDb);
|
||||
dbAlbum.name = deviceAlbum.name;
|
||||
dbAlbum.description = deviceAlbum.description;
|
||||
dbAlbum.modifiedAt = deviceAlbum.modifiedAt;
|
||||
if (dbAlbum.thumbnail.value != null &&
|
||||
toDelete.contains(dbAlbum.thumbnail.value)) {
|
||||
@ -943,6 +946,7 @@ class SyncService {
|
||||
Album dbAlbum,
|
||||
) async {
|
||||
return deviceAlbum.name != dbAlbum.name ||
|
||||
deviceAlbum.description != dbAlbum.description ||
|
||||
!deviceAlbum.modifiedAt.isAtSameMomentAs(dbAlbum.modifiedAt) ||
|
||||
await _albumMediaRepository.getAssetCount(deviceAlbum.localId!) !=
|
||||
(await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))
|
||||
@ -1101,6 +1105,7 @@ class SyncService {
|
||||
bool _hasRemoteAlbumChanged(Album remoteAlbum, Album dbAlbum) {
|
||||
return remoteAlbum.remoteAssetCount != dbAlbum.assetCount ||
|
||||
remoteAlbum.name != dbAlbum.name ||
|
||||
remoteAlbum.description != dbAlbum.description ||
|
||||
remoteAlbum.remoteThumbnailAssetId != dbAlbum.thumbnail.value?.remoteId ||
|
||||
remoteAlbum.shared != dbAlbum.shared ||
|
||||
remoteAlbum.remoteUsers.length != dbAlbum.sharedUsers.length ||
|
||||
|
@ -16,7 +16,7 @@ class AlbumActionFilledButton extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 16.0),
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: FilledButton.icon(
|
||||
style: FilledButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 16),
|
||||
@ -32,9 +32,7 @@ class AlbumActionFilledButton extends StatelessWidget {
|
||||
),
|
||||
label: Text(
|
||||
labelText,
|
||||
style: context.textTheme.labelMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
style: context.textTheme.labelLarge?.copyWith(),
|
||||
),
|
||||
onPressed: onPressed,
|
||||
),
|
||||
|
@ -18,6 +18,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
||||
super.key,
|
||||
required this.userId,
|
||||
required this.titleFocusNode,
|
||||
required this.descriptionFocusNode,
|
||||
this.onAddPhotos,
|
||||
this.onAddUsers,
|
||||
required this.onActivities,
|
||||
@ -25,6 +26,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
||||
|
||||
final String userId;
|
||||
final FocusNode titleFocusNode;
|
||||
final FocusNode descriptionFocusNode;
|
||||
final void Function()? onAddPhotos;
|
||||
final void Function()? onAddUsers;
|
||||
final void Function() onActivities;
|
||||
@ -48,6 +50,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
||||
|
||||
final albumViewer = ref.watch(albumViewerProvider);
|
||||
final newAlbumTitle = albumViewer.editTitleText;
|
||||
final newAlbumDescription = albumViewer.editDescriptionText;
|
||||
final isEditAlbum = albumViewer.isEditAlbum;
|
||||
|
||||
final comments = album.shared
|
||||
@ -277,10 +280,10 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
||||
if (isEditAlbum) {
|
||||
return IconButton(
|
||||
onPressed: () async {
|
||||
if (newAlbumTitle.isNotEmpty) {
|
||||
bool isSuccess = await ref
|
||||
.watch(albumViewerProvider.notifier)
|
||||
.changeAlbumTitle(album, newAlbumTitle);
|
||||
|
||||
if (!isSuccess) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
@ -289,8 +292,25 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
||||
toastType: ToastType.error,
|
||||
);
|
||||
}
|
||||
|
||||
titleFocusNode.unfocus();
|
||||
} else if (newAlbumDescription.isNotEmpty) {
|
||||
bool isSuccessDescription = await ref
|
||||
.watch(albumViewerProvider.notifier)
|
||||
.changeAlbumDescription(album, newAlbumDescription);
|
||||
if (!isSuccessDescription) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: "album_viewer_appbar_share_err_description".tr(),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
toastType: ToastType.error,
|
||||
);
|
||||
}
|
||||
descriptionFocusNode.unfocus();
|
||||
} else {
|
||||
titleFocusNode.unfocus();
|
||||
descriptionFocusNode.unfocus();
|
||||
ref.read(albumViewerProvider.notifier).disableEditAlbum();
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.check_rounded),
|
||||
splashRadius: 25,
|
||||
|
102
mobile/lib/widgets/album/album_viewer_editable_description.dart
Normal file
102
mobile/lib/widgets/album/album_viewer_editable_description.dart
Normal file
@ -0,0 +1,102 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/providers/album/album_viewer.provider.dart';
|
||||
|
||||
class AlbumViewerEditableDescription extends HookConsumerWidget {
|
||||
final String albumDescription;
|
||||
final FocusNode descriptionFocusNode;
|
||||
const AlbumViewerEditableDescription({
|
||||
super.key,
|
||||
required this.albumDescription,
|
||||
required this.descriptionFocusNode,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final albumViewerState = ref.watch(albumViewerProvider);
|
||||
|
||||
final descriptionTextEditController = useTextEditingController(
|
||||
text: albumViewerState.isEditAlbum &&
|
||||
albumViewerState.editDescriptionText.isNotEmpty
|
||||
? albumViewerState.editDescriptionText
|
||||
: albumDescription,
|
||||
);
|
||||
|
||||
void onFocusModeChange() {
|
||||
if (!descriptionFocusNode.hasFocus &&
|
||||
descriptionTextEditController.text.isEmpty) {
|
||||
ref.watch(albumViewerProvider.notifier).setEditDescriptionText("");
|
||||
descriptionTextEditController.text = "";
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
descriptionFocusNode.addListener(onFocusModeChange);
|
||||
return () {
|
||||
descriptionFocusNode.removeListener(onFocusModeChange);
|
||||
};
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: TextField(
|
||||
onChanged: (value) {
|
||||
if (value.isEmpty) {
|
||||
} else {
|
||||
ref
|
||||
.watch(albumViewerProvider.notifier)
|
||||
.setEditDescriptionText(value);
|
||||
}
|
||||
},
|
||||
focusNode: descriptionFocusNode,
|
||||
style: context.textTheme.bodyMedium,
|
||||
maxLines: 3,
|
||||
minLines: 1,
|
||||
controller: descriptionTextEditController,
|
||||
onTap: () {
|
||||
context.focusScope.requestFocus(descriptionFocusNode);
|
||||
|
||||
ref
|
||||
.watch(albumViewerProvider.notifier)
|
||||
.setEditDescriptionText(albumDescription);
|
||||
ref.watch(albumViewerProvider.notifier).enableEditAlbum();
|
||||
|
||||
if (descriptionTextEditController.text == '') {
|
||||
descriptionTextEditController.clear();
|
||||
}
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
contentPadding: const EdgeInsets.all(8),
|
||||
suffixIcon: descriptionFocusNode.hasFocus
|
||||
? IconButton(
|
||||
onPressed: () {
|
||||
descriptionTextEditController.clear();
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.cancel_rounded,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
splashRadius: 10,
|
||||
)
|
||||
: null,
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.transparent),
|
||||
),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.transparent),
|
||||
),
|
||||
focusColor: Colors.grey[300],
|
||||
fillColor: context.scaffoldBackgroundColor,
|
||||
filled: descriptionFocusNode.hasFocus,
|
||||
hintText: 'add_a_description'.tr(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -52,7 +52,9 @@ class AlbumViewerEditableTitle extends HookConsumerWidget {
|
||||
}
|
||||
},
|
||||
focusNode: titleFocusNode,
|
||||
style: context.textTheme.headlineMedium,
|
||||
style: context.textTheme.headlineLarge?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
controller: titleTextEditController,
|
||||
onTap: () {
|
||||
context.focusScope.requestFocus(titleFocusNode);
|
||||
@ -65,8 +67,10 @@ class AlbumViewerEditableTitle extends HookConsumerWidget {
|
||||
}
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 0,
|
||||
),
|
||||
suffixIcon: titleFocusNode.hasFocus
|
||||
? IconButton(
|
||||
onPressed: () {
|
||||
|
@ -39,6 +39,7 @@ class ControlBottomAppBar extends HookConsumerWidget {
|
||||
final void Function()? onEditLocation;
|
||||
final void Function()? onRemoveFromAlbum;
|
||||
final void Function()? onToggleLocked;
|
||||
final void Function()? onDownload;
|
||||
|
||||
final bool enabled;
|
||||
final bool unfavorite;
|
||||
@ -56,6 +57,7 @@ class ControlBottomAppBar extends HookConsumerWidget {
|
||||
required this.onAddToAlbum,
|
||||
required this.onCreateNewAlbum,
|
||||
required this.onUpload,
|
||||
this.onDownload,
|
||||
this.onStack,
|
||||
this.onEditTime,
|
||||
this.onEditLocation,
|
||||
@ -158,6 +160,15 @@ class ControlBottomAppBar extends HookConsumerWidget {
|
||||
label: (unfavorite ? "unfavorite" : "favorite").tr(),
|
||||
onPressed: enabled ? onFavorite : null,
|
||||
),
|
||||
if (hasRemote && onDownload != null)
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 90),
|
||||
child: ControlBoxButton(
|
||||
iconData: Icons.download,
|
||||
label: "download".tr(),
|
||||
onPressed: onDownload,
|
||||
),
|
||||
),
|
||||
if (hasLocal && hasRemote && onDelete != null && !isInLockedView)
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 90),
|
||||
|
@ -14,6 +14,7 @@ import 'package:immich_mobile/extensions/collection_extensions.dart';
|
||||
import 'package:immich_mobile/models/asset_selection_state.dart';
|
||||
import 'package:immich_mobile/providers/album/album.provider.dart';
|
||||
import 'package:immich_mobile/providers/asset.provider.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/download.provider.dart';
|
||||
import 'package:immich_mobile/providers/backup/manual_upload.provider.dart';
|
||||
import 'package:immich_mobile/providers/multiselect.provider.dart';
|
||||
import 'package:immich_mobile/providers/routes.provider.dart';
|
||||
@ -23,6 +24,7 @@ import 'package:immich_mobile/services/album.service.dart';
|
||||
import 'package:immich_mobile/services/stack.service.dart';
|
||||
import 'package:immich_mobile/utils/immich_loading_overlay.dart';
|
||||
import 'package:immich_mobile/utils/selection_handlers.dart';
|
||||
import 'package:immich_mobile/utils/translation.dart';
|
||||
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
||||
import 'package:immich_mobile/widgets/asset_grid/control_bottom_app_bar.dart';
|
||||
import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart';
|
||||
@ -44,6 +46,7 @@ class MultiselectGrid extends HookConsumerWidget {
|
||||
this.editEnabled = false,
|
||||
this.unarchive = false,
|
||||
this.unfavorite = false,
|
||||
this.downloadEnabled = true,
|
||||
this.emptyIndicator,
|
||||
});
|
||||
|
||||
@ -57,6 +60,7 @@ class MultiselectGrid extends HookConsumerWidget {
|
||||
final bool archiveEnabled;
|
||||
final bool unarchive;
|
||||
final bool deleteEnabled;
|
||||
final bool downloadEnabled;
|
||||
final bool favoriteEnabled;
|
||||
final bool unfavorite;
|
||||
final bool editEnabled;
|
||||
@ -239,6 +243,39 @@ class MultiselectGrid extends HookConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
void onDownload() async {
|
||||
processing.value = true;
|
||||
try {
|
||||
final toDownload = selection.value.toList();
|
||||
|
||||
final results = await ref
|
||||
.read(downloadStateProvider.notifier)
|
||||
.downloadAllAsset(toDownload);
|
||||
|
||||
final totalCount = toDownload.length;
|
||||
final successCount = results.where((e) => e).length;
|
||||
final failedCount = totalCount - successCount;
|
||||
|
||||
final msg = failedCount > 0
|
||||
? t('assets_downloaded_failed', {
|
||||
'count': successCount,
|
||||
'error': failedCount,
|
||||
})
|
||||
: t('assets_downloaded_successfully', {
|
||||
'count': successCount,
|
||||
});
|
||||
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: msg,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
} finally {
|
||||
processing.value = false;
|
||||
selectionEnabledHook.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
void onDeleteRemote([bool shouldDeletePermanently = false]) async {
|
||||
processing.value = true;
|
||||
try {
|
||||
@ -474,6 +511,7 @@ class MultiselectGrid extends HookConsumerWidget {
|
||||
onArchive: archiveEnabled ? onArchiveAsset : null,
|
||||
onDelete: deleteEnabled ? onDelete : null,
|
||||
onDeleteServer: deleteEnabled ? onDeleteRemote : null,
|
||||
onDownload: downloadEnabled ? onDownload : null,
|
||||
|
||||
/// local file deletion is allowed irrespective of [deleteEnabled] since it has
|
||||
/// nothing to do with the state of the asset in the Immich server
|
||||
|
20
mobile/openapi/lib/api/timeline_api.dart
generated
20
mobile/openapi/lib/api/timeline_api.dart
generated
@ -31,10 +31,6 @@ class TimelineApi {
|
||||
///
|
||||
/// * [AssetOrder] order:
|
||||
///
|
||||
/// * [num] page:
|
||||
///
|
||||
/// * [num] pageSize:
|
||||
///
|
||||
/// * [String] personId:
|
||||
///
|
||||
/// * [String] tagId:
|
||||
@ -46,7 +42,7 @@ class TimelineApi {
|
||||
/// * [bool] withPartners:
|
||||
///
|
||||
/// * [bool] withStacked:
|
||||
Future<Response> getTimeBucketWithHttpInfo(String timeBucket, { String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, num? page, num? pageSize, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async {
|
||||
Future<Response> getTimeBucketWithHttpInfo(String timeBucket, { String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final apiPath = r'/timeline/bucket';
|
||||
|
||||
@ -72,12 +68,6 @@ class TimelineApi {
|
||||
if (order != null) {
|
||||
queryParams.addAll(_queryParams('', 'order', order));
|
||||
}
|
||||
if (page != null) {
|
||||
queryParams.addAll(_queryParams('', 'page', page));
|
||||
}
|
||||
if (pageSize != null) {
|
||||
queryParams.addAll(_queryParams('', 'pageSize', pageSize));
|
||||
}
|
||||
if (personId != null) {
|
||||
queryParams.addAll(_queryParams('', 'personId', personId));
|
||||
}
|
||||
@ -126,10 +116,6 @@ class TimelineApi {
|
||||
///
|
||||
/// * [AssetOrder] order:
|
||||
///
|
||||
/// * [num] page:
|
||||
///
|
||||
/// * [num] pageSize:
|
||||
///
|
||||
/// * [String] personId:
|
||||
///
|
||||
/// * [String] tagId:
|
||||
@ -141,8 +127,8 @@ class TimelineApi {
|
||||
/// * [bool] withPartners:
|
||||
///
|
||||
/// * [bool] withStacked:
|
||||
Future<TimeBucketAssetResponseDto?> getTimeBucket(String timeBucket, { String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, num? page, num? pageSize, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async {
|
||||
final response = await getTimeBucketWithHttpInfo(timeBucket, albumId: albumId, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, page: page, pageSize: pageSize, personId: personId, tagId: tagId, userId: userId, visibility: visibility, withPartners: withPartners, withStacked: withStacked, );
|
||||
Future<TimeBucketAssetResponseDto?> getTimeBucket(String timeBucket, { String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async {
|
||||
final response = await getTimeBucketWithHttpInfo(timeBucket, albumId: albumId, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, tagId: tagId, userId: userId, visibility: visibility, withPartners: withPartners, withStacked: withStacked, );
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
|
@ -14,25 +14,31 @@ class ActivityStatisticsResponseDto {
|
||||
/// Returns a new [ActivityStatisticsResponseDto] instance.
|
||||
ActivityStatisticsResponseDto({
|
||||
required this.comments,
|
||||
required this.likes,
|
||||
});
|
||||
|
||||
int comments;
|
||||
|
||||
int likes;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is ActivityStatisticsResponseDto &&
|
||||
other.comments == comments;
|
||||
other.comments == comments &&
|
||||
other.likes == likes;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(comments.hashCode);
|
||||
(comments.hashCode) +
|
||||
(likes.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'ActivityStatisticsResponseDto[comments=$comments]';
|
||||
String toString() => 'ActivityStatisticsResponseDto[comments=$comments, likes=$likes]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'comments'] = this.comments;
|
||||
json[r'likes'] = this.likes;
|
||||
return json;
|
||||
}
|
||||
|
||||
@ -46,6 +52,7 @@ class ActivityStatisticsResponseDto {
|
||||
|
||||
return ActivityStatisticsResponseDto(
|
||||
comments: mapValueOfType<int>(json, r'comments')!,
|
||||
likes: mapValueOfType<int>(json, r'likes')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
@ -94,6 +101,7 @@ class ActivityStatisticsResponseDto {
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'comments',
|
||||
'likes',
|
||||
};
|
||||
}
|
||||
|
||||
|
78
mobile/openapi/lib/model/sync_album_user_v1.dart
generated
78
mobile/openapi/lib/model/sync_album_user_v1.dart
generated
@ -20,7 +20,7 @@ class SyncAlbumUserV1 {
|
||||
|
||||
String albumId;
|
||||
|
||||
SyncAlbumUserV1RoleEnum role;
|
||||
AlbumUserRole role;
|
||||
|
||||
String userId;
|
||||
|
||||
@ -58,7 +58,7 @@ class SyncAlbumUserV1 {
|
||||
|
||||
return SyncAlbumUserV1(
|
||||
albumId: mapValueOfType<String>(json, r'albumId')!,
|
||||
role: SyncAlbumUserV1RoleEnum.fromJson(json[r'role'])!,
|
||||
role: AlbumUserRole.fromJson(json[r'role'])!,
|
||||
userId: mapValueOfType<String>(json, r'userId')!,
|
||||
);
|
||||
}
|
||||
@ -113,77 +113,3 @@ class SyncAlbumUserV1 {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
class SyncAlbumUserV1RoleEnum {
|
||||
/// Instantiate a new enum with the provided [value].
|
||||
const SyncAlbumUserV1RoleEnum._(this.value);
|
||||
|
||||
/// The underlying value of this enum member.
|
||||
final String value;
|
||||
|
||||
@override
|
||||
String toString() => value;
|
||||
|
||||
String toJson() => value;
|
||||
|
||||
static const editor = SyncAlbumUserV1RoleEnum._(r'editor');
|
||||
static const viewer = SyncAlbumUserV1RoleEnum._(r'viewer');
|
||||
|
||||
/// List of all possible values in this [enum][SyncAlbumUserV1RoleEnum].
|
||||
static const values = <SyncAlbumUserV1RoleEnum>[
|
||||
editor,
|
||||
viewer,
|
||||
];
|
||||
|
||||
static SyncAlbumUserV1RoleEnum? fromJson(dynamic value) => SyncAlbumUserV1RoleEnumTypeTransformer().decode(value);
|
||||
|
||||
static List<SyncAlbumUserV1RoleEnum> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <SyncAlbumUserV1RoleEnum>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = SyncAlbumUserV1RoleEnum.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
}
|
||||
|
||||
/// Transformation class that can [encode] an instance of [SyncAlbumUserV1RoleEnum] to String,
|
||||
/// and [decode] dynamic data back to [SyncAlbumUserV1RoleEnum].
|
||||
class SyncAlbumUserV1RoleEnumTypeTransformer {
|
||||
factory SyncAlbumUserV1RoleEnumTypeTransformer() => _instance ??= const SyncAlbumUserV1RoleEnumTypeTransformer._();
|
||||
|
||||
const SyncAlbumUserV1RoleEnumTypeTransformer._();
|
||||
|
||||
String encode(SyncAlbumUserV1RoleEnum data) => data.value;
|
||||
|
||||
/// Decodes a [dynamic value][data] to a SyncAlbumUserV1RoleEnum.
|
||||
///
|
||||
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
|
||||
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
|
||||
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
|
||||
///
|
||||
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
|
||||
/// and users are still using an old app with the old code.
|
||||
SyncAlbumUserV1RoleEnum? decode(dynamic data, {bool allowNull = true}) {
|
||||
if (data != null) {
|
||||
switch (data) {
|
||||
case r'editor': return SyncAlbumUserV1RoleEnum.editor;
|
||||
case r'viewer': return SyncAlbumUserV1RoleEnum.viewer;
|
||||
default:
|
||||
if (!allowNull) {
|
||||
throw ArgumentError('Unknown enum value to decode: $data');
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Singleton [SyncAlbumUserV1RoleEnumTypeTransformer] instance.
|
||||
static SyncAlbumUserV1RoleEnumTypeTransformer? _instance;
|
||||
}
|
||||
|
||||
|
||||
|
168
mobile/openapi/lib/model/sync_asset_v1.dart
generated
168
mobile/openapi/lib/model/sync_asset_v1.dart
generated
@ -47,9 +47,9 @@ class SyncAssetV1 {
|
||||
|
||||
String? thumbhash;
|
||||
|
||||
SyncAssetV1TypeEnum type;
|
||||
AssetTypeEnum type;
|
||||
|
||||
SyncAssetV1VisibilityEnum visibility;
|
||||
AssetVisibility visibility;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is SyncAssetV1 &&
|
||||
@ -141,8 +141,8 @@ class SyncAssetV1 {
|
||||
originalFileName: mapValueOfType<String>(json, r'originalFileName')!,
|
||||
ownerId: mapValueOfType<String>(json, r'ownerId')!,
|
||||
thumbhash: mapValueOfType<String>(json, r'thumbhash'),
|
||||
type: SyncAssetV1TypeEnum.fromJson(json[r'type'])!,
|
||||
visibility: SyncAssetV1VisibilityEnum.fromJson(json[r'visibility'])!,
|
||||
type: AssetTypeEnum.fromJson(json[r'type'])!,
|
||||
visibility: AssetVisibility.fromJson(json[r'visibility'])!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
@ -205,163 +205,3 @@ class SyncAssetV1 {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
class SyncAssetV1TypeEnum {
|
||||
/// Instantiate a new enum with the provided [value].
|
||||
const SyncAssetV1TypeEnum._(this.value);
|
||||
|
||||
/// The underlying value of this enum member.
|
||||
final String value;
|
||||
|
||||
@override
|
||||
String toString() => value;
|
||||
|
||||
String toJson() => value;
|
||||
|
||||
static const IMAGE = SyncAssetV1TypeEnum._(r'IMAGE');
|
||||
static const VIDEO = SyncAssetV1TypeEnum._(r'VIDEO');
|
||||
static const AUDIO = SyncAssetV1TypeEnum._(r'AUDIO');
|
||||
static const OTHER = SyncAssetV1TypeEnum._(r'OTHER');
|
||||
|
||||
/// List of all possible values in this [enum][SyncAssetV1TypeEnum].
|
||||
static const values = <SyncAssetV1TypeEnum>[
|
||||
IMAGE,
|
||||
VIDEO,
|
||||
AUDIO,
|
||||
OTHER,
|
||||
];
|
||||
|
||||
static SyncAssetV1TypeEnum? fromJson(dynamic value) => SyncAssetV1TypeEnumTypeTransformer().decode(value);
|
||||
|
||||
static List<SyncAssetV1TypeEnum> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <SyncAssetV1TypeEnum>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = SyncAssetV1TypeEnum.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
}
|
||||
|
||||
/// Transformation class that can [encode] an instance of [SyncAssetV1TypeEnum] to String,
|
||||
/// and [decode] dynamic data back to [SyncAssetV1TypeEnum].
|
||||
class SyncAssetV1TypeEnumTypeTransformer {
|
||||
factory SyncAssetV1TypeEnumTypeTransformer() => _instance ??= const SyncAssetV1TypeEnumTypeTransformer._();
|
||||
|
||||
const SyncAssetV1TypeEnumTypeTransformer._();
|
||||
|
||||
String encode(SyncAssetV1TypeEnum data) => data.value;
|
||||
|
||||
/// Decodes a [dynamic value][data] to a SyncAssetV1TypeEnum.
|
||||
///
|
||||
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
|
||||
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
|
||||
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
|
||||
///
|
||||
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
|
||||
/// and users are still using an old app with the old code.
|
||||
SyncAssetV1TypeEnum? decode(dynamic data, {bool allowNull = true}) {
|
||||
if (data != null) {
|
||||
switch (data) {
|
||||
case r'IMAGE': return SyncAssetV1TypeEnum.IMAGE;
|
||||
case r'VIDEO': return SyncAssetV1TypeEnum.VIDEO;
|
||||
case r'AUDIO': return SyncAssetV1TypeEnum.AUDIO;
|
||||
case r'OTHER': return SyncAssetV1TypeEnum.OTHER;
|
||||
default:
|
||||
if (!allowNull) {
|
||||
throw ArgumentError('Unknown enum value to decode: $data');
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Singleton [SyncAssetV1TypeEnumTypeTransformer] instance.
|
||||
static SyncAssetV1TypeEnumTypeTransformer? _instance;
|
||||
}
|
||||
|
||||
|
||||
|
||||
class SyncAssetV1VisibilityEnum {
|
||||
/// Instantiate a new enum with the provided [value].
|
||||
const SyncAssetV1VisibilityEnum._(this.value);
|
||||
|
||||
/// The underlying value of this enum member.
|
||||
final String value;
|
||||
|
||||
@override
|
||||
String toString() => value;
|
||||
|
||||
String toJson() => value;
|
||||
|
||||
static const archive = SyncAssetV1VisibilityEnum._(r'archive');
|
||||
static const timeline = SyncAssetV1VisibilityEnum._(r'timeline');
|
||||
static const hidden = SyncAssetV1VisibilityEnum._(r'hidden');
|
||||
static const locked = SyncAssetV1VisibilityEnum._(r'locked');
|
||||
|
||||
/// List of all possible values in this [enum][SyncAssetV1VisibilityEnum].
|
||||
static const values = <SyncAssetV1VisibilityEnum>[
|
||||
archive,
|
||||
timeline,
|
||||
hidden,
|
||||
locked,
|
||||
];
|
||||
|
||||
static SyncAssetV1VisibilityEnum? fromJson(dynamic value) => SyncAssetV1VisibilityEnumTypeTransformer().decode(value);
|
||||
|
||||
static List<SyncAssetV1VisibilityEnum> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <SyncAssetV1VisibilityEnum>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = SyncAssetV1VisibilityEnum.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
}
|
||||
|
||||
/// Transformation class that can [encode] an instance of [SyncAssetV1VisibilityEnum] to String,
|
||||
/// and [decode] dynamic data back to [SyncAssetV1VisibilityEnum].
|
||||
class SyncAssetV1VisibilityEnumTypeTransformer {
|
||||
factory SyncAssetV1VisibilityEnumTypeTransformer() => _instance ??= const SyncAssetV1VisibilityEnumTypeTransformer._();
|
||||
|
||||
const SyncAssetV1VisibilityEnumTypeTransformer._();
|
||||
|
||||
String encode(SyncAssetV1VisibilityEnum data) => data.value;
|
||||
|
||||
/// Decodes a [dynamic value][data] to a SyncAssetV1VisibilityEnum.
|
||||
///
|
||||
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
|
||||
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
|
||||
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
|
||||
///
|
||||
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
|
||||
/// and users are still using an old app with the old code.
|
||||
SyncAssetV1VisibilityEnum? decode(dynamic data, {bool allowNull = true}) {
|
||||
if (data != null) {
|
||||
switch (data) {
|
||||
case r'archive': return SyncAssetV1VisibilityEnum.archive;
|
||||
case r'timeline': return SyncAssetV1VisibilityEnum.timeline;
|
||||
case r'hidden': return SyncAssetV1VisibilityEnum.hidden;
|
||||
case r'locked': return SyncAssetV1VisibilityEnum.locked;
|
||||
default:
|
||||
if (!allowNull) {
|
||||
throw ArgumentError('Unknown enum value to decode: $data');
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Singleton [SyncAssetV1VisibilityEnumTypeTransformer] instance.
|
||||
static SyncAssetV1VisibilityEnumTypeTransformer? _instance;
|
||||
}
|
||||
|
||||
|
||||
|
@ -7380,24 +7380,6 @@
|
||||
"$ref": "#/components/schemas/AssetOrder"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "page",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "pageSize",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "personId",
|
||||
"required": false,
|
||||
@ -8500,10 +8482,14 @@
|
||||
"properties": {
|
||||
"comments": {
|
||||
"type": "integer"
|
||||
},
|
||||
"likes": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"comments"
|
||||
"comments",
|
||||
"likes"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
@ -13006,11 +12992,11 @@
|
||||
"type": "string"
|
||||
},
|
||||
"role": {
|
||||
"enum": [
|
||||
"editor",
|
||||
"viewer"
|
||||
],
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/AlbumUserRole"
|
||||
}
|
||||
]
|
||||
},
|
||||
"userId": {
|
||||
"type": "string"
|
||||
@ -13259,22 +13245,18 @@
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"IMAGE",
|
||||
"VIDEO",
|
||||
"AUDIO",
|
||||
"OTHER"
|
||||
],
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/AssetTypeEnum"
|
||||
}
|
||||
]
|
||||
},
|
||||
"visibility": {
|
||||
"enum": [
|
||||
"archive",
|
||||
"timeline",
|
||||
"hidden",
|
||||
"locked"
|
||||
],
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/AssetVisibility"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
@ -38,6 +38,7 @@ export type ActivityCreateDto = {
|
||||
};
|
||||
export type ActivityStatisticsResponseDto = {
|
||||
comments: number;
|
||||
likes: number;
|
||||
};
|
||||
export type NotificationCreateDto = {
|
||||
data?: object;
|
||||
@ -3435,14 +3436,12 @@ export function tagAssets({ id, bulkIdsDto }: {
|
||||
body: bulkIdsDto
|
||||
})));
|
||||
}
|
||||
export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, page, pageSize, personId, tagId, timeBucket, userId, visibility, withPartners, withStacked }: {
|
||||
export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, personId, tagId, timeBucket, userId, visibility, withPartners, withStacked }: {
|
||||
albumId?: string;
|
||||
isFavorite?: boolean;
|
||||
isTrashed?: boolean;
|
||||
key?: string;
|
||||
order?: AssetOrder;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
personId?: string;
|
||||
tagId?: string;
|
||||
timeBucket: string;
|
||||
@ -3460,8 +3459,6 @@ export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, page
|
||||
isTrashed,
|
||||
key,
|
||||
order,
|
||||
page,
|
||||
pageSize,
|
||||
personId,
|
||||
tagId,
|
||||
timeBucket,
|
||||
|
214
server/package-lock.json
generated
214
server/package-lock.json
generated
@ -2349,12 +2349,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/common": {
|
||||
"version": "11.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.2.tgz",
|
||||
"integrity": "sha512-cHh4OPH44PjaHM93D1jgE1HO/B7XTZVRDxy/cPuGgyMEA4p2zXO+qqcOgTMC5FYcp7dX9jLeCjXAU0ToFAnODw==",
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.1.tgz",
|
||||
"integrity": "sha512-crzp+1qeZ5EGL0nFTPy9NrVMAaUWewV5AwtQyv6SQ9yQPXwRl9W9hm1pt0nAtUu5QbYMbSuo7lYcF81EjM+nCA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"file-type": "21.0.0",
|
||||
"file-type": "20.5.0",
|
||||
"iterare": "1.2.1",
|
||||
"load-esm": "1.0.2",
|
||||
"tslib": "2.8.1",
|
||||
@ -2380,9 +2380,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/core": {
|
||||
"version": "11.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.2.tgz",
|
||||
"integrity": "sha512-QRuyxwu0BjNfmmmunsw1ylX7RSyfDQHt+xD+tKncdtgiMOOzAu+LA1gB4WoZnw4frQkk+qZbhEbM61cIjOxD3w==",
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.1.tgz",
|
||||
"integrity": "sha512-UFoUAgLKFT+RwHTANJdr0dF7p0qS9QjkaUPjg8aafnjM/qxxxrUVDB49nVvyMlk+Hr1+vvcNaOHbWWQBxoZcHA==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -2454,14 +2454,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express": {
|
||||
"version": "11.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.2.tgz",
|
||||
"integrity": "sha512-GlNwOT4htRp8RpZ+TpqGtSHwGKw/abdxxBRse40XE2SWs5ikaoujr9Yd+5sJWDNXB4QTftwb+FplXhyk1Ra+4A==",
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.1.tgz",
|
||||
"integrity": "sha512-IUxk380qnUtz0PCRQ5i+o9UHlGMrFzGPIJxDwyt3JZZwx2AngOlcEcm5e+7YeJQEr2QYX2QyC4tUQg0zde+D7A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cors": "2.8.5",
|
||||
"express": "5.1.0",
|
||||
"multer": "2.0.0",
|
||||
"multer": "1.4.5-lts.2",
|
||||
"path-to-regexp": "8.2.0",
|
||||
"tslib": "2.8.1"
|
||||
},
|
||||
@ -2475,9 +2475,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-socket.io": {
|
||||
"version": "11.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.1.2.tgz",
|
||||
"integrity": "sha512-IkeDPRRddY0In6lE+5H/DJodtF5cEx+ga+GWehs4Il5Y3kK7MVR2/WgUABAhyRsbJYOhIhZD7Dai0V2t9ref1Q==",
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.1.1.tgz",
|
||||
"integrity": "sha512-Bsc8ouysUFasWiO8RKEvppqYM5LNkHfbyIJQTy3V6+PUdYhblkvmOq8QtjuHpv6DiBI4siUcxACx/90/CdXLkQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"socket.io": "4.8.1",
|
||||
@ -2663,9 +2663,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/testing": {
|
||||
"version": "11.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.2.tgz",
|
||||
"integrity": "sha512-BQxVKUVW6gzEbbHAvmg5RgcP3s++pRgTCmsgaDF/DtcLRUeKi8SjAdqzLm14xbkMeibxOf3fNqM2iwqUKj8ffw==",
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.1.tgz",
|
||||
"integrity": "sha512-stzm8YrLDGAijHYQw+8Z9dD6lGdvahL0hIjGVZ/0KBxLZht0/rvRjgV31UK+DUqXaF7yhJTw9ryrPaITxI1J6A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -2691,9 +2691,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/websockets": {
|
||||
"version": "11.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.1.2.tgz",
|
||||
"integrity": "sha512-Ywl7u0C3+qnKIrk0mD3jHWnowO+GScFT1FeP6cNgarA0ujHEfusph9IIbnUJiEiusfnKVpK9fYMGZRSDwnRGPQ==",
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.1.1.tgz",
|
||||
"integrity": "sha512-gxwQoGx5bW5IvparzrX1UOGXz87eqY0fK5Y6yb14z6tSSubQTciNjCDm5osDEkRyRCG6ZB0F+eXF6dRUjwTlBQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"iterare": "1.2.1",
|
||||
@ -5576,9 +5576,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.6.tgz",
|
||||
"integrity": "sha512-JeG0rEWak0N6Itr6QUx+X60uQmN+5t3j9r/OVDtWzFXKaj6kD1BwJzOksD0FF6iWxZlbE1kB0q9vtnU2ekqa1Q==",
|
||||
"version": "19.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.5.tgz",
|
||||
"integrity": "sha512-piErsCVVbpMMT2r7wbawdZsq4xMvIAhQuac2gedQHysu1TZYEigE6pnFfgZT+/jQnrRuF5r+SHzuehFjfRjr4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -5735,17 +5735,17 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.33.0.tgz",
|
||||
"integrity": "sha512-CACyQuqSHt7ma3Ns601xykeBK/rDeZa3w6IS6UtMQbixO5DWy+8TilKkviGDH6jtWCo8FGRKEK5cLLkPvEammQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz",
|
||||
"integrity": "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.33.0",
|
||||
"@typescript-eslint/type-utils": "8.33.0",
|
||||
"@typescript-eslint/utils": "8.33.0",
|
||||
"@typescript-eslint/visitor-keys": "8.33.0",
|
||||
"@typescript-eslint/scope-manager": "8.32.1",
|
||||
"@typescript-eslint/type-utils": "8.32.1",
|
||||
"@typescript-eslint/utils": "8.32.1",
|
||||
"@typescript-eslint/visitor-keys": "8.32.1",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^7.0.0",
|
||||
"natural-compare": "^1.4.0",
|
||||
@ -5759,7 +5759,7 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.33.0",
|
||||
"@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
|
||||
"eslint": "^8.57.0 || ^9.0.0",
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
@ -5775,16 +5775,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.33.0.tgz",
|
||||
"integrity": "sha512-JaehZvf6m0yqYp34+RVnihBAChkqeH+tqqhS0GuX1qgPpwLvmTPheKEs6OeCK6hVJgXZHJ2vbjnC9j119auStQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz",
|
||||
"integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.33.0",
|
||||
"@typescript-eslint/types": "8.33.0",
|
||||
"@typescript-eslint/typescript-estree": "8.33.0",
|
||||
"@typescript-eslint/visitor-keys": "8.33.0",
|
||||
"@typescript-eslint/scope-manager": "8.32.1",
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/typescript-estree": "8.32.1",
|
||||
"@typescript-eslint/visitor-keys": "8.32.1",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@ -5799,34 +5799,15 @@
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.33.0.tgz",
|
||||
"integrity": "sha512-d1hz0u9l6N+u/gcrk6s6gYdl7/+pp8yHheRTqP6X5hVDKALEaTn8WfGiit7G511yueBEL3OpOEpD+3/MBdoN+A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.33.0",
|
||||
"@typescript-eslint/types": "^8.33.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.33.0.tgz",
|
||||
"integrity": "sha512-LMi/oqrzpqxyO72ltP+dBSP6V0xiUb4saY7WLtxSfiNEBI8m321LLVFU9/QDJxjDQG9/tjSqKz/E3380TEqSTw==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz",
|
||||
"integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.33.0",
|
||||
"@typescript-eslint/visitor-keys": "8.33.0"
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/visitor-keys": "8.32.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -5836,32 +5817,15 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.33.0.tgz",
|
||||
"integrity": "sha512-sTkETlbqhEoiFmGr1gsdq5HyVbSOF0145SYDJ/EQmXHtKViCaGvnyLqWFFHtEXoS0J1yU8Wyou2UGmgW88fEug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.33.0.tgz",
|
||||
"integrity": "sha512-lScnHNCBqL1QayuSrWeqAL5GmqNdVUQAAMTaCwdYEdWfIrSrOGzyLGRCHXcCixa5NK6i5l0AfSO2oBSjCjf4XQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz",
|
||||
"integrity": "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "8.33.0",
|
||||
"@typescript-eslint/utils": "8.33.0",
|
||||
"@typescript-eslint/typescript-estree": "8.32.1",
|
||||
"@typescript-eslint/utils": "8.32.1",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
},
|
||||
@ -5878,9 +5842,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.33.0.tgz",
|
||||
"integrity": "sha512-DKuXOKpM5IDT1FA2g9x9x1Ug81YuKrzf4mYX8FAVSNu5Wo/LELHWQyM1pQaDkI42bX15PWl0vNPt1uGiIFUOpg==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz",
|
||||
"integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -5892,16 +5856,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.33.0.tgz",
|
||||
"integrity": "sha512-vegY4FQoB6jL97Tu/lWRsAiUUp8qJTqzAmENH2k59SJhw0Th1oszb9Idq/FyyONLuNqT1OADJPXfyUNOR8SzAQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz",
|
||||
"integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.33.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.33.0",
|
||||
"@typescript-eslint/types": "8.33.0",
|
||||
"@typescript-eslint/visitor-keys": "8.33.0",
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/visitor-keys": "8.32.1",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
@ -5947,16 +5909,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.33.0.tgz",
|
||||
"integrity": "sha512-lPFuQaLA9aSNa7D5u2EpRiqdAUhzShwGg/nhpBlc4GR6kcTABttCuyjFs8BcEZ8VWrjCBof/bePhP3Q3fS+Yrw==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz",
|
||||
"integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.7.0",
|
||||
"@typescript-eslint/scope-manager": "8.33.0",
|
||||
"@typescript-eslint/types": "8.33.0",
|
||||
"@typescript-eslint/typescript-estree": "8.33.0"
|
||||
"@typescript-eslint/scope-manager": "8.32.1",
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/typescript-estree": "8.32.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -5971,13 +5933,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.33.0.tgz",
|
||||
"integrity": "sha512-7RW7CMYoskiz5OOGAWjJFxgb7c5UNjTG292gYhWeOAcFmYCtVCSqjqSBj5zMhxbXo2JOW95YYrUWJfU0zrpaGQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz",
|
||||
"integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.33.0",
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"eslint-visitor-keys": "^4.2.0"
|
||||
},
|
||||
"engines": {
|
||||
@ -9610,18 +9572,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/file-type": {
|
||||
"version": "21.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-type/-/file-type-21.0.0.tgz",
|
||||
"integrity": "sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg==",
|
||||
"version": "20.5.0",
|
||||
"resolved": "https://registry.npmjs.org/file-type/-/file-type-20.5.0.tgz",
|
||||
"integrity": "sha512-BfHZtG/l9iMm4Ecianu7P8HRD2tBHLtjXinm4X62XBOYzi7CYA7jyqfJzOvXHqzVrVPYqBo2/GvbARMaaJkKVg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tokenizer/inflate": "^0.2.7",
|
||||
"strtok3": "^10.2.2",
|
||||
"@tokenizer/inflate": "^0.2.6",
|
||||
"strtok3": "^10.2.0",
|
||||
"token-types": "^6.0.0",
|
||||
"uint8array-extras": "^1.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sindresorhus/file-type?sponsor=1"
|
||||
@ -10239,9 +10201,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-16.2.0.tgz",
|
||||
"integrity": "sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg==",
|
||||
"version": "16.1.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz",
|
||||
"integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -12163,9 +12125,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/multer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-2.0.0.tgz",
|
||||
"integrity": "sha512-bS8rPZurbAuHGAnApbM9d4h1wSoYqrOqkE+6a64KLMK9yWU7gJXBDDVklKQ3TPi9DRb85cRs6yXaC0+cjxRtRg==",
|
||||
"version": "1.4.5-lts.2",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz",
|
||||
"integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"append-field": "^1.0.0",
|
||||
@ -12177,7 +12139,7 @@
|
||||
"xtend": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.16.0"
|
||||
"node": ">= 6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/multer/node_modules/concat-stream": {
|
||||
@ -17090,15 +17052,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.33.0.tgz",
|
||||
"integrity": "sha512-5YmNhF24ylCsvdNW2oJwMzTbaeO4bg90KeGtMjUw0AGtHksgEPLRTUil+coHwCfiu4QjVJFnjp94DmU6zV7DhQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.32.1.tgz",
|
||||
"integrity": "sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "8.33.0",
|
||||
"@typescript-eslint/parser": "8.33.0",
|
||||
"@typescript-eslint/utils": "8.33.0"
|
||||
"@typescript-eslint/eslint-plugin": "8.32.1",
|
||||
"@typescript-eslint/parser": "8.32.1",
|
||||
"@typescript-eslint/utils": "8.32.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -17417,9 +17379,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/validator": {
|
||||
"version": "13.15.15",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz",
|
||||
"integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==",
|
||||
"version": "13.15.0",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.0.tgz",
|
||||
"integrity": "sha512-36B2ryl4+oL5QxZ3AzD0t5SsMNGvTtQHpjgFO5tbNxfXbMFkY822ktCDe1MnlqV3301QQI9SLHDNJokDI+Z9pA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
|
@ -29,6 +29,9 @@ export class ActivityResponseDto {
|
||||
export class ActivityStatisticsResponseDto {
|
||||
@ApiProperty({ type: 'integer' })
|
||||
comments!: number;
|
||||
|
||||
@ApiProperty({ type: 'integer' })
|
||||
likes!: number;
|
||||
}
|
||||
|
||||
export class ActivityDto {
|
||||
|
@ -65,9 +65,11 @@ export class SyncAssetV1 {
|
||||
fileCreatedAt!: Date | null;
|
||||
fileModifiedAt!: Date | null;
|
||||
localDateTime!: Date | null;
|
||||
@ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType })
|
||||
type!: AssetType;
|
||||
deletedAt!: Date | null;
|
||||
isFavorite!: boolean;
|
||||
@ApiProperty({ enumName: 'AssetVisibility', enum: AssetVisibility })
|
||||
visibility!: AssetVisibility;
|
||||
}
|
||||
|
||||
@ -125,6 +127,7 @@ export class SyncAlbumUserDeleteV1 {
|
||||
export class SyncAlbumUserV1 {
|
||||
albumId!: string;
|
||||
userId!: string;
|
||||
@ApiProperty({ enumName: 'AlbumUserRole', enum: AlbumUserRole })
|
||||
role!: AlbumUserRole;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
import { IsEnum, IsInt, IsString, Min } from 'class-validator';
|
||||
import { IsEnum, IsString } from 'class-validator';
|
||||
import { AssetOrder, AssetVisibility } from 'src/enum';
|
||||
import { Optional, ValidateAssetVisibility, ValidateBoolean, ValidateUUID } from 'src/validation';
|
||||
|
||||
@ -41,16 +41,6 @@ export class TimeBucketDto {
|
||||
export class TimeBucketAssetDto extends TimeBucketDto {
|
||||
@IsString()
|
||||
timeBucket!: string;
|
||||
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@Optional()
|
||||
page?: number;
|
||||
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@Optional()
|
||||
pageSize?: number;
|
||||
}
|
||||
|
||||
export class TimelineStackResponseDto {
|
||||
|
@ -62,15 +62,21 @@ where
|
||||
|
||||
-- ActivityRepository.getStatistics
|
||||
select
|
||||
count(*) as "count"
|
||||
count(*) filter (
|
||||
where
|
||||
"activity"."isLiked" = $1
|
||||
) as "comments",
|
||||
count(*) filter (
|
||||
where
|
||||
"activity"."isLiked" = $2
|
||||
) as "likes"
|
||||
from
|
||||
"activity"
|
||||
inner join "users" on "users"."id" = "activity"."userId"
|
||||
and "users"."deletedAt" is null
|
||||
left join "assets" on "assets"."id" = "activity"."assetId"
|
||||
where
|
||||
"activity"."assetId" = $1
|
||||
and "activity"."albumId" = $2
|
||||
and "activity"."isLiked" = $3
|
||||
"activity"."assetId" = $3
|
||||
and "activity"."albumId" = $4
|
||||
and "assets"."deletedAt" is null
|
||||
and "assets"."visibility" != 'locked'
|
||||
|
@ -67,19 +67,27 @@ export class ActivityRepository {
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [{ albumId: DummyValue.UUID, assetId: DummyValue.UUID }] })
|
||||
async getStatistics({ albumId, assetId }: { albumId: string; assetId?: string }): Promise<number> {
|
||||
const { count } = await this.db
|
||||
async getStatistics({
|
||||
albumId,
|
||||
assetId,
|
||||
}: {
|
||||
albumId: string;
|
||||
assetId?: string;
|
||||
}): Promise<{ comments: number; likes: number }> {
|
||||
const result = await this.db
|
||||
.selectFrom('activity')
|
||||
.select((eb) => eb.fn.countAll<number>().as('count'))
|
||||
.select((eb) => [
|
||||
eb.fn.countAll<number>().filterWhere('activity.isLiked', '=', false).as('comments'),
|
||||
eb.fn.countAll<number>().filterWhere('activity.isLiked', '=', true).as('likes'),
|
||||
])
|
||||
.innerJoin('users', (join) => join.onRef('users.id', '=', 'activity.userId').on('users.deletedAt', 'is', null))
|
||||
.leftJoin('assets', 'assets.id', 'activity.assetId')
|
||||
.$if(!!assetId, (qb) => qb.where('activity.assetId', '=', assetId!))
|
||||
.where('activity.albumId', '=', albumId)
|
||||
.where('activity.isLiked', '=', false)
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.where('assets.visibility', '!=', sql.lit(AssetVisibility.LOCKED))
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
return count;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -54,13 +54,13 @@ describe(ActivityService.name, () => {
|
||||
});
|
||||
|
||||
describe('getStatistics', () => {
|
||||
it('should get the comment count', async () => {
|
||||
it('should get the comment and like count', async () => {
|
||||
const [albumId, assetId] = newUuids();
|
||||
|
||||
mocks.activity.getStatistics.mockResolvedValue(1);
|
||||
mocks.activity.getStatistics.mockResolvedValue({ comments: 1, likes: 3 });
|
||||
mocks.access.album.checkOwnerAccess.mockResolvedValue(new Set([albumId]));
|
||||
|
||||
await expect(sut.getStatistics(factory.auth(), { assetId, albumId })).resolves.toEqual({ comments: 1 });
|
||||
await expect(sut.getStatistics(factory.auth(), { assetId, albumId })).resolves.toEqual({ comments: 1, likes: 3 });
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -31,7 +31,7 @@ export class ActivityService extends BaseService {
|
||||
|
||||
async getStatistics(auth: AuthDto, dto: ActivityDto): Promise<ActivityStatisticsResponseDto> {
|
||||
await this.requireAccess({ auth, permission: Permission.ALBUM_READ, ids: [dto.albumId] });
|
||||
return { comments: await this.activityRepository.getStatistics({ albumId: dto.albumId, assetId: dto.assetId }) };
|
||||
return await this.activityRepository.getStatistics({ albumId: dto.albumId, assetId: dto.assetId });
|
||||
}
|
||||
|
||||
async create(auth: AuthDto, dto: ActivityCreateDto): Promise<MaybeDuplicate<ActivityResponseDto>> {
|
||||
|
344
web/package-lock.json
generated
344
web/package-lock.json
generated
@ -2229,9 +2229,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/node": {
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.8.tgz",
|
||||
"integrity": "sha512-OWwBsbC9BFAJelmnNcrKuf+bka2ZxCE2A4Ft53Tkg4uoiE67r/PMEYwCsourC26E+kmxfwE0hVzMdxqeW+xu7Q==",
|
||||
"version": "4.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.7.tgz",
|
||||
"integrity": "sha512-9rsOpdY9idRI2NH6CL4wORFY0+Q6fnx9XP9Ju+iq/0wJwGD5IByIgFmwVbyy4ymuyprj8Qh4ErxMKTUL4uNh3g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -2241,13 +2241,13 @@
|
||||
"lightningcss": "1.30.1",
|
||||
"magic-string": "^0.30.17",
|
||||
"source-map-js": "^1.2.1",
|
||||
"tailwindcss": "4.1.8"
|
||||
"tailwindcss": "4.1.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide": {
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.8.tgz",
|
||||
"integrity": "sha512-d7qvv9PsM5N3VNKhwVUhpK6r4h9wtLkJ6lz9ZY9aeZgrUWk1Z8VPyqyDT9MZlem7GTGseRQHkeB1j3tC7W1P+A==",
|
||||
"version": "4.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.7.tgz",
|
||||
"integrity": "sha512-5SF95Ctm9DFiUyjUPnDGkoKItPX/k+xifcQhcqX5RA85m50jw1pT/KzjdvlqxRja45Y52nR4MR9fD1JYd7f8NQ==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
@ -2259,24 +2259,24 @@
|
||||
"node": ">= 10"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tailwindcss/oxide-android-arm64": "4.1.8",
|
||||
"@tailwindcss/oxide-darwin-arm64": "4.1.8",
|
||||
"@tailwindcss/oxide-darwin-x64": "4.1.8",
|
||||
"@tailwindcss/oxide-freebsd-x64": "4.1.8",
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.8",
|
||||
"@tailwindcss/oxide-linux-arm64-gnu": "4.1.8",
|
||||
"@tailwindcss/oxide-linux-arm64-musl": "4.1.8",
|
||||
"@tailwindcss/oxide-linux-x64-gnu": "4.1.8",
|
||||
"@tailwindcss/oxide-linux-x64-musl": "4.1.8",
|
||||
"@tailwindcss/oxide-wasm32-wasi": "4.1.8",
|
||||
"@tailwindcss/oxide-win32-arm64-msvc": "4.1.8",
|
||||
"@tailwindcss/oxide-win32-x64-msvc": "4.1.8"
|
||||
"@tailwindcss/oxide-android-arm64": "4.1.7",
|
||||
"@tailwindcss/oxide-darwin-arm64": "4.1.7",
|
||||
"@tailwindcss/oxide-darwin-x64": "4.1.7",
|
||||
"@tailwindcss/oxide-freebsd-x64": "4.1.7",
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.7",
|
||||
"@tailwindcss/oxide-linux-arm64-gnu": "4.1.7",
|
||||
"@tailwindcss/oxide-linux-arm64-musl": "4.1.7",
|
||||
"@tailwindcss/oxide-linux-x64-gnu": "4.1.7",
|
||||
"@tailwindcss/oxide-linux-x64-musl": "4.1.7",
|
||||
"@tailwindcss/oxide-wasm32-wasi": "4.1.7",
|
||||
"@tailwindcss/oxide-win32-arm64-msvc": "4.1.7",
|
||||
"@tailwindcss/oxide-win32-x64-msvc": "4.1.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-android-arm64": {
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.8.tgz",
|
||||
"integrity": "sha512-Fbz7qni62uKYceWYvUjRqhGfZKwhZDQhlrJKGtnZfuNtHFqa8wmr+Wn74CTWERiW2hn3mN5gTpOoxWKk0jRxjg==",
|
||||
"version": "4.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.7.tgz",
|
||||
"integrity": "sha512-IWA410JZ8fF7kACus6BrUwY2Z1t1hm0+ZWNEzykKmMNM09wQooOcN/VXr0p/WJdtHZ90PvJf2AIBS/Ceqx1emg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -2291,9 +2291,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-darwin-arm64": {
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.8.tgz",
|
||||
"integrity": "sha512-RdRvedGsT0vwVVDztvyXhKpsU2ark/BjgG0huo4+2BluxdXo8NDgzl77qh0T1nUxmM11eXwR8jA39ibvSTbi7A==",
|
||||
"version": "4.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.7.tgz",
|
||||
"integrity": "sha512-81jUw9To7fimGGkuJ2W5h3/oGonTOZKZ8C2ghm/TTxbwvfSiFSDPd6/A/KE2N7Jp4mv3Ps9OFqg2fEKgZFfsvg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -2308,9 +2308,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-darwin-x64": {
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.8.tgz",
|
||||
"integrity": "sha512-t6PgxjEMLp5Ovf7uMb2OFmb3kqzVTPPakWpBIFzppk4JE4ix0yEtbtSjPbU8+PZETpaYMtXvss2Sdkx8Vs4XRw==",
|
||||
"version": "4.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.7.tgz",
|
||||
"integrity": "sha512-q77rWjEyGHV4PdDBtrzO0tgBBPlQWKY7wZK0cUok/HaGgbNKecegNxCGikuPJn5wFAlIywC3v+WMBt0PEBtwGw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -2325,9 +2325,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-freebsd-x64": {
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.8.tgz",
|
||||
"integrity": "sha512-g8C8eGEyhHTqwPStSwZNSrOlyx0bhK/V/+zX0Y+n7DoRUzyS8eMbVshVOLJTDDC+Qn9IJnilYbIKzpB9n4aBsg==",
|
||||
"version": "4.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.7.tgz",
|
||||
"integrity": "sha512-RfmdbbK6G6ptgF4qqbzoxmH+PKfP4KSVs7SRlTwcbRgBwezJkAO3Qta/7gDy10Q2DcUVkKxFLXUQO6J3CRvBGw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -2342,9 +2342,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.8.tgz",
|
||||
"integrity": "sha512-Jmzr3FA4S2tHhaC6yCjac3rGf7hG9R6Gf2z9i9JFcuyy0u79HfQsh/thifbYTF2ic82KJovKKkIB6Z9TdNhCXQ==",
|
||||
"version": "4.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.7.tgz",
|
||||
"integrity": "sha512-OZqsGvpwOa13lVd1z6JVwQXadEobmesxQ4AxhrwRiPuE04quvZHWn/LnihMg7/XkN+dTioXp/VMu/p6A5eZP3g==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@ -2359,9 +2359,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.8.tgz",
|
||||
"integrity": "sha512-qq7jXtO1+UEtCmCeBBIRDrPFIVI4ilEQ97qgBGdwXAARrUqSn/L9fUrkb1XP/mvVtoVeR2bt/0L77xx53bPZ/Q==",
|
||||
"version": "4.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.7.tgz",
|
||||
"integrity": "sha512-voMvBTnJSfKecJxGkoeAyW/2XRToLZ227LxswLAwKY7YslG/Xkw9/tJNH+3IVh5bdYzYE7DfiaPbRkSHFxY1xA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -2376,9 +2376,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-arm64-musl": {
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.8.tgz",
|
||||
"integrity": "sha512-O6b8QesPbJCRshsNApsOIpzKt3ztG35gfX9tEf4arD7mwNinsoCKxkj8TgEE0YRjmjtO3r9FlJnT/ENd9EVefQ==",
|
||||
"version": "4.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.7.tgz",
|
||||
"integrity": "sha512-PjGuNNmJeKHnP58M7XyjJyla8LPo+RmwHQpBI+W/OxqrwojyuCQ+GUtygu7jUqTEexejZHr/z3nBc/gTiXBj4A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -2393,9 +2393,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-x64-gnu": {
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.8.tgz",
|
||||
"integrity": "sha512-32iEXX/pXwikshNOGnERAFwFSfiltmijMIAbUhnNyjFr3tmWmMJWQKU2vNcFX0DACSXJ3ZWcSkzNbaKTdngH6g==",
|
||||
"version": "4.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.7.tgz",
|
||||
"integrity": "sha512-HMs+Va+ZR3gC3mLZE00gXxtBo3JoSQxtu9lobbZd+DmfkIxR54NO7Z+UQNPsa0P/ITn1TevtFxXTpsRU7qEvWg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -2410,9 +2410,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-x64-musl": {
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.8.tgz",
|
||||
"integrity": "sha512-s+VSSD+TfZeMEsCaFaHTaY5YNj3Dri8rST09gMvYQKwPphacRG7wbuQ5ZJMIJXN/puxPcg/nU+ucvWguPpvBDg==",
|
||||
"version": "4.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.7.tgz",
|
||||
"integrity": "sha512-MHZ6jyNlutdHH8rd+YTdr3QbXrHXqwIhHw9e7yXEBcQdluGwhpQY2Eku8UZK6ReLaWtQ4gijIv5QoM5eE+qlsA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -2427,9 +2427,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi": {
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.8.tgz",
|
||||
"integrity": "sha512-CXBPVFkpDjM67sS1psWohZ6g/2/cd+cq56vPxK4JeawelxwK4YECgl9Y9TjkE2qfF+9/s1tHHJqrC4SS6cVvSg==",
|
||||
"version": "4.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.7.tgz",
|
||||
"integrity": "sha512-ANaSKt74ZRzE2TvJmUcbFQ8zS201cIPxUDm5qez5rLEwWkie2SkGtA4P+GPTj+u8N6JbPrC8MtY8RmJA35Oo+A==",
|
||||
"bundleDependencies": [
|
||||
"@napi-rs/wasm-runtime",
|
||||
"@emnapi/core",
|
||||
@ -2448,7 +2448,7 @@
|
||||
"@emnapi/core": "^1.4.3",
|
||||
"@emnapi/runtime": "^1.4.3",
|
||||
"@emnapi/wasi-threads": "^1.0.2",
|
||||
"@napi-rs/wasm-runtime": "^0.2.10",
|
||||
"@napi-rs/wasm-runtime": "^0.2.9",
|
||||
"@tybys/wasm-util": "^0.9.0",
|
||||
"tslib": "^2.8.0"
|
||||
},
|
||||
@ -2456,10 +2456,70 @@
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": {
|
||||
"version": "1.4.3",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/wasi-threads": "1.0.2",
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": {
|
||||
"version": "1.4.3",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
|
||||
"version": "1.0.2",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
|
||||
"version": "0.2.9",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/core": "^1.4.0",
|
||||
"@emnapi/runtime": "^1.4.0",
|
||||
"@tybys/wasm-util": "^0.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": {
|
||||
"version": "0.9.0",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": {
|
||||
"version": "2.8.0",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "0BSD",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.8.tgz",
|
||||
"integrity": "sha512-7GmYk1n28teDHUjPlIx4Z6Z4hHEgvP5ZW2QS9ygnDAdI/myh3HTHjDqtSqgu1BpRoI4OiLx+fThAyA1JePoENA==",
|
||||
"version": "4.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.7.tgz",
|
||||
"integrity": "sha512-HUiSiXQ9gLJBAPCMVRk2RT1ZrBjto7WvqsPBwUrNK2BcdSxMnk19h4pjZjI7zgPhDxlAbJSumTC4ljeA9y0tEw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -2474,9 +2534,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-win32-x64-msvc": {
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.8.tgz",
|
||||
"integrity": "sha512-fou+U20j+Jl0EHwK92spoWISON2OBnCazIc038Xj2TdweYV33ZRkS9nwqiUi2d/Wba5xg5UoHfvynnb/UB49cQ==",
|
||||
"version": "4.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.7.tgz",
|
||||
"integrity": "sha512-rYHGmvoHiLJ8hWucSfSOEmdCBIGZIq7SpkPRSqLsH2Ab2YUNgKeAPT1Fi2cx3+hnYOrAb0jp9cRyode3bBW4mQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -2568,15 +2628,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/vite": {
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.8.tgz",
|
||||
"integrity": "sha512-CQ+I8yxNV5/6uGaJjiuymgw0kEQiNKRinYbZXPdx1fk5WgiyReG0VaUx/Xq6aVNSUNJFzxm6o8FNKS5aMaim5A==",
|
||||
"version": "4.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.7.tgz",
|
||||
"integrity": "sha512-tYa2fO3zDe41I7WqijyVbRd8oWT0aEID1Eokz5hMT6wShLIHj3yvwj9XbfuloHP9glZ6H+aG2AN/+ZrxJ1Y5RQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tailwindcss/node": "4.1.8",
|
||||
"@tailwindcss/oxide": "4.1.8",
|
||||
"tailwindcss": "4.1.8"
|
||||
"@tailwindcss/node": "4.1.7",
|
||||
"@tailwindcss/oxide": "4.1.7",
|
||||
"tailwindcss": "4.1.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": "^5.2.0 || ^6"
|
||||
@ -2888,17 +2948,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.33.0.tgz",
|
||||
"integrity": "sha512-CACyQuqSHt7ma3Ns601xykeBK/rDeZa3w6IS6UtMQbixO5DWy+8TilKkviGDH6jtWCo8FGRKEK5cLLkPvEammQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz",
|
||||
"integrity": "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.33.0",
|
||||
"@typescript-eslint/type-utils": "8.33.0",
|
||||
"@typescript-eslint/utils": "8.33.0",
|
||||
"@typescript-eslint/visitor-keys": "8.33.0",
|
||||
"@typescript-eslint/scope-manager": "8.32.1",
|
||||
"@typescript-eslint/type-utils": "8.32.1",
|
||||
"@typescript-eslint/utils": "8.32.1",
|
||||
"@typescript-eslint/visitor-keys": "8.32.1",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^7.0.0",
|
||||
"natural-compare": "^1.4.0",
|
||||
@ -2912,7 +2972,7 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.33.0",
|
||||
"@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
|
||||
"eslint": "^8.57.0 || ^9.0.0",
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
@ -2928,16 +2988,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.33.0.tgz",
|
||||
"integrity": "sha512-JaehZvf6m0yqYp34+RVnihBAChkqeH+tqqhS0GuX1qgPpwLvmTPheKEs6OeCK6hVJgXZHJ2vbjnC9j119auStQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz",
|
||||
"integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.33.0",
|
||||
"@typescript-eslint/types": "8.33.0",
|
||||
"@typescript-eslint/typescript-estree": "8.33.0",
|
||||
"@typescript-eslint/visitor-keys": "8.33.0",
|
||||
"@typescript-eslint/scope-manager": "8.32.1",
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/typescript-estree": "8.32.1",
|
||||
"@typescript-eslint/visitor-keys": "8.32.1",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@ -2952,34 +3012,15 @@
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.33.0.tgz",
|
||||
"integrity": "sha512-d1hz0u9l6N+u/gcrk6s6gYdl7/+pp8yHheRTqP6X5hVDKALEaTn8WfGiit7G511yueBEL3OpOEpD+3/MBdoN+A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.33.0",
|
||||
"@typescript-eslint/types": "^8.33.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.33.0.tgz",
|
||||
"integrity": "sha512-LMi/oqrzpqxyO72ltP+dBSP6V0xiUb4saY7WLtxSfiNEBI8m321LLVFU9/QDJxjDQG9/tjSqKz/E3380TEqSTw==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz",
|
||||
"integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.33.0",
|
||||
"@typescript-eslint/visitor-keys": "8.33.0"
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/visitor-keys": "8.32.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -2989,32 +3030,15 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.33.0.tgz",
|
||||
"integrity": "sha512-sTkETlbqhEoiFmGr1gsdq5HyVbSOF0145SYDJ/EQmXHtKViCaGvnyLqWFFHtEXoS0J1yU8Wyou2UGmgW88fEug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.33.0.tgz",
|
||||
"integrity": "sha512-lScnHNCBqL1QayuSrWeqAL5GmqNdVUQAAMTaCwdYEdWfIrSrOGzyLGRCHXcCixa5NK6i5l0AfSO2oBSjCjf4XQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz",
|
||||
"integrity": "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "8.33.0",
|
||||
"@typescript-eslint/utils": "8.33.0",
|
||||
"@typescript-eslint/typescript-estree": "8.32.1",
|
||||
"@typescript-eslint/utils": "8.32.1",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
},
|
||||
@ -3031,9 +3055,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.33.0.tgz",
|
||||
"integrity": "sha512-DKuXOKpM5IDT1FA2g9x9x1Ug81YuKrzf4mYX8FAVSNu5Wo/LELHWQyM1pQaDkI42bX15PWl0vNPt1uGiIFUOpg==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz",
|
||||
"integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -3045,16 +3069,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.33.0.tgz",
|
||||
"integrity": "sha512-vegY4FQoB6jL97Tu/lWRsAiUUp8qJTqzAmENH2k59SJhw0Th1oszb9Idq/FyyONLuNqT1OADJPXfyUNOR8SzAQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz",
|
||||
"integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.33.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.33.0",
|
||||
"@typescript-eslint/types": "8.33.0",
|
||||
"@typescript-eslint/visitor-keys": "8.33.0",
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/visitor-keys": "8.32.1",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
@ -3100,16 +3122,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.33.0.tgz",
|
||||
"integrity": "sha512-lPFuQaLA9aSNa7D5u2EpRiqdAUhzShwGg/nhpBlc4GR6kcTABttCuyjFs8BcEZ8VWrjCBof/bePhP3Q3fS+Yrw==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz",
|
||||
"integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.7.0",
|
||||
"@typescript-eslint/scope-manager": "8.33.0",
|
||||
"@typescript-eslint/types": "8.33.0",
|
||||
"@typescript-eslint/typescript-estree": "8.33.0"
|
||||
"@typescript-eslint/scope-manager": "8.32.1",
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"@typescript-eslint/typescript-estree": "8.32.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -3124,13 +3146,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.33.0.tgz",
|
||||
"integrity": "sha512-7RW7CMYoskiz5OOGAWjJFxgb7c5UNjTG292gYhWeOAcFmYCtVCSqjqSBj5zMhxbXo2JOW95YYrUWJfU0zrpaGQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz",
|
||||
"integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.33.0",
|
||||
"@typescript-eslint/types": "8.32.1",
|
||||
"eslint-visitor-keys": "^4.2.0"
|
||||
},
|
||||
"engines": {
|
||||
@ -4957,9 +4979,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fabric": {
|
||||
"version": "6.6.6",
|
||||
"resolved": "https://registry.npmjs.org/fabric/-/fabric-6.6.6.tgz",
|
||||
"integrity": "sha512-cL0m/RanEIiP67/TAj8kAQcEYlXofeB1SXCB1w7a0ktyUQHdRpnm2/VHlqsD/PfSLlGqftHzmxAS4LvKzSlrEw==",
|
||||
"version": "6.6.5",
|
||||
"resolved": "https://registry.npmjs.org/fabric/-/fabric-6.6.5.tgz",
|
||||
"integrity": "sha512-BFxyLDeLMMgtteqQwKAyRM+oSkf82lDFzsiC7AMob7k7ag7naFuHOtWtcll4v+M9Cpn5aqRBfz1shnsO0vZhbg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
@ -5429,9 +5451,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-16.2.0.tgz",
|
||||
"integrity": "sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg==",
|
||||
"version": "16.1.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz",
|
||||
"integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -8612,9 +8634,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/svelte": {
|
||||
"version": "5.33.6",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.33.6.tgz",
|
||||
"integrity": "sha512-bxg2QY03JlrilCZmDlshY95Argj0rnX43UQFWZN4fct8PZTNBBmvfow2A6yOW1+YweDjhC2qdZF66ASI0Y21Tw==",
|
||||
"version": "5.33.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.33.1.tgz",
|
||||
"integrity": "sha512-7znzaaQALL62NBzkdKV04tmYIVla8qjrW+k6GdgFZcKcj8XOb8iEjmfRPo40iaWZlKv3+uiuc0h4iaGgwoORtA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.3.0",
|
||||
@ -8863,9 +8885,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.8.tgz",
|
||||
"integrity": "sha512-kjeW8gjdxasbmFKpVGrGd5T4i40mV5J2Rasw48QARfYeQ8YS9x02ON9SFWax3Qf616rt4Cp3nVNIj6Hd1mP3og==",
|
||||
"version": "4.1.7",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.7.tgz",
|
||||
"integrity": "sha512-kr1o/ErIdNhTz8uzAYL7TpaUuzKIE6QPQ4qmSdxnoX/lo+5wmUHQA6h3L5yIqEImSRnAAURDirLu/BgiXGPAhg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
@ -9189,15 +9211,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint": {
|
||||
"version": "8.33.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.33.0.tgz",
|
||||
"integrity": "sha512-5YmNhF24ylCsvdNW2oJwMzTbaeO4bg90KeGtMjUw0AGtHksgEPLRTUil+coHwCfiu4QjVJFnjp94DmU6zV7DhQ==",
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.32.1.tgz",
|
||||
"integrity": "sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "8.33.0",
|
||||
"@typescript-eslint/parser": "8.33.0",
|
||||
"@typescript-eslint/utils": "8.33.0"
|
||||
"@typescript-eslint/eslint-plugin": "8.32.1",
|
||||
"@typescript-eslint/parser": "8.32.1",
|
||||
"@typescript-eslint/utils": "8.32.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
@ -7,25 +7,29 @@
|
||||
interface Props {
|
||||
isLiked: ActivityResponseDto | null;
|
||||
numberOfComments: number | undefined;
|
||||
numberOfLikes: number | undefined;
|
||||
disabled: boolean;
|
||||
onOpenActivityTab: () => void;
|
||||
onFavorite: () => void;
|
||||
}
|
||||
|
||||
let { isLiked, numberOfComments, disabled, onOpenActivityTab, onFavorite }: Props = $props();
|
||||
let { isLiked, numberOfComments, numberOfLikes, disabled, onOpenActivityTab, onFavorite }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="w-full flex p-4 text-white items-center justify-center rounded-full gap-5 bg-immich-dark-bg bg-opacity-60">
|
||||
<div class="w-full flex p-4 items-center justify-center rounded-full gap-5 bg-subtle border bg-opacity-60">
|
||||
<button type="button" class={disabled ? 'cursor-not-allowed' : ''} onclick={onFavorite} {disabled}>
|
||||
<div class="items-center justify-center">
|
||||
<Icon path={isLiked ? mdiHeart : mdiHeartOutline} size={24} />
|
||||
<div class="flex gap-2 items-center justify-center">
|
||||
<Icon path={isLiked ? mdiHeart : mdiHeartOutline} size={24} class={isLiked ? 'text-red-400' : 'text-fg'} />
|
||||
{#if numberOfLikes}
|
||||
<div class="text-l">{numberOfLikes.toLocaleString($locale)}</div>
|
||||
{/if}
|
||||
</div>
|
||||
</button>
|
||||
<button type="button" onclick={onOpenActivityTab}>
|
||||
<div class="flex gap-2 items-center justify-center">
|
||||
<Icon path={mdiCommentOutline} class="scale-x-[-1]" size={24} />
|
||||
{#if numberOfComments}
|
||||
<div class="text-xl">{numberOfComments.toLocaleString($locale)}</div>
|
||||
<div class="text-l">{numberOfComments.toLocaleString($locale)}</div>
|
||||
{/if}
|
||||
</div>
|
||||
</button>
|
||||
|
@ -118,12 +118,9 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="overflow-y-hidden relative h-full" bind:offsetHeight={innerHeight}>
|
||||
<div class="dark:bg-immich-dark-bg dark:text-immich-dark-fg w-full h-full">
|
||||
<div
|
||||
class="flex w-full h-fit dark:bg-immich-dark-bg dark:text-immich-dark-fg p-2 bg-white"
|
||||
bind:clientHeight={activityHeight}
|
||||
>
|
||||
<div class="overflow-y-hidden relative h-full border-l border-subtle bg-subtle" bind:offsetHeight={innerHeight}>
|
||||
<div class="w-full h-full">
|
||||
<div class="flex w-full h-fit dark:text-immich-dark-fg p-2 bg-subtle" bind:clientHeight={activityHeight}>
|
||||
<div class="flex place-items-center gap-2">
|
||||
<IconButton
|
||||
shape="round"
|
||||
|
@ -513,6 +513,7 @@
|
||||
disabled={!album?.isActivityEnabled}
|
||||
isLiked={activityManager.isLiked}
|
||||
numberOfComments={activityManager.commentCount}
|
||||
numberOfLikes={activityManager.likeCount}
|
||||
onFavorite={handleFavorite}
|
||||
onOpenActivityTab={handleOpenActivity}
|
||||
/>
|
||||
@ -569,6 +570,7 @@
|
||||
asset={toTimelineAsset(stackedAsset)}
|
||||
onClick={() => {
|
||||
asset = stackedAsset;
|
||||
previewStackedAsset = undefined;
|
||||
}}
|
||||
onMouseEvent={({ isMouseOver }) => handleStackedAssetMouseEvent(isMouseOver, stackedAsset)}
|
||||
readonly
|
||||
|
@ -70,7 +70,9 @@
|
||||
title="Add tag"
|
||||
onclick={handleAddTag}
|
||||
>
|
||||
<span class="text-sm px-1 flex place-items-center place-content-center gap-1"><Icon path={mdiPlus} />Add</span>
|
||||
<span class="text-sm px-1 flex place-items-center place-content-center gap-1"
|
||||
><Icon path={mdiPlus} />{$t('add')}</span
|
||||
>
|
||||
</button>
|
||||
</section>
|
||||
</section>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { featureFlags } from '$lib/stores/server-config.store';
|
||||
import { type OnDelete, deleteAssets } from '$lib/utils/actions';
|
||||
import { type OnDelete, type OnUndoDelete, deleteAssets } from '$lib/utils/actions';
|
||||
import { mdiDeleteForeverOutline, mdiDeleteOutline, mdiTimerSand } from '@mdi/js';
|
||||
import { t } from 'svelte-i18n';
|
||||
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
|
||||
@ -10,11 +10,12 @@
|
||||
|
||||
interface Props {
|
||||
onAssetDelete: OnDelete;
|
||||
onUndoDelete?: OnUndoDelete | undefined;
|
||||
menuItem?: boolean;
|
||||
force?: boolean;
|
||||
}
|
||||
|
||||
let { onAssetDelete, menuItem = false, force = !$featureFlags.trash }: Props = $props();
|
||||
let { onAssetDelete, onUndoDelete = undefined, menuItem = false, force = !$featureFlags.trash }: Props = $props();
|
||||
|
||||
const { clearSelect, getOwnedAssets } = getAssetControlContext();
|
||||
|
||||
@ -34,8 +35,8 @@
|
||||
|
||||
const handleDelete = async () => {
|
||||
loading = true;
|
||||
const ids = [...getOwnedAssets()].map((a) => a.id);
|
||||
await deleteAssets(force, onAssetDelete, ids);
|
||||
const assets = [...getOwnedAssets()];
|
||||
await deleteAssets(force, onAssetDelete, assets, onUndoDelete);
|
||||
clearSelect();
|
||||
isShowConfirmation = false;
|
||||
loading = false;
|
||||
|
@ -382,7 +382,12 @@
|
||||
|
||||
const trashOrDelete = async (force: boolean = false) => {
|
||||
isShowDeleteConfirmation = false;
|
||||
await deleteAssets(!(isTrashEnabled && !force), (assetIds) => assetStore.removeAssets(assetIds), idsSelectedAssets);
|
||||
await deleteAssets(
|
||||
!(isTrashEnabled && !force),
|
||||
(assetIds) => assetStore.removeAssets(assetIds),
|
||||
assetInteraction.selectedAssets,
|
||||
!isTrashEnabled || force ? undefined : (assets) => assetStore.addAssets(assets),
|
||||
);
|
||||
assetInteraction.clearMultiselect();
|
||||
};
|
||||
|
||||
|
@ -38,6 +38,7 @@
|
||||
onPrevious?: (() => Promise<{ id: string } | undefined>) | undefined;
|
||||
onNext?: (() => Promise<{ id: string } | undefined>) | undefined;
|
||||
onRandom?: (() => Promise<{ id: string } | undefined>) | undefined;
|
||||
onReload?: (() => void) | undefined;
|
||||
pageHeaderOffset?: number;
|
||||
slidingWindowOffset?: number;
|
||||
}
|
||||
@ -54,6 +55,7 @@
|
||||
onPrevious = undefined,
|
||||
onNext = undefined,
|
||||
onRandom = undefined,
|
||||
onReload = undefined,
|
||||
slidingWindowOffset = 0,
|
||||
pageHeaderOffset = 0,
|
||||
}: Props = $props();
|
||||
@ -255,7 +257,8 @@
|
||||
await deleteAssets(
|
||||
!(isTrashEnabled && !force),
|
||||
(assetIds) => (assets = assets.filter((asset) => !assetIds.includes(asset.id))),
|
||||
idsSelectedAssets,
|
||||
assetInteraction.selectedAssets,
|
||||
onReload,
|
||||
);
|
||||
assetInteraction.clearMultiselect();
|
||||
};
|
||||
@ -426,7 +429,6 @@
|
||||
};
|
||||
|
||||
let isTrashEnabled = $derived($featureFlags.loaded && $featureFlags.trash);
|
||||
let idsSelectedAssets = $derived(assetInteraction.selectedAssets.map((selectedAsset) => selectedAsset.id));
|
||||
|
||||
$effect(() => {
|
||||
if (!lastAssetMouseEvent) {
|
||||
|
@ -1,13 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { Theme } from '$lib/constants';
|
||||
import { defaultLang, langs, Theme } from '$lib/constants';
|
||||
import { themeManager } from '$lib/managers/theme-manager.svelte';
|
||||
import { lang } from '$lib/stores/preferences.store';
|
||||
import { ThemeSwitcher } from '@immich/ui';
|
||||
import { get } from 'svelte/store';
|
||||
</script>
|
||||
|
||||
{#if !themeManager.theme.system}
|
||||
{#await langs
|
||||
.find((item) => item.code === get(lang))
|
||||
?.loader() ?? defaultLang.loader() then { default: translations }}
|
||||
<ThemeSwitcher
|
||||
size="medium"
|
||||
color="secondary"
|
||||
{translations}
|
||||
onChange={(theme) => themeManager.setTheme(theme == 'dark' ? Theme.DARK : Theme.LIGHT)}
|
||||
/>
|
||||
{/await}
|
||||
{/if}
|
||||
|
@ -39,7 +39,7 @@
|
||||
};
|
||||
|
||||
const handleToggleLocaleBrowser = () => {
|
||||
$locale = $locale ? undefined : fallbackLocale.code;
|
||||
$locale = $locale === 'default' ? fallbackLocale.code : 'default';
|
||||
};
|
||||
|
||||
const handleLocaleChange = (newLocale: string | undefined) => {
|
||||
@ -89,13 +89,13 @@
|
||||
<SettingSwitch
|
||||
title={$t('default_locale')}
|
||||
subtitle={$t('default_locale_description')}
|
||||
checked={$locale == undefined}
|
||||
checked={$locale == 'default'}
|
||||
onToggle={handleToggleLocaleBrowser}
|
||||
>
|
||||
<p class="mt-2 dark:text-gray-400">{selectedDate}</p>
|
||||
</SettingSwitch>
|
||||
</div>
|
||||
{#if $locale !== undefined}
|
||||
{#if $locale !== 'default'}
|
||||
<div class="ms-4">
|
||||
<SettingCombobox
|
||||
comboboxPlaceholder={$t('searching_locales')}
|
||||
@ -113,7 +113,6 @@
|
||||
title={$t('display_original_photos')}
|
||||
subtitle={$t('display_original_photos_setting_description')}
|
||||
bind:checked={$alwaysLoadOriginalFile}
|
||||
onToggle={() => ($alwaysLoadOriginalFile = !$alwaysLoadOriginalFile)}
|
||||
/>
|
||||
</div>
|
||||
<div class="ms-4">
|
||||
@ -121,16 +120,10 @@
|
||||
title={$t('video_hover_setting')}
|
||||
subtitle={$t('video_hover_setting_description')}
|
||||
bind:checked={$playVideoThumbnailOnHover}
|
||||
onToggle={() => ($playVideoThumbnailOnHover = !$playVideoThumbnailOnHover)}
|
||||
/>
|
||||
</div>
|
||||
<div class="ms-4">
|
||||
<SettingSwitch
|
||||
title={$t('loop_videos')}
|
||||
subtitle={$t('loop_videos_description')}
|
||||
bind:checked={$loopVideo}
|
||||
onToggle={() => ($loopVideo = !$loopVideo)}
|
||||
/>
|
||||
<SettingSwitch title={$t('loop_videos')} subtitle={$t('loop_videos_description')} bind:checked={$loopVideo} />
|
||||
</div>
|
||||
|
||||
<div class="ms-4">
|
||||
|
@ -273,9 +273,17 @@ export const locales = [
|
||||
{ code: 'zu-ZA', name: 'Zulu (South Africa)' },
|
||||
];
|
||||
|
||||
export const defaultLang = { name: 'English', code: 'en', loader: () => import('$i18n/en.json') };
|
||||
interface Lang {
|
||||
name: string;
|
||||
code: string;
|
||||
loader: () => Promise<{ default: object }>;
|
||||
rtl?: boolean;
|
||||
weblateCode?: string;
|
||||
}
|
||||
|
||||
export const langs = [
|
||||
export const defaultLang: Lang = { name: 'English', code: 'en', loader: () => import('$i18n/en.json') };
|
||||
|
||||
export const langs: Lang[] = [
|
||||
{ name: 'Afrikaans', code: 'af', loader: () => import('$i18n/af.json') },
|
||||
{ name: 'Arabic', code: 'ar', loader: () => import('$i18n/ar.json'), rtl: true },
|
||||
{ name: 'Azerbaijani', code: 'az', loader: () => import('$i18n/az.json'), rtl: true },
|
||||
@ -359,7 +367,7 @@ export const langs = [
|
||||
weblateCode: 'zh_SIMPLIFIED',
|
||||
loader: () => import('$i18n/zh_SIMPLIFIED.json'),
|
||||
},
|
||||
{ name: 'Development (keys only)', code: 'dev', loader: () => Promise.resolve({}) },
|
||||
{ name: 'Development (keys only)', code: 'dev', loader: () => Promise.resolve({ default: {} }) },
|
||||
];
|
||||
|
||||
export enum ImmichProduct {
|
||||
|
@ -17,6 +17,7 @@ class ActivityManager {
|
||||
#assetId = $state<string | undefined>();
|
||||
#activities = $state<ActivityResponseDto[]>([]);
|
||||
#commentCount = $state(0);
|
||||
#likeCount = $state(0);
|
||||
#isLiked = $state<ActivityResponseDto | null>(null);
|
||||
|
||||
get activities() {
|
||||
@ -27,6 +28,10 @@ class ActivityManager {
|
||||
return this.#commentCount;
|
||||
}
|
||||
|
||||
get likeCount() {
|
||||
return this.#likeCount;
|
||||
}
|
||||
|
||||
get isLiked() {
|
||||
return this.#isLiked;
|
||||
}
|
||||
@ -48,6 +53,10 @@ class ActivityManager {
|
||||
this.#commentCount++;
|
||||
}
|
||||
|
||||
if (activity.type === ReactionType.Like) {
|
||||
this.#likeCount++;
|
||||
}
|
||||
|
||||
handlePromiseError(this.refreshActivities(this.#albumId, this.#assetId));
|
||||
return activity;
|
||||
}
|
||||
@ -61,6 +70,10 @@ class ActivityManager {
|
||||
this.#commentCount--;
|
||||
}
|
||||
|
||||
if (activity.type === ReactionType.Like) {
|
||||
this.#likeCount--;
|
||||
}
|
||||
|
||||
this.#activities = index
|
||||
? this.#activities.splice(index, 1)
|
||||
: this.#activities.filter(({ id }) => id !== activity.id);
|
||||
@ -98,8 +111,9 @@ class ActivityManager {
|
||||
});
|
||||
this.#isLiked = liked ?? null;
|
||||
|
||||
const { comments } = await getActivityStatistics({ albumId, assetId });
|
||||
const { comments, likes } = await getActivityStatistics({ albumId, assetId });
|
||||
this.#commentCount = comments;
|
||||
this.#likeCount = likes;
|
||||
}
|
||||
|
||||
reset() {
|
||||
@ -107,6 +121,7 @@ class ActivityManager {
|
||||
this.#assetId = undefined;
|
||||
this.#activities = [];
|
||||
this.#commentCount = 0;
|
||||
this.#likeCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,9 +9,9 @@ export interface ThemeSetting {
|
||||
}
|
||||
|
||||
// Locale to use for formatting dates, numbers, etc.
|
||||
export const locale = persisted<string | undefined>('locale', undefined, {
|
||||
export const locale = persisted<string | undefined>('locale', 'default', {
|
||||
serializer: {
|
||||
parse: (text) => (text == '' ? 'en-US' : text),
|
||||
parse: (text) => text || 'default',
|
||||
stringify: (object) => object ?? '',
|
||||
},
|
||||
});
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { notificationController, NotificationType } from '$lib/components/shared-components/notification/notification';
|
||||
import type { AssetStore, TimelineAsset } from '$lib/stores/assets-store.svelte';
|
||||
import type { StackResponse } from '$lib/utils/asset-utils';
|
||||
import { AssetVisibility, deleteAssets as deleteBulk } from '@immich/sdk';
|
||||
import { AssetVisibility, deleteAssets as deleteBulk, restoreAssets } from '@immich/sdk';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { get } from 'svelte/store';
|
||||
import { handleError } from './handle-error';
|
||||
|
||||
export type OnDelete = (assetIds: string[]) => void;
|
||||
export type OnUndoDelete = (assets: TimelineAsset[]) => void;
|
||||
export type OnRestore = (ids: string[]) => void;
|
||||
export type OnLink = (assets: { still: TimelineAsset; motion: TimelineAsset }) => void;
|
||||
export type OnUnlink = (assets: { still: TimelineAsset; motion: TimelineAsset }) => void;
|
||||
@ -17,9 +18,15 @@ export type OnStack = (result: StackResponse) => void;
|
||||
export type OnUnstack = (assets: TimelineAsset[]) => void;
|
||||
export type OnSetVisibility = (ids: string[]) => void;
|
||||
|
||||
export const deleteAssets = async (force: boolean, onAssetDelete: OnDelete, ids: string[]) => {
|
||||
export const deleteAssets = async (
|
||||
force: boolean,
|
||||
onAssetDelete: OnDelete,
|
||||
assets: TimelineAsset[],
|
||||
onUndoDelete: OnUndoDelete | undefined = undefined,
|
||||
) => {
|
||||
const $t = get(t);
|
||||
try {
|
||||
const ids = assets.map((a) => a.id);
|
||||
await deleteBulk({ assetBulkDeleteDto: { ids, force } });
|
||||
onAssetDelete(ids);
|
||||
|
||||
@ -28,12 +35,28 @@ export const deleteAssets = async (force: boolean, onAssetDelete: OnDelete, ids:
|
||||
? $t('assets_permanently_deleted_count', { values: { count: ids.length } })
|
||||
: $t('assets_trashed_count', { values: { count: ids.length } }),
|
||||
type: NotificationType.Info,
|
||||
...(onUndoDelete &&
|
||||
!force && {
|
||||
button: { text: $t('undo'), onClick: () => undoDeleteAssets(onUndoDelete, assets) },
|
||||
timeout: 5000,
|
||||
}),
|
||||
});
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.unable_to_delete_assets'));
|
||||
}
|
||||
};
|
||||
|
||||
const undoDeleteAssets = async (onUndoDelete: OnUndoDelete, assets: TimelineAsset[]) => {
|
||||
const $t = get(t);
|
||||
try {
|
||||
const ids = assets.map((a) => a.id);
|
||||
await restoreAssets({ bulkIdsDto: { ids } });
|
||||
onUndoDelete?.(assets);
|
||||
} catch (error) {
|
||||
handleError(error, $t('errors.unable_to_restore_assets'));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the asset stack state in the asset store based on the provided stack response.
|
||||
* This function updates the stack information so that the icon is shown for the primary asset
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { locale } from '$lib/stores/preferences.store';
|
||||
import { parseUtcDate } from '$lib/utils/date-time';
|
||||
import { formatGroupTitle } from '$lib/utils/timeline-util';
|
||||
import { DateTime } from 'luxon';
|
||||
@ -16,48 +17,63 @@ describe('formatGroupTitle', () => {
|
||||
|
||||
it('formats today', () => {
|
||||
const date = parseUtcDate('2024-07-27T01:00:00Z');
|
||||
expect(formatGroupTitle(date.setLocale('en'))).toBe('today');
|
||||
expect(formatGroupTitle(date.setLocale('es'))).toBe('hoy');
|
||||
locale.set('en');
|
||||
expect(formatGroupTitle(date)).toBe('today');
|
||||
locale.set('es');
|
||||
expect(formatGroupTitle(date)).toBe('hoy');
|
||||
});
|
||||
|
||||
it('formats yesterday', () => {
|
||||
const date = parseUtcDate('2024-07-26T23:59:59Z');
|
||||
expect(formatGroupTitle(date.setLocale('en'))).toBe('yesterday');
|
||||
expect(formatGroupTitle(date.setLocale('fr'))).toBe('hier');
|
||||
locale.set('en');
|
||||
expect(formatGroupTitle(date)).toBe('yesterday');
|
||||
locale.set('fr');
|
||||
expect(formatGroupTitle(date)).toBe('hier');
|
||||
});
|
||||
|
||||
it('formats last week', () => {
|
||||
const date = parseUtcDate('2024-07-21T00:00:00Z');
|
||||
expect(formatGroupTitle(date.setLocale('en'))).toBe('Sunday');
|
||||
expect(formatGroupTitle(date.setLocale('ar-SA'))).toBe('الأحد');
|
||||
locale.set('en');
|
||||
expect(formatGroupTitle(date)).toBe('Sunday');
|
||||
locale.set('ar-SA');
|
||||
expect(formatGroupTitle(date)).toBe('الأحد');
|
||||
});
|
||||
|
||||
it('formats date 7 days ago', () => {
|
||||
const date = parseUtcDate('2024-07-20T00:00:00Z');
|
||||
expect(formatGroupTitle(date.setLocale('en'))).toBe('Sat, Jul 20');
|
||||
expect(formatGroupTitle(date.setLocale('de'))).toBe('Sa., 20. Juli');
|
||||
locale.set('en');
|
||||
expect(formatGroupTitle(date)).toBe('Sat, Jul 20');
|
||||
locale.set('de');
|
||||
expect(formatGroupTitle(date)).toBe('Sa., 20. Juli');
|
||||
});
|
||||
|
||||
it('formats date this year', () => {
|
||||
const date = parseUtcDate('2020-01-01T00:00:00Z');
|
||||
expect(formatGroupTitle(date.setLocale('en'))).toBe('Wed, Jan 1, 2020');
|
||||
expect(formatGroupTitle(date.setLocale('ja'))).toBe('2020年1月1日(水)');
|
||||
locale.set('en');
|
||||
expect(formatGroupTitle(date)).toBe('Wed, Jan 1, 2020');
|
||||
locale.set('ja');
|
||||
expect(formatGroupTitle(date)).toBe('2020年1月1日(水)');
|
||||
});
|
||||
|
||||
it('formats future date', () => {
|
||||
const tomorrow = parseUtcDate('2024-07-28T00:00:00Z');
|
||||
expect(formatGroupTitle(tomorrow.setLocale('en'))).toBe('Sun, Jul 28');
|
||||
locale.set('en');
|
||||
expect(formatGroupTitle(tomorrow)).toBe('Sun, Jul 28');
|
||||
|
||||
const nextMonth = parseUtcDate('2024-08-28T00:00:00Z');
|
||||
expect(formatGroupTitle(nextMonth.setLocale('en'))).toBe('Wed, Aug 28');
|
||||
locale.set('en');
|
||||
expect(formatGroupTitle(nextMonth)).toBe('Wed, Aug 28');
|
||||
|
||||
const nextYear = parseUtcDate('2025-01-10T12:00:00Z');
|
||||
expect(formatGroupTitle(nextYear.setLocale('en'))).toBe('Fri, Jan 10, 2025');
|
||||
locale.set('en');
|
||||
expect(formatGroupTitle(nextYear)).toBe('Fri, Jan 10, 2025');
|
||||
});
|
||||
|
||||
it('returns "Invalid DateTime" when date is invalid', () => {
|
||||
const date = DateTime.invalid('test');
|
||||
expect(formatGroupTitle(date.setLocale('en'))).toBe('Invalid DateTime');
|
||||
expect(formatGroupTitle(date.setLocale('es'))).toBe('Invalid DateTime');
|
||||
locale.set('en');
|
||||
expect(formatGroupTitle(date)).toBe('Invalid DateTime');
|
||||
locale.set('es');
|
||||
expect(formatGroupTitle(date)).toBe('Invalid DateTime');
|
||||
});
|
||||
});
|
||||
|
@ -62,12 +62,12 @@ export function formatGroupTitle(_date: DateTime): string {
|
||||
|
||||
// Today
|
||||
if (today.hasSame(date, 'day')) {
|
||||
return date.toRelativeCalendar();
|
||||
return date.toRelativeCalendar({ locale: get(locale) });
|
||||
}
|
||||
|
||||
// Yesterday
|
||||
if (today.minus({ days: 1 }).hasSame(date, 'day')) {
|
||||
return date.toRelativeCalendar();
|
||||
return date.toRelativeCalendar({ locale: get(locale) });
|
||||
}
|
||||
|
||||
// Last week
|
||||
|
@ -87,6 +87,7 @@
|
||||
import { t } from 'svelte-i18n';
|
||||
import { fly } from 'svelte/transition';
|
||||
import type { PageData } from './$types';
|
||||
import { type TimelineAsset } from '$lib/stores/assets-store.svelte';
|
||||
|
||||
interface Props {
|
||||
data: PageData;
|
||||
@ -317,6 +318,11 @@
|
||||
await refreshAlbum();
|
||||
};
|
||||
|
||||
const handleUndoRemoveAssets = async (assets: TimelineAsset[]) => {
|
||||
assetStore.addAssets(assets);
|
||||
await refreshAlbum();
|
||||
};
|
||||
|
||||
const handleUpdateThumbnail = async (assetId: string) => {
|
||||
if (viewMode !== AlbumPageViewMode.SELECT_THUMBNAIL) {
|
||||
return;
|
||||
@ -570,6 +576,7 @@
|
||||
disabled={!album.isActivityEnabled}
|
||||
isLiked={activityManager.isLiked}
|
||||
numberOfComments={activityManager.commentCount}
|
||||
numberOfLikes={undefined}
|
||||
onFavorite={handleFavorite}
|
||||
onOpenActivityTab={handleOpenAndCloseActivityTab}
|
||||
/>
|
||||
@ -623,7 +630,7 @@
|
||||
<RemoveFromAlbum menuItem bind:album onRemove={handleRemoveAssets} />
|
||||
{/if}
|
||||
{#if assetInteraction.isAllUserOwned}
|
||||
<DeleteAssets menuItem onAssetDelete={handleRemoveAssets} />
|
||||
<DeleteAssets menuItem onAssetDelete={handleRemoveAssets} onUndoDelete={handleUndoRemoveAssets} />
|
||||
{/if}
|
||||
</ButtonContextMenu>
|
||||
</AssetSelectControlBar>
|
||||
|
@ -13,6 +13,7 @@
|
||||
import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte';
|
||||
import { AssetAction } from '$lib/constants';
|
||||
|
||||
import SetVisibilityAction from '$lib/components/photos-page/actions/set-visibility-action.svelte';
|
||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||
import { AssetStore } from '$lib/stores/assets-store.svelte';
|
||||
import { AssetVisibility } from '@immich/sdk';
|
||||
@ -20,7 +21,6 @@
|
||||
import { onDestroy } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import type { PageData } from './$types';
|
||||
import SetVisibilityAction from '$lib/components/photos-page/actions/set-visibility-action.svelte';
|
||||
|
||||
interface Props {
|
||||
data: PageData;
|
||||
|
@ -92,7 +92,11 @@
|
||||
<TagAction menuItem />
|
||||
{/if}
|
||||
<SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} />
|
||||
<DeleteAssets menuItem onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)} />
|
||||
<DeleteAssets
|
||||
menuItem
|
||||
onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)}
|
||||
onUndoDelete={(assets) => assetStore.addAssets(assets)}
|
||||
/>
|
||||
</ButtonContextMenu>
|
||||
</AssetSelectControlBar>
|
||||
{/if}
|
||||
|
@ -122,6 +122,7 @@
|
||||
{viewport}
|
||||
showAssetName={true}
|
||||
pageHeaderOffset={54}
|
||||
onReload={triggerAssetUpdate}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
@ -170,7 +171,7 @@
|
||||
{#if $preferences.tags.enabled && assetInteraction.isAllUserOwned}
|
||||
<TagAction menuItem />
|
||||
{/if}
|
||||
<DeleteAssets menuItem onAssetDelete={triggerAssetUpdate} />
|
||||
<DeleteAssets menuItem onAssetDelete={triggerAssetUpdate} onUndoDelete={triggerAssetUpdate} />
|
||||
<hr />
|
||||
<AssetJobActions />
|
||||
</ButtonContextMenu>
|
||||
|
@ -351,6 +351,11 @@
|
||||
await updateAssetCount();
|
||||
};
|
||||
|
||||
const handleUndoDeleteAssets = async (assets: TimelineAsset[]) => {
|
||||
assetStore.addAssets(assets);
|
||||
await updateAssetCount();
|
||||
};
|
||||
|
||||
let person = $derived(data.person);
|
||||
|
||||
let thumbnailData = $derived(getPeopleThumbnailUrl(person));
|
||||
@ -532,7 +537,11 @@
|
||||
<TagAction menuItem />
|
||||
{/if}
|
||||
<SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} />
|
||||
<DeleteAssets menuItem onAssetDelete={(assetIds) => handleDeleteAssets(assetIds)} />
|
||||
<DeleteAssets
|
||||
menuItem
|
||||
onAssetDelete={(assetIds) => handleDeleteAssets(assetIds)}
|
||||
onUndoDelete={(assets) => handleUndoDeleteAssets(assets)}
|
||||
/>
|
||||
</ButtonContextMenu>
|
||||
</AssetSelectControlBar>
|
||||
{:else}
|
||||
|
@ -150,7 +150,11 @@
|
||||
{#if $preferences.tags.enabled}
|
||||
<TagAction menuItem />
|
||||
{/if}
|
||||
<DeleteAssets menuItem onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)} />
|
||||
<DeleteAssets
|
||||
menuItem
|
||||
onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)}
|
||||
onUndoDelete={(assets) => assetStore.addAssets(assets)}
|
||||
/>
|
||||
<SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} />
|
||||
<hr />
|
||||
<AssetJobActions />
|
||||
|
@ -301,7 +301,7 @@
|
||||
{#if $preferences.tags.enabled && assetInteraction.isAllUserOwned}
|
||||
<TagAction menuItem />
|
||||
{/if}
|
||||
<DeleteAssets menuItem {onAssetDelete} />
|
||||
<DeleteAssets menuItem {onAssetDelete} onUndoDelete={onSearchQueryUpdate} />
|
||||
<hr />
|
||||
<AssetJobActions />
|
||||
</ButtonContextMenu>
|
||||
@ -382,6 +382,7 @@
|
||||
showArchiveIcon={true}
|
||||
{viewport}
|
||||
pageHeaderOffset={54}
|
||||
onReload={onSearchQueryUpdate}
|
||||
/>
|
||||
{:else if !isLoading}
|
||||
<div class="flex min-h-[calc(66vh-11rem)] w-full place-content-center items-center dark:text-white">
|
||||
@ -444,7 +445,7 @@
|
||||
{#if $preferences.tags.enabled && assetInteraction.isAllUserOwned}
|
||||
<TagAction menuItem />
|
||||
{/if}
|
||||
<DeleteAssets menuItem {onAssetDelete} />
|
||||
<DeleteAssets menuItem {onAssetDelete} onUndoDelete={onSearchQueryUpdate} />
|
||||
<hr />
|
||||
<AssetJobActions />
|
||||
</ButtonContextMenu>
|
||||
|
Loading…
x
Reference in New Issue
Block a user