Compare commits

..

40 Commits

Author SHA1 Message Date
mertalev
683bb88f8b updated dockerfile 2023-11-12 18:03:08 -05:00
mertalev
ae80def7f2 export cli 2023-11-12 18:01:12 -05:00
Fynn Petersen-Frey
069a32dcdb fix(mobile): run user sync operation with lock (#4984) 2023-11-12 10:35:08 -06:00
bo0tzz
388144823a chore(renovate): PR exiftool updates separately (#4985) 2023-11-12 17:07:55 +01:00
renovate[bot]
c23d84be39 chore(deps): update dependency @types/node to v20.9.0 (#4983)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-12 15:15:44 +00:00
renovate[bot]
66120025b7 fix(deps): update dependency tailwindcss to v3.3.5 (#4980)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-12 10:04:24 -05:00
renovate[bot]
da33653b0a chore(deps): update dependency @types/js-yaml to v4.0.9 (#4970)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-12 09:51:24 -05:00
renovate[bot]
3ea0210c1d chore(deps): update dependency @types/mime-types to v2.1.4 (#4971)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-12 09:50:55 -05:00
Ploonet
98f1e85c87 fix(docs): Typo in libraries exclusion pattern (#4972)
Co-authored-by: Antony Mota <amota.confluenceconseil@boiron.fr>
2023-11-12 09:49:47 -05:00
renovate[bot]
d2509c619e chore(deps): update dependency jest-extended to v4.0.2 (#4976)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-12 09:48:43 -05:00
renovate[bot]
2bfe5d1573 chore(deps): update dependency @types/mock-fs to v4.13.4 (#4975)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-12 09:48:17 -05:00
Daniel Dietzler
d7d464570f fix maplibre latlng import (#4977) 2023-11-12 14:46:17 +01:00
Alex Tran
2e82476cff chore: styling for partner stylesheet 2023-11-11 23:02:26 -06:00
renovate[bot]
2f462717aa chore(deps): update dependency @types/cli-progress to v3.11.5 (#4968)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-11 22:36:03 -05:00
renovate[bot]
86e04832a1 chore(deps): update dependency @types/jest to v29.5.8 (#4969)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-11 22:35:33 -05:00
Alex
96f1a271ef chore: update Makefile to use docker compose (#4967) 2023-11-12 01:31:01 +00:00
Sergey Kondrikov
55e3605ca4 feat(web): uniform random asset selection in slideshow mode (#4953)
* Implement weighted asset selection

* Rename totalAssets -> bucketCount

* Remove redundant check
2023-11-12 01:23:15 +00:00
renovate[bot]
0bf55d8e32 chore(deps): update dependency @types/chai to v4.3.10 (#4966)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-12 01:10:37 +00:00
renovate[bot]
2dcad93d9c chore(deps): update dependency @types/byte-size to v8.1.2 (#4965)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-12 01:09:16 +00:00
Mert
328a58ac0d feat(ml): add face models (#4952)
added models to config dropdown

fixed downloading

updated tests

use hf for face models

formatting
2023-11-11 19:04:49 -06:00
Brian Austin
7fca0d8da5 fix: replace first and last name with single field (#4915) 2023-11-11 19:03:32 -06:00
renovate[bot]
413ab2c538 chore(deps): update postgres:14-alpine docker digest to 874f566 (#4963)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-11 19:58:46 -05:00
renovate[bot]
394e0dfe37 chore(deps): update redis:6.2-alpine docker digest to 3995fe6 (#4964)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-11 19:58:01 -05:00
renovate[bot]
a9b6acec28 chore(deps): update ghcr.io/nginxinc/nginx-unprivileged:1.25.0-alpine3.17 docker digest to 5ebb90a (#4960)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-11 16:54:55 -06:00
Daniel Dietzler
ad4cbf20de refactor: map (#4957)
* fix mixed up lng lat for asset map

* minor cleanup

* update packages
2023-11-11 16:32:39 -06:00
bo0tzz
26fd797ac9 chore: re-enable renovate (#4955)
* Chore: reenable renovate

* chore(renovate): Don't group major updates

* chore(renovate): Use matchFileNames
2023-11-11 16:31:58 -06:00
Alex
35767591d2 feat(web): show partners assets on the main timeline (#4933) 2023-11-11 21:06:19 +00:00
aphymi
3b11854702 fix(mobile): fix JSON-format typos in Asset model (#4942) 2023-11-10 02:56:30 +00:00
Mansour
895129c997 feat!: add docker project name (#4906)
* add: docker project name

* chore: linting

---------

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
2023-11-09 21:43:21 -05:00
martin
92ec1ce77f fix(server,web): correctly show album level like (#4916)
* fix: like in global activity

* refactor: rename isGlobal to ReactionLevel.Album

* chore: open api

* chore: e2e test for album vs comment duplicate like checking

---------

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
2023-11-09 21:32:31 -05:00
Sushain Cherivirala
986bbfa831 feat(server): don't re-run face recognition on assets without any faces (#4854)
* Add AssetJobStatus

* fentity

* Add jobStatus field to AssetEntity

* Fix the migration doc paths

* Filter on facesRecognizedAt

* Set facesRecognizedAt field

* Test for facesRecognizedAt

* Done testing

* Adjust FK properties

* Add tests for WithoutProperty.FACES

* chore: non-nullable

---------

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
2023-11-09 20:55:00 -05:00
Jason Rasmussen
75c065c83a chore(server): e2e test (#4941) 2023-11-09 20:23:03 -05:00
Alex
9c0805c37a fix(server): non-admin cannot use map (#4934)
* fix(server): non-admin cannot user map

* fix: admin route

---------

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
2023-11-09 13:52:10 -06:00
shenlong
bffc2cdf60 refactor(mobile): build context extensions (#4923)
* refactor: move all extensions to separate package

* refactor(mobile): add BuildContext extension

* refactor(mobile): use theme getters from context

* refactor(mobile): use media query size from context

* refactor(mobile): use auto router methods from context

* refactor(mobile): use navigator methods from context

---------

Co-authored-by: shalong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2023-11-09 10:19:53 -06:00
Daniel Dietzler
a147dee4b6 feat: Maplibre (#4294)
* maplibre on web, custom styles from server

Actually use new vector tile server, custom style.json

support multiple style files, light/dark mode

cleanup, use new map everywhere

send file directly instead of loading first

better light/dark mode switching

remove leaflet

fix mapstyles dto, first draft of map settings

delete and add styles

fix delete default styles

fix tests

only allow one light and one dark style url

revert config core changes

fix server config store

fix tests

move axios fetches to repo

fix package-lock

fix tests

* open api

* add assets to docker container

* web: use mapSettings color for style

* style: add unique ids to map styles

* mobile: use style json for vector / raster

* do not use svelte-material-icons

* add click events to markers, simplify asset detail map

* improve map performance by using asset thumbnails for markers instead of original file

* Remove custom attribution

(by request)

* mobile: update map attribution

* style: map dark mode

* style: map light mode

* zoom level for state

* styling

* overflow gradient

* Limit maxZoom to 14

* mobile: listen for mapStyle changes in MapThumbnail

* mobile: update concurrency

---------

Co-authored-by: shalong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: bo0tzz <git@bo0tzz.me>
Co-authored-by: Alex <alex.tran1502@gmail.com>
2023-11-09 10:10:56 -06:00
Jason Rasmussen
5423f1c25b refactor(server): auth dtos (#4881)
* refactor: auth dtos

* chore: open api
2023-11-09 10:14:15 -05:00
shenlong
5c602bf4d4 fix(mobile): add label for expire after in shared link edit page (#4920)
Co-authored-by: shalong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2023-11-09 08:36:57 -06:00
bo0tzz
5db73c5c5c chore(ci): use custom base for server image build (#4456)
* Use base image for server build
* Clean up build scripts
* target tags for base image
* use prod tag instead of runtime
* use runtime stage for dev

---------

Co-authored-by: mertalev <101130780+mertalev@users.noreply.github.com>
Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
2023-11-09 13:12:05 +00:00
Michael Manganiello
52fe392a9e fix(mobile): display simple album date when range is single day (#4919)
This change makes albums with same start and end date, to just display a
single date.

Currently, date range is displayed as `Nov 9 - Nov 9 2023`. With this
change, just `Nov 9 2023` is displayed. No changes are made for albums
where start and end dates do not match.
2023-11-09 03:16:56 +00:00
Alex
5e1c0fb465 chore: post release tasks 2023-11-08 12:51:34 -06:00
371 changed files with 9592 additions and 12823 deletions

View File

@@ -33,91 +33,10 @@ jobs:
- context: "nginx" - context: "nginx"
image: "immich-proxy" image: "immich-proxy"
platforms: "linux/amd64,linux/arm64" platforms: "linux/amd64,linux/arm64"
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3.0.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.0.0
# Workaround to fix error:
# failed to push: failed to copy: io: read/write on closed pipe
# See https://github.com/docker/build-push-action/issues/761
with:
driver-opts: |
image=moby/buildkit:v0.10.6
- name: Login to Docker Hub
# Only push to Docker Hub when making a release
if: ${{ github.event_name == 'release' }}
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
# Skip when PR from a fork
if: ${{ !github.event.pull_request.head.repo.fork }}
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Generate docker image tags
id: metadata
uses: docker/metadata-action@v5
with:
flavor: |
# Disable latest tag
latest=false
images: |
name=ghcr.io/${{ github.repository_owner }}/${{matrix.image}}
name=altran1502/${{matrix.image}},enable=${{ github.event_name == 'release' }}
tags: |
# Tag with branch name
type=ref,event=branch
# Tag with pr-number
type=ref,event=pr
# Tag with git tag on release
type=ref,event=tag
type=raw,value=release,enable=${{ github.event_name == 'release' }}
- name: Determine build cache output
id: cache-target
run: |
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
# Essentially just ignore the cache output (PR can't write to registry cache)
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
else
echo "cache-to=type=registry,mode=max,ref=ghcr.io/${{ github.repository_owner }}/immich-build-cache:${{ matrix.image }}" >> $GITHUB_OUTPUT
fi
- name: Build and push image
uses: docker/build-push-action@v5.0.0
with:
context: ${{ matrix.context }}
platforms: ${{ matrix.platforms }}
# Skip pushing when PR from a fork
push: ${{ !github.event.pull_request.head.repo.fork }}
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/immich-build-cache:${{matrix.image}}
cache-to: ${{ steps.cache-target.outputs.cache-to }}
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
build_and_push_server_arm_64:
runs-on: self-hosted
strategy:
# Prevent a failure in one image from stopping the other builds
fail-fast: false
matrix:
include:
- context: "server" - context: "server"
image: "immich-server" image: "immich-server"
platforms: "linux/arm64,linux/amd64" platforms: "linux/arm64,linux/amd64"
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4

View File

@@ -1,35 +1,29 @@
dev: dev:
docker-compose -f ./docker/docker-compose.dev.yml up --remove-orphans
dev-new:
docker compose -f ./docker/docker-compose.dev.yml up --remove-orphans || make dev-down docker compose -f ./docker/docker-compose.dev.yml up --remove-orphans || make dev-down
dev-down: dev-down:
docker compose -f ./docker/docker-compose.dev.yml down --remove-orphans docker compose -f ./docker/docker-compose.dev.yml down --remove-orphans
dev-new-update: dev-update:
docker compose -f ./docker/docker-compose.dev.yml up --build -V --remove-orphans docker compose -f ./docker/docker-compose.dev.yml up --build -V --remove-orphans
dev-update:
docker-compose -f ./docker/docker-compose.dev.yml up --build -V --remove-orphans
dev-scale: dev-scale:
docker-compose -f ./docker/docker-compose.dev.yml up --build -V --scale immich-server=3 --remove-orphans docker compose -f ./docker/docker-compose.dev.yml up --build -V --scale immich-server=3 --remove-orphans
stage: stage:
docker-compose -f ./docker/docker-compose.staging.yml up --build -V --remove-orphans docker compose -f ./docker/docker-compose.staging.yml up --build -V --remove-orphans
pull-stage: pull-stage:
docker-compose -f ./docker/docker-compose.staging.yml pull docker compose -f ./docker/docker-compose.staging.yml pull
test-e2e: test-e2e:
docker compose -f ./docker/docker-compose.test.yml up --renew-anon-volumes --abort-on-container-exit --exit-code-from immich-server --remove-orphans --build docker compose -f ./docker/docker-compose.test.yml up --renew-anon-volumes --abort-on-container-exit --exit-code-from immich-server --remove-orphans --build
prod: prod:
docker-compose -f ./docker/docker-compose.prod.yml up --build -V --remove-orphans docker compose -f ./docker/docker-compose.prod.yml up --build -V --remove-orphans
prod-scale: prod-scale:
docker-compose -f ./docker/docker-compose.prod.yml up --build -V --scale immich-server=3 --scale immich-microservices=3 --remove-orphans docker compose -f ./docker/docker-compose.prod.yml up --build -V --scale immich-server=3 --scale immich-microservices=3 --remove-orphans
api: api:
cd ./server && npm run api:generate cd ./server && npm run api:generate

130
cli/package-lock.json generated
View File

@@ -1489,21 +1489,21 @@
} }
}, },
"node_modules/@types/byte-size": { "node_modules/@types/byte-size": {
"version": "8.1.0", "version": "8.1.2",
"resolved": "https://registry.npmjs.org/@types/byte-size/-/byte-size-8.1.0.tgz", "resolved": "https://registry.npmjs.org/@types/byte-size/-/byte-size-8.1.2.tgz",
"integrity": "sha512-LCIlZh8vyx+I2fgRycE1D34c33QDppYY6quBYYoaOpQ1nGhJ/avSP2VlrAefVotjJxgSk6WkKo0rTcCJwGG7vA==", "integrity": "sha512-jGyVzYu6avI8yuqQCNTZd65tzI8HZrLjKX9sdMqZrGWVlNChu0rf6p368oVEDCYJe5BMx2Ov04tD1wqtgTwGSA==",
"dev": true "dev": true
}, },
"node_modules/@types/chai": { "node_modules/@types/chai": {
"version": "4.3.6", "version": "4.3.10",
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.6.tgz", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.10.tgz",
"integrity": "sha512-VOVRLM1mBxIRxydiViqPcKn6MIxZytrbMpd6RJLIWKxUNr3zux8no0Oc7kJx0WAPIitgZ0gkrDS+btlqQpubpw==", "integrity": "sha512-of+ICnbqjmFCiixUnqRulbylyXQrPqIGf/B3Jax1wIF3DvSheysQxAWvqHhZiW3IQrycvokcLcFQlveGp+vyNg==",
"dev": true "dev": true
}, },
"node_modules/@types/cli-progress": { "node_modules/@types/cli-progress": {
"version": "3.11.3", "version": "3.11.5",
"resolved": "https://registry.npmjs.org/@types/cli-progress/-/cli-progress-3.11.3.tgz", "resolved": "https://registry.npmjs.org/@types/cli-progress/-/cli-progress-3.11.5.tgz",
"integrity": "sha512-/+C9xAdVtc+g5yHHkGBThgAA8rYpi5B+2ve3wLtybYj0JHEBs57ivR4x/zGfSsplRnV+psE91Nfin1soNKqz5Q==", "integrity": "sha512-D4PbNRbviKyppS5ivBGyFO29POlySLmA2HyUFE4p5QGazAMM3CwkKWcvTl8gvElSuxRh6FPKL8XmidX873ou4g==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@types/node": "*" "@types/node": "*"
@@ -1543,9 +1543,9 @@
} }
}, },
"node_modules/@types/jest": { "node_modules/@types/jest": {
"version": "29.5.5", "version": "29.5.8",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.5.tgz", "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.8.tgz",
"integrity": "sha512-ebylz2hnsWR9mYvmBFbXJXr+33UPc4+ZdxyDXh5w0FlPBTfCVN3wPL+kuOiQt3xvrK419v7XWeAs+AeOksafXg==", "integrity": "sha512-fXEFTxMV2Co8ZF5aYFJv+YeA08RTYJfhtN5c9JSv/mFEMe+xxjufCb+PHL+bJcMs/ebPUsBu+UNTEz+ydXrR6g==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"expect": "^29.0.0", "expect": "^29.0.0",
@@ -1553,9 +1553,9 @@
} }
}, },
"node_modules/@types/js-yaml": { "node_modules/@types/js-yaml": {
"version": "4.0.6", "version": "4.0.9",
"resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.6.tgz", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz",
"integrity": "sha512-ACTuifTSIIbyksx2HTon3aFtCKWcID7/h3XEmRpDYdMCXxPbl+m9GteOJeaAkiAta/NJaSFuA7ahZ0NkwajDSw==", "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==",
"dev": true "dev": true
}, },
"node_modules/@types/json-schema": { "node_modules/@types/json-schema": {
@@ -1565,25 +1565,28 @@
"dev": true "dev": true
}, },
"node_modules/@types/mime-types": { "node_modules/@types/mime-types": {
"version": "2.1.2", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.2.tgz", "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz",
"integrity": "sha512-q9QGHMGCiBJCHEvd4ZLdasdqXv570agPsUW0CeIm/B8DzhxsYMerD0l3IlI+EQ1A2RWHY2mmM9x1YIuuWxisCg==", "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==",
"dev": true "dev": true
}, },
"node_modules/@types/mock-fs": { "node_modules/@types/mock-fs": {
"version": "4.13.2", "version": "4.13.4",
"resolved": "https://registry.npmjs.org/@types/mock-fs/-/mock-fs-4.13.2.tgz", "resolved": "https://registry.npmjs.org/@types/mock-fs/-/mock-fs-4.13.4.tgz",
"integrity": "sha512-mSIMAOjrNTVUFmZgJEigSIm+GlS4hbrk8U5+M8EB45uMrykKdN9TidjjSaOY1yFph2+TD7bsIfB4r+IrMYVyPQ==", "integrity": "sha512-mXmM0o6lULPI8z3XNnQCpL0BGxPwx1Ul1wXYEPBGl4efShyxW2Rln0JOPEWGyZaYZMM6OVXM/15zUuFMY52ljg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.8.2", "version": "20.9.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.2.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz",
"integrity": "sha512-Vvycsc9FQdwhxE3y3DzeIxuEJbWGDsnrxvMADzTDF/lcdR9/K+AQIeAghTQsHtotg/q0j3WEOYS/jQgSdWue3w==", "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==",
"dev": true "dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
}, },
"node_modules/@types/normalize-package-data": { "node_modules/@types/normalize-package-data": {
"version": "2.4.2", "version": "2.4.2",
@@ -3867,9 +3870,9 @@
} }
}, },
"node_modules/jest-extended": { "node_modules/jest-extended": {
"version": "4.0.1", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-4.0.1.tgz", "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-4.0.2.tgz",
"integrity": "sha512-KM6dwuBUAgy6QONuR19CGubZB9Hkjqvl/d5Yc/FXsdB8+gsGxB2VQ+NEdOrr95J4GMPeLnDoPOKyi6+mKCCnZQ==", "integrity": "sha512-FH7aaPgtGYHc9mRjriS0ZEHYM5/W69tLrFTIdzm+yJgeoCmmrSB/luSfMSqWP9O29QWHPEmJ4qmU6EwsZideog==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"jest-diff": "^29.0.0", "jest-diff": "^29.0.0",
@@ -5862,6 +5865,12 @@
"node": ">=4.2.0" "node": ">=4.2.0"
} }
}, },
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
"node_modules/update-browserslist-db": { "node_modules/update-browserslist-db": {
"version": "1.0.13", "version": "1.0.13",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
@@ -7270,21 +7279,21 @@
} }
}, },
"@types/byte-size": { "@types/byte-size": {
"version": "8.1.0", "version": "8.1.2",
"resolved": "https://registry.npmjs.org/@types/byte-size/-/byte-size-8.1.0.tgz", "resolved": "https://registry.npmjs.org/@types/byte-size/-/byte-size-8.1.2.tgz",
"integrity": "sha512-LCIlZh8vyx+I2fgRycE1D34c33QDppYY6quBYYoaOpQ1nGhJ/avSP2VlrAefVotjJxgSk6WkKo0rTcCJwGG7vA==", "integrity": "sha512-jGyVzYu6avI8yuqQCNTZd65tzI8HZrLjKX9sdMqZrGWVlNChu0rf6p368oVEDCYJe5BMx2Ov04tD1wqtgTwGSA==",
"dev": true "dev": true
}, },
"@types/chai": { "@types/chai": {
"version": "4.3.6", "version": "4.3.10",
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.6.tgz", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.10.tgz",
"integrity": "sha512-VOVRLM1mBxIRxydiViqPcKn6MIxZytrbMpd6RJLIWKxUNr3zux8no0Oc7kJx0WAPIitgZ0gkrDS+btlqQpubpw==", "integrity": "sha512-of+ICnbqjmFCiixUnqRulbylyXQrPqIGf/B3Jax1wIF3DvSheysQxAWvqHhZiW3IQrycvokcLcFQlveGp+vyNg==",
"dev": true "dev": true
}, },
"@types/cli-progress": { "@types/cli-progress": {
"version": "3.11.3", "version": "3.11.5",
"resolved": "https://registry.npmjs.org/@types/cli-progress/-/cli-progress-3.11.3.tgz", "resolved": "https://registry.npmjs.org/@types/cli-progress/-/cli-progress-3.11.5.tgz",
"integrity": "sha512-/+C9xAdVtc+g5yHHkGBThgAA8rYpi5B+2ve3wLtybYj0JHEBs57ivR4x/zGfSsplRnV+psE91Nfin1soNKqz5Q==", "integrity": "sha512-D4PbNRbviKyppS5ivBGyFO29POlySLmA2HyUFE4p5QGazAMM3CwkKWcvTl8gvElSuxRh6FPKL8XmidX873ou4g==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/node": "*" "@types/node": "*"
@@ -7324,9 +7333,9 @@
} }
}, },
"@types/jest": { "@types/jest": {
"version": "29.5.5", "version": "29.5.8",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.5.tgz", "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.8.tgz",
"integrity": "sha512-ebylz2hnsWR9mYvmBFbXJXr+33UPc4+ZdxyDXh5w0FlPBTfCVN3wPL+kuOiQt3xvrK419v7XWeAs+AeOksafXg==", "integrity": "sha512-fXEFTxMV2Co8ZF5aYFJv+YeA08RTYJfhtN5c9JSv/mFEMe+xxjufCb+PHL+bJcMs/ebPUsBu+UNTEz+ydXrR6g==",
"dev": true, "dev": true,
"requires": { "requires": {
"expect": "^29.0.0", "expect": "^29.0.0",
@@ -7334,9 +7343,9 @@
} }
}, },
"@types/js-yaml": { "@types/js-yaml": {
"version": "4.0.6", "version": "4.0.9",
"resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.6.tgz", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz",
"integrity": "sha512-ACTuifTSIIbyksx2HTon3aFtCKWcID7/h3XEmRpDYdMCXxPbl+m9GteOJeaAkiAta/NJaSFuA7ahZ0NkwajDSw==", "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==",
"dev": true "dev": true
}, },
"@types/json-schema": { "@types/json-schema": {
@@ -7346,25 +7355,28 @@
"dev": true "dev": true
}, },
"@types/mime-types": { "@types/mime-types": {
"version": "2.1.2", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.2.tgz", "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz",
"integrity": "sha512-q9QGHMGCiBJCHEvd4ZLdasdqXv570agPsUW0CeIm/B8DzhxsYMerD0l3IlI+EQ1A2RWHY2mmM9x1YIuuWxisCg==", "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==",
"dev": true "dev": true
}, },
"@types/mock-fs": { "@types/mock-fs": {
"version": "4.13.2", "version": "4.13.4",
"resolved": "https://registry.npmjs.org/@types/mock-fs/-/mock-fs-4.13.2.tgz", "resolved": "https://registry.npmjs.org/@types/mock-fs/-/mock-fs-4.13.4.tgz",
"integrity": "sha512-mSIMAOjrNTVUFmZgJEigSIm+GlS4hbrk8U5+M8EB45uMrykKdN9TidjjSaOY1yFph2+TD7bsIfB4r+IrMYVyPQ==", "integrity": "sha512-mXmM0o6lULPI8z3XNnQCpL0BGxPwx1Ul1wXYEPBGl4efShyxW2Rln0JOPEWGyZaYZMM6OVXM/15zUuFMY52ljg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/node": { "@types/node": {
"version": "20.8.2", "version": "20.9.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.2.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz",
"integrity": "sha512-Vvycsc9FQdwhxE3y3DzeIxuEJbWGDsnrxvMADzTDF/lcdR9/K+AQIeAghTQsHtotg/q0j3WEOYS/jQgSdWue3w==", "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==",
"dev": true "dev": true,
"requires": {
"undici-types": "~5.26.4"
}
}, },
"@types/normalize-package-data": { "@types/normalize-package-data": {
"version": "2.4.2", "version": "2.4.2",
@@ -8969,9 +8981,9 @@
} }
}, },
"jest-extended": { "jest-extended": {
"version": "4.0.1", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-4.0.1.tgz", "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-4.0.2.tgz",
"integrity": "sha512-KM6dwuBUAgy6QONuR19CGubZB9Hkjqvl/d5Yc/FXsdB8+gsGxB2VQ+NEdOrr95J4GMPeLnDoPOKyi6+mKCCnZQ==", "integrity": "sha512-FH7aaPgtGYHc9mRjriS0ZEHYM5/W69tLrFTIdzm+yJgeoCmmrSB/luSfMSqWP9O29QWHPEmJ4qmU6EwsZideog==",
"dev": true, "dev": true,
"requires": { "requires": {
"jest-diff": "^29.0.0", "jest-diff": "^29.0.0",
@@ -10419,6 +10431,12 @@
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"dev": true "dev": true
}, },
"undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
"update-browserslist-db": { "update-browserslist-db": {
"version": "1.0.13", "version": "1.0.13",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",

View File

@@ -209,43 +209,6 @@ export interface AddUsersDto {
*/ */
'sharedUserIds': Array<string>; 'sharedUserIds': Array<string>;
} }
/**
*
* @export
* @interface AdminSignupResponseDto
*/
export interface AdminSignupResponseDto {
/**
*
* @type {string}
* @memberof AdminSignupResponseDto
*/
'createdAt': string;
/**
*
* @type {string}
* @memberof AdminSignupResponseDto
*/
'email': string;
/**
*
* @type {string}
* @memberof AdminSignupResponseDto
*/
'firstName': string;
/**
*
* @type {string}
* @memberof AdminSignupResponseDto
*/
'id': string;
/**
*
* @type {string}
* @memberof AdminSignupResponseDto
*/
'lastName': string;
}
/** /**
* *
* @export * @export
@@ -1378,24 +1341,18 @@ export interface CreateUserDto {
* @memberof CreateUserDto * @memberof CreateUserDto
*/ */
'externalPath'?: string | null; 'externalPath'?: string | null;
/**
*
* @type {string}
* @memberof CreateUserDto
*/
'firstName': string;
/**
*
* @type {string}
* @memberof CreateUserDto
*/
'lastName': string;
/** /**
* *
* @type {boolean} * @type {boolean}
* @memberof CreateUserDto * @memberof CreateUserDto
*/ */
'memoriesEnabled'?: boolean; 'memoriesEnabled'?: boolean;
/**
*
* @type {string}
* @memberof CreateUserDto
*/
'name': string;
/** /**
* *
* @type {string} * @type {string}
@@ -2174,12 +2131,6 @@ export interface LoginResponseDto {
* @memberof LoginResponseDto * @memberof LoginResponseDto
*/ */
'accessToken': string; 'accessToken': string;
/**
*
* @type {string}
* @memberof LoginResponseDto
*/
'firstName': string;
/** /**
* *
* @type {boolean} * @type {boolean}
@@ -2191,7 +2142,7 @@ export interface LoginResponseDto {
* @type {string} * @type {string}
* @memberof LoginResponseDto * @memberof LoginResponseDto
*/ */
'lastName': string; 'name': string;
/** /**
* *
* @type {string} * @type {string}
@@ -2261,6 +2212,20 @@ export interface MapMarkerResponseDto {
*/ */
'lon': number; 'lon': number;
} }
/**
*
* @export
* @enum {string}
*/
export const MapTheme = {
Light: 'light',
Dark: 'dark'
} as const;
export type MapTheme = typeof MapTheme[keyof typeof MapTheme];
/** /**
* *
* @export * @export
@@ -2384,6 +2349,97 @@ export interface OAuthConfigResponseDto {
*/ */
'url'?: string; 'url'?: string;
} }
/**
*
* @export
* @interface PartnerResponseDto
*/
export interface PartnerResponseDto {
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'createdAt': string;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'deletedAt': string | null;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'email': string;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'externalPath': string | null;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'id': string;
/**
*
* @type {boolean}
* @memberof PartnerResponseDto
*/
'inTimeline'?: boolean;
/**
*
* @type {boolean}
* @memberof PartnerResponseDto
*/
'isAdmin': boolean;
/**
*
* @type {boolean}
* @memberof PartnerResponseDto
*/
'memoriesEnabled'?: boolean;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'name': string;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'oauthId': string;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'profileImagePath': string;
/**
*
* @type {boolean}
* @memberof PartnerResponseDto
*/
'shouldChangePassword': boolean;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'storageLabel': string | null;
/**
*
* @type {string}
* @memberof PartnerResponseDto
*/
'updatedAt': string;
}
/** /**
* *
* @export * @export
@@ -2593,6 +2649,20 @@ export interface QueueStatusDto {
*/ */
'isPaused': boolean; 'isPaused': boolean;
} }
/**
*
* @export
* @enum {string}
*/
export const ReactionLevel = {
Album: 'album',
Asset: 'asset'
} as const;
export type ReactionLevel = typeof ReactionLevel[keyof typeof ReactionLevel];
/** /**
* *
* @export * @export
@@ -2859,12 +2929,6 @@ export interface ServerConfigDto {
* @memberof ServerConfigDto * @memberof ServerConfigDto
*/ */
'loginPageMessage': string; 'loginPageMessage': string;
/**
*
* @type {string}
* @memberof ServerConfigDto
*/
'mapTileUrl': string;
/** /**
* *
* @type {string} * @type {string}
@@ -3349,13 +3413,7 @@ export interface SignUpDto {
* @type {string} * @type {string}
* @memberof SignUpDto * @memberof SignUpDto
*/ */
'firstName': string; 'name': string;
/**
*
* @type {string}
* @memberof SignUpDto
*/
'lastName': string;
/** /**
* *
* @type {string} * @type {string}
@@ -3732,6 +3790,12 @@ export interface SystemConfigMachineLearningDto {
* @interface SystemConfigMapDto * @interface SystemConfigMapDto
*/ */
export interface SystemConfigMapDto { export interface SystemConfigMapDto {
/**
*
* @type {string}
* @memberof SystemConfigMapDto
*/
'darkStyle': string;
/** /**
* *
* @type {boolean} * @type {boolean}
@@ -3743,7 +3807,7 @@ export interface SystemConfigMapDto {
* @type {string} * @type {string}
* @memberof SystemConfigMapDto * @memberof SystemConfigMapDto
*/ */
'tileUrl': string; 'lightStyle': string;
} }
/** /**
* *
@@ -4229,6 +4293,19 @@ export interface UpdateLibraryDto {
*/ */
'name'?: string; 'name'?: string;
} }
/**
*
* @export
* @interface UpdatePartnerDto
*/
export interface UpdatePartnerDto {
/**
*
* @type {boolean}
* @memberof UpdatePartnerDto
*/
'inTimeline': boolean;
}
/** /**
* *
* @export * @export
@@ -4279,12 +4356,6 @@ export interface UpdateUserDto {
* @memberof UpdateUserDto * @memberof UpdateUserDto
*/ */
'externalPath'?: string; 'externalPath'?: string;
/**
*
* @type {string}
* @memberof UpdateUserDto
*/
'firstName'?: string;
/** /**
* *
* @type {string} * @type {string}
@@ -4297,18 +4368,18 @@ export interface UpdateUserDto {
* @memberof UpdateUserDto * @memberof UpdateUserDto
*/ */
'isAdmin'?: boolean; 'isAdmin'?: boolean;
/**
*
* @type {string}
* @memberof UpdateUserDto
*/
'lastName'?: string;
/** /**
* *
* @type {boolean} * @type {boolean}
* @memberof UpdateUserDto * @memberof UpdateUserDto
*/ */
'memoriesEnabled'?: boolean; 'memoriesEnabled'?: boolean;
/**
*
* @type {string}
* @memberof UpdateUserDto
*/
'name'?: string;
/** /**
* *
* @type {string} * @type {string}
@@ -4346,12 +4417,6 @@ export interface UsageByUserDto {
* @memberof UsageByUserDto * @memberof UsageByUserDto
*/ */
'usage': number; 'usage': number;
/**
*
* @type {string}
* @memberof UsageByUserDto
*/
'userFirstName': string;
/** /**
* *
* @type {string} * @type {string}
@@ -4363,7 +4428,7 @@ export interface UsageByUserDto {
* @type {string} * @type {string}
* @memberof UsageByUserDto * @memberof UsageByUserDto
*/ */
'userLastName': string; 'userName': string;
/** /**
* *
* @type {number} * @type {number}
@@ -4383,12 +4448,6 @@ export interface UserDto {
* @memberof UserDto * @memberof UserDto
*/ */
'email': string; 'email': string;
/**
*
* @type {string}
* @memberof UserDto
*/
'firstName': string;
/** /**
* *
* @type {string} * @type {string}
@@ -4400,7 +4459,7 @@ export interface UserDto {
* @type {string} * @type {string}
* @memberof UserDto * @memberof UserDto
*/ */
'lastName': string; 'name': string;
/** /**
* *
* @type {string} * @type {string}
@@ -4438,12 +4497,6 @@ export interface UserResponseDto {
* @memberof UserResponseDto * @memberof UserResponseDto
*/ */
'externalPath': string | null; 'externalPath': string | null;
/**
*
* @type {string}
* @memberof UserResponseDto
*/
'firstName': string;
/** /**
* *
* @type {string} * @type {string}
@@ -4456,18 +4509,18 @@ export interface UserResponseDto {
* @memberof UserResponseDto * @memberof UserResponseDto
*/ */
'isAdmin': boolean; 'isAdmin': boolean;
/**
*
* @type {string}
* @memberof UserResponseDto
*/
'lastName': string;
/** /**
* *
* @type {boolean} * @type {boolean}
* @memberof UserResponseDto * @memberof UserResponseDto
*/ */
'memoriesEnabled'?: boolean; 'memoriesEnabled'?: boolean;
/**
*
* @type {string}
* @memberof UserResponseDto
*/
'name': string;
/** /**
* *
* @type {string} * @type {string}
@@ -5088,11 +5141,12 @@ export const ActivityApiAxiosParamCreator = function (configuration?: Configurat
* @param {string} albumId * @param {string} albumId
* @param {string} [assetId] * @param {string} [assetId]
* @param {ReactionType} [type] * @param {ReactionType} [type]
* @param {ReactionLevel} [level]
* @param {string} [userId] * @param {string} [userId]
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
getActivities: async (albumId: string, assetId?: string, type?: ReactionType, userId?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { getActivities: async (albumId: string, assetId?: string, type?: ReactionType, level?: ReactionLevel, userId?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'albumId' is not null or undefined // verify required parameter 'albumId' is not null or undefined
assertParamExists('getActivities', 'albumId', albumId) assertParamExists('getActivities', 'albumId', albumId)
const localVarPath = `/activity`; const localVarPath = `/activity`;
@@ -5128,6 +5182,10 @@ export const ActivityApiAxiosParamCreator = function (configuration?: Configurat
localVarQueryParameter['type'] = type; localVarQueryParameter['type'] = type;
} }
if (level !== undefined) {
localVarQueryParameter['level'] = level;
}
if (userId !== undefined) { if (userId !== undefined) {
localVarQueryParameter['userId'] = userId; localVarQueryParameter['userId'] = userId;
} }
@@ -5228,12 +5286,13 @@ export const ActivityApiFp = function(configuration?: Configuration) {
* @param {string} albumId * @param {string} albumId
* @param {string} [assetId] * @param {string} [assetId]
* @param {ReactionType} [type] * @param {ReactionType} [type]
* @param {ReactionLevel} [level]
* @param {string} [userId] * @param {string} [userId]
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async getActivities(albumId: string, assetId?: string, type?: ReactionType, userId?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<ActivityResponseDto>>> { async getActivities(albumId: string, assetId?: string, type?: ReactionType, level?: ReactionLevel, userId?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<ActivityResponseDto>>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getActivities(albumId, assetId, type, userId, options); const localVarAxiosArgs = await localVarAxiosParamCreator.getActivities(albumId, assetId, type, level, userId, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
/** /**
@@ -5282,7 +5341,7 @@ export const ActivityApiFactory = function (configuration?: Configuration, baseP
* @throws {RequiredError} * @throws {RequiredError}
*/ */
getActivities(requestParameters: ActivityApiGetActivitiesRequest, options?: AxiosRequestConfig): AxiosPromise<Array<ActivityResponseDto>> { getActivities(requestParameters: ActivityApiGetActivitiesRequest, options?: AxiosRequestConfig): AxiosPromise<Array<ActivityResponseDto>> {
return localVarFp.getActivities(requestParameters.albumId, requestParameters.assetId, requestParameters.type, requestParameters.userId, options).then((request) => request(axios, basePath)); return localVarFp.getActivities(requestParameters.albumId, requestParameters.assetId, requestParameters.type, requestParameters.level, requestParameters.userId, options).then((request) => request(axios, basePath));
}, },
/** /**
* *
@@ -5351,6 +5410,13 @@ export interface ActivityApiGetActivitiesRequest {
*/ */
readonly type?: ReactionType readonly type?: ReactionType
/**
*
* @type {ReactionLevel}
* @memberof ActivityApiGetActivities
*/
readonly level?: ReactionLevel
/** /**
* *
* @type {string} * @type {string}
@@ -5417,7 +5483,7 @@ export class ActivityApi extends BaseAPI {
* @memberof ActivityApi * @memberof ActivityApi
*/ */
public getActivities(requestParameters: ActivityApiGetActivitiesRequest, options?: AxiosRequestConfig) { public getActivities(requestParameters: ActivityApiGetActivitiesRequest, options?: AxiosRequestConfig) {
return ActivityApiFp(this.configuration).getActivities(requestParameters.albumId, requestParameters.assetId, requestParameters.type, requestParameters.userId, options).then((request) => request(this.axios, this.basePath)); return ActivityApiFp(this.configuration).getActivities(requestParameters.albumId, requestParameters.assetId, requestParameters.type, requestParameters.level, requestParameters.userId, options).then((request) => request(this.axios, this.basePath));
} }
/** /**
@@ -7270,11 +7336,12 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
* @param {boolean} [isFavorite] * @param {boolean} [isFavorite]
* @param {boolean} [isTrashed] * @param {boolean} [isTrashed]
* @param {boolean} [withStacked] * @param {boolean} [withStacked]
* @param {boolean} [withPartners]
* @param {string} [key] * @param {string} [key]
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
getTimeBucket: async (size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, withStacked?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { getTimeBucket: async (size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, withStacked?: boolean, withPartners?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'size' is not null or undefined // verify required parameter 'size' is not null or undefined
assertParamExists('getTimeBucket', 'size', size) assertParamExists('getTimeBucket', 'size', size)
// verify required parameter 'timeBucket' is not null or undefined // verify required parameter 'timeBucket' is not null or undefined
@@ -7332,6 +7399,10 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
localVarQueryParameter['withStacked'] = withStacked; localVarQueryParameter['withStacked'] = withStacked;
} }
if (withPartners !== undefined) {
localVarQueryParameter['withPartners'] = withPartners;
}
if (timeBucket !== undefined) { if (timeBucket !== undefined) {
localVarQueryParameter['timeBucket'] = timeBucket; localVarQueryParameter['timeBucket'] = timeBucket;
} }
@@ -7361,11 +7432,12 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
* @param {boolean} [isFavorite] * @param {boolean} [isFavorite]
* @param {boolean} [isTrashed] * @param {boolean} [isTrashed]
* @param {boolean} [withStacked] * @param {boolean} [withStacked]
* @param {boolean} [withPartners]
* @param {string} [key] * @param {string} [key]
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
getTimeBuckets: async (size: TimeBucketSize, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, withStacked?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { getTimeBuckets: async (size: TimeBucketSize, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, withStacked?: boolean, withPartners?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'size' is not null or undefined // verify required parameter 'size' is not null or undefined
assertParamExists('getTimeBuckets', 'size', size) assertParamExists('getTimeBuckets', 'size', size)
const localVarPath = `/asset/time-buckets`; const localVarPath = `/asset/time-buckets`;
@@ -7421,6 +7493,10 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
localVarQueryParameter['withStacked'] = withStacked; localVarQueryParameter['withStacked'] = withStacked;
} }
if (withPartners !== undefined) {
localVarQueryParameter['withPartners'] = withPartners;
}
if (key !== undefined) { if (key !== undefined) {
localVarQueryParameter['key'] = key; localVarQueryParameter['key'] = key;
} }
@@ -8223,12 +8299,13 @@ export const AssetApiFp = function(configuration?: Configuration) {
* @param {boolean} [isFavorite] * @param {boolean} [isFavorite]
* @param {boolean} [isTrashed] * @param {boolean} [isTrashed]
* @param {boolean} [withStacked] * @param {boolean} [withStacked]
* @param {boolean} [withPartners]
* @param {string} [key] * @param {string} [key]
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async getTimeBucket(size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, withStacked?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<AssetResponseDto>>> { async getTimeBucket(size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, withStacked?: boolean, withPartners?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<AssetResponseDto>>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getTimeBucket(size, timeBucket, userId, albumId, personId, isArchived, isFavorite, isTrashed, withStacked, key, options); const localVarAxiosArgs = await localVarAxiosParamCreator.getTimeBucket(size, timeBucket, userId, albumId, personId, isArchived, isFavorite, isTrashed, withStacked, withPartners, key, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
/** /**
@@ -8241,12 +8318,13 @@ export const AssetApiFp = function(configuration?: Configuration) {
* @param {boolean} [isFavorite] * @param {boolean} [isFavorite]
* @param {boolean} [isTrashed] * @param {boolean} [isTrashed]
* @param {boolean} [withStacked] * @param {boolean} [withStacked]
* @param {boolean} [withPartners]
* @param {string} [key] * @param {string} [key]
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async getTimeBuckets(size: TimeBucketSize, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, withStacked?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<TimeBucketResponseDto>>> { async getTimeBuckets(size: TimeBucketSize, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, withStacked?: boolean, withPartners?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<TimeBucketResponseDto>>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getTimeBuckets(size, userId, albumId, personId, isArchived, isFavorite, isTrashed, withStacked, key, options); const localVarAxiosArgs = await localVarAxiosParamCreator.getTimeBuckets(size, userId, albumId, personId, isArchived, isFavorite, isTrashed, withStacked, withPartners, key, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
/** /**
@@ -8543,7 +8621,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
* @throws {RequiredError} * @throws {RequiredError}
*/ */
getTimeBucket(requestParameters: AssetApiGetTimeBucketRequest, options?: AxiosRequestConfig): AxiosPromise<Array<AssetResponseDto>> { getTimeBucket(requestParameters: AssetApiGetTimeBucketRequest, options?: AxiosRequestConfig): AxiosPromise<Array<AssetResponseDto>> {
return localVarFp.getTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.withStacked, requestParameters.key, options).then((request) => request(axios, basePath)); return localVarFp.getTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.withStacked, requestParameters.withPartners, requestParameters.key, options).then((request) => request(axios, basePath));
}, },
/** /**
* *
@@ -8552,7 +8630,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
* @throws {RequiredError} * @throws {RequiredError}
*/ */
getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: AxiosRequestConfig): AxiosPromise<Array<TimeBucketResponseDto>> { getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: AxiosRequestConfig): AxiosPromise<Array<TimeBucketResponseDto>> {
return localVarFp.getTimeBuckets(requestParameters.size, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.withStacked, requestParameters.key, options).then((request) => request(axios, 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. * Get all asset of a device that are in the database, ID only.
@@ -9039,6 +9117,13 @@ export interface AssetApiGetTimeBucketRequest {
*/ */
readonly withStacked?: boolean readonly withStacked?: boolean
/**
*
* @type {boolean}
* @memberof AssetApiGetTimeBucket
*/
readonly withPartners?: boolean
/** /**
* *
* @type {string} * @type {string}
@@ -9109,6 +9194,13 @@ export interface AssetApiGetTimeBucketsRequest {
*/ */
readonly withStacked?: boolean readonly withStacked?: boolean
/**
*
* @type {boolean}
* @memberof AssetApiGetTimeBuckets
*/
readonly withPartners?: boolean
/** /**
* *
* @type {string} * @type {string}
@@ -9588,7 +9680,7 @@ export class AssetApi extends BaseAPI {
* @memberof AssetApi * @memberof AssetApi
*/ */
public getTimeBucket(requestParameters: AssetApiGetTimeBucketRequest, options?: AxiosRequestConfig) { public getTimeBucket(requestParameters: AssetApiGetTimeBucketRequest, options?: AxiosRequestConfig) {
return AssetApiFp(this.configuration).getTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.withStacked, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); return AssetApiFp(this.configuration).getTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.withStacked, requestParameters.withPartners, requestParameters.key, options).then((request) => request(this.axios, this.basePath));
} }
/** /**
@@ -9599,7 +9691,7 @@ export class AssetApi extends BaseAPI {
* @memberof AssetApi * @memberof AssetApi
*/ */
public getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: AxiosRequestConfig) { public getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: AxiosRequestConfig) {
return AssetApiFp(this.configuration).getTimeBuckets(requestParameters.size, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.withStacked, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); return AssetApiFp(this.configuration).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(this.axios, this.basePath));
} }
/** /**
@@ -10509,7 +10601,7 @@ export const AuthenticationApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async signUpAdmin(signUpDto: SignUpDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AdminSignupResponseDto>> { async signUpAdmin(signUpDto: SignUpDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<UserResponseDto>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.signUpAdmin(signUpDto, options); const localVarAxiosArgs = await localVarAxiosParamCreator.signUpAdmin(signUpDto, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
@@ -10589,7 +10681,7 @@ export const AuthenticationApiFactory = function (configuration?: Configuration,
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
signUpAdmin(requestParameters: AuthenticationApiSignUpAdminRequest, options?: AxiosRequestConfig): AxiosPromise<AdminSignupResponseDto> { signUpAdmin(requestParameters: AuthenticationApiSignUpAdminRequest, options?: AxiosRequestConfig): AxiosPromise<UserResponseDto> {
return localVarFp.signUpAdmin(requestParameters.signUpDto, options).then((request) => request(axios, basePath)); return localVarFp.signUpAdmin(requestParameters.signUpDto, options).then((request) => request(axios, basePath));
}, },
/** /**
@@ -12308,6 +12400,54 @@ export const PartnerApiAxiosParamCreator = function (configuration?: Configurati
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @param {string} id
* @param {UpdatePartnerDto} updatePartnerDto
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
updatePartner: async (id: string, updatePartnerDto: UpdatePartnerDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'id' is not null or undefined
assertParamExists('updatePartner', 'id', id)
// verify required parameter 'updatePartnerDto' is not null or undefined
assertParamExists('updatePartner', 'updatePartnerDto', updatePartnerDto)
const localVarPath = `/partner/{id}`
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
// 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: 'PUT', ...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)
localVarHeaderParameter['Content-Type'] = 'application/json';
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
localVarRequestOptions.data = serializeDataIfNeeded(updatePartnerDto, localVarRequestOptions, configuration)
return { return {
url: toPathString(localVarUrlObj), url: toPathString(localVarUrlObj),
options: localVarRequestOptions, options: localVarRequestOptions,
@@ -12329,7 +12469,7 @@ export const PartnerApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async createPartner(id: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<UserResponseDto>> { async createPartner(id: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<PartnerResponseDto>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.createPartner(id, options); const localVarAxiosArgs = await localVarAxiosParamCreator.createPartner(id, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
@@ -12339,7 +12479,7 @@ export const PartnerApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async getPartners(direction: 'shared-by' | 'shared-with', options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<UserResponseDto>>> { async getPartners(direction: 'shared-by' | 'shared-with', options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<PartnerResponseDto>>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getPartners(direction, options); const localVarAxiosArgs = await localVarAxiosParamCreator.getPartners(direction, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
@@ -12353,6 +12493,17 @@ export const PartnerApiFp = function(configuration?: Configuration) {
const localVarAxiosArgs = await localVarAxiosParamCreator.removePartner(id, options); const localVarAxiosArgs = await localVarAxiosParamCreator.removePartner(id, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
/**
*
* @param {string} id
* @param {UpdatePartnerDto} updatePartnerDto
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async updatePartner(id: string, updatePartnerDto: UpdatePartnerDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<PartnerResponseDto>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.updatePartner(id, updatePartnerDto, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
} }
}; };
@@ -12369,7 +12520,7 @@ export const PartnerApiFactory = function (configuration?: Configuration, basePa
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
createPartner(requestParameters: PartnerApiCreatePartnerRequest, options?: AxiosRequestConfig): AxiosPromise<UserResponseDto> { createPartner(requestParameters: PartnerApiCreatePartnerRequest, options?: AxiosRequestConfig): AxiosPromise<PartnerResponseDto> {
return localVarFp.createPartner(requestParameters.id, options).then((request) => request(axios, basePath)); return localVarFp.createPartner(requestParameters.id, options).then((request) => request(axios, basePath));
}, },
/** /**
@@ -12378,7 +12529,7 @@ export const PartnerApiFactory = function (configuration?: Configuration, basePa
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
getPartners(requestParameters: PartnerApiGetPartnersRequest, options?: AxiosRequestConfig): AxiosPromise<Array<UserResponseDto>> { getPartners(requestParameters: PartnerApiGetPartnersRequest, options?: AxiosRequestConfig): AxiosPromise<Array<PartnerResponseDto>> {
return localVarFp.getPartners(requestParameters.direction, options).then((request) => request(axios, basePath)); return localVarFp.getPartners(requestParameters.direction, options).then((request) => request(axios, basePath));
}, },
/** /**
@@ -12390,6 +12541,15 @@ export const PartnerApiFactory = function (configuration?: Configuration, basePa
removePartner(requestParameters: PartnerApiRemovePartnerRequest, options?: AxiosRequestConfig): AxiosPromise<void> { removePartner(requestParameters: PartnerApiRemovePartnerRequest, options?: AxiosRequestConfig): AxiosPromise<void> {
return localVarFp.removePartner(requestParameters.id, options).then((request) => request(axios, basePath)); return localVarFp.removePartner(requestParameters.id, options).then((request) => request(axios, basePath));
}, },
/**
*
* @param {PartnerApiUpdatePartnerRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
updatePartner(requestParameters: PartnerApiUpdatePartnerRequest, options?: AxiosRequestConfig): AxiosPromise<PartnerResponseDto> {
return localVarFp.updatePartner(requestParameters.id, requestParameters.updatePartnerDto, options).then((request) => request(axios, basePath));
},
}; };
}; };
@@ -12435,6 +12595,27 @@ export interface PartnerApiRemovePartnerRequest {
readonly id: string readonly id: string
} }
/**
* Request parameters for updatePartner operation in PartnerApi.
* @export
* @interface PartnerApiUpdatePartnerRequest
*/
export interface PartnerApiUpdatePartnerRequest {
/**
*
* @type {string}
* @memberof PartnerApiUpdatePartner
*/
readonly id: string
/**
*
* @type {UpdatePartnerDto}
* @memberof PartnerApiUpdatePartner
*/
readonly updatePartnerDto: UpdatePartnerDto
}
/** /**
* PartnerApi - object-oriented interface * PartnerApi - object-oriented interface
* @export * @export
@@ -12474,6 +12655,17 @@ export class PartnerApi extends BaseAPI {
public removePartner(requestParameters: PartnerApiRemovePartnerRequest, options?: AxiosRequestConfig) { public removePartner(requestParameters: PartnerApiRemovePartnerRequest, options?: AxiosRequestConfig) {
return PartnerApiFp(this.configuration).removePartner(requestParameters.id, options).then((request) => request(this.axios, this.basePath)); return PartnerApiFp(this.configuration).removePartner(requestParameters.id, options).then((request) => request(this.axios, this.basePath));
} }
/**
*
* @param {PartnerApiUpdatePartnerRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof PartnerApi
*/
public updatePartner(requestParameters: PartnerApiUpdatePartnerRequest, options?: AxiosRequestConfig) {
return PartnerApiFp(this.configuration).updatePartner(requestParameters.id, requestParameters.updatePartnerDto, options).then((request) => request(this.axios, this.basePath));
}
} }
@@ -15100,6 +15292,51 @@ export const SystemConfigApiAxiosParamCreator = function (configuration?: Config
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @param {MapTheme} theme
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getMapStyle: async (theme: MapTheme, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'theme' is not null or undefined
assertParamExists('getMapStyle', 'theme', theme)
const localVarPath = `/system-config/map/style.json`;
// 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)
if (theme !== undefined) {
localVarQueryParameter['theme'] = theme;
}
setSearchParams(localVarUrlObj, localVarQueryParameter); setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
@@ -15219,6 +15456,16 @@ export const SystemConfigApiFp = function(configuration?: Configuration) {
const localVarAxiosArgs = await localVarAxiosParamCreator.getConfigDefaults(options); const localVarAxiosArgs = await localVarAxiosParamCreator.getConfigDefaults(options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
/**
*
* @param {MapTheme} theme
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async getMapStyle(theme: MapTheme, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<object>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getMapStyle(theme, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/** /**
* *
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
@@ -15264,6 +15511,15 @@ export const SystemConfigApiFactory = function (configuration?: Configuration, b
getConfigDefaults(options?: AxiosRequestConfig): AxiosPromise<SystemConfigDto> { getConfigDefaults(options?: AxiosRequestConfig): AxiosPromise<SystemConfigDto> {
return localVarFp.getConfigDefaults(options).then((request) => request(axios, basePath)); return localVarFp.getConfigDefaults(options).then((request) => request(axios, basePath));
}, },
/**
*
* @param {SystemConfigApiGetMapStyleRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getMapStyle(requestParameters: SystemConfigApiGetMapStyleRequest, options?: AxiosRequestConfig): AxiosPromise<object> {
return localVarFp.getMapStyle(requestParameters.theme, options).then((request) => request(axios, basePath));
},
/** /**
* *
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
@@ -15284,6 +15540,20 @@ export const SystemConfigApiFactory = function (configuration?: Configuration, b
}; };
}; };
/**
* Request parameters for getMapStyle operation in SystemConfigApi.
* @export
* @interface SystemConfigApiGetMapStyleRequest
*/
export interface SystemConfigApiGetMapStyleRequest {
/**
*
* @type {MapTheme}
* @memberof SystemConfigApiGetMapStyle
*/
readonly theme: MapTheme
}
/** /**
* Request parameters for updateConfig operation in SystemConfigApi. * Request parameters for updateConfig operation in SystemConfigApi.
* @export * @export
@@ -15325,6 +15595,17 @@ export class SystemConfigApi extends BaseAPI {
return SystemConfigApiFp(this.configuration).getConfigDefaults(options).then((request) => request(this.axios, this.basePath)); return SystemConfigApiFp(this.configuration).getConfigDefaults(options).then((request) => request(this.axios, this.basePath));
} }
/**
*
* @param {SystemConfigApiGetMapStyleRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof SystemConfigApi
*/
public getMapStyle(requestParameters: SystemConfigApiGetMapStyleRequest, options?: AxiosRequestConfig) {
return SystemConfigApiFp(this.configuration).getMapStyle(requestParameters.theme, options).then((request) => request(this.axios, this.basePath));
}
/** /**
* *
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.

View File

@@ -4,6 +4,8 @@
version: "3.8" version: "3.8"
name: immich-dev
services: services:
immich-server: immich-server:
container_name: immich_server container_name: immich_server
@@ -121,11 +123,11 @@ services:
redis: redis:
container_name: immich_redis container_name: immich_redis
image: redis:6.2-alpine@sha256:70a7a5b641117670beae0d80658430853896b5ef269ccf00d1827427e3263fa3 image: redis:6.2-alpine@sha256:3995fe6ea6a619313e31046bd3c8643f9e70f8f2b294ff82659d409b47d06abb
database: database:
container_name: immich_postgres container_name: immich_postgres
image: postgres:14-alpine@sha256:28407a9961e76f2d285dc6991e8e48893503cc3836a4755bbc2d40bcc272a441 image: postgres:14-alpine@sha256:874f566dd512d79cf74f59754833e869ae76ece96716d153b0fa3e64aec88d92
env_file: env_file:
- .env - .env
environment: environment:

View File

@@ -1,5 +1,7 @@
version: "3.8" version: "3.8"
name: immich-prod
services: services:
immich-server: immich-server:
container_name: immich_server container_name: immich_server
@@ -77,12 +79,12 @@ services:
redis: redis:
container_name: immich_redis container_name: immich_redis
image: redis:6.2-alpine@sha256:70a7a5b641117670beae0d80658430853896b5ef269ccf00d1827427e3263fa3 image: redis:6.2-alpine@sha256:3995fe6ea6a619313e31046bd3c8643f9e70f8f2b294ff82659d409b47d06abb
restart: always restart: always
database: database:
container_name: immich_postgres container_name: immich_postgres
image: postgres:14-alpine@sha256:28407a9961e76f2d285dc6991e8e48893503cc3836a4755bbc2d40bcc272a441 image: postgres:14-alpine@sha256:874f566dd512d79cf74f59754833e869ae76ece96716d153b0fa3e64aec88d92
env_file: env_file:
- .env - .env
environment: environment:

View File

@@ -23,7 +23,7 @@ services:
- database - database
database: database:
image: postgres:14-alpine@sha256:28407a9961e76f2d285dc6991e8e48893503cc3836a4755bbc2d40bcc272a441 image: postgres:14-alpine@sha256:874f566dd512d79cf74f59754833e869ae76ece96716d153b0fa3e64aec88d92
command: -c fsync=off command: -c fsync=off
environment: environment:
POSTGRES_PASSWORD: postgres POSTGRES_PASSWORD: postgres

View File

@@ -1,10 +1,12 @@
version: "3.8" version: "3.8"
name: immich
services: services:
immich-server: immich-server:
container_name: immich_server container_name: immich_server
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release} image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
command: ["start.sh", "immich"] command: [ "start.sh", "immich" ]
volumes: volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload - ${UPLOAD_LOCATION}:/usr/src/app/upload
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
@@ -22,7 +24,7 @@ services:
# extends: # extends:
# file: hwaccel.yml # file: hwaccel.yml
# service: hwaccel # service: hwaccel
command: ["start.sh", "microservices"] command: [ "start.sh", "microservices" ]
volumes: volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload - ${UPLOAD_LOCATION}:/usr/src/app/upload
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
@@ -64,12 +66,12 @@ services:
redis: redis:
container_name: immich_redis container_name: immich_redis
image: redis:6.2-alpine@sha256:70a7a5b641117670beae0d80658430853896b5ef269ccf00d1827427e3263fa3 image: redis:6.2-alpine@sha256:3995fe6ea6a619313e31046bd3c8643f9e70f8f2b294ff82659d409b47d06abb
restart: always restart: always
database: database:
container_name: immich_postgres container_name: immich_postgres
image: postgres:14-alpine@sha256:28407a9961e76f2d285dc6991e8e48893503cc3836a4755bbc2d40bcc272a441 image: postgres:14-alpine@sha256:874f566dd512d79cf74f59754833e869ae76ece96716d153b0fa3e64aec88d92
env_file: env_file:
- .env - .env
environment: environment:

View File

@@ -51,8 +51,7 @@ immich-admin list-users
{ {
id: 'e65e6f88-2a30-4dbe-8dd9-1885f4889b53', id: 'e65e6f88-2a30-4dbe-8dd9-1885f4889b53',
email: 'immich@example.com.com', email: 'immich@example.com.com',
firstName: 'Immich', name: 'Immich Admin',
lastName: 'Admin',
storageLabel: 'admin', storageLabel: 'admin',
externalPath: null, externalPath: null,
profileImagePath: 'upload/profile/e65e6f88-2a30-4dbe-8dd9-1885f4889b53/e65e6f88-2a30-4dbe-8dd9-1885f4889b53.jpg', profileImagePath: 'upload/profile/e65e6f88-2a30-4dbe-8dd9-1885f4889b53/e65e6f88-2a30-4dbe-8dd9-1885f4889b53.jpg',

View File

@@ -9,6 +9,6 @@ npm run typeorm:migrations:generate ./src/infra/<migration-name>
``` ```
2. Check if the migration file makes sense. 2. Check if the migration file makes sense.
3. Move the migration file to folder `./src/infra/database/migrations` in your code editor. 3. Move the migration file to folder `./server/src/infra/migrations` in your code editor.
The server will automatically detect `*.ts` file changes and restart. Part of the server start-up process includes running any new migrations, so it will be applied immediately. The server will automatically detect `*.ts` file changes and restart. Part of the server start-up process includes running any new migrations, so it will be applied immediately.

View File

@@ -75,7 +75,7 @@ Some basic examples:
- `*.tif` will exclude all files with the extension `.tif` - `*.tif` will exclude all files with the extension `.tif`
- `hidden.jpg` will exclude all files named `hidden.jpg` - `hidden.jpg` will exclude all files named `hidden.jpg`
- `**/Raw/**` will exclude all files in any directory named `Raw` - `**/Raw/**` will exclude all files in any directory named `Raw`
- `*.(tif,jpg)` will exclude all files with the extension `.tif` or `.jpg` - `*.{tif,jpg}` will exclude all files with the extension `.tif` or `.jpg`
### Nightly job ### Nightly job

20
docs/package-lock.json generated
View File

@@ -13232,19 +13232,19 @@
} }
}, },
"node_modules/tailwindcss": { "node_modules/tailwindcss": {
"version": "3.3.3", "version": "3.3.5",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.3.tgz", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.5.tgz",
"integrity": "sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==", "integrity": "sha512-5SEZU4J7pxZgSkv7FP1zY8i2TIAOooNZ1e/OGtxIEv6GltpoiXUqWvLy89+a10qYTB1N5Ifkuw9lqQkN9sscvA==",
"dependencies": { "dependencies": {
"@alloc/quick-lru": "^5.2.0", "@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2", "arg": "^5.0.2",
"chokidar": "^3.5.3", "chokidar": "^3.5.3",
"didyoumean": "^1.2.2", "didyoumean": "^1.2.2",
"dlv": "^1.1.3", "dlv": "^1.1.3",
"fast-glob": "^3.2.12", "fast-glob": "^3.3.0",
"glob-parent": "^6.0.2", "glob-parent": "^6.0.2",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
"jiti": "^1.18.2", "jiti": "^1.19.1",
"lilconfig": "^2.1.0", "lilconfig": "^2.1.0",
"micromatch": "^4.0.5", "micromatch": "^4.0.5",
"normalize-path": "^3.0.0", "normalize-path": "^3.0.0",
@@ -24517,19 +24517,19 @@
} }
}, },
"tailwindcss": { "tailwindcss": {
"version": "3.3.3", "version": "3.3.5",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.3.tgz", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.5.tgz",
"integrity": "sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==", "integrity": "sha512-5SEZU4J7pxZgSkv7FP1zY8i2TIAOooNZ1e/OGtxIEv6GltpoiXUqWvLy89+a10qYTB1N5Ifkuw9lqQkN9sscvA==",
"requires": { "requires": {
"@alloc/quick-lru": "^5.2.0", "@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2", "arg": "^5.0.2",
"chokidar": "^3.5.3", "chokidar": "^3.5.3",
"didyoumean": "^1.2.2", "didyoumean": "^1.2.2",
"dlv": "^1.1.3", "dlv": "^1.1.3",
"fast-glob": "^3.2.12", "fast-glob": "^3.3.0",
"glob-parent": "^6.0.2", "glob-parent": "^6.0.2",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
"jiti": "^1.18.2", "jiti": "^1.19.1",
"lilconfig": "^2.1.0", "lilconfig": "^2.1.0",
"micromatch": "^4.0.5", "micromatch": "^4.0.5",
"normalize-path": "^3.0.0", "normalize-path": "^3.0.0",

View File

@@ -38,8 +38,16 @@ class LogSettings(BaseSettings):
_clean_name = str.maketrans(":\\/", "___", ".") _clean_name = str.maketrans(":\\/", "___", ".")
def clean_name(model_name: str) -> str:
return model_name.split("/")[-1].translate(_clean_name)
def get_cache_dir(model_name: str, model_type: ModelType) -> Path: def get_cache_dir(model_name: str, model_type: ModelType) -> Path:
return Path(settings.cache_folder) / model_type.value / model_name.translate(_clean_name) return Path(settings.cache_folder) / model_type.value / clean_name(model_name)
def get_hf_model_name(model_name: str) -> str:
return f"immich-app/{clean_name(model_name)}"
LOG_LEVELS: dict[str, int] = { LOG_LEVELS: dict[str, int] = {

View File

@@ -3,7 +3,8 @@ from typing import Any
from app.schemas import ModelType from app.schemas import ModelType
from .base import InferenceModel from .base import InferenceModel
from .clip import MCLIPEncoder, OpenCLIPEncoder, is_mclip, is_openclip from .clip import MCLIPEncoder, OpenCLIPEncoder
from .constants import is_insightface, is_mclip, is_openclip
from .facial_recognition import FaceRecognizer from .facial_recognition import FaceRecognizer
from .image_classification import ImageClassifier from .image_classification import ImageClassifier
@@ -15,11 +16,12 @@ def from_model_type(model_type: ModelType, model_name: str, **model_kwargs: Any)
return OpenCLIPEncoder(model_name, **model_kwargs) return OpenCLIPEncoder(model_name, **model_kwargs)
elif is_mclip(model_name): elif is_mclip(model_name):
return MCLIPEncoder(model_name, **model_kwargs) return MCLIPEncoder(model_name, **model_kwargs)
else:
raise ValueError(f"Unknown CLIP model {model_name}")
case ModelType.FACIAL_RECOGNITION: case ModelType.FACIAL_RECOGNITION:
if is_insightface(model_name):
return FaceRecognizer(model_name, **model_kwargs) return FaceRecognizer(model_name, **model_kwargs)
case ModelType.IMAGE_CLASSIFICATION: case ModelType.IMAGE_CLASSIFICATION:
return ImageClassifier(model_name, **model_kwargs) return ImageClassifier(model_name, **model_kwargs)
case _: case _:
raise ValueError(f"Unknown model type {model_type}") raise ValueError(f"Unknown model type {model_type}")
raise ValueError(f"Unknown ${model_type} model {model_name}")

View File

@@ -7,8 +7,9 @@ from shutil import rmtree
from typing import Any from typing import Any
import onnxruntime as ort import onnxruntime as ort
from huggingface_hub import snapshot_download
from ..config import get_cache_dir, log, settings from ..config import get_cache_dir, get_hf_model_name, log, settings
from ..schemas import ModelType from ..schemas import ModelType
@@ -78,9 +79,13 @@ class InferenceModel(ABC):
def configure(self, **model_kwargs: Any) -> None: def configure(self, **model_kwargs: Any) -> None:
pass pass
@abstractmethod
def _download(self) -> None: def _download(self) -> None:
... snapshot_download(
get_hf_model_name(self.model_name),
cache_dir=self.cache_dir,
local_dir=self.cache_dir,
local_dir_use_symlinks=False,
)
@abstractmethod @abstractmethod
def _load(self) -> None: def _load(self) -> None:

View File

@@ -7,11 +7,10 @@ from typing import Any, Literal
import numpy as np import numpy as np
import onnxruntime as ort import onnxruntime as ort
from huggingface_hub import snapshot_download
from PIL import Image from PIL import Image
from transformers import AutoTokenizer from transformers import AutoTokenizer
from app.config import log from app.config import clean_name, log
from app.models.transforms import crop, get_pil_resampling, normalize, resize, to_numpy from app.models.transforms import crop, get_pil_resampling, normalize, resize, to_numpy
from app.schemas import ModelType, ndarray_f32, ndarray_i32, ndarray_i64 from app.schemas import ModelType, ndarray_f32, ndarray_i32, ndarray_i64
@@ -117,15 +116,7 @@ class OpenCLIPEncoder(BaseCLIPEncoder):
mode: Literal["text", "vision"] | None = None, mode: Literal["text", "vision"] | None = None,
**model_kwargs: Any, **model_kwargs: Any,
) -> None: ) -> None:
super().__init__(_clean_model_name(model_name), cache_dir, mode, **model_kwargs) super().__init__(clean_name(model_name), cache_dir, mode, **model_kwargs)
def _download(self) -> None:
snapshot_download(
f"immich-app/{self.model_name}",
cache_dir=self.cache_dir,
local_dir=self.cache_dir,
local_dir_use_symlinks=False,
)
def _load(self) -> None: def _load(self) -> None:
super()._load() super()._load()
@@ -171,52 +162,3 @@ class MCLIPEncoder(OpenCLIPEncoder):
def tokenize(self, text: str) -> dict[str, ndarray_i32]: def tokenize(self, text: str) -> dict[str, ndarray_i32]:
tokens: dict[str, ndarray_i64] = self.tokenizer(text, return_tensors="np") tokens: dict[str, ndarray_i64] = self.tokenizer(text, return_tensors="np")
return {k: v.astype(np.int32) for k, v in tokens.items()} return {k: v.astype(np.int32) for k, v in tokens.items()}
_OPENCLIP_MODELS = {
"RN50__openai",
"RN50__yfcc15m",
"RN50__cc12m",
"RN101__openai",
"RN101__yfcc15m",
"RN50x4__openai",
"RN50x16__openai",
"RN50x64__openai",
"ViT-B-32__openai",
"ViT-B-32__laion2b_e16",
"ViT-B-32__laion400m_e31",
"ViT-B-32__laion400m_e32",
"ViT-B-32__laion2b-s34b-b79k",
"ViT-B-16__openai",
"ViT-B-16__laion400m_e31",
"ViT-B-16__laion400m_e32",
"ViT-B-16-plus-240__laion400m_e31",
"ViT-B-16-plus-240__laion400m_e32",
"ViT-L-14__openai",
"ViT-L-14__laion400m_e31",
"ViT-L-14__laion400m_e32",
"ViT-L-14__laion2b-s32b-b82k",
"ViT-L-14-336__openai",
"ViT-H-14__laion2b-s32b-b79k",
"ViT-g-14__laion2b-s12b-b42k",
}
_MCLIP_MODELS = {
"LABSE-Vit-L-14",
"XLM-Roberta-Large-Vit-B-32",
"XLM-Roberta-Large-Vit-B-16Plus",
"XLM-Roberta-Large-Vit-L-14",
}
def _clean_model_name(model_name: str) -> str:
return model_name.split("/")[-1].replace("::", "__")
def is_openclip(model_name: str) -> bool:
return _clean_model_name(model_name) in _OPENCLIP_MODELS
def is_mclip(model_name: str) -> bool:
return _clean_model_name(model_name) in _MCLIP_MODELS

View File

@@ -0,0 +1,57 @@
from app.config import clean_name
_OPENCLIP_MODELS = {
"RN50__openai",
"RN50__yfcc15m",
"RN50__cc12m",
"RN101__openai",
"RN101__yfcc15m",
"RN50x4__openai",
"RN50x16__openai",
"RN50x64__openai",
"ViT-B-32__openai",
"ViT-B-32__laion2b_e16",
"ViT-B-32__laion400m_e31",
"ViT-B-32__laion400m_e32",
"ViT-B-32__laion2b-s34b-b79k",
"ViT-B-16__openai",
"ViT-B-16__laion400m_e31",
"ViT-B-16__laion400m_e32",
"ViT-B-16-plus-240__laion400m_e31",
"ViT-B-16-plus-240__laion400m_e32",
"ViT-L-14__openai",
"ViT-L-14__laion400m_e31",
"ViT-L-14__laion400m_e32",
"ViT-L-14__laion2b-s32b-b82k",
"ViT-L-14-336__openai",
"ViT-H-14__laion2b-s32b-b79k",
"ViT-g-14__laion2b-s12b-b42k",
}
_MCLIP_MODELS = {
"LABSE-Vit-L-14",
"XLM-Roberta-Large-Vit-B-32",
"XLM-Roberta-Large-Vit-B-16Plus",
"XLM-Roberta-Large-Vit-L-14",
}
_INSIGHTFACE_MODELS = {
"antelopev2",
"buffalo_l",
"buffalo_m",
"buffalo_s",
}
def is_openclip(model_name: str) -> bool:
return clean_name(model_name) in _OPENCLIP_MODELS
def is_mclip(model_name: str) -> bool:
return clean_name(model_name) in _MCLIP_MODELS
def is_insightface(model_name: str) -> bool:
return clean_name(model_name) in _INSIGHTFACE_MODELS

View File

@@ -1,4 +1,3 @@
import zipfile
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
@@ -7,8 +6,8 @@ import numpy as np
import onnxruntime as ort import onnxruntime as ort
from insightface.model_zoo import ArcFaceONNX, RetinaFace from insightface.model_zoo import ArcFaceONNX, RetinaFace
from insightface.utils.face_align import norm_crop from insightface.utils.face_align import norm_crop
from insightface.utils.storage import BASE_REPO_URL, download_file
from app.config import clean_name
from app.schemas import ModelType, ndarray_f32 from app.schemas import ModelType, ndarray_f32
from .base import InferenceModel from .base import InferenceModel
@@ -25,37 +24,21 @@ class FaceRecognizer(InferenceModel):
**model_kwargs: Any, **model_kwargs: Any,
) -> None: ) -> None:
self.min_score = model_kwargs.pop("minScore", min_score) self.min_score = model_kwargs.pop("minScore", min_score)
super().__init__(model_name, cache_dir, **model_kwargs) super().__init__(clean_name(model_name), cache_dir, **model_kwargs)
def _download(self) -> None:
zip_file = self.cache_dir / f"{self.model_name}.zip"
download_file(f"{BASE_REPO_URL}/{self.model_name}.zip", zip_file)
with zipfile.ZipFile(zip_file, "r") as zip:
members = zip.namelist()
det_file = next(model for model in members if model.startswith("det_"))
rec_file = next(model for model in members if model.startswith("w600k_"))
zip.extractall(self.cache_dir, members=[det_file, rec_file])
zip_file.unlink()
def _load(self) -> None: def _load(self) -> None:
try:
det_file = next(self.cache_dir.glob("det_*.onnx"))
rec_file = next(self.cache_dir.glob("w600k_*.onnx"))
except StopIteration:
raise FileNotFoundError("Facial recognition models not found in cache directory")
self.det_model = RetinaFace( self.det_model = RetinaFace(
session=ort.InferenceSession( session=ort.InferenceSession(
det_file.as_posix(), self.det_file.as_posix(),
sess_options=self.sess_options, sess_options=self.sess_options,
providers=self.providers, providers=self.providers,
provider_options=self.provider_options, provider_options=self.provider_options,
), ),
) )
self.rec_model = ArcFaceONNX( self.rec_model = ArcFaceONNX(
rec_file.as_posix(), self.rec_file.as_posix(),
session=ort.InferenceSession( session=ort.InferenceSession(
rec_file.as_posix(), self.rec_file.as_posix(),
sess_options=self.sess_options, sess_options=self.sess_options,
providers=self.providers, providers=self.providers,
provider_options=self.provider_options, provider_options=self.provider_options,
@@ -103,7 +86,15 @@ class FaceRecognizer(InferenceModel):
@property @property
def cached(self) -> bool: def cached(self) -> bool:
return self.cache_dir.is_dir() and any(self.cache_dir.glob("*.onnx")) return self.det_file.is_file() and self.rec_file.is_file()
@property
def det_file(self) -> Path:
return self.cache_dir / "detection" / "model.onnx"
@property
def rec_file(self) -> Path:
return self.cache_dir / "recognition" / "model.onnx"
def configure(self, **model_kwargs: Any) -> None: def configure(self, **model_kwargs: Any) -> None:
self.det_model.det_thresh = model_kwargs.pop("minScore", self.det_model.det_thresh) self.det_model.det_thresh = model_kwargs.pop("minScore", self.det_model.det_thresh)

View File

@@ -106,13 +106,13 @@ class TestCLIP:
class TestFaceRecognition: class TestFaceRecognition:
def test_set_min_score(self, mocker: MockerFixture) -> None: def test_set_min_score(self, mocker: MockerFixture) -> None:
mocker.patch.object(FaceRecognizer, "load") mocker.patch.object(FaceRecognizer, "load")
face_recognizer = FaceRecognizer("test_model_name", cache_dir="test_cache", min_score=0.5) face_recognizer = FaceRecognizer("buffalo_s", cache_dir="test_cache", min_score=0.5)
assert face_recognizer.min_score == 0.5 assert face_recognizer.min_score == 0.5
def test_basic(self, cv_image: cv2.Mat, mocker: MockerFixture) -> None: def test_basic(self, cv_image: cv2.Mat, mocker: MockerFixture) -> None:
mocker.patch.object(FaceRecognizer, "load") mocker.patch.object(FaceRecognizer, "load")
face_recognizer = FaceRecognizer("test_model_name", min_score=0.0, cache_dir="test_cache") face_recognizer = FaceRecognizer("buffalo_s", min_score=0.0, cache_dir="test_cache")
det_model = mock.Mock() det_model = mock.Mock()
num_faces = 2 num_faces = 2

View File

@@ -14,8 +14,7 @@ RUN micromamba install -y -n base -f /tmp/conda-lock.yml && \
WORKDIR /usr/src/app WORKDIR /usr/src/app
COPY --chown=$MAMBA_USER:$MAMBA_USER start.sh . COPY --chown=$MAMBA_USER:$MAMBA_USER export .
COPY --chown=$MAMBA_USER:$MAMBA_USER app .
ENTRYPOINT ["/usr/local/bin/_entrypoint.sh"] ENTRYPOINT ["/usr/local/bin/_entrypoint.sh"]
CMD ["./start.sh"] CMD ["python -m run"]

View File

View File

@@ -0,0 +1,9 @@
from export.models.openclip import OpenCLIPModelConfig
MCLIP_TO_OPENCLIP = {
"XLM-Roberta-Large-Vit-B-32": OpenCLIPModelConfig("ViT-B-32", "openai"),
"XLM-Roberta-Large-Vit-B-16Plus": OpenCLIPModelConfig("ViT-B-16-plus-240", "laion400m_e32"),
"LABSE-Vit-L-14": OpenCLIPModelConfig("ViT-L-14", "openai"),
"XLM-Roberta-Large-Vit-L-14": OpenCLIPModelConfig("ViT-L-14", "openai"),
}

View File

@@ -1,22 +1,15 @@
import tempfile import tempfile
import warnings import warnings
from pathlib import Path from pathlib import Path
from export.models.constants import MCLIP_TO_OPENCLIP
import torch import torch
from multilingual_clip.pt_multilingual_clip import MultilingualCLIP from multilingual_clip.pt_multilingual_clip import MultilingualCLIP
from transformers import AutoTokenizer from transformers import AutoTokenizer
from .openclip import OpenCLIPModelConfig
from .openclip import to_onnx as openclip_to_onnx from .openclip import to_onnx as openclip_to_onnx
from .optimize import optimize from .optimize import optimize
from .util import get_model_path from .util import get_model_path, clean_name
_MCLIP_TO_OPENCLIP = {
"M-CLIP/XLM-Roberta-Large-Vit-B-32": OpenCLIPModelConfig("ViT-B-32", "openai"),
"M-CLIP/XLM-Roberta-Large-Vit-B-16Plus": OpenCLIPModelConfig("ViT-B-16-plus-240", "laion400m_e32"),
"M-CLIP/LABSE-Vit-L-14": OpenCLIPModelConfig("ViT-L-14", "openai"),
"M-CLIP/XLM-Roberta-Large-Vit-L-14": OpenCLIPModelConfig("ViT-L-14", "openai"),
}
def to_onnx( def to_onnx(
@@ -33,7 +26,7 @@ def to_onnx(
param.requires_grad_(False) param.requires_grad_(False)
export_text_encoder(model, textual_path) export_text_encoder(model, textual_path)
openclip_to_onnx(_MCLIP_TO_OPENCLIP[model_name], output_dir_visual) openclip_to_onnx(MCLIP_TO_OPENCLIP[clean_name(model_name)], output_dir_visual)
optimize(textual_path) optimize(textual_path)

View File

@@ -3,6 +3,9 @@ from pathlib import Path
from typing import Any from typing import Any
_clean_name = str.maketrans(":\\/", "___", ".")
def get_model_path(output_dir: Path | str) -> Path: def get_model_path(output_dir: Path | str) -> Path:
output_dir = Path(output_dir) output_dir = Path(output_dir)
output_dir.mkdir(parents=True, exist_ok=True) output_dir.mkdir(parents=True, exist_ok=True)
@@ -13,3 +16,7 @@ def save_config(config: Any, output_path: Path | str) -> None:
output_path = Path(output_path) output_path = Path(output_path)
output_path.parent.mkdir(parents=True, exist_ok=True) output_path.parent.mkdir(parents=True, exist_ok=True)
json.dump(config, output_path.open("w")) json.dump(config, output_path.open("w"))
def clean_name(model_name: str) -> str:
return model_name.split("/")[-1].translate(_clean_name)

View File

@@ -1,76 +1,131 @@
from enum import StrEnum
import gc import gc
import os import os
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from typing import Optional
from huggingface_hub import create_repo, login, upload_folder from huggingface_hub import create_repo, upload_folder
from models import mclip, openclip from export.models import mclip, openclip, insightface
from export.models.util import clean_name
from rich.progress import Progress from rich.progress import Progress
import typer
models = [
"RN50::openai",
"RN50::yfcc15m",
"RN50::cc12m",
"RN101::openai",
"RN101::yfcc15m",
"RN50x4::openai",
"RN50x16::openai",
"RN50x64::openai",
"ViT-B-32::openai",
"ViT-B-32::laion2b_e16",
"ViT-B-32::laion400m_e31",
"ViT-B-32::laion400m_e32",
"ViT-B-32::laion2b-s34b-b79k",
"ViT-B-16::openai",
"ViT-B-16::laion400m_e31",
"ViT-B-16::laion400m_e32",
"ViT-B-16-plus-240::laion400m_e31",
"ViT-B-16-plus-240::laion400m_e32",
"ViT-L-14::openai",
"ViT-L-14::laion400m_e31",
"ViT-L-14::laion400m_e32",
"ViT-L-14::laion2b-s32b-b82k",
"ViT-L-14-336::openai",
"ViT-H-14::laion2b-s32b-b79k",
"ViT-g-14::laion2b-s12b-b42k",
"M-CLIP/LABSE-Vit-L-14",
"M-CLIP/XLM-Roberta-Large-Vit-B-32",
"M-CLIP/XLM-Roberta-Large-Vit-B-16Plus",
"M-CLIP/XLM-Roberta-Large-Vit-L-14",
]
login(token=os.environ["HF_AUTH_TOKEN"]) app = typer.Typer()
with Progress() as progress:
task1 = progress.add_task("[green]Exporting models...", total=len(models))
task2 = progress.add_task("[yellow]Uploading models...", total=len(models))
with TemporaryDirectory() as tmp: class ModelLibrary(StrEnum):
tmpdir = Path(tmp) MCLIP = "mclip"
for model in models: OPENCLIP = "openclip"
model_name = model.split("/")[-1].replace("::", "__") INSIGHTFACE = "insightface"
config_path = tmpdir / model_name / "config.json"
def upload() -> None:
progress.update(task2, description=f"[yellow]Uploading {model_name}")
repo_id = f"immich-app/{model_name}"
create_repo(repo_id, exist_ok=True) def _export(model_name: str, library: ModelLibrary, export_dir: Path) -> None:
upload_folder(repo_id=repo_id, folder_path=tmpdir / model_name) visual_dir = export_dir / "visual"
progress.update(task2, advance=1) textual_dir = export_dir / "textual"
match library:
def export() -> None: case ModelLibrary.MCLIP:
progress.update(task1, description=f"[green]Exporting {model_name}") insightface.to_onnx(model_name, visual_dir, textual_dir)
visual_dir = tmpdir / model_name / "visual" case ModelLibrary.OPENCLIP:
textual_dir = tmpdir / model_name / "textual" mclip.to_onnx(model_name, visual_dir, textual_dir)
if model.startswith("M-CLIP"): case ModelLibrary.INSIGHTFACE:
mclip.to_onnx(model, visual_dir, textual_dir)
else:
name, _, pretrained = model_name.partition("__") name, _, pretrained = model_name.partition("__")
openclip.to_onnx(openclip.OpenCLIPModelConfig(name, pretrained), visual_dir, textual_dir) openclip.to_onnx(openclip.OpenCLIPModelConfig(name, pretrained), visual_dir, textual_dir)
progress.update(task1, advance=1)
gc.collect() gc.collect()
export()
upload() def _upload(repo_id: str, upload_dir: Path, auth_token: str | None = os.environ.get("HF_AUTH_TOKEN", None)) -> None:
create_repo(repo_id, exist_ok=True, token=auth_token)
upload_folder(repo_id=repo_id, folder_path=upload_dir, token=auth_token)
@app.command()
def export(
models: list[str] = typer.Argument(
..., help="The model(s) to be exported. Model names should be the same as used in the associated library."
),
library: ModelLibrary = typer.Option(
..., "--library", "-l", help="The library associated with the models to be exported."
),
output_dir: Optional[Path] = typer.Option(
None,
"--output-dir",
"-o",
help="Directory where exported models will be stored. Defaults to a temporary directory.",
),
should_upload: bool = typer.Option(False, "--upload", "-u", help="Whether to upload the exported models."),
auth_token: Optional[str] = typer.Option(
os.environ.get("HF_AUTH_TOKEN", None),
"--auth_token",
"-t",
help="If uploading models to Hugging Face, the auth token of the user or organisation.",
),
repo_prefix: str = typer.Option(
"immich-app",
"--repo_prefix",
"-p",
help="If uploading models to Hugging Face, the prefix to put before the model name. Can be a username or organisation.",
),
) -> None:
if not models:
raise ValueError("No models specified")
with Progress() as progress:
task1 = progress.add_task("[green]Exporting model(s)...", total=len(models))
with TemporaryDirectory() as tmp:
output_dir = output_dir if output_dir else Path(tmp)
for model_name in models:
cleaned_name = clean_name(model_name)
model_dir = output_dir / cleaned_name
progress.update(task1, description=f"[green]Exporting {cleaned_name}")
_export(model_name, library, model_dir)
progress.update(task1, advance=1, description=f"[green]Exported {cleaned_name}")
if should_upload:
upload(models, output_dir, auth_token, repo_prefix)
@app.command()
def upload(
models: list[str] = typer.Argument(
..., help="The model(s) to be uploaded. Model names should be the same as used in the associated library."
),
output_dir: Optional[Path] = typer.Option(
None,
"--output-dir",
"-o",
help="Directory where exported models will be stored. Defaults to a temporary directory.",
),
auth_token: Optional[str] = typer.Option(
os.environ.get("HF_AUTH_TOKEN", None),
"--auth_token",
"-t",
help="The Hugging Face auth token of the user or organisation.",
),
repo_prefix: str = typer.Option(
"immich-app",
"--repo_prefix",
"-p",
help="The name to put before the model name to form the Hugging Face repo name. Can be a username or organisation.",
),
) -> None:
if not models:
raise ValueError("No models specified")
with Progress() as progress:
task2 = progress.add_task("[yellow]Uploading models...", total=len(models))
for model_name in models:
cleaned_name = clean_name(model_name)
repo_id = f"{repo_prefix}/{cleaned_name}"
model_dir = output_dir / cleaned_name
progress.update(task2, description=f"[yellow]Uploading {cleaned_name}")
_upload(repo_id, model_dir, auth_token)
progress.update(task2, advance=1, description=f"[yellow]Uploaded {cleaned_name}")
if __name__ == "__main__":
app()

View File

@@ -5,17 +5,17 @@
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000625"> <testcase classname="fastlane.lanes" name="0: default_platform" time="0.000244">
</testcase> </testcase>
<testcase classname="fastlane.lanes" name="1: bundleRelease" time="70.943413"> <testcase classname="fastlane.lanes" name="1: bundleRelease" time="67.0562">
</testcase> </testcase>
<testcase classname="fastlane.lanes" name="2: upload_to_play_store" time="30.374484"> <testcase classname="fastlane.lanes" name="2: upload_to_play_store" time="33.087498">
</testcase> </testcase>

View File

@@ -114,7 +114,7 @@
"cache_settings_thumbnail_size": "Thumbnail cache size ({} assets)", "cache_settings_thumbnail_size": "Thumbnail cache size ({} assets)",
"cache_settings_title": "Caching Settings", "cache_settings_title": "Caching Settings",
"change_password_form_confirm_password": "Confirm Password", "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_new_password": "New Password",
"change_password_form_password_mismatch": "Passwords do not match", "change_password_form_password_mismatch": "Passwords do not match",
"change_password_form_reenter_new_password": "Re-enter New Password", "change_password_form_reenter_new_password": "Re-enter New Password",
@@ -316,6 +316,7 @@
"shared_link_edit_description": "Description", "shared_link_edit_description": "Description",
"shared_link_edit_description_hint": "Enter the share description", "shared_link_edit_description_hint": "Enter the share description",
"shared_link_edit_password": "Password", "shared_link_edit_password": "Password",
"shared_link_edit_expire_after": "Expire after",
"shared_link_edit_password_hint": "Enter the share password", "shared_link_edit_password_hint": "Enter the share password",
"shared_link_edit_show_meta": "Show metadata", "shared_link_edit_show_meta": "Show metadata",
"shared_link_edit_submit_button": "Update link", "shared_link_edit_submit_button": "Update link",

View File

@@ -169,4 +169,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 599d8aeb73728400c15364e734525722250a5382 PODFILE CHECKSUM: 599d8aeb73728400c15364e734525722250a5382
COCOAPODS: 1.11.3 COCOAPODS: 1.12.1

View File

@@ -379,7 +379,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 124; CURRENT_PROJECT_VERSION = 125;
DEVELOPMENT_TEAM = 2F67MQ8R79; DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
@@ -515,7 +515,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 124; CURRENT_PROJECT_VERSION = 125;
DEVELOPMENT_TEAM = 2F67MQ8R79; DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
@@ -543,7 +543,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 124; CURRENT_PROJECT_VERSION = 125;
DEVELOPMENT_TEAM = 2F67MQ8R79; DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;

View File

@@ -59,11 +59,11 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.84.0</string> <string>1.85.0</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>124</string> <string>125</string>
<key>FLTEnableImpeller</key> <key>FLTEnableImpeller</key>
<true /> <true />
<key>ITSAppUsesNonExemptEncryption</key> <key>ITSAppUsesNonExemptEncryption</key>

View File

@@ -5,32 +5,32 @@
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000253"> <testcase classname="fastlane.lanes" name="0: default_platform" time="0.000291">
</testcase> </testcase>
<testcase classname="fastlane.lanes" name="1: increment_version_number" time="0.181977"> <testcase classname="fastlane.lanes" name="1: increment_version_number" time="0.199372">
</testcase> </testcase>
<testcase classname="fastlane.lanes" name="2: latest_testflight_build_number" time="16.12614"> <testcase classname="fastlane.lanes" name="2: latest_testflight_build_number" time="6.104477">
</testcase> </testcase>
<testcase classname="fastlane.lanes" name="3: increment_build_number" time="0.162663"> <testcase classname="fastlane.lanes" name="3: increment_build_number" time="0.164465">
</testcase> </testcase>
<testcase classname="fastlane.lanes" name="4: build_app" time="145.399278"> <testcase classname="fastlane.lanes" name="4: build_app" time="108.828838">
</testcase> </testcase>
<testcase classname="fastlane.lanes" name="5: upload_to_testflight" time="61.317235"> <testcase classname="fastlane.lanes" name="5: upload_to_testflight" time="60.89387">
</testcase> </testcase>

View File

@@ -0,0 +1,54 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
extension ContextHelper on BuildContext {
// Returns the current size from MediaQuery
Size get size => MediaQuery.sizeOf(this);
// Returns the current width from MediaQuery
double get width => size.width;
// Returns the current height from MediaQuery
double get height => size.height;
// Returns true if the app is running on a mobile device (!tablets)
bool get isMobile => width < 550;
// Returns the current ThemeData
ThemeData get themeData => Theme.of(this);
// Returns true if the app is using a dark theme
bool get isDarkTheme => themeData.brightness == Brightness.dark;
// Returns the current Primary color of the Theme
Color get primaryColor => themeData.primaryColor;
// Returns the Scaffold background color of the Theme
Color get scaffoldBackgroundColor => themeData.scaffoldBackgroundColor;
// Returns the current TextTheme
TextTheme get textTheme => themeData.textTheme;
// Current ColorScheme used
ColorScheme get colorScheme => themeData.colorScheme;
// Pop-out from the current context with optional result
void pop<T>([T? result]) => Navigator.of(this).pop(result);
// Auto-Push new route from the current context
Future<T?> autoPush<T extends Object?>(PageRouteInfo<dynamic> route) =>
AutoRouter.of(this).push(route);
// Auto-Push navigate route from the current context
Future<dynamic> autoNavigate<T extends Object?>(
PageRouteInfo<dynamic> route,
) =>
AutoRouter.of(this).navigate(route);
// Auto-Push replace route from the current context
Future<T?> autoReplace<T extends Object?>(PageRouteInfo<dynamic> route) =>
AutoRouter.of(this).replace(route);
// Auto-Pop from the current context
Future<bool> autoPop<T>([T? result]) => AutoRouter.of(this).pop(result);
}

View File

@@ -2,27 +2,6 @@ import 'dart:typed_data';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
extension DurationExtension on String {
Duration? toDuration() {
try {
final parts = split(':')
.map((e) => double.parse(e).toInt())
.toList(growable: false);
return Duration(hours: parts[0], minutes: parts[1], seconds: parts[2]);
} catch (e) {
return null;
}
}
double toDouble() {
return double.parse(this);
}
int toInt() {
return int.parse(this);
}
}
extension ListExtension<E> on List<E> { extension ListExtension<E> on List<E> {
List<E> uniqueConsecutive({ List<E> uniqueConsecutive({
int Function(E a, E b)? compare, int Function(E a, E b)? compare,

View File

@@ -0,0 +1,30 @@
extension StringExtension on String {
String capitalize() {
return split(" ")
.map(
(str) => str.isEmpty ? str : str[0].toUpperCase() + str.substring(1),
)
.join(" ");
}
}
extension DurationExtension on String {
Duration? toDuration() {
try {
final parts = split(':')
.map((e) => double.parse(e).toInt())
.toList(growable: false);
return Duration(hours: parts[0], minutes: parts[1], seconds: parts[2]);
} catch (e) {
return null;
}
}
double toDouble() {
return double.parse(this);
}
int toInt() {
return int.parse(this);
}
}

View File

@@ -48,8 +48,7 @@ class Activity {
: ActivityType.like, : ActivityType.like,
user = User( user = User(
email: dto.user.email, email: dto.user.email,
firstName: dto.user.firstName, name: dto.user.name,
lastName: dto.user.lastName,
profileImagePath: dto.user.profileImagePath, profileImagePath: dto.user.profileImagePath,
id: dto.user.id, id: dto.user.id,
// Placeholder values // Placeholder values

View File

@@ -4,13 +4,14 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart' hide Store; import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.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/models/activity.model.dart';
import 'package:immich_mobile/modules/activities/providers/activity.provider.dart'; import 'package:immich_mobile/modules/activities/providers/activity.provider.dart';
import 'package:immich_mobile/shared/models/store.dart'; import 'package:immich_mobile/shared/models/store.dart';
import 'package:immich_mobile/shared/ui/confirm_dialog.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_loading_indicator.dart';
import 'package:immich_mobile/shared/ui/user_circle_avatar.dart'; import 'package:immich_mobile/shared/ui/user_circle_avatar.dart';
import 'package:immich_mobile/utils/datetime_extensions.dart'; import 'package:immich_mobile/extensions/datetime_extensions.dart';
import 'package:immich_mobile/utils/image_url_builder.dart'; import 'package:immich_mobile/utils/image_url_builder.dart';
class ActivitiesPage extends HookConsumerWidget { class ActivitiesPage extends HookConsumerWidget {
@@ -49,12 +50,8 @@ class ActivitiesPage extends HookConsumerWidget {
); );
buildTitleWithTimestamp(Activity activity, {bool leftAlign = true}) { buildTitleWithTimestamp(Activity activity, {bool leftAlign = true}) {
final textColor = Theme.of(context).brightness == Brightness.dark final textColor = context.isDarkTheme ? Colors.white : Colors.black;
? Colors.white final textStyle = context.textTheme.bodyMedium
: Colors.black;
final textStyle = Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(color: textColor.withOpacity(0.6)); ?.copyWith(color: textColor.withOpacity(0.6));
return Row( return Row(
@@ -64,7 +61,7 @@ class ActivitiesPage extends HookConsumerWidget {
mainAxisSize: leftAlign ? MainAxisSize.min : MainAxisSize.max, mainAxisSize: leftAlign ? MainAxisSize.min : MainAxisSize.max,
children: [ children: [
Text( Text(
"${activity.user.firstName} ${activity.user.lastName}", activity.user.name,
style: textStyle, style: textStyle,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
@@ -306,7 +303,7 @@ class ActivitiesPage extends HookConsumerWidget {
Align( Align(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: Container( child: Container(
color: Theme.of(context).scaffoldBackgroundColor, color: context.scaffoldBackgroundColor,
child: buildTextField(liked?.id), child: buildTextField(liked?.id),
), ),
), ),

View File

@@ -1,8 +1,8 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/album/providers/album.provider.dart'; import 'package:immich_mobile/modules/album/providers/album.provider.dart';
import 'package:immich_mobile/modules/album/providers/album_detail.provider.dart'; import 'package:immich_mobile/modules/album/providers/album_detail.provider.dart';
import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart'; import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
@@ -95,20 +95,19 @@ class AddToAlbumBottomSheet extends HookConsumerWidget {
children: [ children: [
Text( Text(
'common_add_to_album'.tr(), 'common_add_to_album'.tr(),
style: Theme.of(context).textTheme.displayMedium, style: context.textTheme.displayMedium,
), ),
TextButton.icon( TextButton.icon(
icon: Icon( icon: Icon(
Icons.add, Icons.add,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
label: Text( label: Text(
'common_create_new_album'.tr(), 'common_create_new_album'.tr(),
style: style: TextStyle(color: context.primaryColor),
TextStyle(color: Theme.of(context).primaryColor),
), ),
onPressed: () { onPressed: () {
AutoRouter.of(context).push( context.autoPush(
CreateAlbumRoute( CreateAlbumRoute(
isSharedAlbum: false, isSharedAlbum: false,
initialAssets: assets, initialAssets: assets,

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
class AlbumActionOutlinedButton extends StatelessWidget { class AlbumActionOutlinedButton extends StatelessWidget {
final VoidCallback? onPressed; final VoidCallback? onPressed;
@@ -14,8 +15,6 @@ class AlbumActionOutlinedButton extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDarkTheme = Theme.of(context).brightness == Brightness.dark;
return Padding( return Padding(
padding: const EdgeInsets.only(right: 8.0), padding: const EdgeInsets.only(right: 8.0),
child: OutlinedButton.icon( child: OutlinedButton.icon(
@@ -26,7 +25,7 @@ class AlbumActionOutlinedButton extends StatelessWidget {
), ),
side: BorderSide( side: BorderSide(
width: 1, width: 1,
color: isDarkTheme color: context.isDarkTheme
? const Color.fromARGB(255, 63, 63, 63) ? const Color.fromARGB(255, 63, 63, 63)
: const Color.fromARGB(255, 206, 206, 206), : const Color.fromARGB(255, 206, 206, 206),
), ),
@@ -34,11 +33,11 @@ class AlbumActionOutlinedButton extends StatelessWidget {
icon: Icon( icon: Icon(
iconData, iconData,
size: 15, size: 15,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
label: Text( label: Text(
labelText, labelText,
style: Theme.of(context).textTheme.labelSmall?.copyWith( style: context.textTheme.labelSmall?.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),

View File

@@ -1,5 +1,6 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/shared/models/album.dart'; import 'package:immich_mobile/shared/models/album.dart';
import 'package:immich_mobile/shared/models/store.dart'; import 'package:immich_mobile/shared/models/store.dart';
import 'package:immich_mobile/shared/ui/immich_image.dart'; import 'package:immich_mobile/shared/ui/immich_image.dart';
@@ -22,7 +23,8 @@ class AlbumThumbnailCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var isDarkMode = Theme.of(context).brightness == Brightness.dark; var isDarkTheme = context.isDarkTheme;
return LayoutBuilder( return LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
var cardSize = constraints.maxWidth; var cardSize = constraints.maxWidth;
@@ -32,7 +34,7 @@ class AlbumThumbnailCard extends StatelessWidget {
height: cardSize, height: cardSize,
width: cardSize, width: cardSize,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[800] : Colors.grey[200], color: isDarkTheme ? Colors.grey[800] : Colors.grey[200],
), ),
child: Center( child: Center(
child: Icon( child: Icon(
@@ -73,14 +75,14 @@ class AlbumThumbnailCard extends StatelessWidget {
style: TextStyle( style: TextStyle(
fontFamily: 'WorkSans', fontFamily: 'WorkSans',
fontSize: 12, fontSize: 12,
color: isDarkMode ? Colors.white : Colors.black, color: isDarkTheme ? Colors.white : Colors.black,
), ),
), ),
if (owner != null) const TextSpan(text: ' · '), if (owner != null) const TextSpan(text: ' · '),
if (owner != null) if (owner != null)
TextSpan( TextSpan(
text: owner, text: owner,
style: Theme.of(context).textTheme.labelSmall, style: context.textTheme.labelSmall,
), ),
], ],
), ),
@@ -114,8 +116,8 @@ class AlbumThumbnailCard extends StatelessWidget {
album.name, album.name,
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: isDarkMode color: isDarkTheme
? Theme.of(context).primaryColor ? context.primaryColor
: Colors.black, : Colors.black,
), ),
), ),

View File

@@ -1,7 +1,7 @@
import 'package:auto_route/auto_route.dart';
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/shared/models/album.dart'; import 'package:immich_mobile/shared/models/album.dart';
import 'package:immich_mobile/shared/models/store.dart'; import 'package:immich_mobile/shared/models/store.dart';
@@ -21,12 +21,11 @@ class AlbumThumbnailListTile extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var cardSize = 68.0; var cardSize = 68.0;
var isDarkMode = Theme.of(context).brightness == Brightness.dark;
buildEmptyThumbnail() { buildEmptyThumbnail() {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[800] : Colors.grey[200], color: context.isDarkTheme ? Colors.grey[800] : Colors.grey[200],
), ),
child: SizedBox( child: SizedBox(
height: cardSize, height: cardSize,
@@ -61,7 +60,7 @@ class AlbumThumbnailListTile extends StatelessWidget {
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
onTap: onTap ?? onTap: onTap ??
() { () {
AutoRouter.of(context).push(AlbumViewerRoute(albumId: album.id)); context.autoPush(AlbumViewerRoute(albumId: album.id));
}, },
child: Padding( child: Padding(
padding: const EdgeInsets.only(bottom: 12.0), padding: const EdgeInsets.only(bottom: 12.0),

View File

@@ -1,6 +1,7 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.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/album_title.provider.dart';
class AlbumTitleTextField extends ConsumerWidget { class AlbumTitleTextField extends ConsumerWidget {
@@ -19,7 +20,7 @@ class AlbumTitleTextField extends ConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final isDarkTheme = Theme.of(context).brightness == Brightness.dark; final isDarkTheme = context.isDarkTheme;
return TextField( return TextField(
onChanged: (v) { onChanged: (v) {
@@ -55,7 +56,7 @@ class AlbumTitleTextField extends ConsumerWidget {
}, },
icon: Icon( icon: Icon(
Icons.cancel_rounded, Icons.cancel_rounded,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
splashRadius: 10, splashRadius: 10,
) )

View File

@@ -1,8 +1,8 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/activities/providers/activity.provider.dart'; import 'package:immich_mobile/modules/activities/providers/activity.provider.dart';
import 'package:immich_mobile/modules/album/providers/album.provider.dart'; import 'package:immich_mobile/modules/album/providers/album.provider.dart';
import 'package:immich_mobile/modules/album/providers/album_detail.provider.dart'; import 'package:immich_mobile/modules/album/providers/album_detail.provider.dart';
@@ -58,12 +58,12 @@ class AlbumViewerAppbar extends HookConsumerWidget
if (album.shared) { if (album.shared) {
success = success =
await ref.watch(sharedAlbumProvider.notifier).deleteAlbum(album); await ref.watch(sharedAlbumProvider.notifier).deleteAlbum(album);
AutoRouter.of(context) context
.navigate(const TabControllerRoute(children: [SharingRoute()])); .autoNavigate(const TabControllerRoute(children: [SharingRoute()]));
} else { } else {
success = await ref.watch(albumProvider.notifier).deleteAlbum(album); success = await ref.watch(albumProvider.notifier).deleteAlbum(album);
AutoRouter.of(context) context
.navigate(const TabControllerRoute(children: [LibraryRoute()])); .autoNavigate(const TabControllerRoute(children: [LibraryRoute()]));
} }
if (!success) { if (!success) {
ImmichToast.show( ImmichToast.show(
@@ -93,7 +93,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
child: Text( child: Text(
'Cancel', 'Cancel',
style: TextStyle( style: TextStyle(
color: Theme.of(context).primaryColor, color: context.primaryColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
@@ -107,9 +107,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
'Confirm', 'Confirm',
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Theme.of(context).brightness == Brightness.light color: !context.isDarkTheme ? Colors.red : Colors.red[300],
? Colors.red
: Colors.red[300],
), ),
), ),
), ),
@@ -130,8 +128,8 @@ class AlbumViewerAppbar extends HookConsumerWidget
await ref.watch(sharedAlbumProvider.notifier).leaveAlbum(album); await ref.watch(sharedAlbumProvider.notifier).leaveAlbum(album);
if (isSuccess) { if (isSuccess) {
AutoRouter.of(context) context
.navigate(const TabControllerRoute(children: [SharingRoute()])); .autoNavigate(const TabControllerRoute(children: [SharingRoute()]));
} else { } else {
Navigator.pop(context); Navigator.pop(context);
ImmichToast.show( ImmichToast.show(
@@ -190,7 +188,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
gravity: ToastGravity.BOTTOM, gravity: ToastGravity.BOTTOM,
); );
} }
Navigator.of(buildContext).pop(); context.pop();
}, },
); );
return const ShareDialog(); return const ShareDialog();
@@ -266,8 +264,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
ListTile( ListTile(
leading: const Icon(Icons.share_rounded), leading: const Icon(Icons.share_rounded),
onTap: () { onTap: () {
AutoRouter.of(context) context.autoPush(SharedLinkEditRoute(albumId: album.remoteId));
.push(SharedLinkEditRoute(albumId: album.remoteId));
Navigator.pop(context); Navigator.pop(context);
}, },
title: const Text( title: const Text(
@@ -277,8 +274,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
), ),
ListTile( ListTile(
leading: const Icon(Icons.settings_rounded), leading: const Icon(Icons.settings_rounded),
onTap: () => onTap: () => context.autoNavigate(AlbumOptionsRoute(album: album)),
AutoRouter.of(context).navigate(AlbumOptionsRoute(album: album)),
title: const Text( title: const Text(
"translated_text_options", "translated_text_options",
style: TextStyle(fontWeight: FontWeight.bold), style: TextStyle(fontWeight: FontWeight.bold),
@@ -300,7 +296,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
), ),
]; ];
showModalBottomSheet( showModalBottomSheet(
backgroundColor: Theme.of(context).scaffoldBackgroundColor, backgroundColor: context.scaffoldBackgroundColor,
isScrollControlled: false, isScrollControlled: false,
context: context, context: context,
builder: (context) { builder: (context) {
@@ -342,7 +338,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
comments.toString(), comments.toString(),
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
), ),
), ),
@@ -381,7 +377,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
); );
} else { } else {
return IconButton( return IconButton(
onPressed: () async => await AutoRouter.of(context).pop(), onPressed: () async => await context.autoPop(),
icon: const Icon(Icons.arrow_back_ios_rounded), icon: const Icon(Icons.arrow_back_ios_rounded),
splashRadius: 25, splashRadius: 25,
); );

View File

@@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/album/providers/album_viewer.provider.dart'; import 'package:immich_mobile/modules/album/providers/album_viewer.provider.dart';
import 'package:immich_mobile/shared/models/album.dart'; import 'package:immich_mobile/shared/models/album.dart';
@@ -17,7 +18,6 @@ class AlbumViewerEditableTitle extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final titleTextEditController = useTextEditingController(text: album.name); final titleTextEditController = useTextEditingController(text: album.name);
final isDarkTheme = Theme.of(context).brightness == Brightness.dark;
void onFocusModeChange() { void onFocusModeChange() {
if (!titleFocusNode.hasFocus && titleTextEditController.text.isEmpty) { if (!titleFocusNode.hasFocus && titleTextEditController.text.isEmpty) {
@@ -65,7 +65,7 @@ class AlbumViewerEditableTitle extends HookConsumerWidget {
}, },
icon: Icon( icon: Icon(
Icons.cancel_rounded, Icons.cancel_rounded,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
splashRadius: 10, splashRadius: 10,
) )
@@ -79,14 +79,14 @@ class AlbumViewerEditableTitle extends HookConsumerWidget {
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
focusColor: Colors.grey[300], focusColor: Colors.grey[300],
fillColor: isDarkTheme fillColor: context.isDarkTheme
? const Color.fromARGB(255, 32, 33, 35) ? const Color.fromARGB(255, 32, 33, 35)
: Colors.grey[200], : Colors.grey[200],
filled: titleFocusNode.hasFocus, filled: titleFocusNode.hasFocus,
hintText: 'share_add_title'.tr(), hintText: 'share_add_title'.tr(),
hintStyle: TextStyle( hintStyle: TextStyle(
fontSize: 28, fontSize: 28,
color: isDarkTheme ? Colors.grey[300] : Colors.grey[700], color: context.isDarkTheme ? Colors.grey[300] : Colors.grey[700],
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),

View File

@@ -1,9 +1,9 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart'; import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
@@ -44,8 +44,9 @@ class AlbumOptionsPage extends HookConsumerWidget {
await ref.read(sharedAlbumProvider.notifier).leaveAlbum(album); await ref.read(sharedAlbumProvider.notifier).leaveAlbum(album);
if (isSuccess) { if (isSuccess) {
AutoRouter.of(context) context.autoNavigate(
.navigate(const TabControllerRoute(children: [SharingRoute()])); const TabControllerRoute(children: [SharingRoute()]),
);
} else { } else {
showErrorMessage(); showErrorMessage();
} }
@@ -97,7 +98,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
} }
showModalBottomSheet( showModalBottomSheet(
backgroundColor: Theme.of(context).scaffoldBackgroundColor, backgroundColor: context.scaffoldBackgroundColor,
isScrollControlled: false, isScrollControlled: false,
context: context, context: context,
builder: (context) { builder: (context) {
@@ -123,7 +124,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
) )
: const SizedBox(), : const SizedBox(),
title: Text( title: Text(
album.owner.value?.firstName ?? "", album.owner.value?.name ?? "",
style: const TextStyle( style: const TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -154,7 +155,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
radius: 22, radius: 22,
), ),
title: Text( title: Text(
user.firstName, user.name,
style: const TextStyle( style: const TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -177,7 +178,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
buildSectionTitle(String text) { buildSectionTitle(String text) {
return Padding( return Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Text(text, style: Theme.of(context).textTheme.bodySmall), child: Text(text, style: context.textTheme.bodySmall),
); );
} }
@@ -186,7 +187,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
leading: IconButton( leading: IconButton(
icon: const Icon(Icons.arrow_back_ios_new_rounded), icon: const Icon(Icons.arrow_back_ios_new_rounded),
onPressed: () { onPressed: () {
AutoRouter.of(context).pop(null); context.autoPop(null);
}, },
), ),
centerTitle: true, centerTitle: true,
@@ -208,14 +209,12 @@ class AlbumOptionsPage extends HookConsumerWidget {
} }
}, },
activeColor: activityEnabled.value activeColor: activityEnabled.value
? Theme.of(context).primaryColor ? context.primaryColor
: Theme.of(context).disabledColor, : context.themeData.disabledColor,
dense: true, dense: true,
title: Text( title: Text(
"shared_album_activity_setting_title", "shared_album_activity_setting_title",
style: Theme.of(context) style: context.textTheme.labelLarge
.textTheme
.labelLarge
?.copyWith(fontWeight: FontWeight.bold), ?.copyWith(fontWeight: FontWeight.bold),
).tr(), ).tr(),
subtitle: subtitle:

View File

@@ -1,10 +1,10 @@
import 'dart:async'; import 'dart:async';
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.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/models/asset_selection_page_result.model.dart';
import 'package:immich_mobile/modules/album/providers/album_detail.provider.dart'; import 'package:immich_mobile/modules/album/providers/album_detail.provider.dart';
import 'package:immich_mobile/modules/album/services/album.service.dart'; import 'package:immich_mobile/modules/album/services/album.service.dart';
@@ -67,7 +67,7 @@ class AlbumViewerPage extends HookConsumerWidget {
/// If they exist, add to selected asset state to show they are already selected. /// If they exist, add to selected asset state to show they are already selected.
void onAddPhotosPressed(Album albumInfo) async { void onAddPhotosPressed(Album albumInfo) async {
AssetSelectionPageResult? returnPayload = AssetSelectionPageResult? returnPayload =
await AutoRouter.of(context).push<AssetSelectionPageResult?>( await context.autoPush<AssetSelectionPageResult?>(
AssetSelectionRoute( AssetSelectionRoute(
existingAssets: albumInfo.assets, existingAssets: albumInfo.assets,
canDeselect: false, canDeselect: false,
@@ -97,8 +97,7 @@ class AlbumViewerPage extends HookConsumerWidget {
} }
void onAddUsersPressed(Album album) async { void onAddUsersPressed(Album album) async {
List<String>? sharedUserIds = List<String>? sharedUserIds = await context.autoPush<List<String>?>(
await AutoRouter.of(context).push<List<String>?>(
SelectAdditionalUserForSharingRoute(album: album), SelectAdditionalUserForSharingRoute(album: album),
); );
@@ -171,11 +170,19 @@ class AlbumViewerPage extends HookConsumerWidget {
return const SizedBox(); return const SizedBox();
} }
final String dateRangeText;
if (startDate.day == endDate.day &&
startDate.month == endDate.month &&
startDate.year == endDate.year) {
dateRangeText = DateFormat.yMMMd().format(startDate);
} else {
final String startDateText = (startDate.year == endDate.year final String startDateText = (startDate.year == endDate.year
? DateFormat.MMMd() ? DateFormat.MMMd()
: DateFormat.yMMMd()) : DateFormat.yMMMd())
.format(startDate); .format(startDate);
final String endDateText = DateFormat.yMMMd().format(endDate); final String endDateText = DateFormat.yMMMd().format(endDate);
dateRangeText = "$startDateText - $endDateText";
}
return Padding( return Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
@@ -183,7 +190,7 @@ class AlbumViewerPage extends HookConsumerWidget {
bottom: album.shared ? 0.0 : 8.0, bottom: album.shared ? 0.0 : 8.0,
), ),
child: Text( child: Text(
"$startDateText - $endDateText", dateRangeText,
style: const TextStyle( style: const TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@@ -195,7 +202,7 @@ class AlbumViewerPage extends HookConsumerWidget {
Widget buildSharedUserIconsRow(Album album) { Widget buildSharedUserIconsRow(Album album) {
return GestureDetector( return GestureDetector(
onTap: () async { onTap: () async {
await AutoRouter.of(context).push(AlbumOptionsRoute(album: album)); await context.autoPush(AlbumOptionsRoute(album: album));
ref.invalidate(albumDetailProvider(album.id)); ref.invalidate(albumDetailProvider(album.id));
}, },
child: SizedBox( child: SizedBox(
@@ -234,7 +241,7 @@ class AlbumViewerPage extends HookConsumerWidget {
onActivitiesPressed(Album album) { onActivitiesPressed(Album album) {
if (album.remoteId != null) { if (album.remoteId != null) {
AutoRouter.of(context).push( context.autoPush(
ActivitiesRoute( ActivitiesRoute(
albumId: album.remoteId!, albumId: album.remoteId!,
appBarTitle: album.name, appBarTitle: album.name,

View File

@@ -3,6 +3,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.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/models/asset_selection_page_result.model.dart';
import 'package:immich_mobile/modules/asset_viewer/providers/render_list.provider.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/asset_grid_data_structure.dart';
@@ -78,7 +79,7 @@ class AssetSelectionPage extends HookConsumerWidget {
canDeselect ? "share_done" : "share_add", canDeselect ? "share_done" : "share_add",
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
).tr(), ).tr(),
), ),

View File

@@ -1,8 +1,8 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.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/models/asset_selection_page_result.model.dart';
import 'package:immich_mobile/modules/album/providers/album.provider.dart'; import 'package:immich_mobile/modules/album/providers/album.provider.dart';
import 'package:immich_mobile/modules/album/providers/album_title.provider.dart'; import 'package:immich_mobile/modules/album/providers/album_title.provider.dart';
@@ -34,11 +34,11 @@ class CreateAlbumPage extends HookConsumerWidget {
final selectedAssets = useState<Set<Asset>>( final selectedAssets = useState<Set<Asset>>(
initialAssets != null ? Set.from(initialAssets!) : const {}, initialAssets != null ? Set.from(initialAssets!) : const {},
); );
final isDarkTheme = Theme.of(context).brightness == Brightness.dark;
showSelectUserPage() async { showSelectUserPage() async {
final bool? ok = await AutoRouter.of(context) final bool? ok = await context.autoPush<bool?>(
.push<bool?>(SelectUserForSharingRoute(assets: selectedAssets.value)); SelectUserForSharingRoute(assets: selectedAssets.value),
);
if (ok == true) { if (ok == true) {
selectedAssets.value = {}; selectedAssets.value = {};
} }
@@ -58,7 +58,7 @@ class CreateAlbumPage extends HookConsumerWidget {
onSelectPhotosButtonPressed() async { onSelectPhotosButtonPressed() async {
AssetSelectionPageResult? selectedAsset = AssetSelectionPageResult? selectedAsset =
await AutoRouter.of(context).push<AssetSelectionPageResult?>( await context.autoPush<AssetSelectionPageResult?>(
AssetSelectionRoute( AssetSelectionRoute(
existingAssets: selectedAssets.value, existingAssets: selectedAssets.value,
canDeselect: true, canDeselect: true,
@@ -94,7 +94,7 @@ class CreateAlbumPage extends HookConsumerWidget {
padding: const EdgeInsets.only(top: 200, left: 18), padding: const EdgeInsets.only(top: 200, left: 18),
child: Text( child: Text(
'create_shared_album_page_share_add_assets', 'create_shared_album_page_share_add_assets',
style: Theme.of(context).textTheme.displayMedium?.copyWith( style: context.textTheme.displayMedium?.copyWith(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
@@ -117,7 +117,7 @@ class CreateAlbumPage extends HookConsumerWidget {
padding: padding:
const EdgeInsets.symmetric(vertical: 22, horizontal: 16), const EdgeInsets.symmetric(vertical: 22, horizontal: 16),
side: BorderSide( side: BorderSide(
color: isDarkTheme color: context.isDarkTheme
? const Color.fromARGB(255, 63, 63, 63) ? const Color.fromARGB(255, 63, 63, 63)
: const Color.fromARGB(255, 206, 206, 206), : const Color.fromARGB(255, 206, 206, 206),
), ),
@@ -128,13 +128,13 @@ class CreateAlbumPage extends HookConsumerWidget {
onPressed: onSelectPhotosButtonPressed, onPressed: onSelectPhotosButtonPressed,
icon: Icon( icon: Icon(
Icons.add_rounded, Icons.add_rounded,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
label: Padding( label: Padding(
padding: const EdgeInsets.only(left: 8.0), padding: const EdgeInsets.only(left: 8.0),
child: Text( child: Text(
'create_shared_album_page_share_select_photos', 'create_shared_album_page_share_select_photos',
style: Theme.of(context).textTheme.labelLarge?.copyWith( style: context.textTheme.labelLarge?.copyWith(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -206,7 +206,7 @@ class CreateAlbumPage extends HookConsumerWidget {
selectedAssets.value = {}; selectedAssets.value = {};
ref.watch(albumTitleProvider.notifier).clearAlbumTitle(); ref.watch(albumTitleProvider.notifier).clearAlbumTitle();
AutoRouter.of(context).replace(AlbumViewerRoute(albumId: newAlbum.id)); context.autoReplace(AlbumViewerRoute(albumId: newAlbum.id));
} }
} }
@@ -214,18 +214,18 @@ class CreateAlbumPage extends HookConsumerWidget {
appBar: AppBar( appBar: AppBar(
elevation: 0, elevation: 0,
centerTitle: false, centerTitle: false,
backgroundColor: Theme.of(context).scaffoldBackgroundColor, backgroundColor: context.scaffoldBackgroundColor,
leading: IconButton( leading: IconButton(
onPressed: () { onPressed: () {
selectedAssets.value = {}; selectedAssets.value = {};
AutoRouter.of(context).pop(); context.autoPop();
}, },
icon: const Icon(Icons.close_rounded), icon: const Icon(Icons.close_rounded),
), ),
title: Text( title: Text(
'share_create_album', 'share_create_album',
style: Theme.of(context).textTheme.displayMedium?.copyWith( style: context.textTheme.displayMedium?.copyWith(
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
).tr(), ).tr(),
actions: [ actions: [
@@ -239,8 +239,8 @@ class CreateAlbumPage extends HookConsumerWidget {
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: albumTitleController.text.isEmpty color: albumTitleController.text.isEmpty
? Theme.of(context).disabledColor ? context.themeData.disabledColor
: Theme.of(context).primaryColor, : context.primaryColor,
), ),
), ),
), ),
@@ -254,7 +254,7 @@ class CreateAlbumPage extends HookConsumerWidget {
'create_shared_album_page_create'.tr(), 'create_shared_album_page_create'.tr(),
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
), ),
), ),
@@ -265,7 +265,7 @@ class CreateAlbumPage extends HookConsumerWidget {
child: CustomScrollView( child: CustomScrollView(
slivers: [ slivers: [
SliverAppBar( SliverAppBar(
backgroundColor: Theme.of(context).scaffoldBackgroundColor, backgroundColor: context.scaffoldBackgroundColor,
elevation: 5, elevation: 5,
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
pinned: true, pinned: true,

View File

@@ -1,9 +1,9 @@
import 'package:auto_route/auto_route.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/album/providers/album.provider.dart'; import 'package:immich_mobile/modules/album/providers/album.provider.dart';
import 'package:immich_mobile/modules/album/ui/album_thumbnail_card.dart'; import 'package:immich_mobile/modules/album/ui/album_thumbnail_card.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
@@ -21,7 +21,7 @@ class LibraryPage extends HookConsumerWidget {
final trashEnabled = final trashEnabled =
ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash)); ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash));
final albums = ref.watch(albumProvider); final albums = ref.watch(albumProvider);
var isDarkMode = Theme.of(context).brightness == Brightness.dark; var isDarkTheme = context.isDarkTheme;
var settings = ref.watch(appSettingsServiceProvider); var settings = ref.watch(appSettingsServiceProvider);
useEffect( useEffect(
@@ -96,15 +96,14 @@ class LibraryPage extends HookConsumerWidget {
padding: const EdgeInsets.only(right: 12.0), padding: const EdgeInsets.only(right: 12.0),
child: Icon( child: Icon(
Icons.check, Icons.check,
color: selected color:
? Theme.of(context).primaryColor selected ? context.primaryColor : Colors.transparent,
: Colors.transparent,
), ),
), ),
Text( Text(
option, option,
style: TextStyle( style: TextStyle(
color: selected ? Theme.of(context).primaryColor : null, color: selected ? context.primaryColor : null,
fontSize: 12.0, fontSize: 12.0,
), ),
), ),
@@ -122,13 +121,13 @@ class LibraryPage extends HookConsumerWidget {
Icon( Icon(
Icons.swap_vert_rounded, Icons.swap_vert_rounded,
size: 18, size: 18,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
Text( Text(
options[selectedAlbumSortOrder.value], options[selectedAlbumSortOrder.value],
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Theme.of(context).primaryColor, color: context.primaryColor,
fontSize: 12.0, fontSize: 12.0,
), ),
), ),
@@ -140,7 +139,7 @@ class LibraryPage extends HookConsumerWidget {
Widget buildCreateAlbumButton() { Widget buildCreateAlbumButton() {
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
AutoRouter.of(context).push(CreateAlbumRoute(isSharedAlbum: false)); context.autoPush(CreateAlbumRoute(isSharedAlbum: false));
}, },
child: Padding( child: Padding(
padding: const EdgeInsets.only(bottom: 32), padding: const EdgeInsets.only(bottom: 32),
@@ -152,18 +151,18 @@ class LibraryPage extends HookConsumerWidget {
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all( border: Border.all(
color: isDarkMode color: isDarkTheme
? const Color.fromARGB(255, 53, 53, 53) ? const Color.fromARGB(255, 53, 53, 53)
: const Color.fromARGB(255, 203, 203, 203), : const Color.fromARGB(255, 203, 203, 203),
), ),
color: isDarkMode ? Colors.grey[900] : Colors.grey[50], color: isDarkTheme ? Colors.grey[900] : Colors.grey[50],
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
), ),
child: Center( child: Center(
child: Icon( child: Icon(
Icons.add_rounded, Icons.add_rounded,
size: 28, size: 28,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
), ),
), ),
@@ -201,21 +200,21 @@ class LibraryPage extends HookConsumerWidget {
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 13.0, fontSize: 13.0,
color: isDarkMode ? Colors.white : Colors.grey[800], color: isDarkTheme ? Colors.white : Colors.grey[800],
), ),
), ),
), ),
style: OutlinedButton.styleFrom( style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
backgroundColor: isDarkMode ? Colors.grey[900] : Colors.grey[50], backgroundColor: isDarkTheme ? Colors.grey[900] : Colors.grey[50],
side: BorderSide( side: BorderSide(
color: isDarkMode ? Colors.grey[800]! : Colors.grey[300]!, color: isDarkTheme ? Colors.grey[800]! : Colors.grey[300]!,
), ),
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
), ),
icon: Icon( icon: Icon(
icon, icon,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
), ),
); );
@@ -228,7 +227,7 @@ class LibraryPage extends HookConsumerWidget {
Widget? shareTrashButton() { Widget? shareTrashButton() {
return trashEnabled return trashEnabled
? InkWell( ? InkWell(
onTap: () => AutoRouter.of(context).push(const TrashRoute()), onTap: () => context.autoPush(const TrashRoute()),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
child: const Icon( child: const Icon(
Icons.delete_rounded, Icons.delete_rounded,
@@ -257,12 +256,12 @@ class LibraryPage extends HookConsumerWidget {
children: [ children: [
buildLibraryNavButton( buildLibraryNavButton(
"library_page_favorites".tr(), Icons.favorite_border, () { "library_page_favorites".tr(), Icons.favorite_border, () {
AutoRouter.of(context).navigate(const FavoritesRoute()); context.autoNavigate(const FavoritesRoute());
}), }),
const SizedBox(width: 12.0), const SizedBox(width: 12.0),
buildLibraryNavButton( buildLibraryNavButton(
"library_page_archive".tr(), Icons.archive_outlined, () { "library_page_archive".tr(), Icons.archive_outlined, () {
AutoRouter.of(context).navigate(const ArchiveRoute()); context.autoNavigate(const ArchiveRoute());
}), }),
], ],
), ),
@@ -306,7 +305,7 @@ class LibraryPage extends HookConsumerWidget {
return AlbumThumbnailCard( return AlbumThumbnailCard(
album: sorted[index - 1], album: sorted[index - 1],
onTap: () => AutoRouter.of(context).push( onTap: () => context.autoPush(
AlbumViewerRoute( AlbumViewerRoute(
albumId: sorted[index - 1].id, albumId: sorted[index - 1].id,
), ),
@@ -348,7 +347,7 @@ class LibraryPage extends HookConsumerWidget {
childCount: local.length, childCount: local.length,
(context, index) => AlbumThumbnailCard( (context, index) => AlbumThumbnailCard(
album: local[index], album: local[index],
onTap: () => AutoRouter.of(context).push( onTap: () => context.autoPush(
AlbumViewerRoute( AlbumViewerRoute(
albumId: local[index].id, albumId: local[index].id,
), ),

View File

@@ -1,8 +1,8 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.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/modules/album/providers/suggested_shared_users.provider.dart';
import 'package:immich_mobile/shared/models/album.dart'; import 'package:immich_mobile/shared/models/album.dart';
import 'package:immich_mobile/shared/models/user.dart'; import 'package:immich_mobile/shared/models/user.dart';
@@ -22,14 +22,13 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
final sharedUsersList = useState<Set<User>>({}); final sharedUsersList = useState<Set<User>>({});
addNewUsersHandler() { addNewUsersHandler() {
AutoRouter.of(context) context.autoPop(sharedUsersList.value.map((e) => e.id).toList());
.pop(sharedUsersList.value.map((e) => e.id).toList());
} }
buildTileIcon(User user) { buildTileIcon(User user) {
if (sharedUsersList.value.contains(user)) { if (sharedUsersList.value.contains(user)) {
return CircleAvatar( return CircleAvatar(
backgroundColor: Theme.of(context).primaryColor, backgroundColor: context.primaryColor,
child: const Icon( child: const Icon(
Icons.check_rounded, Icons.check_rounded,
size: 25, size: 25,
@@ -50,7 +49,7 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0), padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Chip( child: Chip(
backgroundColor: Theme.of(context).primaryColor.withOpacity(0.15), backgroundColor: context.primaryColor.withOpacity(0.15),
label: Text( label: Text(
user.email, user.email,
style: const TextStyle( style: const TextStyle(
@@ -124,7 +123,7 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
leading: IconButton( leading: IconButton(
icon: const Icon(Icons.close_rounded), icon: const Icon(Icons.close_rounded),
onPressed: () { onPressed: () {
AutoRouter.of(context).pop(null); context.autoPop(null);
}, },
), ),
actions: [ actions: [

View File

@@ -1,8 +1,8 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.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/album_title.provider.dart';
import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart'; import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
import 'package:immich_mobile/modules/album/providers/suggested_shared_users.provider.dart'; import 'package:immich_mobile/modules/album/providers/suggested_shared_users.provider.dart';
@@ -35,9 +35,9 @@ class SelectUserForSharingPage extends HookConsumerWidget {
await ref.watch(sharedAlbumProvider.notifier).getAllSharedAlbums(); await ref.watch(sharedAlbumProvider.notifier).getAllSharedAlbums();
// ref.watch(assetSelectionProvider.notifier).removeAll(); // ref.watch(assetSelectionProvider.notifier).removeAll();
ref.watch(albumTitleProvider.notifier).clearAlbumTitle(); ref.watch(albumTitleProvider.notifier).clearAlbumTitle();
AutoRouter.of(context).pop(true); context.autoPop(true);
AutoRouter.of(context) context
.navigate(const TabControllerRoute(children: [SharingRoute()])); .autoNavigate(const TabControllerRoute(children: [SharingRoute()]));
} }
ScaffoldMessenger( ScaffoldMessenger(
@@ -50,7 +50,7 @@ class SelectUserForSharingPage extends HookConsumerWidget {
buildTileIcon(User user) { buildTileIcon(User user) {
if (sharedUsersList.value.contains(user)) { if (sharedUsersList.value.contains(user)) {
return CircleAvatar( return CircleAvatar(
backgroundColor: Theme.of(context).primaryColor, backgroundColor: context.primaryColor,
child: const Icon( child: const Icon(
Icons.check_rounded, Icons.check_rounded,
size: 25, size: 25,
@@ -71,7 +71,7 @@ class SelectUserForSharingPage extends HookConsumerWidget {
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0), padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Chip( child: Chip(
backgroundColor: Theme.of(context).primaryColor.withOpacity(0.15), backgroundColor: context.primaryColor.withOpacity(0.15),
label: Text( label: Text(
user.email, user.email,
style: const TextStyle( style: const TextStyle(
@@ -139,20 +139,20 @@ class SelectUserForSharingPage extends HookConsumerWidget {
appBar: AppBar( appBar: AppBar(
title: Text( title: Text(
'share_invite', 'share_invite',
style: TextStyle(color: Theme.of(context).primaryColor), style: TextStyle(color: context.primaryColor),
).tr(), ).tr(),
elevation: 0, elevation: 0,
centerTitle: false, centerTitle: false,
leading: IconButton( leading: IconButton(
icon: const Icon(Icons.close_rounded), icon: const Icon(Icons.close_rounded),
onPressed: () async { onPressed: () async {
AutoRouter.of(context).pop(); context.autoPop();
}, },
), ),
actions: [ actions: [
TextButton( TextButton(
style: TextButton.styleFrom( style: TextButton.styleFrom(
foregroundColor: Theme.of(context).primaryColor, foregroundColor: context.primaryColor,
), ),
onPressed: sharedUsersList.value.isEmpty ? null : createSharedAlbum, onPressed: sharedUsersList.value.isEmpty ? null : createSharedAlbum,
child: const Text( child: const Text(
@@ -160,7 +160,7 @@ class SelectUserForSharingPage extends HookConsumerWidget {
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
// color: Theme.of(context).primaryColor, // color: context.primaryColor,
), ),
).tr(), ).tr(),
), ),

View File

@@ -1,8 +1,8 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart'; import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
import 'package:immich_mobile/modules/album/ui/album_thumbnail_card.dart'; import 'package:immich_mobile/modules/album/ui/album_thumbnail_card.dart';
import 'package:immich_mobile/modules/partner/providers/partner.provider.dart'; import 'package:immich_mobile/modules/partner/providers/partner.provider.dart';
@@ -21,7 +21,6 @@ class SharingPage extends HookConsumerWidget {
final List<Album> sharedAlbums = ref.watch(sharedAlbumProvider); final List<Album> sharedAlbums = ref.watch(sharedAlbumProvider);
final userId = ref.watch(currentUserProvider)?.id; final userId = ref.watch(currentUserProvider)?.id;
final partner = ref.watch(partnerSharedWithProvider); final partner = ref.watch(partnerSharedWithProvider);
var isDarkMode = Theme.of(context).brightness == Brightness.dark;
useEffect( useEffect(
() { () {
@@ -47,8 +46,9 @@ class SharingPage extends HookConsumerWidget {
album: sharedAlbums[index], album: sharedAlbums[index],
showOwner: true, showOwner: true,
onTap: () { onTap: () {
AutoRouter.of(context) context.autoPush(
.push(AlbumViewerRoute(albumId: sharedAlbums[index].id)); AlbumViewerRoute(albumId: sharedAlbums[index].id),
);
}, },
); );
}, },
@@ -79,11 +79,10 @@ class SharingPage extends HookConsumerWidget {
album.name, album.name,
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodyMedium?.copyWith( style: context.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: isDarkMode color:
? Theme.of(context).primaryColor context.isDarkTheme ? context.primaryColor : Colors.black,
: Colors.black,
), ),
), ),
subtitle: isOwner subtitle: isOwner
@@ -103,8 +102,9 @@ class SharingPage extends HookConsumerWidget {
) )
: null, : null,
onTap: () { onTap: () {
AutoRouter.of(context) context.autoPush(
.push(AlbumViewerRoute(albumId: sharedAlbums[index].id)); AlbumViewerRoute(albumId: sharedAlbums[index].id),
);
}, },
); );
}, },
@@ -127,8 +127,7 @@ class SharingPage extends HookConsumerWidget {
Expanded( Expanded(
child: ElevatedButton.icon( child: ElevatedButton.icon(
onPressed: () { onPressed: () {
AutoRouter.of(context) context.autoPush(CreateAlbumRoute(isSharedAlbum: true));
.push(CreateAlbumRoute(isSharedAlbum: true));
}, },
icon: const Icon( icon: const Icon(
Icons.photo_album_outlined, Icons.photo_album_outlined,
@@ -147,8 +146,7 @@ class SharingPage extends HookConsumerWidget {
const SizedBox(width: 12.0), const SizedBox(width: 12.0),
Expanded( Expanded(
child: ElevatedButton.icon( child: ElevatedButton.icon(
onPressed: () => onPressed: () => context.autoPush(const SharedLinkRoute()),
AutoRouter.of(context).push(const SharedLinkRoute()),
icon: const Icon( icon: const Icon(
Icons.link, Icons.link,
size: 20, size: 20,
@@ -191,21 +189,21 @@ class SharingPage extends HookConsumerWidget {
child: Icon( child: Icon(
Icons.insert_photo_rounded, Icons.insert_photo_rounded,
size: 50, size: 50,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
), ),
Padding( Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Text( child: Text(
'sharing_page_empty_list', 'sharing_page_empty_list',
style: Theme.of(context).textTheme.displaySmall, style: context.textTheme.displaySmall,
).tr(), ).tr(),
), ),
Padding( Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Text( child: Text(
'sharing_page_description', 'sharing_page_description',
style: Theme.of(context).textTheme.bodyMedium, style: context.textTheme.bodyMedium,
).tr(), ).tr(),
), ),
], ],
@@ -218,7 +216,7 @@ class SharingPage extends HookConsumerWidget {
Widget sharePartnerButton() { Widget sharePartnerButton() {
return InkWell( return InkWell(
onTap: () => AutoRouter.of(context).push(const PartnerRoute()), onTap: () => context.autoPush(const PartnerRoute()),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
child: const Icon( child: const Icon(
Icons.swap_horizontal_circle_rounded, Icons.swap_horizontal_circle_rounded,

View File

@@ -1,8 +1,8 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.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/archive/providers/archive_asset_provider.dart';
import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart'; import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart';
import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/models/asset.dart';
@@ -30,7 +30,7 @@ class ArchivePage extends HookConsumerWidget {
AppBar buildAppBar(String count) { AppBar buildAppBar(String count) {
return AppBar( return AppBar(
leading: IconButton( leading: IconButton(
onPressed: () => AutoRouter.of(context).pop(), onPressed: () => context.autoPop(),
icon: const Icon(Icons.arrow_back_ios_rounded), icon: const Icon(Icons.arrow_back_ios_rounded),
), ),
centerTitle: true, centerTitle: true,

View File

@@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/album/services/album.service.dart'; import 'package:immich_mobile/modules/album/services/album.service.dart';
import 'package:immich_mobile/modules/asset_viewer/models/image_viewer_page_state.model.dart'; import 'package:immich_mobile/modules/asset_viewer/models/image_viewer_page_state.model.dart';
import 'package:immich_mobile/modules/asset_viewer/services/image_viewer.service.dart'; import 'package:immich_mobile/modules/asset_viewer/services/image_viewer.service.dart';
@@ -67,7 +68,7 @@ class ImageViewerStateNotifier extends StateNotifier<ImageViewerPageState> {
gravity: ToastGravity.BOTTOM, gravity: ToastGravity.BOTTOM,
); );
} }
Navigator.of(buildContext).pop(); context.pop();
}, },
); );
return const ShareDialog(); return const ShareDialog();

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/models/asset.dart';
class AdvancedBottomSheet extends HookConsumerWidget { class AdvancedBottomSheet extends HookConsumerWidget {
@@ -11,8 +12,6 @@ class AdvancedBottomSheet extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
var isDarkMode = Theme.of(context).brightness == Brightness.dark;
return SingleChildScrollView( return SingleChildScrollView(
child: Card( child: Card(
shape: const RoundedRectangleBorder( shape: const RoundedRectangleBorder(
@@ -40,7 +39,9 @@ class AdvancedBottomSheet extends HookConsumerWidget {
const SizedBox(height: 32.0), const SizedBox(height: 32.0),
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[900] : Colors.grey[200], color: context.isDarkTheme
? Colors.grey[900]
: Colors.grey[200],
borderRadius: BorderRadius.circular(15.0), borderRadius: BorderRadius.circular(15.0),
), ),
child: Padding( child: Padding(
@@ -70,7 +71,7 @@ class AdvancedBottomSheet extends HookConsumerWidget {
icon: Icon( icon: Icon(
Icons.copy, Icons.copy,
size: 16.0, size: 16.0,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
), ),
), ),

View File

@@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/asset_viewer/providers/asset_description.provider.dart'; import 'package:immich_mobile/modules/asset_viewer/providers/asset_description.provider.dart';
import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/providers/user.provider.dart'; import 'package:immich_mobile/shared/providers/user.provider.dart';
@@ -19,8 +20,7 @@ class DescriptionInput extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final isDarkTheme = Theme.of(context).brightness == Brightness.dark; final textColor = context.isDarkTheme ? Colors.white : Colors.black;
final textColor = isDarkTheme ? Colors.white : Colors.black;
final controller = useTextEditingController(); final controller = useTextEditingController();
final focusNode = useFocusNode(); final focusNode = useFocusNode();
final isFocus = useState(false); final isFocus = useState(false);

View File

@@ -4,6 +4,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/flutter_map.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:timezone/timezone.dart'; import 'package:timezone/timezone.dart';
import 'package:immich_mobile/modules/asset_viewer/ui/description_input.dart'; import 'package:immich_mobile/modules/asset_viewer/ui/description_input.dart';
import 'package:immich_mobile/modules/map/ui/map_thumbnail.dart'; import 'package:immich_mobile/modules/map/ui/map_thumbnail.dart';
@@ -28,7 +29,7 @@ class ExifBottomSheet extends HookConsumerWidget {
exifInfo.longitude != 0; exifInfo.longitude != 0;
String formatTimeZone(Duration d) => String formatTimeZone(Duration d) =>
"GMT${d.isNegative ? '-': '+'}${d.inHours.abs().toString().padLeft(2, '0')}:${d.inMinutes.abs().remainder(60).toString().padLeft(2, '0')}"; "GMT${d.isNegative ? '-' : '+'}${d.inHours.abs().toString().padLeft(2, '0')}:${d.inMinutes.abs().remainder(60).toString().padLeft(2, '0')}";
String get formattedDateTime { String get formattedDateTime {
DateTime dt = asset.fileCreatedAt.toLocal(); DateTime dt = asset.fileCreatedAt.toLocal();
@@ -41,10 +42,16 @@ class ExifBottomSheet extends HookConsumerWidget {
final location = getLocation(asset.exifInfo!.timeZone!); final location = getLocation(asset.exifInfo!.timeZone!);
dt = TZDateTime.from(dt, location); dt = TZDateTime.from(dt, location);
} on LocationNotFoundException { } on LocationNotFoundException {
RegExp re = RegExp(r'^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$', caseSensitive: false); RegExp re = RegExp(
r'^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$',
caseSensitive: false,
);
final m = re.firstMatch(asset.exifInfo!.timeZone!); final m = re.firstMatch(asset.exifInfo!.timeZone!);
if (m != null) { if (m != null) {
final duration = Duration(hours: int.parse(m.group(1) ?? '0'), minutes: int.parse(m.group(2) ?? '0')); final duration = Duration(
hours: int.parse(m.group(1) ?? '0'),
minutes: int.parse(m.group(2) ?? '0'),
);
dt = dt.add(duration); dt = dt.add(duration);
timeZone = formatTimeZone(duration); timeZone = formatTimeZone(duration);
} }
@@ -105,8 +112,7 @@ class ExifBottomSheet extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final assetWithExif = ref.watch(assetDetailProvider(asset)); final assetWithExif = ref.watch(assetDetailProvider(asset));
final exifInfo = (assetWithExif.value ?? asset).exifInfo; final exifInfo = (assetWithExif.value ?? asset).exifInfo;
var isDarkTheme = Theme.of(context).brightness == Brightness.dark; var textColor = context.isDarkTheme ? Colors.white : Colors.black;
var textColor = isDarkTheme ? Colors.white : Colors.black;
buildMap() { buildMap() {
return Padding( return Padding(
@@ -322,9 +328,14 @@ class ExifBottomSheet extends HookConsumerWidget {
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
subtitle: exifInfo.f != null || exifInfo.exposureSeconds != null || exifInfo.mm != null || exifInfo.iso != null ? Text( subtitle: exifInfo.f != null ||
exifInfo.exposureSeconds != null ||
exifInfo.mm != null ||
exifInfo.iso != null
? Text(
"ƒ/${exifInfo.fNumber} ${exifInfo.exposureTime} ${exifInfo.focalLength} mm ISO ${exifInfo.iso ?? ''} ", "ƒ/${exifInfo.fNumber} ${exifInfo.exposureTime} ${exifInfo.focalLength} mm ISO ${exifInfo.iso ?? ''} ",
) : null, )
: null,
), ),
], ],
); );
@@ -393,7 +404,7 @@ class ExifBottomSheet extends HookConsumerWidget {
data: (data) => DescriptionInput(asset: data), data: (data) => DescriptionInput(asset: data),
error: (error, stackTrace) => Icon( error: (error, stackTrace) => Icon(
Icons.image_not_supported_outlined, Icons.image_not_supported_outlined,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
loading: () => const SizedBox( loading: () => const SizedBox(
width: 75, width: 75,

View File

@@ -1,6 +1,6 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/activities/providers/activity.provider.dart'; import 'package:immich_mobile/modules/activities/providers/activity.provider.dart';
import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/providers/asset.provider.dart'; import 'package:immich_mobile/shared/providers/asset.provider.dart';
@@ -147,7 +147,7 @@ class TopControlAppBar extends HookConsumerWidget {
Widget buildBackButton() { Widget buildBackButton() {
return IconButton( return IconButton(
onPressed: () { onPressed: () {
AutoRouter.of(context).pop(); context.autoPop();
}, },
icon: Icon( icon: Icon(
Icons.arrow_back_ios_new_rounded, Icons.arrow_back_ios_new_rounded,

View File

@@ -8,6 +8,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart' hide Store; import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/asset_viewer/providers/asset_stack.provider.dart'; import 'package:immich_mobile/modules/asset_viewer/providers/asset_stack.provider.dart';
import 'package:immich_mobile/modules/asset_viewer/providers/show_controls.provider.dart'; import 'package:immich_mobile/modules/asset_viewer/providers/show_controls.provider.dart';
import 'package:immich_mobile/modules/asset_viewer/providers/video_player_controls_provider.dart'; import 'package:immich_mobile/modules/asset_viewer/providers/video_player_controls_provider.dart';
@@ -209,7 +210,7 @@ class GalleryViewerPage extends HookConsumerWidget {
if (isDeleted && isParent) { if (isDeleted && isParent) {
if (totalAssets == 1) { if (totalAssets == 1) {
// Handle only one asset // Handle only one asset
AutoRouter.of(context).pop(); context.autoPop();
} else { } else {
// Go to next page otherwise // Go to next page otherwise
controller.nextPage( controller.nextPage(
@@ -293,7 +294,7 @@ class GalleryViewerPage extends HookConsumerWidget {
final ratio = d.dy / max(d.dx.abs(), 1); final ratio = d.dy / max(d.dx.abs(), 1);
if (d.dy > sensitivity && ratio > ratioThreshold) { if (d.dy > sensitivity && ratio > ratioThreshold) {
AutoRouter.of(context).pop(); context.autoPop();
} else if (d.dy < -sensitivity && ratio < -ratioThreshold) { } else if (d.dy < -sensitivity && ratio < -ratioThreshold) {
showInfo(); showInfo();
} }
@@ -308,7 +309,7 @@ class GalleryViewerPage extends HookConsumerWidget {
.watch(assetProvider.notifier) .watch(assetProvider.notifier)
.toggleArchive([asset], !asset.isArchived); .toggleArchive([asset], !asset.isArchived);
if (isParent) { if (isParent) {
AutoRouter.of(context).pop(); context.autoPop();
return; return;
} }
removeAssetFromStack(); removeAssetFromStack();
@@ -331,7 +332,7 @@ class GalleryViewerPage extends HookConsumerWidget {
handleActivities() { handleActivities() {
if (sharedAlbumId != null) { if (sharedAlbumId != null) {
AutoRouter.of(context).push( context.autoPush(
ActivitiesRoute( ActivitiesRoute(
albumId: sharedAlbumId!, albumId: sharedAlbumId!,
assetId: asset().remoteId, assetId: asset().remoteId,
@@ -514,7 +515,7 @@ class GalleryViewerPage extends HookConsumerWidget {
stackElements.elementAt(stackIndex.value), stackElements.elementAt(stackIndex.value),
); );
Navigator.pop(ctx); Navigator.pop(ctx);
AutoRouter.of(context).pop(); context.autoPop();
}, },
title: const Text( title: const Text(
"viewer_stack_use_as_main_asset", "viewer_stack_use_as_main_asset",
@@ -541,7 +542,7 @@ class GalleryViewerPage extends HookConsumerWidget {
childrenToRemove: [currentAsset], childrenToRemove: [currentAsset],
); );
Navigator.pop(ctx); Navigator.pop(ctx);
AutoRouter.of(context).pop(); context.autoPop();
} else { } else {
await ref.read(assetStackServiceProvider).updateStack( await ref.read(assetStackServiceProvider).updateStack(
currentAsset, currentAsset,
@@ -569,7 +570,7 @@ class GalleryViewerPage extends HookConsumerWidget {
childrenToRemove: stack, childrenToRemove: stack,
); );
Navigator.pop(ctx); Navigator.pop(ctx);
AutoRouter.of(context).pop(); context.autoPop();
}, },
title: const Text( title: const Text(
"viewer_unstack", "viewer_unstack",
@@ -829,8 +830,8 @@ class GalleryViewerPage extends HookConsumerWidget {
placeholder: Image( placeholder: Image(
image: provider, image: provider,
fit: BoxFit.fitWidth, fit: BoxFit.fitWidth,
height: MediaQuery.of(context).size.height, height: context.height,
width: MediaQuery.of(context).size.width, width: context.width,
alignment: Alignment.center, alignment: Alignment.center,
), ),
onVideoEnded: () { onVideoEnded: () {

View File

@@ -3,6 +3,7 @@ import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:chewie/chewie.dart'; import 'package:chewie/chewie.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/asset_viewer/models/image_viewer_page_state.model.dart'; import 'package:immich_mobile/modules/asset_viewer/models/image_viewer_page_state.model.dart';
import 'package:immich_mobile/modules/asset_viewer/providers/image_viewer_page_state.provider.dart'; import 'package:immich_mobile/modules/asset_viewer/providers/image_viewer_page_state.provider.dart';
import 'package:immich_mobile/modules/asset_viewer/ui/video_player_controls.dart'; import 'package:immich_mobile/modules/asset_viewer/ui/video_player_controls.dart';
@@ -44,7 +45,7 @@ class VideoViewerPage extends HookConsumerWidget {
), ),
error: (error, stackTrace) => Icon( error: (error, stackTrace) => Icon(
Icons.image_not_supported_outlined, Icons.image_not_supported_outlined,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
loading: () => const Center( loading: () => const Center(
child: SizedBox( child: SizedBox(
@@ -74,8 +75,8 @@ class VideoViewerPage extends HookConsumerWidget {
), ),
if (downloadAssetStatus == DownloadAssetStatus.loading) if (downloadAssetStatus == DownloadAssetStatus.loading)
SizedBox( SizedBox(
height: MediaQuery.of(context).size.height, height: context.height,
width: MediaQuery.of(context).size.width, width: context.width,
child: const Center( child: const Center(
child: ImmichLoadingIndicator(), child: ImmichLoadingIndicator(),
), ),
@@ -205,8 +206,8 @@ class _VideoPlayerState extends State<VideoPlayer> {
); );
} else { } else {
return SizedBox( return SizedBox(
height: MediaQuery.of(context).size.height, height: context.height,
width: MediaQuery.of(context).size.width, width: context.width,
child: Center( child: Center(
child: Stack( child: Stack(
children: [ children: [

View File

@@ -1,9 +1,9 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/backup/models/available_album.model.dart'; import 'package:immich_mobile/modules/backup/models/available_album.model.dart';
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart'; import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
@@ -22,10 +22,10 @@ class AlbumInfoCard extends HookConsumerWidget {
ref.watch(backupProvider).selectedBackupAlbums.contains(albumInfo); ref.watch(backupProvider).selectedBackupAlbums.contains(albumInfo);
final bool isExcluded = final bool isExcluded =
ref.watch(backupProvider).excludedBackupAlbums.contains(albumInfo); ref.watch(backupProvider).excludedBackupAlbums.contains(albumInfo);
final isDarkTheme = Theme.of(context).brightness == Brightness.dark; final isDarkTheme = context.isDarkTheme;
ColorFilter selectedFilter = ColorFilter.mode( ColorFilter selectedFilter = ColorFilter.mode(
Theme.of(context).primaryColor.withAlpha(100), context.primaryColor.withAlpha(100),
BlendMode.darken, BlendMode.darken,
); );
ColorFilter excludedFilter = ColorFilter excludedFilter =
@@ -46,7 +46,7 @@ class AlbumInfoCard extends HookConsumerWidget {
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
).tr(), ).tr(),
backgroundColor: Theme.of(context).primaryColor, backgroundColor: context.primaryColor,
); );
} else if (isExcluded) { } else if (isExcluded) {
return Chip( return Chip(
@@ -194,7 +194,7 @@ class AlbumInfoCard extends HookConsumerWidget {
albumInfo.name, albumInfo.name,
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
color: Theme.of(context).primaryColor, color: context.primaryColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
@@ -224,13 +224,13 @@ class AlbumInfoCard extends HookConsumerWidget {
), ),
IconButton( IconButton(
onPressed: () { onPressed: () {
AutoRouter.of(context).push( context.autoPush(
AlbumPreviewRoute(album: albumInfo.albumEntity), AlbumPreviewRoute(album: albumInfo.albumEntity),
); );
}, },
icon: Icon( icon: Icon(
Icons.image_outlined, Icons.image_outlined,
color: Theme.of(context).primaryColor, color: context.primaryColor,
size: 24, size: 24,
), ),
splashRadius: 25, splashRadius: 25,

View File

@@ -1,10 +1,10 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/backup/models/available_album.model.dart'; import 'package:immich_mobile/modules/backup/models/available_album.model.dart';
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart'; import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
@@ -25,14 +25,13 @@ class AlbumInfoListTile extends HookConsumerWidget {
ref.watch(backupProvider).excludedBackupAlbums.contains(albumInfo); ref.watch(backupProvider).excludedBackupAlbums.contains(albumInfo);
ColorFilter selectedFilter = ColorFilter.mode( ColorFilter selectedFilter = ColorFilter.mode(
Theme.of(context).primaryColor.withAlpha(100), context.primaryColor.withAlpha(100),
BlendMode.darken, BlendMode.darken,
); );
ColorFilter excludedFilter = ColorFilter excludedFilter =
ColorFilter.mode(Colors.red.withAlpha(75), BlendMode.darken); ColorFilter.mode(Colors.red.withAlpha(75), BlendMode.darken);
ColorFilter unselectedFilter = ColorFilter unselectedFilter =
const ColorFilter.mode(Colors.black, BlendMode.color); const ColorFilter.mode(Colors.black, BlendMode.color);
var isDarkTheme = Theme.of(context).brightness == Brightness.dark;
var assetCount = useState(0); var assetCount = useState(0);
@@ -56,11 +55,11 @@ class AlbumInfoListTile extends HookConsumerWidget {
buildTileColor() { buildTileColor() {
if (isSelected) { if (isSelected) {
return isDarkTheme return context.isDarkTheme
? Theme.of(context).primaryColor.withAlpha(100) ? context.primaryColor.withAlpha(100)
: Theme.of(context).primaryColor.withAlpha(25); : context.primaryColor.withAlpha(25);
} else if (isExcluded) { } else if (isExcluded) {
return isDarkTheme return context.isDarkTheme
? Colors.red[300]?.withAlpha(150) ? Colors.red[300]?.withAlpha(150)
: Colors.red[100]?.withAlpha(150); : Colors.red[100]?.withAlpha(150);
} else { } else {
@@ -159,13 +158,13 @@ class AlbumInfoListTile extends HookConsumerWidget {
subtitle: Text(assetCount.value.toString()), subtitle: Text(assetCount.value.toString()),
trailing: IconButton( trailing: IconButton(
onPressed: () { onPressed: () {
AutoRouter.of(context).push( context.autoPush(
AlbumPreviewRoute(album: albumInfo.albumEntity), AlbumPreviewRoute(album: albumInfo.albumEntity),
); );
}, },
icon: Icon( icon: Icon(
Icons.image_outlined, Icons.image_outlined,
color: Theme.of(context).primaryColor, color: context.primaryColor,
size: 24, size: 24,
), ),
splashRadius: 25, splashRadius: 25,

View File

@@ -1,5 +1,6 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
class BackupInfoCard extends StatelessWidget { class BackupInfoCard extends StatelessWidget {
final String title; final String title;
@@ -14,13 +15,11 @@ class BackupInfoCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var isDarkMode = Theme.of(context).brightness == Brightness.dark;
return Card( return Card(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20), // if you need this borderRadius: BorderRadius.circular(20), // if you need this
side: BorderSide( side: BorderSide(
color: isDarkMode color: context.isDarkTheme
? const Color.fromARGB(255, 56, 56, 56) ? const Color.fromARGB(255, 56, 56, 56)
: Colors.black12, : Colors.black12,
width: 1, width: 1,

View File

@@ -1,9 +1,9 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/backup/models/backup_state.model.dart'; import 'package:immich_mobile/modules/backup/models/backup_state.model.dart';
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart'; import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
import 'package:immich_mobile/modules/backup/providers/error_backup_list.provider.dart'; import 'package:immich_mobile/modules/backup/providers/error_backup_list.provider.dart';
@@ -53,7 +53,7 @@ class CurrentUploadingAssetInfoBox extends HookConsumerWidget {
), ),
backgroundColor: Colors.white, backgroundColor: Colors.white,
onPressed: () { onPressed: () {
AutoRouter.of(context).push(const FailedBackupStatusRoute()); context.autoPush(const FailedBackupStatusRoute());
}, },
); );
} }
@@ -61,7 +61,7 @@ class CurrentUploadingAssetInfoBox extends HookConsumerWidget {
Widget buildAssetInfoTable() { Widget buildAssetInfoTable() {
return Table( return Table(
border: TableBorder.all( border: TableBorder.all(
color: Theme.of(context).primaryColorLight, color: context.themeData.primaryColorLight,
width: 1, width: 1,
), ),
children: [ children: [
@@ -176,7 +176,7 @@ class CurrentUploadingAssetInfoBox extends HookConsumerWidget {
onTap: () => isShowThumbnail.value = true, onTap: () => isShowThumbnail.value = true,
child: Icon( child: Icon(
Icons.image_outlined, Icons.image_outlined,
color: Theme.of(context).primaryColor, color: context.primaryColor,
size: 30, size: 30,
), ),
), ),
@@ -206,7 +206,7 @@ class CurrentUploadingAssetInfoBox extends HookConsumerWidget {
minHeight: 10.0, minHeight: 10.0,
value: uploadProgress / 100.0, value: uploadProgress / 100.0,
backgroundColor: Colors.grey, backgroundColor: Colors.grey,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
), ),
Text( Text(

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/backup/providers/ios_background_settings.provider.dart'; import 'package:immich_mobile/modules/backup/providers/ios_background_settings.provider.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
@@ -43,7 +44,7 @@ class IosDebugInfoTile extends HookConsumerWidget {
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 14, fontSize: 14,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
), ),
subtitle: Text( subtitle: Text(
@@ -54,7 +55,7 @@ class IosDebugInfoTile extends HookConsumerWidget {
), ),
leading: Icon( leading: Icon(
Icons.bug_report, Icons.bug_report,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
); );
} }

View File

@@ -1,9 +1,9 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
import 'package:photo_manager/photo_manager.dart'; import 'package:photo_manager/photo_manager.dart';
@@ -53,7 +53,7 @@ class AlbumPreviewPage extends HookConsumerWidget {
], ],
), ),
leading: IconButton( leading: IconButton(
onPressed: () => AutoRouter.of(context).pop(), onPressed: () => context.autoPop(),
icon: const Icon(Icons.arrow_back_ios_new_rounded), icon: const Icon(Icons.arrow_back_ios_new_rounded),
), ),
), ),

View File

@@ -1,10 +1,10 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/immich_colors.dart'; import 'package:immich_mobile/constants/immich_colors.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart'; import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
import 'package:immich_mobile/modules/backup/ui/album_info_card.dart'; import 'package:immich_mobile/modules/backup/ui/album_info_card.dart';
import 'package:immich_mobile/modules/backup/ui/album_info_list_tile.dart'; import 'package:immich_mobile/modules/backup/ui/album_info_list_tile.dart';
@@ -18,7 +18,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
// final availableAlbums = ref.watch(backupProvider).availableAlbums; // final availableAlbums = ref.watch(backupProvider).availableAlbums;
final selectedBackupAlbums = ref.watch(backupProvider).selectedBackupAlbums; final selectedBackupAlbums = ref.watch(backupProvider).selectedBackupAlbums;
final excludedBackupAlbums = ref.watch(backupProvider).excludedBackupAlbums; final excludedBackupAlbums = ref.watch(backupProvider).excludedBackupAlbums;
final isDarkTheme = Theme.of(context).brightness == Brightness.dark; final isDarkTheme = context.isDarkTheme;
final allAlbums = ref.watch(backupProvider).availableAlbums; final allAlbums = ref.watch(backupProvider).availableAlbums;
// Albums which are displayed to the user // Albums which are displayed to the user
@@ -118,7 +118,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
backgroundColor: Theme.of(context).primaryColor, backgroundColor: context.primaryColor,
deleteIconColor: isDarkTheme ? Colors.black : Colors.white, deleteIconColor: isDarkTheme ? Colors.black : Colors.white,
deleteIcon: const Icon( deleteIcon: const Icon(
Icons.cancel_rounded, Icons.cancel_rounded,
@@ -211,7 +211,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
leading: IconButton( leading: IconButton(
onPressed: () => AutoRouter.of(context).pop(), onPressed: () => context.autoPop(),
icon: const Icon(Icons.arrow_back_ios_rounded), icon: const Icon(Icons.arrow_back_ios_rounded),
), ),
title: const Text( title: const Text(
@@ -315,7 +315,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
"backup_album_selection_page_albums_tap", "backup_album_selection_page_albums_tap",
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
color: Theme.of(context).primaryColor, color: context.primaryColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
).tr(), ).tr(),
@@ -325,7 +325,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
icon: Icon( icon: Icon(
Icons.info, Icons.info,
size: 20, size: 20,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
onPressed: () { onPressed: () {
// show the dialog // show the dialog
@@ -342,7 +342,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
).tr(), ).tr(),
content: SingleChildScrollView( content: SingleChildScrollView(

View File

@@ -1,11 +1,11 @@
import 'dart:io'; import 'dart:io';
import 'package:auto_route/auto_route.dart';
import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/backup/background_service/background.service.dart'; import 'package:immich_mobile/modules/backup/background_service/background.service.dart';
import 'package:immich_mobile/modules/backup/providers/error_backup_list.provider.dart'; import 'package:immich_mobile/modules/backup/providers/error_backup_list.provider.dart';
import 'package:immich_mobile/modules/backup/providers/ios_background_settings.provider.dart'; import 'package:immich_mobile/modules/backup/providers/ios_background_settings.provider.dart';
@@ -49,7 +49,6 @@ class BackupControllerPage extends HookConsumerWidget {
!hasExclusiveAccess !hasExclusiveAccess
? false ? false
: true; : true;
var isDarkMode = Theme.of(context).brightness == Brightness.dark;
final checkInProgress = useState(false); final checkInProgress = useState(false);
useEffect( useEffect(
@@ -151,7 +150,7 @@ class BackupControllerPage extends HookConsumerWidget {
return ListTile( return ListTile(
leading: Icon( leading: Icon(
Icons.warning_rounded, Icons.warning_rounded,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
title: const Text( title: const Text(
"Check for corrupt asset backups", "Check for corrupt asset backups",
@@ -187,7 +186,7 @@ class BackupControllerPage extends HookConsumerWidget {
leading: isAutoBackup leading: isAutoBackup
? Icon( ? Icon(
Icons.cloud_done_rounded, Icons.cloud_done_rounded,
color: Theme.of(context).primaryColor, color: context.primaryColor,
) )
: const Icon(Icons.cloud_off_rounded), : const Icon(Icons.cloud_off_rounded),
title: Text( title: Text(
@@ -266,7 +265,7 @@ class BackupControllerPage extends HookConsumerWidget {
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12), style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
).tr(), ).tr(),
onPressed: () { onPressed: () {
Navigator.of(context).pop(); context.pop();
}, },
), ),
], ],
@@ -279,7 +278,7 @@ class BackupControllerPage extends HookConsumerWidget {
final bool isBackgroundEnabled = backupState.backgroundBackup; final bool isBackgroundEnabled = backupState.backgroundBackup;
final bool isWifiRequired = backupState.backupRequireWifi; final bool isWifiRequired = backupState.backupRequireWifi;
final bool isChargingRequired = backupState.backupRequireCharging; final bool isChargingRequired = backupState.backupRequireCharging;
final Color activeColor = Theme.of(context).primaryColor; final Color activeColor = context.primaryColor;
String formatBackupDelaySliderValue(double v) { String formatBackupDelaySliderValue(double v) {
if (v == 0.0) { if (v == 0.0) {
@@ -410,7 +409,7 @@ class BackupControllerPage extends HookConsumerWidget {
max: 3.0, max: 3.0,
divisions: 3, divisions: 3,
label: formatBackupDelaySliderValue(triggerDelay.value), label: formatBackupDelaySliderValue(triggerDelay.value),
activeColor: Theme.of(context).primaryColor, activeColor: context.primaryColor,
), ),
), ),
ElevatedButton( ElevatedButton(
@@ -511,7 +510,7 @@ class BackupControllerPage extends HookConsumerWidget {
child: Text( child: Text(
text.trim().substring(0, text.length - 2), text.trim().substring(0, text.length - 2),
style: TextStyle( style: TextStyle(
color: Theme.of(context).primaryColor, color: context.primaryColor,
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -523,7 +522,7 @@ class BackupControllerPage extends HookConsumerWidget {
child: Text( child: Text(
"backup_controller_page_none_selected".tr(), "backup_controller_page_none_selected".tr(),
style: TextStyle( style: TextStyle(
color: Theme.of(context).primaryColor, color: context.primaryColor,
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -562,7 +561,7 @@ class BackupControllerPage extends HookConsumerWidget {
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
side: BorderSide( side: BorderSide(
color: isDarkMode color: context.isDarkTheme
? const Color.fromARGB(255, 56, 56, 56) ? const Color.fromARGB(255, 56, 56, 56)
: Colors.black12, : Colors.black12,
width: 1, width: 1,
@@ -592,7 +591,7 @@ class BackupControllerPage extends HookConsumerWidget {
), ),
trailing: ElevatedButton( trailing: ElevatedButton(
onPressed: () { onPressed: () {
AutoRouter.of(context).push(const BackupAlbumSelectionRoute()); context.autoPush(const BackupAlbumSelectionRoute());
}, },
child: const Text( child: const Text(
"backup_controller_page_select", "backup_controller_page_select",
@@ -678,7 +677,7 @@ class BackupControllerPage extends HookConsumerWidget {
leading: IconButton( leading: IconButton(
onPressed: () { onPressed: () {
ref.watch(websocketProvider.notifier).listenUploadEvent(); ref.watch(websocketProvider.notifier).listenUploadEvent();
AutoRouter.of(context).pop(true); context.autoPop(true);
}, },
splashRadius: 24, splashRadius: 24,
icon: const Icon( icon: const Icon(

View File

@@ -1,6 +1,6 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/backup/providers/error_backup_list.provider.dart'; import 'package:immich_mobile/modules/backup/providers/error_backup_list.provider.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:photo_manager/photo_manager.dart'; import 'package:photo_manager/photo_manager.dart';
@@ -20,7 +20,7 @@ class FailedBackupStatusPage extends HookConsumerWidget {
), ),
leading: IconButton( leading: IconButton(
onPressed: () { onPressed: () {
AutoRouter.of(context).pop(true); context.autoPop(true);
}, },
splashRadius: 24, splashRadius: 24,
icon: const Icon( icon: const Icon(
@@ -114,7 +114,7 @@ class FailedBackupStatusPage extends HookConsumerWidget {
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 12, fontSize: 12,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
), ),
), ),

View File

@@ -1,8 +1,8 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.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/favorite/providers/favorite_provider.dart';
import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart'; import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart';
import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/models/asset.dart';
@@ -28,7 +28,7 @@ class FavoritesPage extends HookConsumerWidget {
AppBar buildAppBar() { AppBar buildAppBar() {
return AppBar( return AppBar(
leading: IconButton( leading: IconButton(
onPressed: () => AutoRouter.of(context).pop(), onPressed: () => context.autoPop(),
icon: const Icon(Icons.arrow_back_ios_rounded), icon: const Icon(Icons.arrow_back_ios_rounded),
), ),
centerTitle: true, centerTitle: true,

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
class GroupDividerTitle extends ConsumerWidget { class GroupDividerTitle extends ConsumerWidget {
const GroupDividerTitle({ const GroupDividerTitle({
@@ -51,7 +52,7 @@ class GroupDividerTitle extends ConsumerWidget {
child: multiselectEnabled && selected child: multiselectEnabled && selected
? Icon( ? Icon(
Icons.check_circle_rounded, Icons.check_circle_rounded,
color: Theme.of(context).primaryColor, color: context.primaryColor,
) )
: const Icon( : const Icon(
Icons.check_circle_outline_rounded, Icons.check_circle_outline_rounded,

View File

@@ -4,10 +4,11 @@ import 'dart:math';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/asset_viewer/providers/scroll_notifier.provider.dart'; import 'package:immich_mobile/modules/asset_viewer/providers/scroll_notifier.provider.dart';
import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_image.dart'; import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_image.dart';
import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/utils/builtin_extensions.dart'; import 'package:immich_mobile/extensions/collection_extensions.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import 'asset_grid_data_structure.dart'; import 'asset_grid_data_structure.dart';
import 'group_divider_title.dart'; import 'group_divider_title.dart';
@@ -224,7 +225,7 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
style: TextStyle( style: TextStyle(
fontSize: 26, fontSize: 26,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Theme.of(context).textTheme.displayLarge?.color, color: context.textTheme.displayLarge?.color,
), ),
), ),
); );
@@ -372,7 +373,7 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
scrollStateListener: dragScrolling, scrollStateListener: dragScrolling,
itemPositionsListener: _itemPositionsListener, itemPositionsListener: _itemPositionsListener,
controller: _itemScrollController, controller: _itemScrollController,
backgroundColor: Theme.of(context).hintColor, backgroundColor: context.themeData.hintColor,
labelTextBuilder: _labelBuilder, labelTextBuilder: _labelBuilder,
labelConstraints: const BoxConstraints(maxHeight: 28), labelConstraints: const BoxConstraints(maxHeight: 28),
scrollbarAnimationDuration: const Duration(milliseconds: 300), scrollbarAnimationDuration: const Duration(milliseconds: 300),

View File

@@ -1,6 +1,6 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/ui/immich_image.dart'; import 'package:immich_mobile/shared/ui/immich_image.dart';
@@ -43,9 +43,9 @@ class ThumbnailImage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDarkTheme = Theme.of(context).brightness == Brightness.dark; final assetContainerColor = context.isDarkTheme
final assetContainerColor = ? Colors.blueGrey
isDarkTheme ? Colors.blueGrey : Theme.of(context).primaryColorLight; : context.themeData.primaryColorLight;
// Assets from response DTOs do not have an isar id, querying which would give us the default autoIncrement id // Assets from response DTOs do not have an isar id, querying which would give us the default autoIncrement id
final isFromDto = asset.id == Isar.autoIncrement; final isFromDto = asset.id == Isar.autoIncrement;
@@ -58,7 +58,7 @@ class ThumbnailImage extends StatelessWidget {
), ),
child: Icon( child: Icon(
Icons.check_circle_rounded, Icons.check_circle_rounded,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
); );
} else { } else {
@@ -178,7 +178,7 @@ class ThumbnailImage extends StatelessWidget {
onSelect?.call(); onSelect?.call();
} }
} else { } else {
AutoRouter.of(context).push( context.autoPush(
GalleryViewerRoute( GalleryViewerRoute(
initialIndex: index, initialIndex: index,
loadAsset: loadAsset, loadAsset: loadAsset,

View File

@@ -1,6 +1,7 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/album/ui/add_to_album_sliverlist.dart'; import 'package:immich_mobile/modules/album/ui/add_to_album_sliverlist.dart';
import 'package:immich_mobile/modules/home/models/selection_state.dart'; import 'package:immich_mobile/modules/home/models/selection_state.dart';
import 'package:immich_mobile/modules/home/ui/delete_dialog.dart'; import 'package:immich_mobile/modules/home/ui/delete_dialog.dart';
@@ -42,7 +43,6 @@ class ControlBottomAppBar extends ConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
var isDarkMode = Theme.of(context).brightness == Brightness.dark;
var hasRemote = var hasRemote =
selectionAssetState.hasRemote || selectionAssetState.hasMerged; selectionAssetState.hasRemote || selectionAssetState.hasMerged;
var hasLocal = selectionAssetState.hasLocal; var hasLocal = selectionAssetState.hasLocal;
@@ -128,7 +128,7 @@ class ControlBottomAppBar extends ConsumerWidget {
ScrollController scrollController, ScrollController scrollController,
) { ) {
return Card( return Card(
color: isDarkMode ? Colors.grey[900] : Colors.grey[100], color: context.isDarkTheme ? Colors.grey[900] : Colors.grey[100],
surfaceTintColor: Colors.transparent, surfaceTintColor: Colors.transparent,
elevation: 18.0, elevation: 18.0,
shape: const RoundedRectangleBorder( shape: const RoundedRectangleBorder(
@@ -211,12 +211,12 @@ class AddToAlbumTitleRow extends StatelessWidget {
onPressed: onCreateNewAlbum, onPressed: onCreateNewAlbum,
icon: Icon( icon: Icon(
Icons.add, Icons.add,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
label: Text( label: Text(
"common_create_new_album", "common_create_new_album",
style: TextStyle( style: TextStyle(
color: Theme.of(context).primaryColor, color: context.primaryColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 14, fontSize: 14,
), ),

View File

@@ -1,12 +1,12 @@
import 'dart:async'; import 'dart:async';
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/album/providers/album.provider.dart'; import 'package:immich_mobile/modules/album/providers/album.provider.dart';
import 'package:immich_mobile/modules/album/providers/album_detail.provider.dart'; import 'package:immich_mobile/modules/album/providers/album_detail.provider.dart';
import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart'; import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
@@ -106,8 +106,7 @@ class HomePage extends HookConsumerWidget {
handleShareAssets(ref, context, selection.value.toList()); handleShareAssets(ref, context, selection.value.toList());
} else { } else {
final ids = remoteOnlySelection().map((e) => e.remoteId!); final ids = remoteOnlySelection().map((e) => e.remoteId!);
AutoRouter.of(context) context.autoPush(SharedLinkEditRoute(assetsList: ids.toList()));
.push(SharedLinkEditRoute(assetsList: ids.toList()));
} }
processing.value = false; processing.value = false;
selectionEnabledHook.value = false; selectionEnabledHook.value = false;
@@ -243,7 +242,7 @@ class HomePage extends HookConsumerWidget {
ref.watch(sharedAlbumProvider.notifier).getAllSharedAlbums(); ref.watch(sharedAlbumProvider.notifier).getAllSharedAlbums();
selectionEnabledHook.value = false; selectionEnabledHook.value = false;
AutoRouter.of(context).push(AlbumViewerRoute(albumId: result.id)); context.autoPush(AlbumViewerRoute(albumId: result.id));
} }
} finally { } finally {
processing.value = false; processing.value = false;
@@ -300,7 +299,7 @@ class HomePage extends HookConsumerWidget {
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
fontSize: 16, fontSize: 16,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
).tr(), ).tr(),
), ),

View File

@@ -3,8 +3,7 @@ class AuthenticationState {
final String userId; final String userId;
final String userEmail; final String userEmail;
final bool isAuthenticated; final bool isAuthenticated;
final String firstName; final String name;
final String lastName;
final bool isAdmin; final bool isAdmin;
final bool shouldChangePassword; final bool shouldChangePassword;
final String profileImagePath; final String profileImagePath;
@@ -13,8 +12,7 @@ class AuthenticationState {
required this.userId, required this.userId,
required this.userEmail, required this.userEmail,
required this.isAuthenticated, required this.isAuthenticated,
required this.firstName, required this.name,
required this.lastName,
required this.isAdmin, required this.isAdmin,
required this.shouldChangePassword, required this.shouldChangePassword,
required this.profileImagePath, required this.profileImagePath,
@@ -25,8 +23,7 @@ class AuthenticationState {
String? userId, String? userId,
String? userEmail, String? userEmail,
bool? isAuthenticated, bool? isAuthenticated,
String? firstName, String? name,
String? lastName,
bool? isAdmin, bool? isAdmin,
bool? shouldChangePassword, bool? shouldChangePassword,
String? profileImagePath, String? profileImagePath,
@@ -36,8 +33,7 @@ class AuthenticationState {
userId: userId ?? this.userId, userId: userId ?? this.userId,
userEmail: userEmail ?? this.userEmail, userEmail: userEmail ?? this.userEmail,
isAuthenticated: isAuthenticated ?? this.isAuthenticated, isAuthenticated: isAuthenticated ?? this.isAuthenticated,
firstName: firstName ?? this.firstName, name: name ?? this.name,
lastName: lastName ?? this.lastName,
isAdmin: isAdmin ?? this.isAdmin, isAdmin: isAdmin ?? this.isAdmin,
shouldChangePassword: shouldChangePassword ?? this.shouldChangePassword, shouldChangePassword: shouldChangePassword ?? this.shouldChangePassword,
profileImagePath: profileImagePath ?? this.profileImagePath, profileImagePath: profileImagePath ?? this.profileImagePath,
@@ -46,7 +42,7 @@ class AuthenticationState {
@override @override
String toString() { String toString() {
return 'AuthenticationState(deviceId: $deviceId, userId: $userId, userEmail: $userEmail, isAuthenticated: $isAuthenticated, firstName: $firstName, lastName: $lastName, isAdmin: $isAdmin, shouldChangePassword: $shouldChangePassword, profileImagePath: $profileImagePath)'; return 'AuthenticationState(deviceId: $deviceId, userId: $userId, userEmail: $userEmail, isAuthenticated: $isAuthenticated, name: $name, isAdmin: $isAdmin, shouldChangePassword: $shouldChangePassword, profileImagePath: $profileImagePath)';
} }
@override @override
@@ -58,8 +54,7 @@ class AuthenticationState {
other.userId == userId && other.userId == userId &&
other.userEmail == userEmail && other.userEmail == userEmail &&
other.isAuthenticated == isAuthenticated && other.isAuthenticated == isAuthenticated &&
other.firstName == firstName && other.name == name &&
other.lastName == lastName &&
other.isAdmin == isAdmin && other.isAdmin == isAdmin &&
other.shouldChangePassword == shouldChangePassword && other.shouldChangePassword == shouldChangePassword &&
other.profileImagePath == profileImagePath; other.profileImagePath == profileImagePath;
@@ -71,8 +66,7 @@ class AuthenticationState {
userId.hashCode ^ userId.hashCode ^
userEmail.hashCode ^ userEmail.hashCode ^
isAuthenticated.hashCode ^ isAuthenticated.hashCode ^
firstName.hashCode ^ name.hashCode ^
lastName.hashCode ^
isAdmin.hashCode ^ isAdmin.hashCode ^
shouldChangePassword.hashCode ^ shouldChangePassword.hashCode ^
profileImagePath.hashCode; profileImagePath.hashCode;

View File

@@ -26,8 +26,7 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
deviceId: "", deviceId: "",
userId: "", userId: "",
userEmail: "", userEmail: "",
firstName: '', name: '',
lastName: '',
profileImagePath: '', profileImagePath: '',
isAdmin: false, isAdmin: false,
shouldChangePassword: false, shouldChangePassword: false,
@@ -117,8 +116,7 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
deviceId: "", deviceId: "",
userId: "", userId: "",
userEmail: "", userEmail: "",
firstName: '', name: '',
lastName: '',
profileImagePath: '', profileImagePath: '',
isAdmin: false, isAdmin: false,
shouldChangePassword: false, shouldChangePassword: false,
@@ -187,12 +185,15 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
if (userResponseDto != null) { if (userResponseDto != null) {
Store.put(StoreKey.deviceId, deviceId); Store.put(StoreKey.deviceId, deviceId);
Store.put(StoreKey.deviceIdHash, fastHash(deviceId)); Store.put(StoreKey.deviceIdHash, fastHash(deviceId));
Store.put(StoreKey.currentUser, User.fromDto(userResponseDto)); Store.put(
StoreKey.currentUser,
User.fromUserDto(userResponseDto),
);
Store.put(StoreKey.serverUrl, serverUrl); Store.put(StoreKey.serverUrl, serverUrl);
Store.put(StoreKey.accessToken, accessToken); Store.put(StoreKey.accessToken, accessToken);
shouldChangePassword = userResponseDto.shouldChangePassword; shouldChangePassword = userResponseDto.shouldChangePassword;
user = User.fromDto(userResponseDto); user = User.fromUserDto(userResponseDto);
retResult = true; retResult = true;
} else { } else {
@@ -205,8 +206,7 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
isAuthenticated: true, isAuthenticated: true,
userId: user.id, userId: user.id,
userEmail: user.email, userEmail: user.email,
firstName: user.firstName, name: user.name,
lastName: user.lastName,
profileImagePath: user.profileImagePath, profileImagePath: user.profileImagePath,
isAdmin: user.isAdmin, isAdmin: user.isAdmin,
shouldChangePassword: shouldChangePassword, shouldChangePassword: shouldChangePassword,

View File

@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart'; import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
import 'package:immich_mobile/modules/backup/providers/manual_upload.provider.dart'; import 'package:immich_mobile/modules/backup/providers/manual_upload.provider.dart';
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
@@ -37,7 +38,7 @@ class ChangePasswordForm extends HookConsumerWidget {
style: TextStyle( style: TextStyle(
fontSize: 24, fontSize: 24,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
), ),
Padding( Padding(
@@ -45,8 +46,7 @@ class ChangePasswordForm extends HookConsumerWidget {
child: Text( child: Text(
'change_password_form_description'.tr( 'change_password_form_description'.tr(
namedArgs: { namedArgs: {
'firstName': authState.firstName, 'name': authState.name,
'lastName': authState.lastName,
}, },
), ),
style: TextStyle( style: TextStyle(
@@ -191,7 +191,7 @@ class ChangePasswordButton extends ConsumerWidget {
return ElevatedButton( return ElevatedButton(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
visualDensity: VisualDensity.standard, visualDensity: VisualDensity.standard,
backgroundColor: Theme.of(context).primaryColor, backgroundColor: context.primaryColor,
foregroundColor: Colors.grey[50], foregroundColor: Colors.grey[50],
elevation: 2, elevation: 2,
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 25), padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 25),

View File

@@ -1,9 +1,9 @@
import 'dart:io'; import 'dart:io';
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart' hide Store; import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/login/providers/oauth.provider.dart'; import 'package:immich_mobile/modules/login/providers/oauth.provider.dart';
import 'package:immich_mobile/modules/onboarding/providers/gallery_permission.provider.dart'; import 'package:immich_mobile/modules/onboarding/providers/gallery_permission.provider.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
@@ -150,7 +150,7 @@ class LoginForm extends HookConsumerWidget {
// Resume backup (if enable) then navigate // Resume backup (if enable) then navigate
if (ref.read(authenticationProvider).shouldChangePassword && if (ref.read(authenticationProvider).shouldChangePassword &&
!ref.read(authenticationProvider).isAdmin) { !ref.read(authenticationProvider).isAdmin) {
AutoRouter.of(context).push(const ChangePasswordRoute()); context.autoPush(const ChangePasswordRoute());
} else { } else {
final hasPermission = await ref final hasPermission = await ref
.read(galleryPermissionNotifier.notifier) .read(galleryPermissionNotifier.notifier)
@@ -159,7 +159,7 @@ class LoginForm extends HookConsumerWidget {
// Don't resume the backup until we have gallery permission // Don't resume the backup until we have gallery permission
ref.read(backupProvider.notifier).resumeBackup(); ref.read(backupProvider.notifier).resumeBackup();
} }
AutoRouter.of(context).replace(const TabControllerRoute()); context.autoReplace(const TabControllerRoute());
} }
} else { } else {
ImmichToast.show( ImmichToast.show(
@@ -212,9 +212,7 @@ class LoginForm extends HookConsumerWidget {
if (permission.isGranted || permission.isLimited) { if (permission.isGranted || permission.isLimited) {
ref.watch(backupProvider.notifier).resumeBackup(); ref.watch(backupProvider.notifier).resumeBackup();
} }
AutoRouter.of(context).replace( context.autoReplace(const TabControllerRoute());
const TabControllerRoute(),
);
} else { } else {
ImmichToast.show( ImmichToast.show(
context: context, context: context,
@@ -260,8 +258,7 @@ class LoginForm extends HookConsumerWidget {
), ),
), ),
), ),
onPressed: () => onPressed: () => context.autoPush(const SettingsRoute()),
AutoRouter.of(context).push(const SettingsRoute()),
icon: const Icon(Icons.settings_rounded), icon: const Icon(Icons.settings_rounded),
label: const SizedBox.shrink(), label: const SizedBox.shrink(),
), ),
@@ -303,7 +300,7 @@ class LoginForm extends HookConsumerWidget {
children: [ children: [
Text( Text(
serverEndpointController.text, serverEndpointController.text,
style: Theme.of(context).textTheme.displaySmall, style: context.textTheme.displaySmall,
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
if (isPasswordLoginEnable.value) ...[ if (isPasswordLoginEnable.value) ...[
@@ -339,8 +336,7 @@ class LoginForm extends HookConsumerWidget {
horizontal: 16.0, horizontal: 16.0,
), ),
child: Divider( child: Divider(
color: Brightness.dark == color: context.isDarkTheme
Theme.of(context).brightness
? Colors.white ? Colors.white
: Colors.black, : Colors.black,
), ),
@@ -588,7 +584,7 @@ class OAuthLoginButton extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
return ElevatedButton.icon( return ElevatedButton.icon(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).primaryColor.withAlpha(230), backgroundColor: context.primaryColor.withAlpha(230),
padding: const EdgeInsets.symmetric(vertical: 12), padding: const EdgeInsets.symmetric(vertical: 12),
), ),
onPressed: onPressed, onPressed: onPressed,

View File

@@ -1,7 +1,7 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/login/ui/login_form.dart'; import 'package:immich_mobile/modules/login/ui/login_form.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
@@ -47,13 +47,13 @@ class LoginPage extends HookConsumerWidget {
child: Text( child: Text(
'Logs', 'Logs',
style: TextStyle( style: TextStyle(
color: Theme.of(context).primaryColor, color: context.primaryColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontFamily: "Inconsolata", fontFamily: "Inconsolata",
), ),
), ),
onTap: () { onTap: () {
AutoRouter.of(context).push(const AppLogRoute()); context.autoPush(const AppLogRoute());
}, },
), ),
], ],

View File

@@ -1,14 +1,20 @@
import 'package:vector_map_tiles/vector_map_tiles.dart';
class MapState { class MapState {
final bool isDarkTheme; final bool isDarkTheme;
final bool showFavoriteOnly; final bool showFavoriteOnly;
final bool includeArchived; final bool includeArchived;
final int relativeTime; final int relativeTime;
final Style? mapStyle;
final bool isLoading;
MapState({ MapState({
this.isDarkTheme = false, this.isDarkTheme = false,
this.showFavoriteOnly = false, this.showFavoriteOnly = false,
this.includeArchived = false, this.includeArchived = false,
this.relativeTime = 0, this.relativeTime = 0,
this.mapStyle,
this.isLoading = false,
}); });
MapState copyWith({ MapState copyWith({
@@ -16,18 +22,22 @@ class MapState {
bool? showFavoriteOnly, bool? showFavoriteOnly,
bool? includeArchived, bool? includeArchived,
int? relativeTime, int? relativeTime,
Style? mapStyle,
bool? isLoading,
}) { }) {
return MapState( return MapState(
isDarkTheme: isDarkTheme ?? this.isDarkTheme, isDarkTheme: isDarkTheme ?? this.isDarkTheme,
showFavoriteOnly: showFavoriteOnly ?? this.showFavoriteOnly, showFavoriteOnly: showFavoriteOnly ?? this.showFavoriteOnly,
includeArchived: includeArchived ?? this.includeArchived, includeArchived: includeArchived ?? this.includeArchived,
relativeTime: relativeTime ?? this.relativeTime, relativeTime: relativeTime ?? this.relativeTime,
mapStyle: mapStyle ?? this.mapStyle,
isLoading: isLoading ?? this.isLoading,
); );
} }
@override @override
String toString() { String toString() {
return 'MapSettingsState(isDarkTheme: $isDarkTheme, showFavoriteOnly: $showFavoriteOnly, relativeTime: $relativeTime, includeArchived: $includeArchived)'; return 'MapSettingsState(isDarkTheme: $isDarkTheme, showFavoriteOnly: $showFavoriteOnly, relativeTime: $relativeTime, includeArchived: $includeArchived, mapStyle: $mapStyle, isLoading: $isLoading)';
} }
@override @override
@@ -38,7 +48,9 @@ class MapState {
other.isDarkTheme == isDarkTheme && other.isDarkTheme == isDarkTheme &&
other.showFavoriteOnly == showFavoriteOnly && other.showFavoriteOnly == showFavoriteOnly &&
other.relativeTime == relativeTime && other.relativeTime == relativeTime &&
other.includeArchived == includeArchived; other.includeArchived == includeArchived &&
other.mapStyle == mapStyle &&
other.isLoading == isLoading;
} }
@override @override
@@ -46,6 +58,8 @@ class MapState {
return isDarkTheme.hashCode ^ return isDarkTheme.hashCode ^
showFavoriteOnly.hashCode ^ showFavoriteOnly.hashCode ^
relativeTime.hashCode ^ relativeTime.hashCode ^
includeArchived.hashCode; includeArchived.hashCode ^
mapStyle.hashCode ^
isLoading.hashCode;
} }
} }

View File

@@ -1,10 +1,23 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/map/models/map_state.model.dart'; import 'package:immich_mobile/modules/map/models/map_state.model.dart';
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.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/modules/settings/services/app_settings.service.dart';
import 'package:immich_mobile/shared/providers/api.provider.dart';
import 'package:immich_mobile/shared/services/api.service.dart';
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
import 'package:immich_mobile/utils/color_filter_generator.dart';
import 'package:logging/logging.dart';
import 'package:openapi/api.dart';
import 'package:vector_map_tiles/vector_map_tiles.dart';
class MapStateNotifier extends StateNotifier<MapState> { class MapStateNotifier extends StateNotifier<MapState> {
MapStateNotifier(this._appSettingsProvider) MapStateNotifier(this._appSettingsProvider, this._apiService)
: super( : super(
MapState( MapState(
isDarkTheme: _appSettingsProvider isDarkTheme: _appSettingsProvider
@@ -15,17 +28,69 @@ class MapStateNotifier extends StateNotifier<MapState> {
.getSetting<bool>(AppSettingsEnum.mapIncludeArchived), .getSetting<bool>(AppSettingsEnum.mapIncludeArchived),
relativeTime: _appSettingsProvider relativeTime: _appSettingsProvider
.getSetting<int>(AppSettingsEnum.mapRelativeDate), .getSetting<int>(AppSettingsEnum.mapRelativeDate),
isLoading: true,
), ),
) {
_fetchStyleFromServer(
_appSettingsProvider.getSetting<bool>(AppSettingsEnum.mapThemeMode),
); );
}
final AppSettingsService _appSettingsProvider; final AppSettingsService _appSettingsProvider;
final ApiService _apiService;
final Logger _log = Logger("MapStateNotifier");
bool get isRaster =>
state.mapStyle != null && state.mapStyle!.rasterTileProvider != null;
double get maxZoom =>
(isRaster ? state.mapStyle!.rasterTileProvider!.maximumZoom : 14)
.toDouble();
void switchTheme(bool isDarkTheme) { void switchTheme(bool isDarkTheme) {
_updateThemeMode(isDarkTheme);
_fetchStyleFromServer(isDarkTheme);
}
void _updateThemeMode(bool isDarkTheme) {
_appSettingsProvider.setSetting( _appSettingsProvider.setSetting(
AppSettingsEnum.mapThemeMode, AppSettingsEnum.mapThemeMode,
isDarkTheme, isDarkTheme,
); );
state = state.copyWith(isDarkTheme: isDarkTheme); state = state.copyWith(isDarkTheme: isDarkTheme, isLoading: true);
}
void _fetchStyleFromServer(bool isDarkTheme) async {
final styleResponse = await _apiService.systemConfigApi
.getMapStyleWithHttpInfo(isDarkTheme ? MapTheme.dark : MapTheme.light);
if (styleResponse.statusCode >= HttpStatus.badRequest) {
throw ApiException(styleResponse.statusCode, styleResponse.body);
}
final styleJsonString = styleResponse.body.isNotEmpty &&
styleResponse.statusCode != HttpStatus.noContent
? styleResponse.body
: null;
if (styleJsonString == null) {
_log.severe('Style JSON from server is empty');
return;
}
final styleJson = await compute(jsonDecode, styleJsonString);
if (styleJson is! Map<String, dynamic>) {
_log.severe('Style JSON from server is invalid');
return;
}
final styleReader = StyleReader(uri: '');
Style? style;
try {
style = await styleReader.readFromMap(styleJson);
} finally {
// Consume all error
}
state = state.copyWith(
mapStyle: style,
isLoading: false,
);
} }
void switchFavoriteOnly(bool isFavoriteOnly) { void switchFavoriteOnly(bool isFavoriteOnly) {
@@ -51,9 +116,44 @@ class MapStateNotifier extends StateNotifier<MapState> {
); );
state = state.copyWith(relativeTime: relativeTime); state = state.copyWith(relativeTime: relativeTime);
} }
Widget getTileLayer([bool forceDark = false]) {
if (isRaster) {
final rasterProvider = state.mapStyle!.rasterTileProvider;
final rasterLayer = TileLayer(
urlTemplate: rasterProvider!.url,
maxNativeZoom: rasterProvider.maximumZoom,
maxZoom: rasterProvider.maximumZoom.toDouble(),
);
return state.isDarkTheme || forceDark
? InvertionFilter(
child: SaturationFilter(
saturation: -1,
child: BrightnessFilter(
brightness: -1,
child: rasterLayer,
),
),
)
: rasterLayer;
}
if (state.mapStyle != null && !isRaster) {
return VectorTileLayer(
// Tiles and themes will be set for vector providers
tileProviders: state.mapStyle!.providers!,
theme: state.mapStyle!.theme!,
sprites: state.mapStyle!.sprites,
concurrency: 6,
);
}
return const Center(child: ImmichLoadingIndicator());
}
} }
final mapStateNotifier = final mapStateNotifier =
StateNotifierProvider<MapStateNotifier, MapState>((ref) { StateNotifierProvider<MapStateNotifier, MapState>((ref) {
return MapStateNotifier(ref.watch(appSettingsServiceProvider)); return MapStateNotifier(
ref.watch(appSettingsServiceProvider),
ref.watch(apiServiceProvider),
);
}); });

View File

@@ -1,8 +1,8 @@
import 'dart:io'; import 'dart:io';
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/home/ui/asset_grid/disable_multi_select_button.dart'; import 'package:immich_mobile/modules/home/ui/asset_grid/disable_multi_select_button.dart';
import 'package:immich_mobile/modules/map/ui/map_settings_dialog.dart'; import 'package:immich_mobile/modules/map/ui/map_settings_dialog.dart';
@@ -30,7 +30,7 @@ class MapAppBar extends HookWidget implements PreferredSizeWidget {
Padding( Padding(
padding: const EdgeInsets.only(left: 15, top: 15), padding: const EdgeInsets.only(left: 15, top: 15),
child: ElevatedButton( child: ElevatedButton(
onPressed: () => AutoRouter.of(context).pop(), onPressed: () => context.autoPop(),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
shape: const CircleBorder(), shape: const CircleBorder(),
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),

View File

@@ -5,6 +5,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/asset_viewer/providers/render_list.provider.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/asset_grid_data_structure.dart';
import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart'; import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart';
@@ -15,7 +16,6 @@ import 'package:immich_mobile/shared/ui/drag_sheet.dart';
import 'package:immich_mobile/utils/color_filter_generator.dart'; import 'package:immich_mobile/utils/color_filter_generator.dart';
import 'package:immich_mobile/utils/debounce.dart'; import 'package:immich_mobile/utils/debounce.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import 'package:url_launcher/url_launcher.dart';
class MapPageBottomSheet extends StatefulHookConsumerWidget { class MapPageBottomSheet extends StatefulHookConsumerWidget {
final Stream mapPageEventStream; final Stream mapPageEventStream;
@@ -57,10 +57,10 @@ class AssetsInBoundBottomSheetState extends ConsumerState<MapPageBottomSheet> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark; final isDarkTheme = context.isDarkTheme;
final bottomPadding = final bottomPadding =
Platform.isAndroid ? MediaQuery.of(context).padding.bottom - 10 : 0.0; Platform.isAndroid ? MediaQuery.of(context).padding.bottom - 10 : 0.0;
final maxHeight = MediaQuery.of(context).size.height - bottomPadding; final maxHeight = context.height - bottomPadding;
final isSheetScrolled = useState(false); final isSheetScrolled = useState(false);
final isSheetExpanded = useState(false); final isSheetExpanded = useState(false);
final assetsInBound = useState(<Asset>[]); final assetsInBound = useState(<Asset>[]);
@@ -137,7 +137,7 @@ class AssetsInBoundBottomSheetState extends ConsumerState<MapPageBottomSheet> {
SizedBox( SizedBox(
height: 150, height: 150,
width: 150, width: 150,
child: isDarkMode child: isDarkTheme
? const InvertionFilter( ? const InvertionFilter(
child: SaturationFilter( child: SaturationFilter(
saturation: -1, saturation: -1,
@@ -156,7 +156,7 @@ class AssetsInBoundBottomSheetState extends ConsumerState<MapPageBottomSheet> {
"map_zoom_to_see_photos".tr(), "map_zoom_to_see_photos".tr(),
style: TextStyle( style: TextStyle(
fontSize: 20, fontSize: 20,
color: Theme.of(context).textTheme.displayLarge?.color, color: context.textTheme.displayLarge?.color,
), ),
), ),
], ],
@@ -182,7 +182,7 @@ class AssetsInBoundBottomSheetState extends ConsumerState<MapPageBottomSheet> {
height: 60, height: 60,
width: double.infinity, width: double.infinity,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[900] : Colors.grey[100], color: isDarkTheme ? Colors.grey[900] : Colors.grey[100],
), ),
child: Stack( child: Stack(
children: [ children: [
@@ -197,17 +197,14 @@ class AssetsInBoundBottomSheetState extends ConsumerState<MapPageBottomSheet> {
textToDisplay, textToDisplay,
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
color: Theme.of(context).textTheme.displayLarge?.color, color: context.textTheme.displayLarge?.color,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
Divider( Divider(
height: 10, height: 10,
color: Theme.of(context) color:
.textTheme context.textTheme.displayLarge?.color?.withOpacity(0.5),
.displayLarge
?.color
?.withOpacity(0.5),
), ),
], ],
), ),
@@ -218,7 +215,7 @@ class AssetsInBoundBottomSheetState extends ConsumerState<MapPageBottomSheet> {
child: IconButton( child: IconButton(
icon: Icon( icon: Icon(
Icons.map_outlined, Icons.map_outlined,
color: Theme.of(context).textTheme.displayLarge?.color, color: context.textTheme.displayLarge?.color,
), ),
iconSize: 20, iconSize: 20,
tooltip: 'Zoom to bounds', tooltip: 'Zoom to bounds',
@@ -266,7 +263,7 @@ class AssetsInBoundBottomSheetState extends ConsumerState<MapPageBottomSheet> {
ScrollController scrollController, ScrollController scrollController,
) { ) {
return Card( return Card(
color: isDarkMode ? Colors.grey[900] : Colors.grey[100], color: isDarkTheme ? Colors.grey[900] : Colors.grey[100],
surfaceTintColor: Colors.transparent, surfaceTintColor: Colors.transparent,
elevation: 18.0, elevation: 18.0,
margin: const EdgeInsets.all(0), margin: const EdgeInsets.all(0),
@@ -320,18 +317,13 @@ class AssetsInBoundBottomSheetState extends ConsumerState<MapPageBottomSheet> {
Positioned( Positioned(
bottom: maxHeight * currentExtend.value, bottom: maxHeight * currentExtend.value,
left: 0, left: 0,
child: GestureDetector(
onTap: () => launchUrl(
Uri.parse('https://openstreetmap.org/copyright'),
),
child: ColoredBox( child: ColoredBox(
color: (widget.isDarkTheme color:
? Colors.grey[900] (widget.isDarkTheme ? Colors.grey[900] : Colors.grey[100])!,
: Colors.grey[100])!,
child: Padding( child: Padding(
padding: const EdgeInsets.all(3), padding: const EdgeInsets.all(3),
child: Text( child: Text(
'© OpenStreetMap contributors', 'OpenStreetMap contributors',
style: TextStyle( style: TextStyle(
fontSize: 6, fontSize: 6,
color: !widget.isDarkTheme color: !widget.isDarkTheme
@@ -342,7 +334,6 @@ class AssetsInBoundBottomSheetState extends ConsumerState<MapPageBottomSheet> {
), ),
), ),
), ),
),
Positioned( Positioned(
bottom: maxHeight * (0.14 + (currentExtend.value - 0.1)), bottom: maxHeight * (0.14 + (currentExtend.value - 0.1)),
right: 15, right: 15,

View File

@@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/map/providers/map_state.provider.dart'; import 'package:immich_mobile/modules/map/providers/map_state.provider.dart';
class MapSettingsDialog extends HookConsumerWidget { class MapSettingsDialog extends HookConsumerWidget {
@@ -15,7 +16,7 @@ class MapSettingsDialog extends HookConsumerWidget {
final showFavoriteOnly = useState(mapSettings.showFavoriteOnly); final showFavoriteOnly = useState(mapSettings.showFavoriteOnly);
final showIncludeArchived = useState(mapSettings.includeArchived); final showIncludeArchived = useState(mapSettings.includeArchived);
final showRelativeDate = useState(mapSettings.relativeTime); final showRelativeDate = useState(mapSettings.relativeTime);
final ThemeData theme = Theme.of(context); final ThemeData theme = context.themeData;
Widget buildMapThemeSetting() { Widget buildMapThemeSetting() {
return SwitchListTile.adaptive( return SwitchListTile.adaptive(
@@ -125,7 +126,7 @@ class MapSettingsDialog extends HookConsumerWidget {
List<Widget> getDialogActions() { List<Widget> getDialogActions() {
return <Widget>[ return <Widget>[
TextButton( TextButton(
onPressed: () => Navigator.of(context).pop(), onPressed: () => context.pop(),
style: TextButton.styleFrom( style: TextButton.styleFrom(
backgroundColor: backgroundColor:
mapSettings.isDarkTheme ? Colors.grey[100] : Colors.grey[700], mapSettings.isDarkTheme ? Colors.grey[100] : Colors.grey[700],
@@ -146,7 +147,7 @@ class MapSettingsDialog extends HookConsumerWidget {
mapSettingsNotifier.setRelativeTime(showRelativeDate.value); mapSettingsNotifier.setRelativeTime(showRelativeDate.value);
mapSettingsNotifier mapSettingsNotifier
.switchIncludeArchived(showIncludeArchived.value); .switchIncludeArchived(showIncludeArchived.value);
Navigator.of(context).pop(); context.pop();
}, },
style: TextButton.styleFrom( style: TextButton.styleFrom(
backgroundColor: theme.primaryColor, backgroundColor: theme.primaryColor,
@@ -178,7 +179,7 @@ class MapSettingsDialog extends HookConsumerWidget {
width: double.maxFinite, width: double.maxFinite,
child: ConstrainedBox( child: ConstrainedBox(
constraints: BoxConstraints( constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.6, maxHeight: context.height * 0.6,
), ),
child: ListView( child: ListView(
shrinkWrap: true, shrinkWrap: true,

View File

@@ -1,8 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_map/plugin_api.dart'; import 'package:flutter_map/plugin_api.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/shared/providers/server_info.provider.dart'; import 'package:immich_mobile/modules/map/providers/map_state.provider.dart';
import 'package:immich_mobile/utils/color_filter_generator.dart';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
@@ -29,11 +28,7 @@ class MapThumbnail extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final tileLayer = TileLayer( ref.watch(mapStateNotifier.select((s) => s.mapStyle));
urlTemplate: ref.watch(
serverInfoProvider.select((v) => v.serverConfig.mapTileUrl),
),
);
return SizedBox( return SizedBox(
height: height, height: height,
@@ -55,20 +50,14 @@ class MapThumbnail extends HookConsumerWidget {
'OpenStreetMap contributors', 'OpenStreetMap contributors',
onTap: () => launchUrl( onTap: () => launchUrl(
Uri.parse('https://openstreetmap.org/copyright'), Uri.parse('https://openstreetmap.org/copyright'),
mode: LaunchMode.externalApplication,
), ),
), ),
], ],
), ),
], ],
children: [ children: [
isDarkTheme ref.read(mapStateNotifier.notifier).getTileLayer(isDarkTheme),
? InvertionFilter(
child: SaturationFilter(
saturation: -1,
child: tileLayer,
),
)
: tileLayer,
if (markers.isNotEmpty) MarkerLayer(markers: markers), if (markers.isNotEmpty) MarkerLayer(markers: markers),
], ],
), ),

View File

@@ -1,6 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:math' as math;
import 'package:auto_route/auto_route.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -11,6 +11,7 @@ import 'package:flutter_map_heatmap/flutter_map_heatmap.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:geolocator/geolocator.dart'; import 'package:geolocator/geolocator.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/map/models/map_page_event.model.dart'; import 'package:immich_mobile/modules/map/models/map_page_event.model.dart';
import 'package:immich_mobile/modules/map/providers/map_marker.provider.dart'; import 'package:immich_mobile/modules/map/providers/map_marker.provider.dart';
import 'package:immich_mobile/modules/map/providers/map_state.provider.dart'; import 'package:immich_mobile/modules/map/providers/map_state.provider.dart';
@@ -20,12 +21,10 @@ import 'package:immich_mobile/modules/map/ui/map_page_bottom_sheet.dart';
import 'package:immich_mobile/modules/map/ui/map_page_app_bar.dart'; import 'package:immich_mobile/modules/map/ui/map_page_app_bar.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/models/asset.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/immich_loading_indicator.dart';
import 'package:immich_mobile/shared/ui/immich_toast.dart'; import 'package:immich_mobile/shared/ui/immich_toast.dart';
import 'package:immich_mobile/utils/color_filter_generator.dart';
import 'package:immich_mobile/utils/debounce.dart'; import 'package:immich_mobile/utils/debounce.dart';
import 'package:immich_mobile/utils/flutter_map_extensions.dart'; import 'package:immich_mobile/extensions/flutter_map_extensions.dart';
import 'package:immich_mobile/utils/immich_app_theme.dart'; import 'package:immich_mobile/utils/immich_app_theme.dart';
import 'package:immich_mobile/utils/selection_handlers.dart'; import 'package:immich_mobile/utils/selection_handlers.dart';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
@@ -79,6 +78,7 @@ class MapPageState extends ConsumerState<MapPage> {
Set<AssetMarkerData>? assetMarkers, { Set<AssetMarkerData>? assetMarkers, {
bool forceReload = false, bool forceReload = false,
}) { }) {
try {
final bounds = mapController.bounds; final bounds = mapController.bounds;
if (bounds != null) { if (bounds != null) {
final oldAssetsInBounds = assetsInBounds.toSet(); final oldAssetsInBounds = assetsInBounds.toSet();
@@ -95,10 +95,13 @@ class MapPageState extends ConsumerState<MapPage> {
); );
} }
} }
} finally {
// Consume all error
}
} }
void openAssetInViewer(Asset asset) { void openAssetInViewer(Asset asset) {
AutoRouter.of(context).push( context.autoPush(
GalleryViewerRoute( GalleryViewerRoute(
initialIndex: 0, initialIndex: 0,
loadAsset: (index) => asset, loadAsset: (index) => asset,
@@ -120,6 +123,10 @@ class MapPageState extends ConsumerState<MapPage> {
final selectedAssets = useState(<Asset>{}); final selectedAssets = useState(<Asset>{});
final showLoadingIndicator = useState(false); final showLoadingIndicator = useState(false);
final refetchMarkers = useState(true); final refetchMarkers = useState(true);
final isLoading =
ref.watch(mapStateNotifier.select((state) => state.isLoading));
final maxZoom = ref.read(mapStateNotifier.notifier).maxZoom;
final zoomLevel = math.min(maxZoom, 14.0);
if (refetchMarkers.value) { if (refetchMarkers.value) {
mapMarkerData.value = ref.watch(mapMarkersProvider).when( mapMarkerData.value = ref.watch(mapMarkersProvider).when(
@@ -168,7 +175,6 @@ class MapPageState extends ConsumerState<MapPage> {
final mapMarker = mapMarkerData.value final mapMarker = mapMarkerData.value
.firstWhereOrNull((e) => e.asset.id == assetInBottomSheet.id); .firstWhereOrNull((e) => e.asset.id == assetInBottomSheet.id);
if (mapMarker != null) { if (mapMarker != null) {
const zoomLevel = 16.0;
LatLng? newCenter = mapController.centerBoundsWithPadding( LatLng? newCenter = mapController.centerBoundsWithPadding(
mapMarker.point, mapMarker.point,
const Offset(0, -120), const Offset(0, -120),
@@ -230,7 +236,7 @@ class MapPageState extends ConsumerState<MapPage> {
forceAssetUpdate = true; forceAssetUpdate = true;
mapController.move( mapController.move(
LatLng(currentUserLocation.latitude, currentUserLocation.longitude), LatLng(currentUserLocation.latitude, currentUserLocation.longitude),
12, zoomLevel,
); );
} catch (error) { } catch (error) {
log.severe( log.severe(
@@ -359,24 +365,6 @@ class MapPageState extends ConsumerState<MapPage> {
selectedAssets.value = selection; selectedAssets.value = selection;
} }
final tileLayer = TileLayer(
urlTemplate: ref.watch(
serverInfoProvider.select((v) => v.serverConfig.mapTileUrl),
),
maxNativeZoom: 19,
maxZoom: 19,
);
final darkTileLayer = InvertionFilter(
child: SaturationFilter(
saturation: -1,
child: BrightnessFilter(
brightness: -1,
child: tileLayer,
),
),
);
final markerLayer = MarkerLayer( final markerLayer = MarkerLayer(
markers: [ markers: [
if (closestAssetMarker.value != null) if (closestAssetMarker.value != null)
@@ -451,6 +439,7 @@ class MapPageState extends ConsumerState<MapPage> {
extendBodyBehindAppBar: true, extendBodyBehindAppBar: true,
body: Stack( body: Stack(
children: [ children: [
if (!isLoading)
FlutterMap( FlutterMap(
mapController: mapController, mapController: mapController,
options: MapOptions( options: MapOptions(
@@ -464,17 +453,18 @@ class MapPageState extends ConsumerState<MapPage> {
center: LatLng(20, 20), center: LatLng(20, 20),
zoom: 2, zoom: 2,
minZoom: 1, minZoom: 1,
maxZoom: 18, // max level supported by OSM, maxZoom: maxZoom,
onMapReady: () { onMapReady: () {
mapController.mapEventStream.listen(onMapEvent); mapController.mapEventStream.listen(onMapEvent);
}, },
), ),
children: [ children: [
isDarkTheme ? darkTileLayer : tileLayer, ref.read(mapStateNotifier.notifier).getTileLayer(),
heatMapLayer, heatMapLayer,
markerLayer, markerLayer,
], ],
), ),
if (!isLoading)
MapPageBottomSheet( MapPageBottomSheet(
mapPageEventStream: mapPageEventSC.stream, mapPageEventStream: mapPageEventSC.stream,
bottomSheetEventSC: bottomSheetEventSC, bottomSheetEventSC: bottomSheetEventSC,
@@ -482,10 +472,10 @@ class MapPageState extends ConsumerState<MapPage> {
selectionlistener: selectionListener, selectionlistener: selectionListener,
isDarkTheme: isDarkTheme, isDarkTheme: isDarkTheme,
), ),
if (showLoadingIndicator.value) if (showLoadingIndicator.value || isLoading)
Positioned( Positioned(
top: MediaQuery.of(context).size.height * 0.35, top: context.height * 0.35,
left: MediaQuery.of(context).size.width * 0.425, left: context.width * 0.425,
child: const ImmichLoadingIndicator(), child: const ImmichLoadingIndicator(),
), ),
], ],

View File

@@ -1,7 +1,7 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/memories/providers/memory.provider.dart'; import 'package:immich_mobile/modules/memories/providers/memory.provider.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/shared/ui/immich_image.dart'; import 'package:immich_mobile/shared/ui/immich_image.dart';
@@ -31,7 +31,7 @@ class MemoryLane extends HookConsumerWidget {
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {
HapticFeedback.heavyImpact(); HapticFeedback.heavyImpact();
AutoRouter.of(context).push( context.autoPush(
MemoryRoute( MemoryRoute(
memories: memories, memories: memories,
memoryIndex: index, memoryIndex: index,

View File

@@ -1,8 +1,8 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/memories/models/memory.dart'; import 'package:immich_mobile/modules/memories/models/memory.dart';
import 'package:immich_mobile/modules/memories/ui/memory_card.dart'; import 'package:immich_mobile/modules/memories/ui/memory_card.dart';
import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/models/asset.dart';
@@ -182,14 +182,14 @@ class MemoryPage extends HookConsumerWidget {
currentMemory.value.assets.length; currentMemory.value.assets.length;
if (isLastAsset && if (isLastAsset &&
(offset > notification.metrics.maxScrollExtent + 150)) { (offset > notification.metrics.maxScrollExtent + 150)) {
AutoRouter.of(context).pop(); context.autoPop();
return true; return true;
} }
} }
// Horizontal scroll handling // Horizontal scroll handling
if (notification.depth == 1 && if (notification.depth == 1 &&
(offset > notification.metrics.maxScrollExtent + 100)) { (offset > notification.metrics.maxScrollExtent + 100)) {
AutoRouter.of(context).pop(); context.autoPop();
return true; return true;
} }
} }
@@ -244,7 +244,7 @@ class MemoryPage extends HookConsumerWidget {
child: MemoryCard( child: MemoryCard(
asset: asset, asset: asset,
onTap: () => toNextAsset(index), onTap: () => toNextAsset(index),
onClose: () => AutoRouter.of(context).pop(), onClose: () => context.autoPop(),
rightCornerText: assetProgress.value, rightCornerText: assetProgress.value,
title: memories[mIndex].title, title: memories[mIndex].title,
showTitle: index == 0, showTitle: index == 0,

View File

@@ -1,7 +1,7 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart'; import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
import 'package:immich_mobile/modules/onboarding/providers/gallery_permission.provider.dart'; import 'package:immich_mobile/modules/onboarding/providers/gallery_permission.provider.dart';
@@ -11,7 +11,6 @@ import 'package:immich_mobile/shared/ui/immich_title_text.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
class PermissionOnboardingPage extends HookConsumerWidget { class PermissionOnboardingPage extends HookConsumerWidget {
const PermissionOnboardingPage({super.key}); const PermissionOnboardingPage({super.key});
@override @override
@@ -21,13 +20,10 @@ class PermissionOnboardingPage extends HookConsumerWidget {
// Navigate to the main Tab Controller when permission is granted // Navigate to the main Tab Controller when permission is granted
void goToHome() { void goToHome() {
// Resume backup (if enable) then navigate // Resume backup (if enable) then navigate
ref.watch(backupProvider.notifier).resumeBackup() ref.watch(backupProvider.notifier).resumeBackup().catchError((error) {
.catchError((error) {
debugPrint('PermissionOnboardingPage error: $error'); debugPrint('PermissionOnboardingPage error: $error');
}); });
AutoRouter.of(context).replace( context.autoReplace(const TabControllerRoute());
const TabControllerRoute(),
);
} }
// When the permission is denied, we show a request permission page // When the permission is denied, we show a request permission page
@@ -38,7 +34,7 @@ class PermissionOnboardingPage extends HookConsumerWidget {
children: [ children: [
Text( Text(
'permission_onboarding_request', 'permission_onboarding_request',
style: Theme.of(context).textTheme.titleMedium, style: context.textTheme.titleMedium,
textAlign: TextAlign.center, textAlign: TextAlign.center,
).tr(), ).tr(),
const SizedBox(height: 18), const SizedBox(height: 18),
@@ -70,7 +66,7 @@ class PermissionOnboardingPage extends HookConsumerWidget {
children: [ children: [
Text( Text(
'permission_onboarding_permission_granted', 'permission_onboarding_permission_granted',
style: Theme.of(context).textTheme.titleMedium, style: context.textTheme.titleMedium,
textAlign: TextAlign.center, textAlign: TextAlign.center,
).tr(), ).tr(),
const SizedBox(height: 18), const SizedBox(height: 18),
@@ -90,14 +86,15 @@ class PermissionOnboardingPage extends HookConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const Icon(Icons.warning_outlined, const Icon(
Icons.warning_outlined,
color: Colors.yellow, color: Colors.yellow,
size: 48, size: 48,
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
'permission_onboarding_permission_limited', 'permission_onboarding_permission_limited',
style: Theme.of(context).textTheme.titleMedium, style: context.textTheme.titleMedium,
textAlign: TextAlign.center, textAlign: TextAlign.center,
).tr(), ).tr(),
const SizedBox(height: 18), const SizedBox(height: 18),
@@ -123,14 +120,15 @@ class PermissionOnboardingPage extends HookConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const Icon(Icons.warning_outlined, const Icon(
Icons.warning_outlined,
color: Colors.red, color: Colors.red,
size: 48, size: 48,
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
'permission_onboarding_permission_denied', 'permission_onboarding_permission_denied',
style: Theme.of(context).textTheme.titleMedium, style: context.textTheme.titleMedium,
textAlign: TextAlign.center, textAlign: TextAlign.center,
).tr(), ).tr(),
const SizedBox(height: 18), const SizedBox(height: 18),
@@ -186,13 +184,10 @@ class PermissionOnboardingPage extends HookConsumerWidget {
child: const Text('permission_onboarding_log_out').tr(), child: const Text('permission_onboarding_log_out').tr(),
onPressed: () { onPressed: () {
ref.read(authenticationProvider.notifier).logout(); ref.read(authenticationProvider.notifier).logout();
AutoRouter.of(context).replace( context.autoReplace(const LoginRoute());
const LoginRoute(),
);
}, },
), ),
], ],
), ),
), ),
), ),

View File

@@ -36,7 +36,7 @@ class PartnerService {
final userDtos = final userDtos =
await _apiService.partnerApi.getPartners(direction._value); await _apiService.partnerApi.getPartners(direction._value);
if (userDtos != null) { if (userDtos != null) {
return userDtos.map((u) => User.fromDto(u)).toList(); return userDtos.map((u) => User.fromPartnerDto(u)).toList();
} }
} catch (e) { } catch (e) {
_log.warning("failed to get partners for direction $direction:\n$e"); _log.warning("failed to get partners for direction $direction:\n$e");

View File

@@ -1,6 +1,6 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/shared/models/user.dart'; import 'package:immich_mobile/shared/models/user.dart';
import 'package:immich_mobile/shared/ui/user_avatar.dart'; import 'package:immich_mobile/shared/ui/user_avatar.dart';
@@ -21,17 +21,26 @@ class PartnerList extends HookConsumerWidget {
Widget listEntry(BuildContext context, int index) { Widget listEntry(BuildContext context, int index) {
final User p = partner[index]; final User p = partner[index];
return ListTile( return ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 12.0), contentPadding: const EdgeInsets.only(
leading: userAvatar(context, p, radius: 30), left: 12.0,
right: 18.0,
),
leading: userAvatar(context, p, radius: 24),
title: Text( title: Text(
"${p.firstName} ${p.lastName}'s photos", "${p.name}'s photos",
style: TextStyle( style: const TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 14, fontSize: 14,
color: Theme.of(context).primaryColor,
), ),
), ),
onTap: () => AutoRouter.of(context).push(PartnerDetailRoute(partner: p)), trailing: Text(
"View all",
style: TextStyle(
fontWeight: FontWeight.bold,
color: context.primaryColor,
),
),
onTap: () => context.autoPush((PartnerDetailRoute(partner: p))),
); );
} }
} }

View File

@@ -25,7 +25,7 @@ class PartnerDetailPage extends HookConsumerWidget {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text("${partner.firstName} ${partner.lastName}"), title: Text(partner.name),
elevation: 0, elevation: 0,
centerTitle: false, centerTitle: false,
), ),
@@ -34,7 +34,7 @@ class PartnerDetailPage extends HookConsumerWidget {
? Padding( ? Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Text( child: Text(
"It seems ${partner.firstName} does not have any photos...\n" "It seems ${partner.name} does not have any photos...\n"
"Or your server version does not match the app version."), "Or your server version does not match the app version."),
) )
: ImmichAssetGrid( : ImmichAssetGrid(

View File

@@ -41,7 +41,7 @@ class PartnerPage extends HookConsumerWidget {
padding: const EdgeInsets.only(right: 8), padding: const EdgeInsets.only(right: 8),
child: userAvatar(context, u), child: userAvatar(context, u),
), ),
Text("${u.firstName} ${u.lastName}"), Text(u.name),
], ],
), ),
), ),
@@ -71,7 +71,7 @@ class PartnerPage extends HookConsumerWidget {
return ConfirmDialog( return ConfirmDialog(
title: "partner_page_stop_sharing_title", title: "partner_page_stop_sharing_title",
content: content:
"partner_page_stop_sharing_content".tr(args: [u.firstName]), "partner_page_stop_sharing_content".tr(args: [u.name]),
onOk: () => ref.read(partnerServiceProvider).removePartner(u), onOk: () => ref.read(partnerServiceProvider).removePartner(u),
); );
}, },

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.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/models/curated_content.dart';
import 'package:immich_mobile/modules/search/ui/thumbnail_with_info.dart'; import 'package:immich_mobile/modules/search/ui/thumbnail_with_info.dart';
import 'package:immich_mobile/shared/models/store.dart'; import 'package:immich_mobile/shared/models/store.dart';
@@ -85,7 +86,7 @@ class CuratedPeopleRow extends StatelessWidget {
"Add name", "Add name",
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Theme.of(context).primaryColor, color: context.primaryColor,
), ),
), ),
), ),

View File

@@ -1,5 +1,5 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/map/ui/map_thumbnail.dart'; import 'package:immich_mobile/modules/map/ui/map_thumbnail.dart';
import 'package:immich_mobile/modules/search/ui/curated_row.dart'; import 'package:immich_mobile/modules/search/ui/curated_row.dart';
import 'package:immich_mobile/modules/search/ui/thumbnail_with_info.dart'; import 'package:immich_mobile/modules/search/ui/thumbnail_with_info.dart';
@@ -25,7 +25,7 @@ class CuratedPlacesRow extends CuratedRow {
final int actualContentIndex = isMapEnabled ? 1 : 0; final int actualContentIndex = isMapEnabled ? 1 : 0;
Widget buildMapThumbnail() { Widget buildMapThumbnail() {
return GestureDetector( return GestureDetector(
onTap: () => AutoRouter.of(context).push( onTap: () => context.autoPush(
const MapRoute(), const MapRoute(),
), ),
child: SizedBox( child: SizedBox(
@@ -43,10 +43,12 @@ class CuratedPlacesRow extends CuratedRow {
), ),
height: imageSize, height: imageSize,
showAttribution: false, showAttribution: false,
isDarkTheme: Theme.of(context).brightness == Brightness.dark, isDarkTheme: context.isDarkTheme,
), ),
), ),
Container( Padding(
padding: const EdgeInsets.only(right: 10.0),
child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
color: Colors.black, color: Colors.black,
@@ -57,7 +59,8 @@ class CuratedPlacesRow extends CuratedRow {
Colors.blueGrey.withOpacity(0.0), Colors.blueGrey.withOpacity(0.0),
Colors.black.withOpacity(0.4), Colors.black.withOpacity(0.4),
], ],
stops: const [0.0, 1.0], stops: const [0.0, 0.4],
),
), ),
), ),
), ),

View File

@@ -1,5 +1,5 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.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/models/curated_content.dart';
import 'package:immich_mobile/modules/search/ui/thumbnail_with_info.dart'; import 'package:immich_mobile/modules/search/ui/thumbnail_with_info.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
@@ -50,13 +50,13 @@ class ExploreGrid extends StatelessWidget {
borderRadius: 0, borderRadius: 0,
onTap: () { onTap: () {
isPeople isPeople
? AutoRouter.of(context).push( ? context.autoPush(
PersonResultRoute( PersonResultRoute(
personId: content.id, personId: content.id,
personName: content.label, personName: content.label,
), ),
) )
: AutoRouter.of(context).push( : context.autoPush(
SearchResultRoute(searchTerm: 'm:${content.label}'), SearchResultRoute(searchTerm: 'm:${content.label}'),
); );
}, },

View File

@@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart'; import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart';
class ImmichSearchBar extends HookConsumerWidget class ImmichSearchBar extends HookConsumerWidget
@@ -57,8 +58,8 @@ class ImmichSearchBar extends HookConsumerWidget
}, },
decoration: InputDecoration( decoration: InputDecoration(
hintText: 'search_bar_hint'.tr(), hintText: 'search_bar_hint'.tr(),
hintStyle: Theme.of(context).textTheme.titleSmall?.copyWith( hintStyle: context.textTheme.titleSmall?.copyWith(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.5), color: context.themeData.colorScheme.onSurface.withOpacity(0.5),
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
fontSize: 14, fontSize: 14,
), ),

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.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/providers/people.provider.dart';
class PersonNameEditFormResult { class PersonNameEditFormResult {
@@ -71,7 +72,7 @@ class PersonNameEditForm extends HookConsumerWidget {
child: Text( child: Text(
"Save", "Save",
style: TextStyle( style: TextStyle(
color: Theme.of(context).primaryColor, color: context.primaryColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),

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