Merge branch 'main' of https://github.com/immich-app/immich into feat/cli-e2e
This commit is contained in:
commit
8dac9ed053
200 changed files with 3228 additions and 2030 deletions
3
.github/workflows/cache-cleanup.yml
vendored
3
.github/workflows/cache-cleanup.yml
vendored
|
@ -1,4 +1,4 @@
|
|||
name: Clean up actions cache on PR close
|
||||
name: Cache Cleanup
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
|
@ -10,6 +10,7 @@ concurrency:
|
|||
|
||||
jobs:
|
||||
cleanup:
|
||||
name: Cleanup
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
|
|
3
.github/workflows/cli-release.yml
vendored
3
.github/workflows/cli-release.yml
vendored
|
@ -1,9 +1,10 @@
|
|||
name: Publish Package to npmjs
|
||||
name: CLI Release
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Publish
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
|
|
2
.github/workflows/docker-cleanup.yml
vendored
2
.github/workflows/docker-cleanup.yml
vendored
|
@ -5,7 +5,7 @@
|
|||
#
|
||||
# This workflow will not trigger runs on forked repos.
|
||||
|
||||
name: Cleanup Old Docker Images
|
||||
name: Docker Cleanup
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
|
3
.github/workflows/docker.yml
vendored
3
.github/workflows/docker.yml
vendored
|
@ -1,4 +1,4 @@
|
|||
name: Build and Push Docker Images
|
||||
name: Docker
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
@ -18,6 +18,7 @@ permissions:
|
|||
|
||||
jobs:
|
||||
build_and_push:
|
||||
name: Build and Push
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
# Prevent a failure in one image from stopping the other builds
|
||||
|
|
26
.github/workflows/test.yml
vendored
26
.github/workflows/test.yml
vendored
|
@ -11,7 +11,7 @@ concurrency:
|
|||
|
||||
jobs:
|
||||
e2e-tests:
|
||||
name: Run server end-to-end tests
|
||||
name: Server (e2e)
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
@ -24,7 +24,7 @@ jobs:
|
|||
run: make test-server-e2e
|
||||
|
||||
doc-tests:
|
||||
name: Run documentation checks
|
||||
name: Docs
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
|
@ -45,8 +45,12 @@ jobs:
|
|||
run: npm run check
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run build
|
||||
run: npm run build
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
server-unit-tests:
|
||||
name: Run server checks and unit tests
|
||||
name: Server
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
|
@ -76,7 +80,7 @@ jobs:
|
|||
if: ${{ !cancelled() }}
|
||||
|
||||
cli-unit-tests:
|
||||
name: Run cli checks and unit tests
|
||||
name: CLI
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
|
@ -111,7 +115,7 @@ jobs:
|
|||
if: ${{ !cancelled() }}
|
||||
|
||||
cli-e2e-tests:
|
||||
name: Run cli end-to-end tests
|
||||
name: CLI (e2e)
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest] # TODO: macos and windows
|
||||
|
@ -128,7 +132,7 @@ jobs:
|
|||
run: make test-cli-e2e
|
||||
|
||||
web-unit-tests:
|
||||
name: Run web unit test suites and checks
|
||||
name: Web
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
|
@ -162,7 +166,7 @@ jobs:
|
|||
# if: ${{ !cancelled() }}
|
||||
|
||||
mobile-unit-tests:
|
||||
name: Run mobile unit tests
|
||||
name: Mobile
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
@ -176,7 +180,7 @@ jobs:
|
|||
run: flutter test -j 1
|
||||
|
||||
ml-unit-tests:
|
||||
name: Run ML unit tests and checks
|
||||
name: Machine Learning
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
|
@ -206,7 +210,7 @@ jobs:
|
|||
poetry run pytest --cov app
|
||||
|
||||
generated-api-up-to-date:
|
||||
name: Check generated files are up-to-date
|
||||
name: OpenAPI Clients
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
@ -227,11 +231,11 @@ jobs:
|
|||
exit 1
|
||||
|
||||
generated-typeorm-migrations-up-to-date:
|
||||
name: Check generated TypeORM migrations are up-to-date
|
||||
name: TypeORM Migrations
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
image: postgres@sha256:71da05df8c4f1e1bac9b92ebfba2a0eeb183f6ac6a972fd5e55e8146e29efe9c
|
||||
env:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_USER: postgres
|
||||
|
|
608
cli/package-lock.json
generated
608
cli/package-lock.json
generated
|
@ -29,8 +29,8 @@
|
|||
"@types/mime-types": "^2.1.1",
|
||||
"@types/mock-fs": "^4.13.1",
|
||||
"@types/node": "^20.3.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.60.1",
|
||||
"@typescript-eslint/parser": "^5.48.1",
|
||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||
"@typescript-eslint/parser": "^6.0.0",
|
||||
"chai": "^4.3.7",
|
||||
"eslint": "^8.43.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
|
@ -47,7 +47,7 @@
|
|||
"ts-jest": "^29.1.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"tslib": "^2.5.3",
|
||||
"typescript": "^4.9.4"
|
||||
"typescript": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"../server": {
|
||||
|
@ -1705,9 +1705,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.2.tgz",
|
||||
"integrity": "sha512-WHZXKFCEyIUJzAwh3NyyTHYSR35SevJ6mZ1nWwJafKtiQbqRTIKSRcw3Ma3acqgsent3RRDqeVwpHntMk+9irg==",
|
||||
"version": "20.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.0.tgz",
|
||||
"integrity": "sha512-D0WfRmU9TQ8I9PFx9Yc+EBHw+vSpIub4IDvQivcp26PtPrdMGAq5SDcpXEo/epqa/DXotVpekHiLNTg3iaKXBQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
|
@ -1747,32 +1747,33 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "5.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz",
|
||||
"integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==",
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.1.tgz",
|
||||
"integrity": "sha512-5bQDGkXaxD46bPvQt08BUz9YSaO4S0fB1LB5JHQuXTfkGPI3+UUeS387C/e9jRie5GqT8u5kFTrMvAjtX4O5kA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.4.0",
|
||||
"@typescript-eslint/scope-manager": "5.62.0",
|
||||
"@typescript-eslint/type-utils": "5.62.0",
|
||||
"@typescript-eslint/utils": "5.62.0",
|
||||
"@eslint-community/regexpp": "^4.5.1",
|
||||
"@typescript-eslint/scope-manager": "6.13.1",
|
||||
"@typescript-eslint/type-utils": "6.13.1",
|
||||
"@typescript-eslint/utils": "6.13.1",
|
||||
"@typescript-eslint/visitor-keys": "6.13.1",
|
||||
"debug": "^4.3.4",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.2.0",
|
||||
"natural-compare-lite": "^1.4.0",
|
||||
"semver": "^7.3.7",
|
||||
"tsutils": "^3.21.0"
|
||||
"ignore": "^5.2.4",
|
||||
"natural-compare": "^1.4.0",
|
||||
"semver": "^7.5.4",
|
||||
"ts-api-utils": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
"@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha",
|
||||
"eslint": "^7.0.0 || ^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
|
@ -1780,26 +1781,126 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "5.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz",
|
||||
"integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==",
|
||||
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz",
|
||||
"integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "5.62.0",
|
||||
"@typescript-eslint/types": "5.62.0",
|
||||
"@typescript-eslint/typescript-estree": "5.62.0",
|
||||
"debug": "^4.3.4"
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"@typescript-eslint/visitor-keys": "6.13.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz",
|
||||
"integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz",
|
||||
"integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"@typescript-eslint/visitor-keys": "6.13.1",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
"semver": "^7.5.4",
|
||||
"ts-api-utils": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.1.tgz",
|
||||
"integrity": "sha512-ouPn/zVoan92JgAegesTXDB/oUp6BP1v8WpfYcqh649ejNc9Qv+B4FF2Ff626kO1xg0wWwwG48lAJ4JuesgdOw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@types/json-schema": "^7.0.12",
|
||||
"@types/semver": "^7.5.0",
|
||||
"@typescript-eslint/scope-manager": "6.13.1",
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"@typescript-eslint/typescript-estree": "6.13.1",
|
||||
"semver": "^7.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
"eslint": "^7.0.0 || ^8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz",
|
||||
"integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"eslint-visitor-keys": "^3.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.1.tgz",
|
||||
"integrity": "sha512-fs2XOhWCzRhqMmQf0eicLa/CWSaYss2feXsy7xBD/pLyWke/jCIVc2s1ikEAtSW7ina1HNhv7kONoEfVNEcdDQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "6.13.1",
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"@typescript-eslint/typescript-estree": "6.13.1",
|
||||
"@typescript-eslint/visitor-keys": "6.13.1",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^7.0.0 || ^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
|
@ -1807,6 +1908,80 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz",
|
||||
"integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"@typescript-eslint/visitor-keys": "6.13.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz",
|
||||
"integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz",
|
||||
"integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"@typescript-eslint/visitor-keys": "6.13.1",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
"semver": "^7.5.4",
|
||||
"ts-api-utils": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz",
|
||||
"integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"eslint-visitor-keys": "^3.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "5.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz",
|
||||
|
@ -1825,25 +2000,25 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "5.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz",
|
||||
"integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==",
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.1.tgz",
|
||||
"integrity": "sha512-A2qPlgpxx2v//3meMqQyB1qqTg1h1dJvzca7TugM3Yc2USDY+fsRBiojAEo92HO7f5hW5mjAUF6qobOPzlBCBQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "5.62.0",
|
||||
"@typescript-eslint/utils": "5.62.0",
|
||||
"@typescript-eslint/typescript-estree": "6.13.1",
|
||||
"@typescript-eslint/utils": "6.13.1",
|
||||
"debug": "^4.3.4",
|
||||
"tsutils": "^3.21.0"
|
||||
"ts-api-utils": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "*"
|
||||
"eslint": "^7.0.0 || ^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
|
@ -1851,6 +2026,105 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz",
|
||||
"integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"@typescript-eslint/visitor-keys": "6.13.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz",
|
||||
"integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz",
|
||||
"integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"@typescript-eslint/visitor-keys": "6.13.1",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
"semver": "^7.5.4",
|
||||
"ts-api-utils": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.1.tgz",
|
||||
"integrity": "sha512-ouPn/zVoan92JgAegesTXDB/oUp6BP1v8WpfYcqh649ejNc9Qv+B4FF2Ff626kO1xg0wWwwG48lAJ4JuesgdOw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@types/json-schema": "^7.0.12",
|
||||
"@types/semver": "^7.5.0",
|
||||
"@typescript-eslint/scope-manager": "6.13.1",
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"@typescript-eslint/typescript-estree": "6.13.1",
|
||||
"semver": "^7.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^7.0.0 || ^8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz",
|
||||
"integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"eslint-visitor-keys": "^3.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "5.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz",
|
||||
|
@ -4969,12 +5243,6 @@
|
|||
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/natural-compare-lite": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz",
|
||||
"integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/node-int64": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
|
||||
|
@ -6128,6 +6396,18 @@
|
|||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-api-utils": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz",
|
||||
"integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=16.13.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-jest": {
|
||||
"version": "29.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz",
|
||||
|
@ -6275,16 +6555,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.9.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz",
|
||||
"integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.2.0"
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
|
@ -7815,9 +8095,9 @@
|
|||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "20.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.2.tgz",
|
||||
"integrity": "sha512-WHZXKFCEyIUJzAwh3NyyTHYSR35SevJ6mZ1nWwJafKtiQbqRTIKSRcw3Ma3acqgsent3RRDqeVwpHntMk+9irg==",
|
||||
"version": "20.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.4.tgz",
|
||||
"integrity": "sha512-wmyg8HUhcn6ACjsn8oKYjkN/zUzQeNtMy44weTJSM6p4MMzEOuKbA3OjJ267uPCOW7Xex9dyrNTful8XTQYoDA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"undici-types": "~5.26.4"
|
||||
|
@ -7857,33 +8137,136 @@
|
|||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "5.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz",
|
||||
"integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==",
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.1.tgz",
|
||||
"integrity": "sha512-5bQDGkXaxD46bPvQt08BUz9YSaO4S0fB1LB5JHQuXTfkGPI3+UUeS387C/e9jRie5GqT8u5kFTrMvAjtX4O5kA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@eslint-community/regexpp": "^4.4.0",
|
||||
"@typescript-eslint/scope-manager": "5.62.0",
|
||||
"@typescript-eslint/type-utils": "5.62.0",
|
||||
"@typescript-eslint/utils": "5.62.0",
|
||||
"@eslint-community/regexpp": "^4.5.1",
|
||||
"@typescript-eslint/scope-manager": "6.13.1",
|
||||
"@typescript-eslint/type-utils": "6.13.1",
|
||||
"@typescript-eslint/utils": "6.13.1",
|
||||
"@typescript-eslint/visitor-keys": "6.13.1",
|
||||
"debug": "^4.3.4",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.2.0",
|
||||
"natural-compare-lite": "^1.4.0",
|
||||
"semver": "^7.3.7",
|
||||
"tsutils": "^3.21.0"
|
||||
"ignore": "^5.2.4",
|
||||
"natural-compare": "^1.4.0",
|
||||
"semver": "^7.5.4",
|
||||
"ts-api-utils": "^1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz",
|
||||
"integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"@typescript-eslint/visitor-keys": "6.13.1"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/types": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz",
|
||||
"integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==",
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/typescript-estree": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz",
|
||||
"integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"@typescript-eslint/visitor-keys": "6.13.1",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
"semver": "^7.5.4",
|
||||
"ts-api-utils": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/utils": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.1.tgz",
|
||||
"integrity": "sha512-ouPn/zVoan92JgAegesTXDB/oUp6BP1v8WpfYcqh649ejNc9Qv+B4FF2Ff626kO1xg0wWwwG48lAJ4JuesgdOw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@types/json-schema": "^7.0.12",
|
||||
"@types/semver": "^7.5.0",
|
||||
"@typescript-eslint/scope-manager": "6.13.1",
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"@typescript-eslint/typescript-estree": "6.13.1",
|
||||
"semver": "^7.5.4"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/visitor-keys": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz",
|
||||
"integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"eslint-visitor-keys": "^3.4.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "5.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz",
|
||||
"integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==",
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.1.tgz",
|
||||
"integrity": "sha512-fs2XOhWCzRhqMmQf0eicLa/CWSaYss2feXsy7xBD/pLyWke/jCIVc2s1ikEAtSW7ina1HNhv7kONoEfVNEcdDQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/scope-manager": "5.62.0",
|
||||
"@typescript-eslint/types": "5.62.0",
|
||||
"@typescript-eslint/typescript-estree": "5.62.0",
|
||||
"@typescript-eslint/scope-manager": "6.13.1",
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"@typescript-eslint/typescript-estree": "6.13.1",
|
||||
"@typescript-eslint/visitor-keys": "6.13.1",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz",
|
||||
"integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"@typescript-eslint/visitor-keys": "6.13.1"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/types": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz",
|
||||
"integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==",
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/typescript-estree": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz",
|
||||
"integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"@typescript-eslint/visitor-keys": "6.13.1",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
"semver": "^7.5.4",
|
||||
"ts-api-utils": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/visitor-keys": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz",
|
||||
"integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"eslint-visitor-keys": "^3.4.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/scope-manager": {
|
||||
|
@ -7897,15 +8280,73 @@
|
|||
}
|
||||
},
|
||||
"@typescript-eslint/type-utils": {
|
||||
"version": "5.62.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz",
|
||||
"integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==",
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.1.tgz",
|
||||
"integrity": "sha512-A2qPlgpxx2v//3meMqQyB1qqTg1h1dJvzca7TugM3Yc2USDY+fsRBiojAEo92HO7f5hW5mjAUF6qobOPzlBCBQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/typescript-estree": "5.62.0",
|
||||
"@typescript-eslint/utils": "5.62.0",
|
||||
"@typescript-eslint/typescript-estree": "6.13.1",
|
||||
"@typescript-eslint/utils": "6.13.1",
|
||||
"debug": "^4.3.4",
|
||||
"tsutils": "^3.21.0"
|
||||
"ts-api-utils": "^1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz",
|
||||
"integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"@typescript-eslint/visitor-keys": "6.13.1"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/types": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz",
|
||||
"integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==",
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/typescript-estree": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz",
|
||||
"integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"@typescript-eslint/visitor-keys": "6.13.1",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
"semver": "^7.5.4",
|
||||
"ts-api-utils": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/utils": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.1.tgz",
|
||||
"integrity": "sha512-ouPn/zVoan92JgAegesTXDB/oUp6BP1v8WpfYcqh649ejNc9Qv+B4FF2Ff626kO1xg0wWwwG48lAJ4JuesgdOw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@types/json-schema": "^7.0.12",
|
||||
"@types/semver": "^7.5.0",
|
||||
"@typescript-eslint/scope-manager": "6.13.1",
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"@typescript-eslint/typescript-estree": "6.13.1",
|
||||
"semver": "^7.5.4"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/visitor-keys": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz",
|
||||
"integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "6.13.1",
|
||||
"eslint-visitor-keys": "^3.4.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/types": {
|
||||
|
@ -10232,12 +10673,6 @@
|
|||
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
||||
"dev": true
|
||||
},
|
||||
"natural-compare-lite": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz",
|
||||
"integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
|
||||
"dev": true
|
||||
},
|
||||
"node-int64": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
|
||||
|
@ -11062,6 +11497,13 @@
|
|||
"is-number": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"ts-api-utils": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz",
|
||||
"integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"ts-jest": {
|
||||
"version": "29.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz",
|
||||
|
@ -11144,9 +11586,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.9.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz",
|
||||
"integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==",
|
||||
"dev": true
|
||||
},
|
||||
"undici-types": {
|
||||
|
|
|
@ -29,8 +29,8 @@
|
|||
"@types/mime-types": "^2.1.1",
|
||||
"@types/mock-fs": "^4.13.1",
|
||||
"@types/node": "^20.3.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.60.1",
|
||||
"@typescript-eslint/parser": "^5.48.1",
|
||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||
"@typescript-eslint/parser": "^6.0.0",
|
||||
"chai": "^4.3.7",
|
||||
"eslint": "^8.43.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
|
@ -47,7 +47,7 @@
|
|||
"ts-jest": "^29.1.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"tslib": "^2.5.3",
|
||||
"typescript": "^4.9.4"
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc --project tsconfig.build.json",
|
||||
|
|
126
cli/src/api/open-api/api.ts
generated
126
cli/src/api/open-api/api.ts
generated
|
@ -1164,22 +1164,6 @@ export interface CheckExistingAssetsResponseDto {
|
|||
*/
|
||||
'existingIds': Array<string>;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @enum {string}
|
||||
*/
|
||||
|
||||
export const CitiesFile = {
|
||||
Cities15000: 'cities15000',
|
||||
Cities5000: 'cities5000',
|
||||
Cities1000: 'cities1000',
|
||||
Cities500: 'cities500'
|
||||
} as const;
|
||||
|
||||
export type CitiesFile = typeof CitiesFile[keyof typeof CitiesFile];
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
|
@ -3832,12 +3816,6 @@ export interface SystemConfigPasswordLoginDto {
|
|||
* @interface SystemConfigReverseGeocodingDto
|
||||
*/
|
||||
export interface SystemConfigReverseGeocodingDto {
|
||||
/**
|
||||
*
|
||||
* @type {CitiesFile}
|
||||
* @memberof SystemConfigReverseGeocodingDto
|
||||
*/
|
||||
'citiesFileOverride': CitiesFile;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
|
@ -3845,8 +3823,6 @@ export interface SystemConfigReverseGeocodingDto {
|
|||
*/
|
||||
'enabled': boolean;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
|
@ -6808,6 +6784,48 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
|
|||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Get all asset of a device that are in the database, ID only.
|
||||
* @param {string} deviceId
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getAllUserAssetsByDeviceId: async (deviceId: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'deviceId' is not null or undefined
|
||||
assertParamExists('getAllUserAssetsByDeviceId', 'deviceId', deviceId)
|
||||
const localVarPath = `/asset/device/{deviceId}`
|
||||
.replace(`{${"deviceId"}}`, encodeURIComponent(String(deviceId)));
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication cookie required
|
||||
|
||||
// authentication api_key required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration)
|
||||
|
||||
// authentication bearer required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
@ -7477,9 +7495,11 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
|
|||
};
|
||||
},
|
||||
/**
|
||||
* Get all asset of a device that are in the database, ID only.
|
||||
*
|
||||
* @summary Use /asset/device/:deviceId instead - Remove in 1.92 release
|
||||
* @param {string} deviceId
|
||||
* @param {*} [options] Override http request option.
|
||||
* @deprecated
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getUserAssetsByDeviceId: async (deviceId: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
|
@ -8311,6 +8331,16 @@ export const AssetApiFp = function(configuration?: Configuration) {
|
|||
const localVarAxiosArgs = await localVarAxiosParamCreator.getAllAssets(skip, take, userId, isFavorite, isArchived, updatedAfter, updatedBefore, ifNoneMatch, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
* Get all asset of a device that are in the database, ID only.
|
||||
* @param {string} deviceId
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async getAllUserAssetsByDeviceId(deviceId: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<string>>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.getAllUserAssetsByDeviceId(deviceId, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
* Get a single asset\'s information
|
||||
* @param {string} id
|
||||
|
@ -8458,9 +8488,11 @@ export const AssetApiFp = function(configuration?: Configuration) {
|
|||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
* Get all asset of a device that are in the database, ID only.
|
||||
*
|
||||
* @summary Use /asset/device/:deviceId instead - Remove in 1.92 release
|
||||
* @param {string} deviceId
|
||||
* @param {*} [options] Override http request option.
|
||||
* @deprecated
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async getUserAssetsByDeviceId(deviceId: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<string>>> {
|
||||
|
@ -8686,6 +8718,15 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
|
|||
getAllAssets(requestParameters: AssetApiGetAllAssetsRequest = {}, options?: AxiosRequestConfig): AxiosPromise<Array<AssetResponseDto>> {
|
||||
return localVarFp.getAllAssets(requestParameters.skip, requestParameters.take, requestParameters.userId, requestParameters.isFavorite, requestParameters.isArchived, requestParameters.updatedAfter, requestParameters.updatedBefore, requestParameters.ifNoneMatch, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Get all asset of a device that are in the database, ID only.
|
||||
* @param {AssetApiGetAllUserAssetsByDeviceIdRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getAllUserAssetsByDeviceId(requestParameters: AssetApiGetAllUserAssetsByDeviceIdRequest, options?: AxiosRequestConfig): AxiosPromise<Array<string>> {
|
||||
return localVarFp.getAllUserAssetsByDeviceId(requestParameters.deviceId, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Get a single asset\'s information
|
||||
* @param {AssetApiGetAssetByIdRequest} requestParameters Request parameters.
|
||||
|
@ -8792,9 +8833,11 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
|
|||
return localVarFp.getTimeBuckets(requestParameters.size, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.withStacked, requestParameters.withPartners, requestParameters.key, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Get all asset of a device that are in the database, ID only.
|
||||
*
|
||||
* @summary Use /asset/device/:deviceId instead - Remove in 1.92 release
|
||||
* @param {AssetApiGetUserAssetsByDeviceIdRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @deprecated
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getUserAssetsByDeviceId(requestParameters: AssetApiGetUserAssetsByDeviceIdRequest, options?: AxiosRequestConfig): AxiosPromise<Array<string>> {
|
||||
|
@ -9030,6 +9073,20 @@ export interface AssetApiGetAllAssetsRequest {
|
|||
readonly ifNoneMatch?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Request parameters for getAllUserAssetsByDeviceId operation in AssetApi.
|
||||
* @export
|
||||
* @interface AssetApiGetAllUserAssetsByDeviceIdRequest
|
||||
*/
|
||||
export interface AssetApiGetAllUserAssetsByDeviceIdRequest {
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof AssetApiGetAllUserAssetsByDeviceId
|
||||
*/
|
||||
readonly deviceId: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Request parameters for getAssetById operation in AssetApi.
|
||||
* @export
|
||||
|
@ -9974,6 +10031,17 @@ export class AssetApi extends BaseAPI {
|
|||
return AssetApiFp(this.configuration).getAllAssets(requestParameters.skip, requestParameters.take, requestParameters.userId, requestParameters.isFavorite, requestParameters.isArchived, requestParameters.updatedAfter, requestParameters.updatedBefore, requestParameters.ifNoneMatch, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all asset of a device that are in the database, ID only.
|
||||
* @param {AssetApiGetAllUserAssetsByDeviceIdRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof AssetApi
|
||||
*/
|
||||
public getAllUserAssetsByDeviceId(requestParameters: AssetApiGetAllUserAssetsByDeviceIdRequest, options?: AxiosRequestConfig) {
|
||||
return AssetApiFp(this.configuration).getAllUserAssetsByDeviceId(requestParameters.deviceId, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single asset\'s information
|
||||
* @param {AssetApiGetAssetByIdRequest} requestParameters Request parameters.
|
||||
|
@ -10104,9 +10172,11 @@ export class AssetApi extends BaseAPI {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get all asset of a device that are in the database, ID only.
|
||||
*
|
||||
* @summary Use /asset/device/:deviceId instead - Remove in 1.92 release
|
||||
* @param {AssetApiGetUserAssetsByDeviceIdRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @deprecated
|
||||
* @throws {RequiredError}
|
||||
* @memberof AssetApi
|
||||
*/
|
||||
|
|
|
@ -59,7 +59,7 @@ services:
|
|||
build:
|
||||
context: ../web
|
||||
dockerfile: Dockerfile
|
||||
command: npm run dev --host
|
||||
command: "node ./node_modules/.bin/vite dev --host 0.0.0.0 --port 3000"
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
|
|
|
@ -14,8 +14,6 @@ docker exec -it <id or name> <command> # attach to a container with a c
|
|||
docker exec -it immich_server sh
|
||||
docker exec -it immich_microservices sh
|
||||
docker exec -it immich_machine_learning sh
|
||||
docker exec -it immich_web sh
|
||||
docker exec -it immich_proxy sh
|
||||
```
|
||||
|
||||
## Logs
|
||||
|
@ -26,8 +24,6 @@ docker logs <id or name> # see the logs for a specific container (by id
|
|||
docker logs immich_server
|
||||
docker logs immich_microservices
|
||||
docker logs immich_machine_learning
|
||||
docker logs immich_web
|
||||
docker logs immich_proxy
|
||||
```
|
||||
|
||||
:::tip Follow a log
|
||||
|
|
58
docs/docs/guides/remote-access.md
Normal file
58
docs/docs/guides/remote-access.md
Normal file
|
@ -0,0 +1,58 @@
|
|||
# Remote Access
|
||||
|
||||
This page gives a few pointers on how to access your Immich instance from outside your LAN.
|
||||
|
||||
:::danger
|
||||
Never forward port 2283 directly to the internet without additional configuration. This will expose the web interface via http to the internet, making you succeptible to [man in the middle](https://en.wikipedia.org/wiki/Man-in-the-middle_attack) attacks.
|
||||
:::
|
||||
|
||||
## Option 1: VPN to home network
|
||||
|
||||
You may use a VPN service to open an encrypted connection to your Immich instance. OpenVPN and Wireguard are two popular VPN solutions. Here is a guide on setting up VPN access to your server - [Pihole documentation](https://docs.pi-hole.net/guides/vpn/wireguard/overview/)
|
||||
|
||||
### Pros:
|
||||
|
||||
- Simple to set up and very secure.
|
||||
- Single point of potential failure, i.e., the VPN software itself. Even if there is a zero-day vulnerability on Immich, you will not be at risk.
|
||||
- Both Wireguard and OpenVPN are independently security-audited, so the risk of serious zero-day exploits are minimal.
|
||||
|
||||
### Cons:
|
||||
|
||||
- If you don't have a static IP address, you would need to set up a [Dynamic DNS](https://www.cloudflare.com/learning/dns/glossary/dynamic-dns/). [DuckDNS](https://www.duckdns.org/) is a free DDNS provider.
|
||||
- VPN software needs to be installed and active on both server-side and client-side.
|
||||
- Requires you to open a port on your router to your server.
|
||||
|
||||
## Option 2: Tailscale
|
||||
|
||||
If you are unable to open a port on your router for Wireguard or OpenVPN to your server, [Tailscale](https://tailscale.com/) is a good option. Tailscale mediates a peer-to-peer wireguard tunnel between your server and remote device, even if one or both of them are behind a [NAT firewall](https://en.wikipedia.org/wiki/Network_address_translation).
|
||||
|
||||
### Pros
|
||||
|
||||
- Minimal configuration needed on server and client sides.
|
||||
- You are protected against zero-day vulnerabilities on Immich.
|
||||
|
||||
### Cons
|
||||
|
||||
- The Tailscale client usually needs to run as root on your devices and it increases the attack surface slightly compared to a minimal Wireguard server. e.g., an [RCE vulnerability](https://github.com/tailscale/tailscale/security/advisories/GHSA-vqp6-rc3h-83cp) was discovered in the Windows Tailscale client in November 2022.
|
||||
- Tailscale is a paid service. However, there is a generous [free tier](https://tailscale.com/pricing/) that permits up to 3 users and up to 100 devices.
|
||||
- Tailscale needs to be installed and running on both server-side and client-side.
|
||||
|
||||
## Option 3: Reverse Proxy
|
||||
|
||||
A reverse proxy is a service that sits between web servers and clients. A reverse proxy can either be hosted on the server itself or remotely. Clients can connect to the reverse proxy via https, and the proxy relays data to Immich. This setup makes most sense if you have your own domain and want to access your Immich instance just like any other website, from outside your LAN. You can also use a DDNS provider like DuckDNS or no-ip if you don't have a domain. This configuration allows the Immich Android and iphone apps to connect to your server without a VPN or tailscale app on the client side.
|
||||
|
||||
If you're hosting your own reverse proxy, [Nginx](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/) is a great option. An example configuration for Nginx is provided [here](https://immich.app/docs/administration/reverse-proxy).
|
||||
|
||||
You'll also need your own certificate to authenticate https connections. If you're making Immich publicly accesible, [Let's Encrypt](https://letsencrypt.org/) can provide a free certificate for your domain and is the recommended option. Alternatively, a [self-signed certificate](https://en.wikipedia.org/wiki/Self-signed_certificate) allows you to encrypt your connection to Immich, but it raises a security warning on the client's browser.
|
||||
|
||||
A remote reverse proxy like [Cloudflare](https://www.cloudflare.com/learning/cdn/glossary/reverse-proxy/) increases security by hiding the server IP address, which makes targeted attacks like [DDoS](https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/) harder.
|
||||
|
||||
### Pros
|
||||
|
||||
- No additional software needs to be installed client-side
|
||||
- If you only need access to the web interface remotely, it is possible to set up access controls that shield you from zero-day vulnerabilities on Immich. [Cloudflare Access](https://www.cloudflare.com/zero-trust/products/access/) has a generous free tier.
|
||||
|
||||
### Cons
|
||||
|
||||
- Complex configuration
|
||||
- Depending on your configuration, both the Immich web interface and API may be exposed to the internet. Immich is under very active developement and the existence of severe security vulnerabilities cannot be ruled out.
|
120
docs/package-lock.json
generated
120
docs/package-lock.json
generated
|
@ -15,7 +15,7 @@
|
|||
"@mdx-js/react": "^1.6.22",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"classnames": "^2.3.2",
|
||||
"clsx": "^1.2.1",
|
||||
"clsx": "^2.0.0",
|
||||
"docusaurus-lunr-search": "^2.3.2",
|
||||
"docusaurus-preset-openapi": "^0.6.3",
|
||||
"postcss": "^8.4.25",
|
||||
|
@ -28,7 +28,7 @@
|
|||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "^2.4.1",
|
||||
"@tsconfig/docusaurus": "^1.0.5",
|
||||
"prettier": "^2.8.8",
|
||||
"prettier": "^3.0.0",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -2603,6 +2603,14 @@
|
|||
"react-dom": "^16.8.4 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/theme-classic/node_modules/clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/theme-common": {
|
||||
"version": "2.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-2.4.3.tgz",
|
||||
|
@ -2633,6 +2641,14 @@
|
|||
"react-dom": "^16.8.4 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/theme-common/node_modules/clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/theme-search-algolia": {
|
||||
"version": "2.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.4.3.tgz",
|
||||
|
@ -2663,6 +2679,14 @@
|
|||
"react-dom": "^16.8.4 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/theme-search-algolia/node_modules/clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@docusaurus/theme-translations": {
|
||||
"version": "2.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-2.4.3.tgz",
|
||||
|
@ -4948,9 +4972,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
|
||||
"integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
|
@ -5995,6 +6019,14 @@
|
|||
"react-dom": "^16.8.4 || ^17"
|
||||
}
|
||||
},
|
||||
"node_modules/docusaurus-lunr-search/node_modules/clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/docusaurus-plugin-openapi": {
|
||||
"version": "0.6.4",
|
||||
"resolved": "https://registry.npmjs.org/docusaurus-plugin-openapi/-/docusaurus-plugin-openapi-0.6.4.tgz",
|
||||
|
@ -6025,6 +6057,14 @@
|
|||
"react-dom": "^16.8.4 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/docusaurus-plugin-openapi/node_modules/clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/docusaurus-plugin-openapi/node_modules/fs-extra": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
|
||||
|
@ -6098,6 +6138,14 @@
|
|||
"react-dom": "^16.8.4 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/docusaurus-theme-openapi/node_modules/clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/dom-converter": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
|
||||
|
@ -10801,15 +10849,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "2.8.8",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
|
||||
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz",
|
||||
"integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"prettier": "bin-prettier.js"
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
|
@ -16745,6 +16793,13 @@
|
|||
"rtlcss": "^3.5.0",
|
||||
"tslib": "^2.4.0",
|
||||
"utility-types": "^3.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@docusaurus/theme-common": {
|
||||
|
@ -16768,6 +16823,13 @@
|
|||
"tslib": "^2.4.0",
|
||||
"use-sync-external-store": "^1.2.0",
|
||||
"utility-types": "^3.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@docusaurus/theme-search-algolia": {
|
||||
|
@ -16791,6 +16853,13 @@
|
|||
"lodash": "^4.17.21",
|
||||
"tslib": "^2.4.0",
|
||||
"utility-types": "^3.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@docusaurus/theme-translations": {
|
||||
|
@ -18515,9 +18584,9 @@
|
|||
}
|
||||
},
|
||||
"clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg=="
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
|
||||
"integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q=="
|
||||
},
|
||||
"collapse-white-space": {
|
||||
"version": "1.0.6",
|
||||
|
@ -19243,6 +19312,13 @@
|
|||
"to-vfile": "^6.1.0",
|
||||
"unified": "^9.0.0",
|
||||
"unist-util-is": "^4.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"docusaurus-plugin-openapi": {
|
||||
|
@ -19268,6 +19344,11 @@
|
|||
"webpack": "^5.73.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg=="
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
|
||||
|
@ -19321,6 +19402,13 @@
|
|||
"react-redux": "^7.2.0",
|
||||
"redux-devtools-extension": "^2.13.8",
|
||||
"webpack": "^5.73.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"dom-converter": {
|
||||
|
@ -22663,9 +22751,9 @@
|
|||
"integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA=="
|
||||
},
|
||||
"prettier": {
|
||||
"version": "2.8.8",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
|
||||
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz",
|
||||
"integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==",
|
||||
"dev": true
|
||||
},
|
||||
"pretty-error": {
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
"@mdx-js/react": "^1.6.22",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"classnames": "^2.3.2",
|
||||
"clsx": "^1.2.1",
|
||||
"clsx": "^2.0.0",
|
||||
"docusaurus-lunr-search": "^2.3.2",
|
||||
"docusaurus-preset-openapi": "^0.6.3",
|
||||
"postcss": "^8.4.25",
|
||||
|
@ -37,7 +37,7 @@
|
|||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "^2.4.1",
|
||||
"@tsconfig/docusaurus": "^1.0.5",
|
||||
"prettier": "^2.8.8",
|
||||
"prettier": "^3.0.0",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"browserslist": {
|
||||
|
|
|
@ -61,8 +61,12 @@
|
|||
.searchbox__input {
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
-webkit-transition: box-shadow 0.4s ease, background 0.4s ease;
|
||||
transition: box-shadow 0.4s ease, background 0.4s ease;
|
||||
-webkit-transition:
|
||||
box-shadow 0.4s ease,
|
||||
background 0.4s ease;
|
||||
transition:
|
||||
box-shadow 0.4s ease,
|
||||
background 0.4s ease;
|
||||
border: 0;
|
||||
border-radius: 16px;
|
||||
box-shadow: inset 0 0 0 1px #cccccc;
|
||||
|
@ -243,7 +247,9 @@
|
|||
}
|
||||
|
||||
.algolia-autocomplete .ds-dropdown-menu {
|
||||
box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.2), 0 2px 3px 0 rgba(0, 0, 0, 0.1);
|
||||
box-shadow:
|
||||
0 1px 0 0 rgba(0, 0, 0, 0.2),
|
||||
0 2px 3px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
@media (min-width: 601px) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM python:3.11-bookworm as builder
|
||||
FROM python:3.11-bookworm@sha256:e5a1b0a194a5fbf94f6e350b31c9a508723f9eeb2f9e9e32c3b65df8520a40cc as builder
|
||||
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
|
@ -13,7 +13,7 @@ ENV VIRTUAL_ENV="/opt/venv" PATH="/opt/venv/bin:${PATH}"
|
|||
COPY poetry.lock pyproject.toml ./
|
||||
RUN poetry install --sync --no-interaction --no-ansi --no-root --only main
|
||||
|
||||
FROM python:3.11-slim-bookworm
|
||||
FROM python:3.11-slim-bookworm@sha256:1bc6a3e9356d64ea632791653bc71a56340e8741dab66434ab2739ebf6aed29d
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends tini libmimalloc2.0 && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM mambaorg/micromamba:bookworm-slim as builder
|
||||
FROM mambaorg/micromamba:bookworm-slim@sha256:d20c621f3ae42f50f380166b15b6c88b14fa62ab6ea188f2cef33451d64057c7 as builder
|
||||
|
||||
ENV NODE_ENV=production \
|
||||
TRANSFORMERS_CACHE=/cache \
|
||||
|
|
|
@ -49,7 +49,6 @@ dart_code_metrics:
|
|||
# Common
|
||||
- avoid-accessing-collections-by-constant-index
|
||||
- avoid-accessing-other-classes-private-members
|
||||
- avoid-async-call-in-sync-function
|
||||
- avoid-cascade-after-if-null
|
||||
- avoid-collapsible-if
|
||||
- avoid-collection-methods-with-unrelated-types
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"album_viewer_page_share_add_users": "Afegeix usuaris",
|
||||
"all_people_page_title": "Persones",
|
||||
"all_videos_page_title": "Vídeos",
|
||||
"app_bar_signout_dialog_content": "Are you sure you wanna sign out?",
|
||||
"app_bar_signout_dialog_content": "Are you sure you want to sign out?",
|
||||
"app_bar_signout_dialog_ok": "Yes",
|
||||
"app_bar_signout_dialog_title": "Sign out",
|
||||
"archive_page_no_archived_assets": "No s'ha trobat res arxivat",
|
||||
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Local Storage",
|
||||
"cache_settings_title": "Configuració de la memòria cau",
|
||||
"change_password_form_confirm_password": "Confirma la contrasenya",
|
||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_description": "Hi {name},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_new_password": "New Password",
|
||||
"change_password_form_password_mismatch": "Passwords do not match",
|
||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Místní úložiště",
|
||||
"cache_settings_title": "Nastavení vyrovnávací paměti",
|
||||
"change_password_form_confirm_password": "Potvrďte heslo",
|
||||
"change_password_form_description": "Dobrý den, {firstName} {lastName},\n\nje to buď poprvé, co se přihlašujete do systému, nebo byl vytvořen požadavek na změnu hesla. Níže zadejte nové heslo.",
|
||||
"change_password_form_description": "Dobrý den, {name},\n\nje to buď poprvé, co se přihlašujete do systému, nebo byl vytvořen požadavek na změnu hesla. Níže zadejte nové heslo.",
|
||||
"change_password_form_new_password": "Nové heslo",
|
||||
"change_password_form_password_mismatch": "Hesla se neshodují",
|
||||
"change_password_form_reenter_new_password": "Znovu zadejte nové heslo",
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"album_viewer_page_share_add_users": "Tilføj brugere",
|
||||
"all_people_page_title": "Personer",
|
||||
"all_videos_page_title": "Videoer",
|
||||
"app_bar_signout_dialog_content": "Are you sure you wanna sign out?",
|
||||
"app_bar_signout_dialog_content": "Are you sure you want to sign out?",
|
||||
"app_bar_signout_dialog_ok": "Yes",
|
||||
"app_bar_signout_dialog_title": "Sign out",
|
||||
"archive_page_no_archived_assets": "Ingen arkiverede elementer blev fundet",
|
||||
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Local Storage",
|
||||
"cache_settings_title": "Cache-indstillinger",
|
||||
"change_password_form_confirm_password": "Bekræft kodeord",
|
||||
"change_password_form_description": "Hej {firstName} {lastName},\n\nDette er enten første gang du logger ind eller også er der lavet en anmodning om at ændre dit kodeord. Indtast venligst et nyt kodeord nedenfor.",
|
||||
"change_password_form_description": "Hej {name},\n\nDette er enten første gang du logger ind eller også er der lavet en anmodning om at ændre dit kodeord. Indtast venligst et nyt kodeord nedenfor.",
|
||||
"change_password_form_new_password": "Nyt kodeord",
|
||||
"change_password_form_password_mismatch": "Kodeord er ikke ens",
|
||||
"change_password_form_reenter_new_password": "Gentag nyt kodeord",
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Lokaler Speicher",
|
||||
"cache_settings_title": "Zwischenspeicher Einstellungen",
|
||||
"change_password_form_confirm_password": "Passwort bestätigen",
|
||||
"change_password_form_description": "Hallo {firstName} {lastName}\n\nDas ist entweder das erste Mal dass du dich einloggst oder eine Anfrage zur Änderung deines Passwortes wurde gestellt. Bitte gebe das neue Passwort ein.",
|
||||
"change_password_form_description": "Hallo {name}\n\nDas ist entweder das erste Mal dass du dich einloggst oder eine Anfrage zur Änderung deines Passwortes wurde gestellt. Bitte gebe das neue Passwort ein.",
|
||||
"change_password_form_new_password": "Neues Passwort",
|
||||
"change_password_form_password_mismatch": "Passwörter stimmen nicht überein",
|
||||
"change_password_form_reenter_new_password": "Passwort erneut eingeben",
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
"album_viewer_page_share_add_users": "Add users",
|
||||
"all_people_page_title": "People",
|
||||
"all_videos_page_title": "Videos",
|
||||
"app_bar_signout_dialog_content": "Are you sure you wanna sign out?",
|
||||
"app_bar_signout_dialog_content": "Are you sure you want to sign out?",
|
||||
"app_bar_signout_dialog_ok": "Yes",
|
||||
"app_bar_signout_dialog_title": "Sign out",
|
||||
"archive_page_no_archived_assets": "No archived assets found",
|
||||
|
@ -123,7 +123,7 @@
|
|||
"cache_settings_tile_title": "Local Storage",
|
||||
"cache_settings_title": "Caching Settings",
|
||||
"change_password_form_confirm_password": "Confirm Password",
|
||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_description": "Hi {name},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_new_password": "New Password",
|
||||
"change_password_form_password_mismatch": "Passwords do not match",
|
||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
||||
|
@ -390,6 +390,28 @@
|
|||
"shared_link_edit_show_meta": "Show metadata",
|
||||
"shared_link_edit_submit_button": "Update link",
|
||||
"shared_link_empty": "You don't have any shared links",
|
||||
"shared_link_error_server_url_fetch": "Cannot fetch the server url",
|
||||
"shared_link_expired": "Expired",
|
||||
"shared_link_expires_days": {
|
||||
"one": "Expires in {} day",
|
||||
"other": "Expires in {} days"
|
||||
},
|
||||
"shared_link_expires_hours": {
|
||||
"one": "Expires in {} hour",
|
||||
"other": "Expires in {} hours"
|
||||
},
|
||||
"shared_link_expires_minutes": {
|
||||
"one": "Expires in {} minute",
|
||||
"other": "Expires in {} minutes"
|
||||
},
|
||||
"shared_link_expires_seconds": {
|
||||
"one": "Expires in {} second",
|
||||
"other": "Expires in {} seconds"
|
||||
},
|
||||
"shared_link_expires_never": "Expires ∞",
|
||||
"shared_link_info_chip_download": "Download",
|
||||
"shared_link_info_chip_metadata": "EXIF",
|
||||
"shared_link_info_chip_upload": "Upload",
|
||||
"shared_link_manage_links": "Manage Shared links",
|
||||
"share_done": "Done",
|
||||
"share_invite": "Invite to album",
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Almacenamiento local",
|
||||
"cache_settings_title": "Configuración de la caché",
|
||||
"change_password_form_confirm_password": "Confirmar Contraseña",
|
||||
"change_password_form_description": "Hola {firstName} {lastName},\n\nEsta es la primera vez que inicias sesión en el sistema o se ha solicitado cambiar tu contraseña. Por favor, introduce la nueva contraseña a continuación.",
|
||||
"change_password_form_description": "Hola {name},\n\nEsta es la primera vez que inicias sesión en el sistema o se ha solicitado cambiar tu contraseña. Por favor, introduce la nueva contraseña a continuación.",
|
||||
"change_password_form_new_password": "Nueva Contraseña",
|
||||
"change_password_form_password_mismatch": "Las contraseñas no coinciden",
|
||||
"change_password_form_reenter_new_password": "Vuelve a ingresar la nueva contraseña",
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Almacenamiento local",
|
||||
"cache_settings_title": "Configuración de la caché",
|
||||
"change_password_form_confirm_password": "Confirmar Contraseña",
|
||||
"change_password_form_description": "Hola {firstName} {lastName},\n\nEsta es la primera vez que inicias sesión en el sistema o se ha solicitado cambiar tu contraseña. Por favor, introduce la nueva contraseña a continuación.",
|
||||
"change_password_form_description": "Hola {name},\n\nEsta es la primera vez que inicias sesión en el sistema o se ha solicitado cambiar tu contraseña. Por favor, introduce la nueva contraseña a continuación.",
|
||||
"change_password_form_new_password": "Nueva Contraseña",
|
||||
"change_password_form_password_mismatch": "Las contraseñas no coinciden",
|
||||
"change_password_form_reenter_new_password": "Vuelve a ingresar la nueva contraseña",
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Almacenamiento local",
|
||||
"cache_settings_title": "Configuración de la caché",
|
||||
"change_password_form_confirm_password": "Confirmar Contraseña",
|
||||
"change_password_form_description": "Hola {firstName} {lastName},\n\nEsta es la primera vez que inicias sesión en el sistema o se ha solicitado cambiar tu contraseña. Por favor, introduce la nueva contraseña a continuación.",
|
||||
"change_password_form_description": "Hola {name},\n\nEsta es la primera vez que inicias sesión en el sistema o se ha solicitado cambiar tu contraseña. Por favor, introduce la nueva contraseña a continuación.",
|
||||
"change_password_form_new_password": "Nueva Contraseña",
|
||||
"change_password_form_password_mismatch": "Las contraseñas no coinciden",
|
||||
"change_password_form_reenter_new_password": "Vuelve a ingresar la nueva contraseña",
|
||||
|
|
|
@ -390,6 +390,28 @@
|
|||
"shared_link_edit_show_meta": "Mostrar metadatos",
|
||||
"shared_link_edit_submit_button": "Actualizar enlace",
|
||||
"shared_link_empty": "No tienes ningún enlace compartido",
|
||||
"shared_link_error_server_url_fetch": "No se puede obtener la URL del servidor",
|
||||
"shared_link_expired": "Expirado",
|
||||
"shared_link_expires_days": {
|
||||
"one": "Expira en {} día",
|
||||
"other": "Expira en {} días"
|
||||
},
|
||||
"shared_link_expires_hours": {
|
||||
"one": "Expira en {} hora",
|
||||
"other": "Expira en {} horas"
|
||||
},
|
||||
"shared_link_expires_minutes": {
|
||||
"one": "Expira en {} minuto",
|
||||
"other": "Expira en {} minutos"
|
||||
},
|
||||
"shared_link_expires_seconds": {
|
||||
"one": "Expira en {} segundo",
|
||||
"other": "Expira en {} segundos"
|
||||
},
|
||||
"shared_link_expires_never": "Sin expiración",
|
||||
"shared_link_info_chip_download": "Descargar",
|
||||
"shared_link_info_chip_metadata": "EXIF",
|
||||
"shared_link_info_chip_upload": "Subir",
|
||||
"shared_link_manage_links": "Administrar enlaces compartidos",
|
||||
"share_done": "Hecho",
|
||||
"share_invite": "Invitar al álbum",
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Paikallinen tallennustila",
|
||||
"cache_settings_title": "Välimuistin asetukset",
|
||||
"change_password_form_confirm_password": "Vahvista salasana",
|
||||
"change_password_form_description": "Hei {firstName} {lastName},\n\nTämä on joko ensimmäinen kirjautumisesi järjestelmään tai salasanan vaihtaminen vaihtaminen on pakotettu. Ole hyvä ja syötä uusi salasana alle.",
|
||||
"change_password_form_description": "Hei {name},\n\nTämä on joko ensimmäinen kirjautumisesi järjestelmään tai salasanan vaihtaminen vaihtaminen on pakotettu. Ole hyvä ja syötä uusi salasana alle.",
|
||||
"change_password_form_new_password": "Uusi salasana",
|
||||
"change_password_form_password_mismatch": "Salasanat eivät täsmää",
|
||||
"change_password_form_reenter_new_password": "Uusi salasana uudelleen",
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Stockage local",
|
||||
"cache_settings_title": "Paramètres de mise en cache",
|
||||
"change_password_form_confirm_password": "Confirmez le mot de passe",
|
||||
"change_password_form_description": "Bonjour {firstName} {lastName},\n\nC'est la première fois que vous vous connectez au système ou vous avez demandé de changer votre mot de passe. Veuillez saisir le nouveau mot de passe ci-dessous.",
|
||||
"change_password_form_description": "Bonjour {name},\n\nC'est la première fois que vous vous connectez au système ou vous avez demandé de changer votre mot de passe. Veuillez saisir le nouveau mot de passe ci-dessous.",
|
||||
"change_password_form_new_password": "Nouveau mot de passe",
|
||||
"change_password_form_password_mismatch": "Les mots de passe ne correspondent pas",
|
||||
"change_password_form_reenter_new_password": "Saisissez à nouveau le nouveau mot de passe",
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Stockage local",
|
||||
"cache_settings_title": "Paramètres de mise en cache",
|
||||
"change_password_form_confirm_password": "Confirmez le mot de passe",
|
||||
"change_password_form_description": "Bonjour {firstName} {lastName},\n\nC'est la première fois que vous vous connectez au système ou vous avez demandé à changer votre mot de passe. Veuillez saisir le nouveau mot de passe ci-dessous.",
|
||||
"change_password_form_description": "Bonjour {name},\n\nC'est la première fois que vous vous connectez au système ou vous avez demandé à changer votre mot de passe. Veuillez saisir le nouveau mot de passe ci-dessous.",
|
||||
"change_password_form_new_password": "Nouveau mot de passe",
|
||||
"change_password_form_password_mismatch": "Les mots de passe ne correspondent pas",
|
||||
"change_password_form_reenter_new_password": "Saisissez à nouveau le nouveau mot de passe",
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"album_viewer_page_share_add_users": "Add users",
|
||||
"all_people_page_title": "People",
|
||||
"all_videos_page_title": "Videos",
|
||||
"app_bar_signout_dialog_content": "Are you sure you wanna sign out?",
|
||||
"app_bar_signout_dialog_content": "Are you sure you want to sign out?",
|
||||
"app_bar_signout_dialog_ok": "Yes",
|
||||
"app_bar_signout_dialog_title": "Sign out",
|
||||
"archive_page_no_archived_assets": "No archived assets found",
|
||||
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Local Storage",
|
||||
"cache_settings_title": "Caching Settings",
|
||||
"change_password_form_confirm_password": "Confirm Password",
|
||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_description": "Hi {name},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_new_password": "New Password",
|
||||
"change_password_form_password_mismatch": "Passwords do not match",
|
||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"album_viewer_page_share_add_users": "Felhasználók hozzáadása",
|
||||
"all_people_page_title": "Emberek",
|
||||
"all_videos_page_title": "Videók",
|
||||
"app_bar_signout_dialog_content": "Are you sure you wanna sign out?",
|
||||
"app_bar_signout_dialog_content": "Are you sure you want to sign out?",
|
||||
"app_bar_signout_dialog_ok": "Yes",
|
||||
"app_bar_signout_dialog_title": "Sign out",
|
||||
"archive_page_no_archived_assets": "Nem található archivált média",
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"album_viewer_page_share_add_users": "Aggiungi utenti",
|
||||
"all_people_page_title": "Persone",
|
||||
"all_videos_page_title": "Video",
|
||||
"app_bar_signout_dialog_content": "Are you sure you wanna sign out?",
|
||||
"app_bar_signout_dialog_content": "Are you sure you want to sign out?",
|
||||
"app_bar_signout_dialog_ok": "Yes",
|
||||
"app_bar_signout_dialog_title": "Sign out",
|
||||
"archive_page_no_archived_assets": "Nessuna oggetto archiviato",
|
||||
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Local Storage",
|
||||
"cache_settings_title": "Impostazioni della Cache",
|
||||
"change_password_form_confirm_password": "Conferma Password ",
|
||||
"change_password_form_description": "Ciao {firstName} {lastName},\n\nQuesto è la prima volta che accedi al sistema oppure è stato fatto una richiesta di cambiare la password. Per favore inserisca la nuova password qui sotto",
|
||||
"change_password_form_description": "Ciao {name},\n\nQuesto è la prima volta che accedi al sistema oppure è stato fatto una richiesta di cambiare la password. Per favore inserisca la nuova password qui sotto",
|
||||
"change_password_form_new_password": "Nuova Password",
|
||||
"change_password_form_password_mismatch": "Le password non coincidono",
|
||||
"change_password_form_reenter_new_password": "Inserisci ancora la nuova password ",
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "로컬 저장소",
|
||||
"cache_settings_title": "캐시 설정",
|
||||
"change_password_form_confirm_password": "비밀번호 확인",
|
||||
"change_password_form_description": "{firstName} {lastName} 님, 안녕하세요.\n\n시스템에 처음 로그인했거나 비밀번호 변경 요청이 있었습니다. 아래에 새 비밀번호를 입력하세요.",
|
||||
"change_password_form_description": "{name} 님, 안녕하세요.\n\n시스템에 처음 로그인했거나 비밀번호 변경 요청이 있었습니다. 아래에 새 비밀번호를 입력하세요.",
|
||||
"change_password_form_new_password": "새 비밀번호",
|
||||
"change_password_form_password_mismatch": "비밀번호가 일치하지 않습니다",
|
||||
"change_password_form_reenter_new_password": "새 비밀번호 재입력",
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"album_viewer_page_share_add_users": "Pievienot lietotājus",
|
||||
"all_people_page_title": "Cilvēki",
|
||||
"all_videos_page_title": "Videoklipi",
|
||||
"app_bar_signout_dialog_content": "Are you sure you wanna sign out?",
|
||||
"app_bar_signout_dialog_content": "Are you sure you want to sign out?",
|
||||
"app_bar_signout_dialog_ok": "Yes",
|
||||
"app_bar_signout_dialog_title": "Sign out",
|
||||
"archive_page_no_archived_assets": "Nav atrasts neviens arhivēts aktīvs",
|
||||
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Local Storage",
|
||||
"cache_settings_title": "Kešdarbes iestatījumi",
|
||||
"change_password_form_confirm_password": "Apstiprināt Paroli",
|
||||
"change_password_form_description": "Sveiki {FirstName} {LastName},\n\nŠī ir pirmā reize, kad pierakstāties sistēmā, vai arī ir iesniegts pieprasījums mainīt paroli. Lūdzu, zemāk ievadiet jauno paroli.",
|
||||
"change_password_form_description": "Sveiki {name},\n\nŠī ir pirmā reize, kad pierakstāties sistēmā, vai arī ir iesniegts pieprasījums mainīt paroli. Lūdzu, zemāk ievadiet jauno paroli.",
|
||||
"change_password_form_new_password": "Jauna Parole",
|
||||
"change_password_form_password_mismatch": "Paroles nesakrīt",
|
||||
"change_password_form_reenter_new_password": "Atkārtoti ievadīt jaunu paroli",
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"album_viewer_page_share_add_users": "Add users",
|
||||
"all_people_page_title": "People",
|
||||
"all_videos_page_title": "Videos",
|
||||
"app_bar_signout_dialog_content": "Are you sure you wanna sign out?",
|
||||
"app_bar_signout_dialog_content": "Are you sure you want to sign out?",
|
||||
"app_bar_signout_dialog_ok": "Yes",
|
||||
"app_bar_signout_dialog_title": "Sign out",
|
||||
"archive_page_no_archived_assets": "No archived assets found",
|
||||
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Local Storage",
|
||||
"cache_settings_title": "Caching Settings",
|
||||
"change_password_form_confirm_password": "Confirm Password",
|
||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_description": "Hi {name},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_new_password": "New Password",
|
||||
"change_password_form_password_mismatch": "Passwords do not match",
|
||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Lokal lagring",
|
||||
"cache_settings_title": "Bufringsinnstillinger",
|
||||
"change_password_form_confirm_password": "Bekreft passord",
|
||||
"change_password_form_description": "Hei {firstName} {lastName}!\n\nDette er enten første gang du logger på systemet, eller det er sendt en forespørsel om å endre passordet ditt. Vennligst skriv inn det nye passordet nedenfor.",
|
||||
"change_password_form_description": "Hei {name}!\n\nDette er enten første gang du logger på systemet, eller det er sendt en forespørsel om å endre passordet ditt. Vennligst skriv inn det nye passordet nedenfor.",
|
||||
"change_password_form_new_password": "Nytt passord",
|
||||
"change_password_form_password_mismatch": "Passordene stemmer ikke",
|
||||
"change_password_form_reenter_new_password": "Skriv nytt passord igjen",
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"album_viewer_page_share_add_users": "Gebruikers toevoegen",
|
||||
"all_people_page_title": "Personen",
|
||||
"all_videos_page_title": "Video's",
|
||||
"app_bar_signout_dialog_content": "Are you sure you wanna sign out?",
|
||||
"app_bar_signout_dialog_content": "Are you sure you want to sign out?",
|
||||
"app_bar_signout_dialog_ok": "Yes",
|
||||
"app_bar_signout_dialog_title": "Sign out",
|
||||
"archive_page_no_archived_assets": "Geen gearchiveerde items gevonden",
|
||||
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Local Storage",
|
||||
"cache_settings_title": "Cache-instellingen",
|
||||
"change_password_form_confirm_password": "Bevestig wachtwoord",
|
||||
"change_password_form_description": "Hallo {firstName} {lastName},\n\nDit is ofwel de eerste keer dat je inlogt, of er is een verzoek gedaan om je wachtwoord te wijzigen. Vul hieronder een nieuw wachtwoord in.",
|
||||
"change_password_form_description": "Hallo {name},\n\nDit is ofwel de eerste keer dat je inlogt, of er is een verzoek gedaan om je wachtwoord te wijzigen. Vul hieronder een nieuw wachtwoord in.",
|
||||
"change_password_form_new_password": "Nieuw wachtwoord",
|
||||
"change_password_form_password_mismatch": "Wachtwoorden komen niet overeen",
|
||||
"change_password_form_reenter_new_password": "Vul het wachtwoord opnieuw in",
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Lokalny magazyn",
|
||||
"cache_settings_title": "Ustawienia Buforowania",
|
||||
"change_password_form_confirm_password": "Potwierdź Hasło",
|
||||
"change_password_form_description": "Cześć {firstName} {lastName},\n\nPierwszy raz logujesz się do systemu, albo złożono prośbę o zmianę hasła. Wpisz poniżej nowe hasło.",
|
||||
"change_password_form_description": "Cześć {name},\n\nPierwszy raz logujesz się do systemu, albo złożono prośbę o zmianę hasła. Wpisz poniżej nowe hasło.",
|
||||
"change_password_form_new_password": "Nowe Hasło",
|
||||
"change_password_form_password_mismatch": "Hasła nie są zgodne",
|
||||
"change_password_form_reenter_new_password": "Wprowadź ponownie Nowe Hasło",
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Локальное хранилище",
|
||||
"cache_settings_title": "Настройки кэширования",
|
||||
"change_password_form_confirm_password": "Подтвердите пароль",
|
||||
"change_password_form_description": "Привет {firstName} {lastName},\n\nЭто либо ваш первый вход в систему, либо был сделан запрос на смену пароля. Пожалуйста, введите новый пароль ниже.",
|
||||
"change_password_form_description": "Привет {name},\n\nЭто либо ваш первый вход в систему, либо был сделан запрос на смену пароля. Пожалуйста, введите новый пароль ниже.",
|
||||
"change_password_form_new_password": "Новый пароль",
|
||||
"change_password_form_password_mismatch": "Пароли не совпадают",
|
||||
"change_password_form_reenter_new_password": "Повторно введите новый пароль",
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Lokálne úložisko",
|
||||
"cache_settings_title": "Nastavenia vyrovnávacej pamäte",
|
||||
"change_password_form_confirm_password": "Potvrďte heslo",
|
||||
"change_password_form_description": "Dobrý deň, {firstName} {lastName},\n\nBuď sa do systému prihlasujete prvýkrát, alebo bola podaná žiadosť o zmenu hesla. Prosím, zadajte nové heslo nižšie.",
|
||||
"change_password_form_description": "Dobrý deň, {name},\n\nBuď sa do systému prihlasujete prvýkrát, alebo bola podaná žiadosť o zmenu hesla. Prosím, zadajte nové heslo nižšie.",
|
||||
"change_password_form_new_password": "Nové heslo",
|
||||
"change_password_form_password_mismatch": "Heslá sa nezhodujú",
|
||||
"change_password_form_reenter_new_password": "Znova zadajte nové heslo",
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"album_viewer_page_share_add_users": "Add users",
|
||||
"all_people_page_title": "People",
|
||||
"all_videos_page_title": "Videos",
|
||||
"app_bar_signout_dialog_content": "Are you sure you wanna sign out?",
|
||||
"app_bar_signout_dialog_content": "Are you sure you want to sign out?",
|
||||
"app_bar_signout_dialog_ok": "Yes",
|
||||
"app_bar_signout_dialog_title": "Sign out",
|
||||
"archive_page_no_archived_assets": "No archived assets found",
|
||||
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Local Storage",
|
||||
"cache_settings_title": "Caching Settings",
|
||||
"change_password_form_confirm_password": "Confirm Password",
|
||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_description": "Hi {name},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_new_password": "New Password",
|
||||
"change_password_form_password_mismatch": "Passwords do not match",
|
||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"album_viewer_page_share_add_users": "Dodaj korisnike",
|
||||
"all_people_page_title": "People",
|
||||
"all_videos_page_title": "Videos",
|
||||
"app_bar_signout_dialog_content": "Are you sure you wanna sign out?",
|
||||
"app_bar_signout_dialog_content": "Are you sure you want to sign out?",
|
||||
"app_bar_signout_dialog_ok": "Yes",
|
||||
"app_bar_signout_dialog_title": "Sign out",
|
||||
"archive_page_no_archived_assets": "No archived assets found",
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"album_viewer_page_share_add_users": "Add users",
|
||||
"all_people_page_title": "People",
|
||||
"all_videos_page_title": "Videos",
|
||||
"app_bar_signout_dialog_content": "Are you sure you wanna sign out?",
|
||||
"app_bar_signout_dialog_content": "Are you sure you want to sign out?",
|
||||
"app_bar_signout_dialog_ok": "Yes",
|
||||
"app_bar_signout_dialog_title": "Sign out",
|
||||
"archive_page_no_archived_assets": "No archived assets found",
|
||||
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Local Storage",
|
||||
"cache_settings_title": "Caching Settings",
|
||||
"change_password_form_confirm_password": "Confirm Password",
|
||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_description": "Hi {name},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_new_password": "New Password",
|
||||
"change_password_form_password_mismatch": "Passwords do not match",
|
||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"album_viewer_page_share_add_users": "Lägg till användare",
|
||||
"all_people_page_title": "People",
|
||||
"all_videos_page_title": "Videos",
|
||||
"app_bar_signout_dialog_content": "Are you sure you wanna sign out?",
|
||||
"app_bar_signout_dialog_content": "Are you sure you want to sign out?",
|
||||
"app_bar_signout_dialog_ok": "Yes",
|
||||
"app_bar_signout_dialog_title": "Sign out",
|
||||
"archive_page_no_archived_assets": "No archived assets found",
|
||||
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Local Storage",
|
||||
"cache_settings_title": "Cache Inställningar",
|
||||
"change_password_form_confirm_password": "Bekräfta lösenord",
|
||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_description": "Hi {name},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_new_password": "Nytt lösenord",
|
||||
"change_password_form_password_mismatch": "Passwords do not match",
|
||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"album_viewer_page_share_add_users": "เพิ่มผู้ใช้งาน",
|
||||
"all_people_page_title": "ผู้คน",
|
||||
"all_videos_page_title": "วิดีโอ",
|
||||
"app_bar_signout_dialog_content": "Are you sure you wanna sign out?",
|
||||
"app_bar_signout_dialog_content": "Are you sure you want to sign out?",
|
||||
"app_bar_signout_dialog_ok": "Yes",
|
||||
"app_bar_signout_dialog_title": "Sign out",
|
||||
"archive_page_no_archived_assets": "ไม่พบทรัพยากรในที่เก็บถาวร",
|
||||
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Local Storage",
|
||||
"cache_settings_title": "ตั้งค่าแคช",
|
||||
"change_password_form_confirm_password": "ยืนยันรหัสผ่าน",
|
||||
"change_password_form_description": "สวัสดี {firstName} {lastName},\n\nครั้งนี้อาจจะเป็นครั้งแรกที่คุณเข้าสู่ระบบ หรือมีคำขอเพื่อที่จะเปลี่ยนรหัสผ่านของคุI กรุณาเพิ่มรหัสผ่านใหม่ข้างล่าง",
|
||||
"change_password_form_description": "สวัสดี {name},\n\nครั้งนี้อาจจะเป็นครั้งแรกที่คุณเข้าสู่ระบบ หรือมีคำขอเพื่อที่จะเปลี่ยนรหัสผ่านของคุI กรุณาเพิ่มรหัสผ่านใหม่ข้างล่าง",
|
||||
"change_password_form_new_password": "รหัสผ่านใหม่",
|
||||
"change_password_form_password_mismatch": "รหัสผ่านไม่ตรงกัน",
|
||||
"change_password_form_reenter_new_password": "กรอกรหัสผ่านใหม่",
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"album_viewer_page_share_add_users": "Додати користувачів",
|
||||
"all_people_page_title": "Люди",
|
||||
"all_videos_page_title": "Відео",
|
||||
"app_bar_signout_dialog_content": "Are you sure you wanna sign out?",
|
||||
"app_bar_signout_dialog_content": "Are you sure you want to sign out?",
|
||||
"app_bar_signout_dialog_ok": "Yes",
|
||||
"app_bar_signout_dialog_title": "Sign out",
|
||||
"archive_page_no_archived_assets": "Немає архівних елементів",
|
||||
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Local Storage",
|
||||
"cache_settings_title": "Налаштування Кешування",
|
||||
"change_password_form_confirm_password": "Підтвердити пароль",
|
||||
"change_password_form_description": "Привіт {firstName} {lastName},\n\nВи або або вперше входите у систему, або було зроблено запит на зміну вашого пароля. \nВведіть ваш новий пароль.",
|
||||
"change_password_form_description": "Привіт {name},\n\nВи або або вперше входите у систему, або було зроблено запит на зміну вашого пароля. \nВведіть ваш новий пароль.",
|
||||
"change_password_form_new_password": "Новий Пароль",
|
||||
"change_password_form_password_mismatch": "Паролі не співпадають",
|
||||
"change_password_form_reenter_new_password": "Повторіть Новий Пароль",
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "Lưu trữ cục bộ",
|
||||
"cache_settings_title": "Caching Settings",
|
||||
"change_password_form_confirm_password": "Confirm Password",
|
||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_description": "Hi {name},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_new_password": "New Password",
|
||||
"change_password_form_password_mismatch": "Passwords do not match",
|
||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "本地存储",
|
||||
"cache_settings_title": "缓存设置",
|
||||
"change_password_form_confirm_password": "确认密码",
|
||||
"change_password_form_description": "{firstName} {lastName} 您好,\n\n这是您首次登录系统,或被管理员要求更改密码。\n请在下方输入新密码。",
|
||||
"change_password_form_description": "{name} 您好,\n\n这是您首次登录系统,或被管理员要求更改密码。\n请在下方输入新密码。",
|
||||
"change_password_form_new_password": "新密码",
|
||||
"change_password_form_password_mismatch": "密码不匹配",
|
||||
"change_password_form_reenter_new_password": "重新输入新的密码",
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
"cache_settings_tile_title": "本地存储",
|
||||
"cache_settings_title": "缓存设置",
|
||||
"change_password_form_confirm_password": "确认密码",
|
||||
"change_password_form_description": "{firstName} {lastName} 您好,\n\n这是您首次登录系统,或被管理员要求更改密码。\n请在下方输入新密码。",
|
||||
"change_password_form_description": "{name} 您好,\n\n这是您首次登录系统,或被管理员要求更改密码。\n请在下方输入新密码。",
|
||||
"change_password_form_new_password": "新密码",
|
||||
"change_password_form_password_mismatch": "密码不匹配",
|
||||
"change_password_form_reenter_new_password": "重新输入新的密码",
|
||||
|
|
|
@ -45,7 +45,7 @@ class ImmichTestHelper {
|
|||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
overrides: [dbProvider.overrideWithValue(db)],
|
||||
child: app.getMainWidget(),
|
||||
child: const app.MainWidget(),
|
||||
),
|
||||
);
|
||||
// Post run tasks
|
||||
|
|
|
@ -169,4 +169,4 @@ SPEC CHECKSUMS:
|
|||
|
||||
PODFILE CHECKSUM: 599d8aeb73728400c15364e734525722250a5382
|
||||
|
||||
COCOAPODS: 1.12.1
|
||||
COCOAPODS: 1.11.3
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
Color immichBackgroundColor = const Color(0xFFf6f8fe);
|
||||
Color immichDarkBackgroundColor = const Color.fromARGB(255, 0, 0, 0);
|
||||
Color immichDarkThemePrimaryColor = const Color.fromARGB(255, 173, 203, 250);
|
||||
const Color immichBackgroundColor = Color(0xFFf6f8fe);
|
||||
const Color immichDarkBackgroundColor = Color.fromARGB(255, 0, 0, 0);
|
||||
const Color immichDarkThemePrimaryColor = Color.fromARGB(255, 173, 203, 250);
|
||||
|
|
|
@ -4,22 +4,32 @@ import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
|||
import 'package:immich_mobile/shared/ui/scaffold_error_body.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
extension ScaffoldBody<T> on AsyncValue<T> {
|
||||
static final Logger _scaffoldBodyLog = Logger("ScaffoldBody");
|
||||
extension LogOnError<T> on AsyncValue<T> {
|
||||
static final Logger _asyncErrorLogger = Logger("AsyncValue");
|
||||
|
||||
Widget scaffoldBodyWhen({
|
||||
Widget widgetWhen({
|
||||
bool skipLoadingOnRefresh = true,
|
||||
Widget Function()? onLoading,
|
||||
Widget Function(Object? error, StackTrace? stack)? onError,
|
||||
required Widget Function(T data) onData,
|
||||
Widget? onError,
|
||||
}) {
|
||||
if (isLoading) {
|
||||
return const Center(
|
||||
child: ImmichLoadingIndicator(),
|
||||
);
|
||||
bool skip = false;
|
||||
if (isRefreshing) {
|
||||
skip = skipLoadingOnRefresh;
|
||||
}
|
||||
|
||||
if (!skip) {
|
||||
return onLoading?.call() ??
|
||||
const Center(
|
||||
child: ImmichLoadingIndicator(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasError && !hasValue) {
|
||||
_scaffoldBodyLog.severe("Error occured in AsyncValue", error, stackTrace);
|
||||
return onError ?? const ScaffoldErrorBody();
|
||||
_asyncErrorLogger.severe("Error occured", error, stackTrace);
|
||||
return onError?.call(error, stackTrace) ?? const ScaffoldErrorBody();
|
||||
}
|
||||
|
||||
return onData(requireValue);
|
||||
|
|
|
@ -45,7 +45,7 @@ extension ContextHelper on BuildContext {
|
|||
) =>
|
||||
AutoRouter.of(this).navigate(route);
|
||||
|
||||
// Auto-Push replace route from the current context
|
||||
// Auto-Push replace route from the current context
|
||||
Future<T?> autoReplace<T extends Object?>(PageRouteInfo<dynamic> route) =>
|
||||
AutoRouter.of(this).replace(route);
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
|
@ -7,6 +8,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:timezone/data/latest.dart';
|
||||
import 'package:immich_mobile/constants/locales.dart';
|
||||
import 'package:immich_mobile/modules/backup/background_service/background.service.dart';
|
||||
|
@ -28,7 +30,6 @@ import 'package:immich_mobile/shared/providers/app_state.provider.dart';
|
|||
import 'package:immich_mobile/shared/providers/db.provider.dart';
|
||||
import 'package:immich_mobile/shared/services/immich_logger.service.dart';
|
||||
import 'package:immich_mobile/shared/services/local_notification.service.dart';
|
||||
import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
|
||||
import 'package:immich_mobile/utils/http_ssl_cert_override.dart';
|
||||
import 'package:immich_mobile/utils/immich_app_theme.dart';
|
||||
import 'package:immich_mobile/utils/migration.dart';
|
||||
|
@ -43,10 +44,11 @@ void main() async {
|
|||
await initApp();
|
||||
await migrateDatabaseIfNeeded(db);
|
||||
HttpOverrides.global = HttpSSLCertOverride();
|
||||
|
||||
runApp(
|
||||
ProviderScope(
|
||||
overrides: [dbProvider.overrideWithValue(db)],
|
||||
child: getMainWidget(),
|
||||
child: const MainWidget(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -108,16 +110,6 @@ Future<Isar> loadDb() async {
|
|||
return db;
|
||||
}
|
||||
|
||||
Widget getMainWidget() {
|
||||
return EasyLocalization(
|
||||
supportedLocales: locales,
|
||||
path: translationsPath,
|
||||
useFallbackTranslations: true,
|
||||
fallbackLocale: locales.first,
|
||||
child: const ImmichApp(),
|
||||
);
|
||||
}
|
||||
|
||||
class ImmichApp extends ConsumerStatefulWidget {
|
||||
const ImmichApp({super.key});
|
||||
|
||||
|
@ -167,10 +159,9 @@ class ImmichAppState extends ConsumerState<ImmichApp>
|
|||
// Android 8 does not support transparent app bars
|
||||
final info = await DeviceInfoPlugin().androidInfo;
|
||||
if (info.version.sdkInt <= 26) {
|
||||
overlayStyle =
|
||||
MediaQuery.of(context).platformBrightness == Brightness.light
|
||||
? SystemUiOverlayStyle.light
|
||||
: SystemUiOverlayStyle.dark;
|
||||
overlayStyle = context.isDarkTheme
|
||||
? SystemUiOverlayStyle.dark
|
||||
: SystemUiOverlayStyle.light;
|
||||
}
|
||||
}
|
||||
SystemChrome.setSystemUIOverlayStyle(overlayStyle);
|
||||
|
@ -202,22 +193,33 @@ class ImmichAppState extends ConsumerState<ImmichApp>
|
|||
supportedLocales: context.supportedLocales,
|
||||
locale: context.locale,
|
||||
debugShowCheckedModeBanner: false,
|
||||
home: Stack(
|
||||
children: [
|
||||
MaterialApp.router(
|
||||
title: 'Immich',
|
||||
debugShowCheckedModeBanner: false,
|
||||
themeMode: ref.watch(immichThemeProvider),
|
||||
darkTheme: immichDarkTheme,
|
||||
theme: immichLightTheme,
|
||||
routeInformationParser: router.defaultRouteParser(),
|
||||
routerDelegate: router.delegate(
|
||||
navigatorObservers: () => [TabNavigationObserver(ref: ref)],
|
||||
),
|
||||
),
|
||||
const ImmichLoadingOverlay(),
|
||||
],
|
||||
home: MaterialApp.router(
|
||||
title: 'Immich',
|
||||
debugShowCheckedModeBanner: false,
|
||||
themeMode: ref.watch(immichThemeProvider),
|
||||
darkTheme: immichDarkTheme,
|
||||
theme: immichLightTheme,
|
||||
routeInformationParser: router.defaultRouteParser(),
|
||||
routerDelegate: router.delegate(
|
||||
navigatorObservers: () => [TabNavigationObserver(ref: ref)],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ignore: prefer-single-widget-per-file
|
||||
class MainWidget extends StatelessWidget {
|
||||
const MainWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return EasyLocalization(
|
||||
supportedLocales: locales,
|
||||
path: translationsPath,
|
||||
useFallbackTranslations: true,
|
||||
fallbackLocale: locales.first,
|
||||
child: const ImmichApp(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,12 @@ import 'package:easy_localization/easy_localization.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/modules/activities/models/activity.model.dart';
|
||||
import 'package:immich_mobile/modules/activities/providers/activity.provider.dart';
|
||||
import 'package:immich_mobile/shared/models/store.dart';
|
||||
import 'package:immich_mobile/shared/ui/confirm_dialog.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:immich_mobile/shared/ui/user_circle_avatar.dart';
|
||||
import 'package:immich_mobile/extensions/datetime_extensions.dart';
|
||||
import 'package:immich_mobile/utils/image_url_builder.dart';
|
||||
|
@ -88,7 +88,7 @@ class ActivitiesPage extends HookConsumerWidget {
|
|||
width: 40,
|
||||
height: 30,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(4)),
|
||||
image: DecorationImage(
|
||||
image: CachedNetworkImageProvider(
|
||||
getThumbnailUrlForRemoteId(
|
||||
|
@ -231,11 +231,8 @@ class ActivitiesPage extends HookConsumerWidget {
|
|||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(appBarTitle)),
|
||||
body: activities.maybeWhen(
|
||||
orElse: () {
|
||||
return const Center(child: ImmichLoadingIndicator());
|
||||
},
|
||||
data: (data) {
|
||||
body: activities.widgetWhen(
|
||||
onData: (data) {
|
||||
final liked = data.firstWhereOrNull(
|
||||
(a) =>
|
||||
a.type == ActivityType.like &&
|
||||
|
|
|
@ -65,7 +65,7 @@ class AddToAlbumBottomSheet extends HookConsumerWidget {
|
|||
}
|
||||
|
||||
ref.invalidate(albumDetailProvider(album.id));
|
||||
Navigator.pop(context);
|
||||
context.pop();
|
||||
}
|
||||
|
||||
return Card(
|
||||
|
|
|
@ -43,6 +43,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final newAlbumTitle = ref.watch(albumViewerProvider).editTitleText;
|
||||
final isEditAlbum = ref.watch(albumViewerProvider).isEditAlbum;
|
||||
final isProcessing = useProcessingOverlay();
|
||||
final comments = album.shared
|
||||
? ref.watch(
|
||||
activityStatisticsStateProvider(
|
||||
|
@ -52,7 +53,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
: 0;
|
||||
|
||||
deleteAlbum() async {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
isProcessing.value = true;
|
||||
|
||||
final bool success;
|
||||
if (album.shared) {
|
||||
|
@ -74,7 +75,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
);
|
||||
}
|
||||
|
||||
ImmichLoadingOverlayController.appLoader.hide();
|
||||
isProcessing.value = false;
|
||||
}
|
||||
|
||||
Future<void> showConfirmationDialog() async {
|
||||
|
@ -89,7 +90,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, 'Cancel'),
|
||||
onPressed: () => context.pop('Cancel'),
|
||||
child: Text(
|
||||
'Cancel',
|
||||
style: TextStyle(
|
||||
|
@ -100,7 +101,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context, 'Confirm');
|
||||
context.pop('Confirm');
|
||||
deleteAlbum();
|
||||
},
|
||||
child: Text(
|
||||
|
@ -122,7 +123,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
}
|
||||
|
||||
void onLeaveAlbumPressed() async {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
isProcessing.value = true;
|
||||
|
||||
bool isSuccess =
|
||||
await ref.watch(sharedAlbumProvider.notifier).leaveAlbum(album);
|
||||
|
@ -131,7 +132,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
context
|
||||
.autoNavigate(const TabControllerRoute(children: [SharingRoute()]));
|
||||
} else {
|
||||
Navigator.pop(context);
|
||||
context.pop();
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: "album_viewer_appbar_share_err_leave".tr(),
|
||||
|
@ -140,11 +141,11 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
);
|
||||
}
|
||||
|
||||
ImmichLoadingOverlayController.appLoader.hide();
|
||||
isProcessing.value = false;
|
||||
}
|
||||
|
||||
void onRemoveFromAlbumPressed() async {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
isProcessing.value = true;
|
||||
|
||||
bool isSuccess =
|
||||
await ref.watch(sharedAlbumProvider.notifier).removeAssetFromAlbum(
|
||||
|
@ -153,12 +154,12 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
);
|
||||
|
||||
if (isSuccess) {
|
||||
Navigator.pop(context);
|
||||
context.pop();
|
||||
selectionDisabled();
|
||||
ref.watch(albumProvider.notifier).getAllAlbums();
|
||||
ref.invalidate(albumDetailProvider(album.id));
|
||||
} else {
|
||||
Navigator.pop(context);
|
||||
context.pop();
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: "album_viewer_appbar_share_err_remove".tr(),
|
||||
|
@ -167,7 +168,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
);
|
||||
}
|
||||
|
||||
ImmichLoadingOverlayController.appLoader.hide();
|
||||
isProcessing.value = false;
|
||||
}
|
||||
|
||||
void handleShareAssets(
|
||||
|
@ -198,9 +199,9 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
}
|
||||
|
||||
void onShareAssetsTo() async {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
isProcessing.value = true;
|
||||
handleShareAssets(ref, context, selected);
|
||||
ImmichLoadingOverlayController.appLoader.hide();
|
||||
isProcessing.value = false;
|
||||
}
|
||||
|
||||
buildBottomSheetActions() {
|
||||
|
@ -253,7 +254,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
ListTile(
|
||||
leading: const Icon(Icons.person_add_alt_rounded),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
context.pop();
|
||||
onAddUsers!(album);
|
||||
},
|
||||
title: const Text(
|
||||
|
@ -265,7 +266,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
leading: const Icon(Icons.share_rounded),
|
||||
onTap: () {
|
||||
context.autoPush(SharedLinkEditRoute(albumId: album.remoteId));
|
||||
Navigator.pop(context);
|
||||
context.pop();
|
||||
},
|
||||
title: const Text(
|
||||
"control_bottom_app_bar_share",
|
||||
|
@ -286,7 +287,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
ListTile(
|
||||
leading: const Icon(Icons.add_photo_alternate_outlined),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
context.pop();
|
||||
onAddPhotos!(album);
|
||||
},
|
||||
title: const Text(
|
||||
|
|
|
@ -24,10 +24,11 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
|||
final owner = album.owner.value;
|
||||
final userId = ref.watch(authenticationProvider).userId;
|
||||
final activityEnabled = useState(album.activityEnabled);
|
||||
final isProcessing = useProcessingOverlay();
|
||||
final isOwner = owner?.id == userId;
|
||||
|
||||
void showErrorMessage() {
|
||||
Navigator.pop(context);
|
||||
context.pop();
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: "shared_album_section_people_action_error".tr(),
|
||||
|
@ -37,7 +38,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
|||
}
|
||||
|
||||
void leaveAlbum() async {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
isProcessing.value = true;
|
||||
|
||||
try {
|
||||
final isSuccess =
|
||||
|
@ -54,11 +55,11 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
|||
showErrorMessage();
|
||||
}
|
||||
|
||||
ImmichLoadingOverlayController.appLoader.hide();
|
||||
isProcessing.value = false;
|
||||
}
|
||||
|
||||
void removeUserFromAlbum(User user) async {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
isProcessing.value = true;
|
||||
|
||||
try {
|
||||
await ref
|
||||
|
@ -70,8 +71,8 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
|||
showErrorMessage();
|
||||
}
|
||||
|
||||
Navigator.pop(context);
|
||||
ImmichLoadingOverlayController.appLoader.hide();
|
||||
context.pop();
|
||||
isProcessing.value = false;
|
||||
}
|
||||
|
||||
void handleUserClick(User user) {
|
||||
|
@ -180,9 +181,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
|||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back_ios_new_rounded),
|
||||
onPressed: () {
|
||||
context.autoPop(null);
|
||||
},
|
||||
onPressed: () => context.autoPop(null),
|
||||
),
|
||||
centerTitle: true,
|
||||
title: Text("translated_text_options".tr()),
|
||||
|
|
|
@ -4,6 +4,7 @@ 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/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/modules/album/models/asset_selection_page_result.model.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/album_detail.provider.dart';
|
||||
|
@ -17,7 +18,6 @@ import 'package:immich_mobile/routing/router.dart';
|
|||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/providers/asset.provider.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:immich_mobile/shared/ui/user_circle_avatar.dart';
|
||||
import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
|
||||
|
||||
|
@ -33,6 +33,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
final userId = ref.watch(authenticationProvider).userId;
|
||||
final selection = useState<Set<Asset>>({});
|
||||
final multiSelectEnabled = useState(false);
|
||||
final isProcessing = useProcessingOverlay();
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
|
@ -75,24 +76,21 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
),
|
||||
);
|
||||
|
||||
if (returnPayload != null) {
|
||||
if (returnPayload != null && returnPayload.selectedAssets.isNotEmpty) {
|
||||
// Check if there is new assets add
|
||||
if (returnPayload.selectedAssets.isNotEmpty) {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
isProcessing.value = true;
|
||||
|
||||
var addAssetsResult =
|
||||
await ref.watch(albumServiceProvider).addAdditionalAssetToAlbum(
|
||||
returnPayload.selectedAssets,
|
||||
albumInfo,
|
||||
);
|
||||
var addAssetsResult =
|
||||
await ref.watch(albumServiceProvider).addAdditionalAssetToAlbum(
|
||||
returnPayload.selectedAssets,
|
||||
albumInfo,
|
||||
);
|
||||
|
||||
if (addAssetsResult != null &&
|
||||
addAssetsResult.successfullyAdded > 0) {
|
||||
ref.invalidate(albumDetailProvider(albumId));
|
||||
}
|
||||
|
||||
ImmichLoadingOverlayController.appLoader.hide();
|
||||
if (addAssetsResult != null && addAssetsResult.successfullyAdded > 0) {
|
||||
ref.invalidate(albumDetailProvider(albumId));
|
||||
}
|
||||
|
||||
isProcessing.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,7 +100,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
);
|
||||
|
||||
if (sharedUserIds != null) {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
isProcessing.value = true;
|
||||
|
||||
var isSuccess = await ref
|
||||
.watch(albumServiceProvider)
|
||||
|
@ -112,7 +110,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
ref.invalidate(albumDetailProvider(album.id));
|
||||
}
|
||||
|
||||
ImmichLoadingOverlayController.appLoader.hide();
|
||||
isProcessing.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -260,13 +258,11 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
error: (error, stackTrace) => AppBar(title: const Text("Error")),
|
||||
loading: () => AppBar(),
|
||||
),
|
||||
body: album.when(
|
||||
data: (data) => WillPopScope(
|
||||
body: album.widgetWhen(
|
||||
onData: (data) => WillPopScope(
|
||||
onWillPop: onWillPop,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
titleFocusNode.unfocus();
|
||||
},
|
||||
onTap: () => titleFocusNode.unfocus(),
|
||||
child: ImmichAssetGrid(
|
||||
renderList: data.renderList,
|
||||
listener: selectionListener,
|
||||
|
@ -285,10 +281,6 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
error: (e, _) => Center(child: Text("Error loading album info!\n$e")),
|
||||
loading: () => const Center(
|
||||
child: ImmichLoadingIndicator(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ 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/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/modules/album/models/asset_selection_page_result.model.dart';
|
||||
import 'package:immich_mobile/modules/asset_viewer/providers/render_list.provider.dart';
|
||||
|
@ -85,12 +86,8 @@ class AssetSelectionPage extends HookConsumerWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
body: renderList.when(
|
||||
data: (data) => buildBody(data),
|
||||
error: (error, stackTrace) => Center(
|
||||
child: Text(error.toString()),
|
||||
),
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
body: renderList.widgetWhen(
|
||||
onData: (data) => buildBody(data),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,11 +2,11 @@ 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/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/suggested_shared_users.provider.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
import 'package:immich_mobile/shared/models/user.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:immich_mobile/shared/ui/user_circle_avatar.dart';
|
||||
|
||||
class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
||||
|
@ -137,8 +137,8 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
body: suggestedShareUsers.when(
|
||||
data: (users) {
|
||||
body: suggestedShareUsers.widgetWhen(
|
||||
onData: (users) {
|
||||
for (var sharedUsers in album.sharedUsers) {
|
||||
users.removeWhere(
|
||||
(u) => u.id == sharedUsers.id || u.id == album.ownerId,
|
||||
|
@ -147,10 +147,6 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
|||
|
||||
return buildUserList(users);
|
||||
},
|
||||
error: (e, _) => Text("Error loading suggested users $e"),
|
||||
loading: () => const Center(
|
||||
child: ImmichLoadingIndicator(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ 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/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/album_title.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
|
||||
|
@ -9,7 +10,6 @@ import 'package:immich_mobile/modules/album/providers/suggested_shared_users.pro
|
|||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/models/user.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:immich_mobile/shared/ui/user_circle_avatar.dart';
|
||||
|
||||
class SelectUserForSharingPage extends HookConsumerWidget {
|
||||
|
@ -42,7 +42,12 @@ class SelectUserForSharingPage extends HookConsumerWidget {
|
|||
|
||||
ScaffoldMessenger(
|
||||
child: SnackBar(
|
||||
content: const Text('select_user_for_sharing_page_err_album').tr(),
|
||||
content: Text(
|
||||
'select_user_for_sharing_page_err_album',
|
||||
style: context.textTheme.bodyLarge?.copyWith(
|
||||
color: context.primaryColor,
|
||||
),
|
||||
).tr(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -166,14 +171,10 @@ class SelectUserForSharingPage extends HookConsumerWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
body: suggestedShareUsers.when(
|
||||
data: (users) {
|
||||
body: suggestedShareUsers.widgetWhen(
|
||||
onData: (users) {
|
||||
return buildUserList(users);
|
||||
},
|
||||
error: (e, _) => Text("Error loading suggested users $e"),
|
||||
loading: () => const Center(
|
||||
child: ImmichLoadingIndicator(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ 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/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/modules/archive/providers/archive_asset_provider.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart';
|
||||
|
@ -48,37 +49,33 @@ class ArchivePage extends HookConsumerWidget {
|
|||
child: SizedBox(
|
||||
height: 64,
|
||||
child: Card(
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
leading: const Icon(
|
||||
Icons.unarchive_rounded,
|
||||
),
|
||||
title: Text(
|
||||
'control_bottom_app_bar_unarchive'.tr(),
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
onTap: processing.value
|
||||
? null
|
||||
: () async {
|
||||
processing.value = true;
|
||||
try {
|
||||
await handleArchiveAssets(
|
||||
ref,
|
||||
context,
|
||||
selection.value.toList(),
|
||||
shouldArchive: false,
|
||||
);
|
||||
} finally {
|
||||
processing.value = false;
|
||||
selectionEnabledHook.value = false;
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
child: ListTile(
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
),
|
||||
leading: const Icon(
|
||||
Icons.unarchive_rounded,
|
||||
),
|
||||
title: Text(
|
||||
'control_bottom_app_bar_unarchive'.tr(),
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
onTap: processing.value
|
||||
? null
|
||||
: () async {
|
||||
processing.value = true;
|
||||
try {
|
||||
await handleArchiveAssets(
|
||||
ref,
|
||||
context,
|
||||
selection.value.toList(),
|
||||
shouldArchive: false,
|
||||
);
|
||||
} finally {
|
||||
processing.value = false;
|
||||
selectionEnabledHook.value = false;
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -86,18 +83,13 @@ class ArchivePage extends HookConsumerWidget {
|
|||
);
|
||||
}
|
||||
|
||||
return archivedAssets.when(
|
||||
loading: () => Scaffold(
|
||||
appBar: buildAppBar("?"),
|
||||
body: const Center(child: CircularProgressIndicator()),
|
||||
return Scaffold(
|
||||
appBar: archivedAssets.maybeWhen(
|
||||
data: (data) => buildAppBar(data.totalAssets.toString()),
|
||||
orElse: () => buildAppBar("?"),
|
||||
),
|
||||
error: (error, stackTrace) => Scaffold(
|
||||
appBar: buildAppBar("Error"),
|
||||
body: Center(child: Text(error.toString())),
|
||||
),
|
||||
data: (data) => Scaffold(
|
||||
appBar: buildAppBar(data.totalAssets.toString()),
|
||||
body: data.isEmpty
|
||||
body: archivedAssets.widgetWhen(
|
||||
onData: (data) => data.isEmpty
|
||||
? Center(
|
||||
child: Text('archive_page_no_archived_assets'.tr()),
|
||||
)
|
||||
|
|
|
@ -62,8 +62,14 @@ class AdvancedBottomSheet extends HookConsumerWidget {
|
|||
ClipboardData(text: assetDetail.toString()),
|
||||
).then((_) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text("Copied to clipboard"),
|
||||
SnackBar(
|
||||
content: Text(
|
||||
"Copied to clipboard",
|
||||
style: context.textTheme.bodyLarge
|
||||
?.copyWith(
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
|
|
@ -514,7 +514,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
|||
currentAsset,
|
||||
stackElements.elementAt(stackIndex.value),
|
||||
);
|
||||
Navigator.pop(ctx);
|
||||
ctx.pop();
|
||||
context.autoPop();
|
||||
},
|
||||
title: const Text(
|
||||
|
@ -541,7 +541,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
|||
stackElements.elementAt(1),
|
||||
childrenToRemove: [currentAsset],
|
||||
);
|
||||
Navigator.pop(ctx);
|
||||
ctx.pop();
|
||||
context.autoPop();
|
||||
} else {
|
||||
await ref.read(assetStackServiceProvider).updateStack(
|
||||
|
@ -551,7 +551,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
|||
],
|
||||
);
|
||||
removeAssetFromStack();
|
||||
Navigator.pop(ctx);
|
||||
ctx.pop();
|
||||
}
|
||||
},
|
||||
title: const Text(
|
||||
|
@ -569,7 +569,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
|||
currentAsset,
|
||||
childrenToRemove: stack,
|
||||
);
|
||||
Navigator.pop(ctx);
|
||||
ctx.pop();
|
||||
context.autoPop();
|
||||
},
|
||||
title: const Text(
|
||||
|
|
|
@ -42,6 +42,9 @@ class BackupService {
|
|||
|
||||
try {
|
||||
return await _apiService.assetApi.getUserAssetsByDeviceId(deviceId);
|
||||
|
||||
// TODO! Start using this in 1.92.0
|
||||
// return await _apiService.assetApi.getAllUserAssetsByDeviceId(deviceId);
|
||||
} catch (e) {
|
||||
debugPrint('Error [getDeviceBackupAsset] ${e.toString()}');
|
||||
return null;
|
||||
|
@ -275,13 +278,6 @@ class BackupService {
|
|||
|
||||
req.files.add(assetRawUploadData);
|
||||
|
||||
if (entity.isLivePhoto) {
|
||||
var livePhotoRawUploadData = await _getLivePhotoFile(entity);
|
||||
if (livePhotoRawUploadData != null) {
|
||||
req.files.add(livePhotoRawUploadData);
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentUploadAssetCb(
|
||||
CurrentUploadAsset(
|
||||
id: entity.id,
|
||||
|
@ -296,6 +292,29 @@ class BackupService {
|
|||
var response =
|
||||
await httpClient.send(req, cancellationToken: cancelToken);
|
||||
|
||||
// Send live photo separately
|
||||
if (entity.isLivePhoto) {
|
||||
var livePhotoRawUploadData = await _getLivePhotoFile(entity);
|
||||
if (livePhotoRawUploadData != null) {
|
||||
var livePhotoReq = MultipartRequest(
|
||||
req.method,
|
||||
req.url,
|
||||
onProgress: req.onProgress,
|
||||
)
|
||||
..headers.addAll(req.headers)
|
||||
..fields.addAll(req.fields);
|
||||
|
||||
livePhotoReq.files.add(livePhotoRawUploadData);
|
||||
// Send live photo only if the non-motion part is successful
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
response = await httpClient.send(
|
||||
livePhotoReq,
|
||||
cancellationToken: cancelToken,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
// asset is a duplicate (already exists on the server)
|
||||
duplicatedAssetIds.add(entity.id);
|
||||
|
@ -353,7 +372,7 @@ class BackupService {
|
|||
var fileStream = motionFile.openRead();
|
||||
String fileName = p.basename(motionFile.path);
|
||||
return http.MultipartFile(
|
||||
"livePhotoData",
|
||||
"assetData",
|
||||
fileStream,
|
||||
motionFile.lengthSync(),
|
||||
filename: fileName,
|
||||
|
|
|
@ -229,6 +229,9 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||
final snackBar = SnackBar(
|
||||
content: Text(
|
||||
msg.tr(),
|
||||
style: context.textTheme.bodyLarge?.copyWith(
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
backgroundColor: Colors.red,
|
||||
);
|
||||
|
|
|
@ -2,6 +2,7 @@ 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/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/modules/favorite/providers/favorite_provider.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart';
|
||||
|
@ -62,22 +63,18 @@ class FavoritesPage extends HookConsumerWidget {
|
|||
child: SizedBox(
|
||||
height: 64,
|
||||
child: Card(
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
leading: const Icon(
|
||||
Icons.star_border,
|
||||
),
|
||||
title: const Text(
|
||||
"Unfavorite",
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
onTap: processing.value ? null : unfavorite,
|
||||
),
|
||||
],
|
||||
child: ListTile(
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
),
|
||||
leading: const Icon(
|
||||
Icons.star_border,
|
||||
),
|
||||
title: const Text(
|
||||
"Unfavorite",
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
onTap: processing.value ? null : unfavorite,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -87,10 +84,8 @@ class FavoritesPage extends HookConsumerWidget {
|
|||
|
||||
return Scaffold(
|
||||
appBar: buildAppBar(),
|
||||
body: ref.watch(favoriteAssetsProvider).when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (error, stackTrace) => Center(child: Text(error.toString())),
|
||||
data: (data) => data.isEmpty
|
||||
body: ref.watch(favoriteAssetsProvider).widgetWhen(
|
||||
onData: (data) => data.isEmpty
|
||||
? Center(
|
||||
child: Text('favorites_page_no_favorites'.tr()),
|
||||
)
|
||||
|
|
|
@ -5,13 +5,13 @@ import 'package:flutter/gestures.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/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/modules/asset_viewer/providers/render_list.provider.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/asset_grid/asset_grid_data_structure.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid_view.dart';
|
||||
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
|
||||
class ImmichAssetGrid extends HookConsumerWidget {
|
||||
|
@ -130,12 +130,8 @@ class ImmichAssetGrid extends HookConsumerWidget {
|
|||
if (renderList != null) return buildAssetGridView(renderList!);
|
||||
|
||||
final renderListFuture = ref.watch(renderListProvider(assets!));
|
||||
return renderListFuture.when(
|
||||
data: (renderList) => buildAssetGridView(renderList),
|
||||
error: (err, stack) => Center(child: Text("$err")),
|
||||
loading: () => const Center(
|
||||
child: ImmichLoadingIndicator(),
|
||||
),
|
||||
return renderListFuture.widgetWhen(
|
||||
onData: (renderList) => buildAssetGridView(renderList),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -197,7 +197,9 @@ class ThumbnailImage extends StatelessWidget {
|
|||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.decelerate,
|
||||
decoration: BoxDecoration(
|
||||
border: multiselectEnabled && isSelected
|
||||
? Border.all(
|
||||
|
|
|
@ -28,6 +28,7 @@ import 'package:immich_mobile/shared/providers/websocket.provider.dart';
|
|||
import 'package:immich_mobile/shared/ui/immich_app_bar.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_toast.dart';
|
||||
import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
|
||||
import 'package:immich_mobile/utils/selection_handlers.dart';
|
||||
|
||||
class HomePage extends HookConsumerWidget {
|
||||
|
@ -50,7 +51,7 @@ class HomePage extends HookConsumerWidget {
|
|||
|
||||
final tipOneOpacity = useState(0.0);
|
||||
final refreshCount = useState(0);
|
||||
final processing = useState(false);
|
||||
final processing = useProcessingOverlay();
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
|
@ -212,10 +213,10 @@ class HomePage extends HookConsumerWidget {
|
|||
processing.value = true;
|
||||
selectionEnabledHook.value = false;
|
||||
try {
|
||||
ref.read(manualUploadProvider.notifier).uploadAssets(
|
||||
context,
|
||||
selection.value.where((a) => a.storage == AssetState.local),
|
||||
);
|
||||
ref.read(manualUploadProvider.notifier).uploadAssets(
|
||||
context,
|
||||
selection.value.where((a) => a.storage == AssetState.local),
|
||||
);
|
||||
} finally {
|
||||
processing.value = false;
|
||||
}
|
||||
|
@ -323,16 +324,12 @@ class HomePage extends HookConsumerWidget {
|
|||
} else {
|
||||
refreshCount.value++;
|
||||
// set counter back to 0 if user does not request refresh again
|
||||
Timer(const Duration(seconds: 4), () {
|
||||
refreshCount.value = 0;
|
||||
});
|
||||
Timer(const Duration(seconds: 4), () => refreshCount.value = 0);
|
||||
}
|
||||
}
|
||||
|
||||
buildLoadingIndicator() {
|
||||
Timer(const Duration(seconds: 2), () {
|
||||
tipOneOpacity.value = 1;
|
||||
});
|
||||
Timer(const Duration(seconds: 2), () => tipOneOpacity.value = 1);
|
||||
|
||||
return Center(
|
||||
child: Column(
|
||||
|
@ -415,7 +412,6 @@ class HomePage extends HookConsumerWidget {
|
|||
selectionAssetState: selectionAssetState.value,
|
||||
onStack: onStack,
|
||||
),
|
||||
if (processing.value) const Center(child: ImmichLoadingIndicator()),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
@ -48,7 +48,7 @@ class LoginForm extends HookConsumerWidget {
|
|||
/// Fetch the server login credential and enables oAuth login if necessary
|
||||
/// Returns true if successful, false otherwise
|
||||
Future<bool> getServerLoginCredential() async {
|
||||
final serverUrl = serverEndpointController.text.trim();
|
||||
final serverUrl = sanitizeUrl(serverEndpointController.text);
|
||||
|
||||
// Guard empty URL
|
||||
if (serverUrl.isEmpty) {
|
||||
|
@ -127,6 +127,12 @@ class LoginForm extends HookConsumerWidget {
|
|||
);
|
||||
|
||||
populateTestLoginInfo() {
|
||||
usernameController.text = 'demo@immich.app';
|
||||
passwordController.text = 'demo';
|
||||
serverEndpointController.text = 'https://demo.immich.app';
|
||||
}
|
||||
|
||||
populateTestLoginInfo1() {
|
||||
usernameController.text = 'testuser@email.com';
|
||||
passwordController.text = 'password';
|
||||
serverEndpointController.text = 'http://10.1.15.216:2283/api';
|
||||
|
@ -144,7 +150,7 @@ class LoginForm extends HookConsumerWidget {
|
|||
await ref.read(authenticationProvider.notifier).login(
|
||||
usernameController.text,
|
||||
passwordController.text,
|
||||
serverEndpointController.text.trim(),
|
||||
sanitizeUrl(serverEndpointController.text),
|
||||
);
|
||||
if (isAuthenticated) {
|
||||
// Resume backup (if enable) then navigate
|
||||
|
@ -181,7 +187,7 @@ class LoginForm extends HookConsumerWidget {
|
|||
|
||||
try {
|
||||
oAuthServerConfig = await oAuthService
|
||||
.getOAuthServerConfig(serverEndpointController.text);
|
||||
.getOAuthServerConfig(sanitizeUrl(serverEndpointController.text));
|
||||
|
||||
isLoading.value = true;
|
||||
} catch (e) {
|
||||
|
@ -203,7 +209,7 @@ class LoginForm extends HookConsumerWidget {
|
|||
.watch(authenticationProvider.notifier)
|
||||
.setSuccessLoginInfo(
|
||||
accessToken: loginResponseDto.accessToken,
|
||||
serverUrl: serverEndpointController.text,
|
||||
serverUrl: sanitizeUrl(serverEndpointController.text),
|
||||
);
|
||||
|
||||
if (isSuccess) {
|
||||
|
@ -299,7 +305,7 @@ class LoginForm extends HookConsumerWidget {
|
|||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text(
|
||||
serverEndpointController.text,
|
||||
sanitizeUrl(serverEndpointController.text),
|
||||
style: context.textTheme.displaySmall,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
|
@ -387,6 +393,7 @@ class LoginForm extends HookConsumerWidget {
|
|||
children: [
|
||||
GestureDetector(
|
||||
onDoubleTap: () => populateTestLoginInfo(),
|
||||
onLongPress: () => populateTestLoginInfo1(),
|
||||
child: RotationTransition(
|
||||
turns: logoAnimationController,
|
||||
child: const ImmichLogo(
|
||||
|
|
|
@ -17,7 +17,7 @@ class MemoryLane extends HookConsumerWidget {
|
|||
.whenData(
|
||||
(memories) => memories != null
|
||||
? Container(
|
||||
margin: const EdgeInsets.only(top: 10),
|
||||
margin: const EdgeInsets.only(top: 10, left: 10),
|
||||
height: 200,
|
||||
child: ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart';
|
||||
import 'package:immich_mobile/modules/partner/providers/partner.provider.dart';
|
||||
import 'package:immich_mobile/shared/models/user.dart';
|
||||
import 'package:immich_mobile/shared/providers/asset.provider.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_toast.dart';
|
||||
|
||||
class PartnerDetailPage extends HookConsumerWidget {
|
||||
|
@ -71,8 +71,8 @@ class PartnerDetailPage extends HookConsumerWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
body: assets.when(
|
||||
data: (renderList) => renderList.isEmpty
|
||||
body: assets.widgetWhen(
|
||||
onData: (renderList) => renderList.isEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Text(
|
||||
|
@ -84,8 +84,6 @@ class PartnerDetailPage extends HookConsumerWidget {
|
|||
onRefresh: () =>
|
||||
ref.read(assetProvider.notifier).getPartnerAssets(partner),
|
||||
),
|
||||
error: (e, _) => Text("Error loading partners:\n$e"),
|
||||
loading: () => const Center(child: ImmichLoadingIndicator()),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
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/modules/partner/providers/partner.provider.dart';
|
||||
import 'package:immich_mobile/modules/partner/services/partner.service.dart';
|
||||
import 'package:immich_mobile/shared/models/user.dart';
|
||||
|
@ -34,7 +35,7 @@ class PartnerPage extends HookConsumerWidget {
|
|||
children: [
|
||||
for (User u in users)
|
||||
SimpleDialogOption(
|
||||
onPressed: () => Navigator.pop(context, u),
|
||||
onPressed: () => context.pop(u),
|
||||
child: Row(
|
||||
children: [
|
||||
Padding(
|
||||
|
@ -70,8 +71,7 @@ class PartnerPage extends HookConsumerWidget {
|
|||
builder: (BuildContext context) {
|
||||
return ConfirmDialog(
|
||||
title: "partner_page_stop_sharing_title",
|
||||
content:
|
||||
"partner_page_stop_sharing_content".tr(args: [u.name]),
|
||||
content: "partner_page_stop_sharing_content".tr(args: [u.name]),
|
||||
onOk: () => ref.read(partnerServiceProvider).removePartner(u),
|
||||
);
|
||||
},
|
||||
|
@ -118,6 +118,7 @@ class PartnerPage extends HookConsumerWidget {
|
|||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
|
@ -126,12 +127,15 @@ class PartnerPage extends HookConsumerWidget {
|
|||
style: TextStyle(fontSize: 14),
|
||||
).tr(),
|
||||
),
|
||||
ElevatedButton.icon(
|
||||
onPressed: availableUsers.whenOrNull(
|
||||
data: (data) => addNewUsersHandler,
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: availableUsers.whenOrNull(
|
||||
data: (data) => addNewUsersHandler,
|
||||
),
|
||||
icon: const Icon(Icons.person_add),
|
||||
label: const Text("partner_page_add_partner").tr(),
|
||||
),
|
||||
icon: const Icon(Icons.person_add),
|
||||
label: const Text("partner_page_add_partner").tr(),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart';
|
||||
import 'package:immich_mobile/modules/search/providers/all_motion_photos.provider.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
|
||||
class AllMotionPhotosPage extends HookConsumerWidget {
|
||||
const AllMotionPhotosPage({super.key});
|
||||
|
@ -21,14 +21,10 @@ class AllMotionPhotosPage extends HookConsumerWidget {
|
|||
icon: const Icon(Icons.arrow_back_ios_rounded),
|
||||
),
|
||||
),
|
||||
body: motionPhotos.when(
|
||||
data: (assets) => ImmichAssetGrid(
|
||||
body: motionPhotos.widgetWhen(
|
||||
onData: (assets) => ImmichAssetGrid(
|
||||
assets: assets,
|
||||
),
|
||||
error: (e, s) => Text(e.toString()),
|
||||
loading: () => const Center(
|
||||
child: ImmichLoadingIndicator(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/modules/search/providers/people.provider.dart';
|
||||
import 'package:immich_mobile/modules/search/ui/explore_grid.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
|
||||
class AllPeoplePage extends HookConsumerWidget {
|
||||
const AllPeoplePage({super.key});
|
||||
|
@ -23,12 +23,8 @@ class AllPeoplePage extends HookConsumerWidget {
|
|||
icon: const Icon(Icons.arrow_back_ios_rounded),
|
||||
),
|
||||
),
|
||||
body: curatedPeople.when(
|
||||
loading: () => const Center(child: ImmichLoadingIndicator()),
|
||||
error: (err, stack) => Center(
|
||||
child: Text('Error: $err'),
|
||||
),
|
||||
data: (people) => ExploreGrid(
|
||||
body: curatedPeople.widgetWhen(
|
||||
onData: (people) => ExploreGrid(
|
||||
isPeople: true,
|
||||
curatedContent: people,
|
||||
),
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart';
|
||||
import 'package:immich_mobile/modules/search/providers/all_video_assets.provider.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
|
||||
class AllVideosPage extends HookConsumerWidget {
|
||||
const AllVideosPage({super.key});
|
||||
|
@ -21,14 +21,10 @@ class AllVideosPage extends HookConsumerWidget {
|
|||
icon: const Icon(Icons.arrow_back_ios_rounded),
|
||||
),
|
||||
),
|
||||
body: videos.when(
|
||||
data: (assets) => ImmichAssetGrid(
|
||||
body: videos.widgetWhen(
|
||||
onData: (assets) => ImmichAssetGrid(
|
||||
assets: assets,
|
||||
),
|
||||
error: (e, s) => Text(e.toString()),
|
||||
loading: () => const Center(
|
||||
child: ImmichLoadingIndicator(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/modules/search/models/curated_content.dart';
|
||||
import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart';
|
||||
import 'package:immich_mobile/modules/search/ui/explore_grid.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class CuratedLocationPage extends HookConsumerWidget {
|
||||
|
@ -26,12 +26,8 @@ class CuratedLocationPage extends HookConsumerWidget {
|
|||
icon: const Icon(Icons.arrow_back_ios_rounded),
|
||||
),
|
||||
),
|
||||
body: curatedLocation.when(
|
||||
loading: () => const Center(child: ImmichLoadingIndicator()),
|
||||
error: (err, stack) => Center(
|
||||
child: Text('Error: $err'),
|
||||
),
|
||||
data: (curatedLocations) => ExploreGrid(
|
||||
body: curatedLocation.widgetWhen(
|
||||
onData: (curatedLocations) => ExploreGrid(
|
||||
curatedContent: curatedLocations
|
||||
.map(
|
||||
(l) => CuratedContent(
|
||||
|
|
|
@ -8,7 +8,6 @@ import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart'
|
|||
import 'package:immich_mobile/modules/search/providers/people.provider.dart';
|
||||
import 'package:immich_mobile/modules/search/ui/person_name_edit_form.dart';
|
||||
import 'package:immich_mobile/shared/models/store.dart' as isar_store;
|
||||
import 'package:immich_mobile/shared/ui/scaffold_error_body.dart';
|
||||
import 'package:immich_mobile/utils/image_url_builder.dart';
|
||||
|
||||
class PersonResultPage extends HookConsumerWidget {
|
||||
|
@ -112,7 +111,7 @@ class PersonResultPage extends HookConsumerWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
body: ref.watch(personAssetsProvider(personId)).scaffoldBodyWhen(
|
||||
body: ref.watch(personAssetsProvider(personId)).widgetWhen(
|
||||
onData: (renderList) => ImmichAssetGrid(
|
||||
renderList: renderList,
|
||||
topWidget: Padding(
|
||||
|
@ -137,7 +136,6 @@ class PersonResultPage extends HookConsumerWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
onError: const ScaffoldErrorBody(icon: Icons.person_off_outlined),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart';
|
||||
import 'package:immich_mobile/modules/search/providers/recently_added.provider.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
|
||||
class RecentlyAddedPage extends HookConsumerWidget {
|
||||
const RecentlyAddedPage({super.key});
|
||||
|
@ -21,14 +21,10 @@ class RecentlyAddedPage extends HookConsumerWidget {
|
|||
icon: const Icon(Icons.arrow_back_ios_rounded),
|
||||
),
|
||||
),
|
||||
body: recents.when(
|
||||
data: (searchResponse) => ImmichAssetGrid(
|
||||
body: recents.widgetWhen(
|
||||
onData: (searchResponse) => ImmichAssetGrid(
|
||||
assets: searchResponse,
|
||||
),
|
||||
error: (e, s) => Text(e.toString()),
|
||||
loading: () => const Center(
|
||||
child: ImmichLoadingIndicator(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:easy_localization/easy_localization.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/modules/search/models/curated_content.dart';
|
||||
import 'package:immich_mobile/modules/search/providers/people.provider.dart';
|
||||
|
@ -15,7 +16,7 @@ import 'package:immich_mobile/modules/search/ui/search_row_title.dart';
|
|||
import 'package:immich_mobile/modules/search/ui/search_suggestion_list.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/shared/providers/server_info.provider.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:immich_mobile/shared/ui/scaffold_error_body.dart';
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class SearchPage extends HookConsumerWidget {
|
||||
|
@ -73,10 +74,9 @@ class SearchPage extends HookConsumerWidget {
|
|||
buildPeople() {
|
||||
return SizedBox(
|
||||
height: imageSize,
|
||||
child: curatedPeople.when(
|
||||
loading: () => const Center(child: ImmichLoadingIndicator()),
|
||||
error: (err, stack) => Center(child: Text('Error: $err')),
|
||||
data: (people) => CuratedPeopleRow(
|
||||
child: curatedPeople.widgetWhen(
|
||||
onError: (error, stack) => const ScaffoldErrorBody(withIcon: false),
|
||||
onData: (people) => CuratedPeopleRow(
|
||||
content: people.take(12).toList(),
|
||||
onTap: (content, index) {
|
||||
context.autoPush(
|
||||
|
@ -97,10 +97,9 @@ class SearchPage extends HookConsumerWidget {
|
|||
buildPlaces() {
|
||||
return SizedBox(
|
||||
height: imageSize,
|
||||
child: curatedLocation.when(
|
||||
loading: () => const Center(child: ImmichLoadingIndicator()),
|
||||
error: (err, stack) => Center(child: Text('Error: $err')),
|
||||
data: (locations) => CuratedPlacesRow(
|
||||
child: curatedLocation.widgetWhen(
|
||||
onError: (error, stack) => const ScaffoldErrorBody(withIcon: false),
|
||||
onData: (locations) => CuratedPlacesRow(
|
||||
isMapEnabled: isMapEnabled,
|
||||
content: locations
|
||||
.map(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
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/modules/settings/ui/advanced_settings/advanced_settings.dart';
|
||||
import 'package:immich_mobile/modules/settings/ui/asset_list_settings/asset_list_settings.dart';
|
||||
import 'package:immich_mobile/modules/settings/ui/local_storage_settings/local_storage_settings.dart';
|
||||
|
@ -18,9 +19,7 @@ class SettingsPage extends HookConsumerWidget {
|
|||
leading: IconButton(
|
||||
iconSize: 20,
|
||||
splashRadius: 24,
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
onPressed: () => context.pop(),
|
||||
icon: const Icon(Icons.arrow_back_ios_new_rounded),
|
||||
),
|
||||
automaticallyImplyLeading: false,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:math' as math;
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
|
@ -26,13 +27,13 @@ class SharedLinkItem extends ConsumerWidget {
|
|||
}
|
||||
|
||||
Widget getExpiryDuration(bool isDarkMode) {
|
||||
var expiresText = "Expires ∞";
|
||||
var expiresText = "shared_link_expires_never".tr();
|
||||
if (sharedLink.expiresAt != null) {
|
||||
if (isExpired()) {
|
||||
return Text(
|
||||
"Expired",
|
||||
"shared_link_expired",
|
||||
style: TextStyle(color: Colors.red[300]),
|
||||
);
|
||||
).tr();
|
||||
}
|
||||
final difference = sharedLink.expiresAt!.difference(DateTime.now());
|
||||
debugPrint("Difference: $difference");
|
||||
|
@ -41,13 +42,15 @@ class SharedLinkItem extends ConsumerWidget {
|
|||
if (difference.inHours % 24 > 12) {
|
||||
dayDifference += 1;
|
||||
}
|
||||
expiresText = "in $dayDifference days";
|
||||
expiresText = "shared_link_expires_days".plural(dayDifference);
|
||||
} else if (difference.inHours > 0) {
|
||||
expiresText = "in ${difference.inHours} hours";
|
||||
expiresText = "shared_link_expires_hours".plural(difference.inHours);
|
||||
} else if (difference.inMinutes > 0) {
|
||||
expiresText = "in ${difference.inMinutes} minutes";
|
||||
expiresText =
|
||||
"shared_link_expires_minutes".plural(difference.inMinutes);
|
||||
} else if (difference.inSeconds > 0) {
|
||||
expiresText = "in ${difference.inSeconds} seconds";
|
||||
expiresText =
|
||||
"shared_link_expires_seconds".plural(difference.inSeconds);
|
||||
}
|
||||
}
|
||||
return Text(
|
||||
|
@ -72,7 +75,7 @@ class SharedLinkItem extends ConsumerWidget {
|
|||
context: context,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
toastType: ToastType.error,
|
||||
msg: 'Cannot fetch the server url',
|
||||
msg: "shared_link_error_server_url_fetch".tr(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -83,11 +86,14 @@ class SharedLinkItem extends ConsumerWidget {
|
|||
),
|
||||
).then((_) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
"Copied to clipboard",
|
||||
),
|
||||
duration: Duration(seconds: 2),
|
||||
"shared_link_clipboard_copied_massage",
|
||||
style: context.textTheme.bodyLarge?.copyWith(
|
||||
color: context.primaryColor,
|
||||
),
|
||||
).tr(),
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
@ -163,9 +169,12 @@ class SharedLinkItem extends ConsumerWidget {
|
|||
Widget buildBottomInfo() {
|
||||
return Row(
|
||||
children: [
|
||||
if (sharedLink.allowUpload) buildInfoChip("Upload"),
|
||||
if (sharedLink.allowDownload) buildInfoChip("Download"),
|
||||
if (sharedLink.showMetadata) buildInfoChip("EXIF"),
|
||||
if (sharedLink.allowUpload)
|
||||
buildInfoChip("shared_link_info_chip_upload".tr()),
|
||||
if (sharedLink.allowDownload)
|
||||
buildInfoChip("shared_link_info_chip_download".tr()),
|
||||
if (sharedLink.showMetadata)
|
||||
buildInfoChip("shared_link_info_chip_metadata".tr()),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -275,7 +275,12 @@ class SharedLinkEditPage extends HookConsumerWidget {
|
|||
).then((_) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: const Text("shared_link_clipboard_copied_massage").tr(),
|
||||
content: Text(
|
||||
"shared_link_clipboard_copied_massage",
|
||||
style: context.textTheme.bodyLarge?.copyWith(
|
||||
color: context.primaryColor,
|
||||
),
|
||||
).tr(),
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -2,11 +2,11 @@ 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/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/modules/shared_link/models/shared_link.dart';
|
||||
import 'package:immich_mobile/modules/shared_link/providers/shared_link.provider.dart';
|
||||
import 'package:immich_mobile/modules/shared_link/ui/shared_link_item.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
|
||||
class SharedLinkPage extends HookConsumerWidget {
|
||||
const SharedLinkPage({Key? key}) : super(key: key);
|
||||
|
@ -18,7 +18,10 @@ class SharedLinkPage extends HookConsumerWidget {
|
|||
useEffect(
|
||||
() {
|
||||
ref.read(sharedLinksStateProvider.notifier).fetchLinks();
|
||||
return () => ref.invalidate(sharedLinksStateProvider);
|
||||
return () {
|
||||
if (!context.mounted) return;
|
||||
ref.invalidate(sharedLinksStateProvider);
|
||||
};
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
@ -113,11 +116,10 @@ class SharedLinkPage extends HookConsumerWidget {
|
|||
centerTitle: false,
|
||||
),
|
||||
body: SafeArea(
|
||||
child: sharedLinks.when(
|
||||
data: (links) =>
|
||||
child: sharedLinks.widgetWhen(
|
||||
onError: (error, stackTrace) => buildNoShares(),
|
||||
onData: (links) =>
|
||||
links.isNotEmpty ? buildSharesList(links) : buildNoShares(),
|
||||
error: (error, stackTrace) => buildNoShares(),
|
||||
loading: () => const Center(child: ImmichLoadingIndicator()),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/delete_dialog.dart';
|
||||
|
@ -11,8 +12,8 @@ import 'package:immich_mobile/shared/models/asset.dart';
|
|||
import 'package:immich_mobile/shared/providers/asset.provider.dart';
|
||||
import 'package:immich_mobile/shared/providers/server_info.provider.dart';
|
||||
import 'package:immich_mobile/shared/ui/confirm_dialog.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_toast.dart';
|
||||
import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
|
||||
|
||||
class TrashPage extends HookConsumerWidget {
|
||||
const TrashPage({super.key});
|
||||
|
@ -24,7 +25,7 @@ class TrashPage extends HookConsumerWidget {
|
|||
ref.watch(serverInfoProvider.select((v) => v.serverConfig.trashDays));
|
||||
final selectionEnabledHook = useState(false);
|
||||
final selection = useState(<Asset>{});
|
||||
final processing = useState(false);
|
||||
final processing = useProcessingOverlay();
|
||||
|
||||
void selectionListener(
|
||||
bool multiselect,
|
||||
|
@ -229,18 +230,13 @@ class TrashPage extends HookConsumerWidget {
|
|||
);
|
||||
}
|
||||
|
||||
return trashedAssets.when(
|
||||
loading: () => Scaffold(
|
||||
appBar: buildAppBar("?"),
|
||||
body: const Center(child: CircularProgressIndicator()),
|
||||
return Scaffold(
|
||||
appBar: trashedAssets.maybeWhen(
|
||||
orElse: () => buildAppBar("?"),
|
||||
data: (data) => buildAppBar(data.totalAssets.toString()),
|
||||
),
|
||||
error: (error, stackTrace) => Scaffold(
|
||||
appBar: buildAppBar("!"),
|
||||
body: Center(child: Text(error.toString())),
|
||||
),
|
||||
data: (data) => Scaffold(
|
||||
appBar: buildAppBar(data.totalAssets.toString()),
|
||||
body: data.isEmpty
|
||||
body: trashedAssets.widgetWhen(
|
||||
onData: (data) => data.isEmpty
|
||||
? Center(
|
||||
child: Text('trash_page_no_assets'.tr()),
|
||||
)
|
||||
|
@ -254,11 +250,9 @@ class TrashPage extends HookConsumerWidget {
|
|||
showMultiSelectIndicator: false,
|
||||
showStack: true,
|
||||
topWidget: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 24,
|
||||
bottom: 24,
|
||||
left: 12,
|
||||
right: 12,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 24,
|
||||
),
|
||||
child: const Text(
|
||||
"trash_page_info",
|
||||
|
@ -267,8 +261,6 @@ class TrashPage extends HookConsumerWidget {
|
|||
),
|
||||
),
|
||||
if (selectionEnabledHook.value) buildBottomBar(),
|
||||
if (processing.value)
|
||||
const Center(child: ImmichLoadingIndicator()),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -21,7 +21,7 @@ class ImmichLoadingIndicator extends StatelessWidget {
|
|||
padding: const EdgeInsets.all(15),
|
||||
child: const CircularProgressIndicator(
|
||||
color: Colors.white,
|
||||
strokeWidth: 2,
|
||||
strokeWidth: 3,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
|||
|
||||
// Error widget to be used in Scaffold when an AsyncError is received
|
||||
class ScaffoldErrorBody extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final bool withIcon;
|
||||
|
||||
const ScaffoldErrorBody({this.icon = Icons.error_outline, super.key});
|
||||
const ScaffoldErrorBody({super.key, this.withIcon = true});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -14,19 +14,22 @@ class ScaffoldErrorBody extends StatelessWidget {
|
|||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Text(
|
||||
Text(
|
||||
"scaffold_body_error_occured",
|
||||
style:
|
||||
TextStyle(fontSize: 14, fontWeight: FontWeight.bold, height: 3),
|
||||
style: context.textTheme.displayMedium,
|
||||
textAlign: TextAlign.center,
|
||||
).tr(),
|
||||
Center(
|
||||
child: Icon(
|
||||
icon,
|
||||
size: 100,
|
||||
color: context.themeData.iconTheme.color?.withOpacity(0.5),
|
||||
if (withIcon)
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 15),
|
||||
child: Icon(
|
||||
Icons.error_outline,
|
||||
size: 100,
|
||||
color: context.themeData.iconTheme.color?.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -39,7 +39,14 @@ class AppLogDetailPage extends HookConsumerWidget {
|
|||
Clipboard.setData(ClipboardData(text: stackTrace))
|
||||
.then((_) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text("Copied to clipboard")),
|
||||
SnackBar(
|
||||
content: Text(
|
||||
"Copied to clipboard",
|
||||
style: context.textTheme.bodyLarge?.copyWith(
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
|
@ -98,7 +105,14 @@ class AppLogDetailPage extends HookConsumerWidget {
|
|||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(text: message)).then((_) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text("Copied to clipboard")),
|
||||
SnackBar(
|
||||
content: Text(
|
||||
"Copied to clipboard",
|
||||
style: context.textTheme.bodyLarge?.copyWith(
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
|
|
|
@ -1,41 +1,64 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
|
||||
class ImmichLoadingOverlay extends StatelessWidget {
|
||||
const ImmichLoadingOverlay({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
final _loadingEntry = OverlayEntry(
|
||||
builder: (context) => SizedBox.square(
|
||||
dimension: double.infinity,
|
||||
child: DecoratedBox(
|
||||
decoration:
|
||||
BoxDecoration(color: context.colorScheme.surface.withAlpha(200)),
|
||||
child: const Center(child: ImmichLoadingIndicator()),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
ValueNotifier<bool> useProcessingOverlay() {
|
||||
return use(const _LoadingOverlay());
|
||||
}
|
||||
|
||||
class _LoadingOverlay extends Hook<ValueNotifier<bool>> {
|
||||
const _LoadingOverlay();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder<bool>(
|
||||
valueListenable:
|
||||
ImmichLoadingOverlayController.appLoader.loaderShowingNotifier,
|
||||
builder: (context, shouldShow, child) {
|
||||
return shouldShow
|
||||
? const Scaffold(
|
||||
backgroundColor: Colors.black54,
|
||||
body: Center(
|
||||
child: ImmichLoadingIndicator(),
|
||||
),
|
||||
)
|
||||
: const SizedBox();
|
||||
},
|
||||
);
|
||||
}
|
||||
_LoadingOverlayState createState() => _LoadingOverlayState();
|
||||
}
|
||||
|
||||
class ImmichLoadingOverlayController {
|
||||
static final ImmichLoadingOverlayController appLoader =
|
||||
ImmichLoadingOverlayController();
|
||||
ValueNotifier<bool> loaderShowingNotifier = ValueNotifier(false);
|
||||
ValueNotifier<String> loaderTextNotifier = ValueNotifier('error message');
|
||||
class _LoadingOverlayState
|
||||
extends HookState<ValueNotifier<bool>, _LoadingOverlay> {
|
||||
late final _isProcessing = ValueNotifier(false)..addListener(_listener);
|
||||
OverlayEntry? overlayEntry;
|
||||
|
||||
void show() {
|
||||
loaderShowingNotifier.value = true;
|
||||
void _listener() {
|
||||
setState(() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (_isProcessing.value) {
|
||||
overlayEntry?.remove();
|
||||
overlayEntry = _loadingEntry;
|
||||
Overlay.of(context).insert(_loadingEntry);
|
||||
} else {
|
||||
overlayEntry?.remove();
|
||||
overlayEntry = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void hide() {
|
||||
loaderShowingNotifier.value = false;
|
||||
@override
|
||||
ValueNotifier<bool> build(BuildContext context) {
|
||||
return _isProcessing;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_isProcessing.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Object? get debugValue => _isProcessing.value;
|
||||
|
||||
@override
|
||||
String get debugLabel => 'useProcessingOverlay<>';
|
||||
}
|
||||
|
|
|
@ -48,8 +48,8 @@ ThemeData immichLightTheme = ThemeData(
|
|||
),
|
||||
backgroundColor: Colors.white,
|
||||
),
|
||||
appBarTheme: AppBarTheme(
|
||||
titleTextStyle: const TextStyle(
|
||||
appBarTheme: const AppBarTheme(
|
||||
titleTextStyle: TextStyle(
|
||||
fontFamily: 'Overpass',
|
||||
color: Colors.indigo,
|
||||
fontWeight: FontWeight.bold,
|
||||
|
@ -61,7 +61,7 @@ ThemeData immichLightTheme = ThemeData(
|
|||
scrolledUnderElevation: 0,
|
||||
centerTitle: true,
|
||||
),
|
||||
bottomNavigationBarTheme: BottomNavigationBarThemeData(
|
||||
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
||||
type: BottomNavigationBarType.fixed,
|
||||
backgroundColor: immichBackgroundColor,
|
||||
selectedItemColor: Colors.indigo,
|
||||
|
@ -69,7 +69,7 @@ ThemeData immichLightTheme = ThemeData(
|
|||
cardTheme: const CardTheme(
|
||||
surfaceTintColor: Colors.transparent,
|
||||
),
|
||||
drawerTheme: DrawerThemeData(
|
||||
drawerTheme: const DrawerThemeData(
|
||||
backgroundColor: immichBackgroundColor,
|
||||
),
|
||||
textTheme: const TextTheme(
|
||||
|
@ -162,7 +162,7 @@ ThemeData immichDarkTheme = ThemeData(
|
|||
hintColor: Colors.grey[600],
|
||||
fontFamily: 'Overpass',
|
||||
snackBarTheme: SnackBarThemeData(
|
||||
contentTextStyle: TextStyle(
|
||||
contentTextStyle: const TextStyle(
|
||||
fontFamily: 'Overpass',
|
||||
color: immichDarkThemePrimaryColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
|
@ -174,35 +174,35 @@ ThemeData immichDarkTheme = ThemeData(
|
|||
foregroundColor: immichDarkThemePrimaryColor,
|
||||
),
|
||||
),
|
||||
appBarTheme: AppBarTheme(
|
||||
appBarTheme: const AppBarTheme(
|
||||
titleTextStyle: TextStyle(
|
||||
fontFamily: 'Overpass',
|
||||
color: immichDarkThemePrimaryColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
),
|
||||
backgroundColor: const Color.fromARGB(255, 32, 33, 35),
|
||||
backgroundColor: Color.fromARGB(255, 32, 33, 35),
|
||||
foregroundColor: immichDarkThemePrimaryColor,
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
centerTitle: true,
|
||||
),
|
||||
bottomNavigationBarTheme: BottomNavigationBarThemeData(
|
||||
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
||||
type: BottomNavigationBarType.fixed,
|
||||
backgroundColor: const Color.fromARGB(255, 35, 36, 37),
|
||||
backgroundColor: Color.fromARGB(255, 35, 36, 37),
|
||||
selectedItemColor: immichDarkThemePrimaryColor,
|
||||
),
|
||||
drawerTheme: DrawerThemeData(
|
||||
backgroundColor: immichDarkBackgroundColor,
|
||||
scrimColor: Colors.white.withOpacity(0.1),
|
||||
),
|
||||
textTheme: TextTheme(
|
||||
displayLarge: const TextStyle(
|
||||
textTheme: const TextTheme(
|
||||
displayLarge: TextStyle(
|
||||
fontSize: 26,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color.fromARGB(255, 255, 255, 255),
|
||||
),
|
||||
displayMedium: const TextStyle(
|
||||
displayMedium: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color.fromARGB(255, 255, 255, 255),
|
||||
|
@ -212,15 +212,15 @@ ThemeData immichDarkTheme = ThemeData(
|
|||
fontWeight: FontWeight.bold,
|
||||
color: immichDarkThemePrimaryColor,
|
||||
),
|
||||
titleSmall: const TextStyle(
|
||||
titleSmall: TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
titleMedium: const TextStyle(
|
||||
titleMedium: TextStyle(
|
||||
fontSize: 18.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
titleLarge: const TextStyle(
|
||||
titleLarge: TextStyle(
|
||||
fontSize: 26.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
|
@ -258,7 +258,7 @@ ThemeData immichDarkTheme = ThemeData(
|
|||
dialogTheme: const DialogTheme(
|
||||
surfaceTintColor: Colors.transparent,
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
inputDecorationTheme: const InputDecorationTheme(
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: immichDarkThemePrimaryColor,
|
||||
|
@ -267,12 +267,12 @@ ThemeData immichDarkTheme = ThemeData(
|
|||
labelStyle: TextStyle(
|
||||
color: immichDarkThemePrimaryColor,
|
||||
),
|
||||
hintStyle: const TextStyle(
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
textSelectionTheme: TextSelectionThemeData(
|
||||
textSelectionTheme: const TextSelectionThemeData(
|
||||
cursorColor: immichDarkThemePrimaryColor,
|
||||
),
|
||||
);
|
||||
|
|
|
@ -3,10 +3,10 @@ import 'package:immich_mobile/shared/models/store.dart';
|
|||
String sanitizeUrl(String url) {
|
||||
// Add schema if none is set
|
||||
final urlWithSchema =
|
||||
url.startsWith(RegExp(r"https?://")) ? url : "https://$url";
|
||||
url.trimLeft().startsWith(RegExp(r"https?://")) ? url : "https://$url";
|
||||
|
||||
// Remove trailing slash(es)
|
||||
return urlWithSchema.replaceFirst(RegExp(r"/+$"), "");
|
||||
return urlWithSchema.trimRight().replaceFirst(RegExp(r"/+$"), "");
|
||||
}
|
||||
|
||||
String? getServerUrl() {
|
||||
|
|
3
mobile/openapi/.openapi-generator/FILES
generated
3
mobile/openapi/.openapi-generator/FILES
generated
|
@ -46,7 +46,6 @@ doc/CQMode.md
|
|||
doc/ChangePasswordDto.md
|
||||
doc/CheckExistingAssetsDto.md
|
||||
doc/CheckExistingAssetsResponseDto.md
|
||||
doc/CitiesFile.md
|
||||
doc/ClassificationConfig.md
|
||||
doc/Colorspace.md
|
||||
doc/CreateAlbumDto.md
|
||||
|
@ -231,7 +230,6 @@ lib/model/bulk_ids_dto.dart
|
|||
lib/model/change_password_dto.dart
|
||||
lib/model/check_existing_assets_dto.dart
|
||||
lib/model/check_existing_assets_response_dto.dart
|
||||
lib/model/cities_file.dart
|
||||
lib/model/classification_config.dart
|
||||
lib/model/clip_config.dart
|
||||
lib/model/clip_mode.dart
|
||||
|
@ -388,7 +386,6 @@ test/bulk_ids_dto_test.dart
|
|||
test/change_password_dto_test.dart
|
||||
test/check_existing_assets_dto_test.dart
|
||||
test/check_existing_assets_response_dto_test.dart
|
||||
test/cities_file_test.dart
|
||||
test/classification_config_test.dart
|
||||
test/clip_config_test.dart
|
||||
test/clip_mode_test.dart
|
||||
|
|
4
mobile/openapi/README.md
generated
4
mobile/openapi/README.md
generated
|
@ -98,6 +98,7 @@ Class | Method | HTTP request | Description
|
|||
*AssetApi* | [**downloadFile**](doc//AssetApi.md#downloadfile) | **POST** /asset/download/{id} |
|
||||
*AssetApi* | [**emptyTrash**](doc//AssetApi.md#emptytrash) | **POST** /asset/trash/empty |
|
||||
*AssetApi* | [**getAllAssets**](doc//AssetApi.md#getallassets) | **GET** /asset |
|
||||
*AssetApi* | [**getAllUserAssetsByDeviceId**](doc//AssetApi.md#getalluserassetsbydeviceid) | **GET** /asset/device/{deviceId} |
|
||||
*AssetApi* | [**getAssetById**](doc//AssetApi.md#getassetbyid) | **GET** /asset/assetById/{id} |
|
||||
*AssetApi* | [**getAssetSearchTerms**](doc//AssetApi.md#getassetsearchterms) | **GET** /asset/search-terms |
|
||||
*AssetApi* | [**getAssetStatistics**](doc//AssetApi.md#getassetstatistics) | **GET** /asset/statistics |
|
||||
|
@ -110,7 +111,7 @@ Class | Method | HTTP request | Description
|
|||
*AssetApi* | [**getRandom**](doc//AssetApi.md#getrandom) | **GET** /asset/random |
|
||||
*AssetApi* | [**getTimeBucket**](doc//AssetApi.md#gettimebucket) | **GET** /asset/time-bucket |
|
||||
*AssetApi* | [**getTimeBuckets**](doc//AssetApi.md#gettimebuckets) | **GET** /asset/time-buckets |
|
||||
*AssetApi* | [**getUserAssetsByDeviceId**](doc//AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} |
|
||||
*AssetApi* | [**getUserAssetsByDeviceId**](doc//AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} | Use /asset/device/:deviceId instead - Remove in 1.92 release
|
||||
*AssetApi* | [**restoreAssets**](doc//AssetApi.md#restoreassets) | **POST** /asset/restore |
|
||||
*AssetApi* | [**restoreTrash**](doc//AssetApi.md#restoretrash) | **POST** /asset/trash/restore |
|
||||
*AssetApi* | [**runAssetJobs**](doc//AssetApi.md#runassetjobs) | **POST** /asset/jobs |
|
||||
|
@ -243,7 +244,6 @@ Class | Method | HTTP request | Description
|
|||
- [ChangePasswordDto](doc//ChangePasswordDto.md)
|
||||
- [CheckExistingAssetsDto](doc//CheckExistingAssetsDto.md)
|
||||
- [CheckExistingAssetsResponseDto](doc//CheckExistingAssetsResponseDto.md)
|
||||
- [CitiesFile](doc//CitiesFile.md)
|
||||
- [ClassificationConfig](doc//ClassificationConfig.md)
|
||||
- [Colorspace](doc//Colorspace.md)
|
||||
- [CreateAlbumDto](doc//CreateAlbumDto.md)
|
||||
|
|
66
mobile/openapi/doc/AssetApi.md
generated
66
mobile/openapi/doc/AssetApi.md
generated
|
@ -16,6 +16,7 @@ Method | HTTP request | Description
|
|||
[**downloadFile**](AssetApi.md#downloadfile) | **POST** /asset/download/{id} |
|
||||
[**emptyTrash**](AssetApi.md#emptytrash) | **POST** /asset/trash/empty |
|
||||
[**getAllAssets**](AssetApi.md#getallassets) | **GET** /asset |
|
||||
[**getAllUserAssetsByDeviceId**](AssetApi.md#getalluserassetsbydeviceid) | **GET** /asset/device/{deviceId} |
|
||||
[**getAssetById**](AssetApi.md#getassetbyid) | **GET** /asset/assetById/{id} |
|
||||
[**getAssetSearchTerms**](AssetApi.md#getassetsearchterms) | **GET** /asset/search-terms |
|
||||
[**getAssetStatistics**](AssetApi.md#getassetstatistics) | **GET** /asset/statistics |
|
||||
|
@ -28,7 +29,7 @@ Method | HTTP request | Description
|
|||
[**getRandom**](AssetApi.md#getrandom) | **GET** /asset/random |
|
||||
[**getTimeBucket**](AssetApi.md#gettimebucket) | **GET** /asset/time-bucket |
|
||||
[**getTimeBuckets**](AssetApi.md#gettimebuckets) | **GET** /asset/time-buckets |
|
||||
[**getUserAssetsByDeviceId**](AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} |
|
||||
[**getUserAssetsByDeviceId**](AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} | Use /asset/device/:deviceId instead - Remove in 1.92 release
|
||||
[**restoreAssets**](AssetApi.md#restoreassets) | **POST** /asset/restore |
|
||||
[**restoreTrash**](AssetApi.md#restoretrash) | **POST** /asset/trash/restore |
|
||||
[**runAssetJobs**](AssetApi.md#runassetjobs) | **POST** /asset/jobs |
|
||||
|
@ -443,6 +444,63 @@ Name | Type | Description | Notes
|
|||
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **getAllUserAssetsByDeviceId**
|
||||
> List<String> getAllUserAssetsByDeviceId(deviceId)
|
||||
|
||||
|
||||
|
||||
Get all asset of a device that are in the database, ID only.
|
||||
|
||||
### Example
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
// TODO Configure API key authorization: cookie
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure API key authorization: api_key
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure HTTP Bearer authorization: bearer
|
||||
// Case 1. Use String Token
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||
// Case 2. Use Function which generate token.
|
||||
// String yourTokenGeneratorFunction() { ... }
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = AssetApi();
|
||||
final deviceId = deviceId_example; // String |
|
||||
|
||||
try {
|
||||
final result = api_instance.getAllUserAssetsByDeviceId(deviceId);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling AssetApi->getAllUserAssetsByDeviceId: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**deviceId** | **String**| |
|
||||
|
||||
### Return type
|
||||
|
||||
**List<String>**
|
||||
|
||||
### Authorization
|
||||
|
||||
[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: Not defined
|
||||
- **Accept**: application/json
|
||||
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **getAssetById**
|
||||
> AssetResponseDto getAssetById(id, key)
|
||||
|
||||
|
@ -1154,9 +1212,7 @@ Name | Type | Description | Notes
|
|||
# **getUserAssetsByDeviceId**
|
||||
> List<String> getUserAssetsByDeviceId(deviceId)
|
||||
|
||||
|
||||
|
||||
Get all asset of a device that are in the database, ID only.
|
||||
Use /asset/device/:deviceId instead - Remove in 1.92 release
|
||||
|
||||
### Example
|
||||
```dart
|
||||
|
@ -1177,7 +1233,7 @@ import 'package:openapi/api.dart';
|
|||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = AssetApi();
|
||||
final deviceId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final deviceId = deviceId_example; // String |
|
||||
|
||||
try {
|
||||
final result = api_instance.getUserAssetsByDeviceId(deviceId);
|
||||
|
|
14
mobile/openapi/doc/CitiesFile.md
generated
14
mobile/openapi/doc/CitiesFile.md
generated
|
@ -1,14 +0,0 @@
|
|||
# openapi.model.CitiesFile
|
||||
|
||||
## Load the model package
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
```
|
||||
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
|
@ -8,7 +8,6 @@ import 'package:openapi/api.dart';
|
|||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**citiesFileOverride** | [**CitiesFile**](CitiesFile.md) | |
|
||||
**enabled** | **bool** | |
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
|
1
mobile/openapi/lib/api.dart
generated
1
mobile/openapi/lib/api.dart
generated
|
@ -83,7 +83,6 @@ part 'model/cq_mode.dart';
|
|||
part 'model/change_password_dto.dart';
|
||||
part 'model/check_existing_assets_dto.dart';
|
||||
part 'model/check_existing_assets_response_dto.dart';
|
||||
part 'model/cities_file.dart';
|
||||
part 'model/classification_config.dart';
|
||||
part 'model/colorspace.dart';
|
||||
part 'model/create_album_dto.dart';
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue