Compare commits

...

28 Commits

Author SHA1 Message Date
timonrieger 9049951047 fix(mobile): invisible ink splashes in asset sheet 2026-06-02 00:25:25 +02:00
Mert 7eabac6702 feat(server): hls with real-time transcoding (#28230)
* hls implementation

* fix stale state after ffmpeg exit
2026-06-01 18:52:29 +00:00
renovate[bot] cf4789e008 chore(deps): update github-actions (major) (#28752) 2026-06-01 18:35:36 +00:00
renovate[bot] 412884fce3 chore(deps): update ghcr.io/jdx/mise docker tag to v2026.5.18 (#28749) 2026-06-01 19:47:53 +02:00
Jason Rasmussen 16aee2b869 fix: album name (#28751) 2026-06-01 19:45:24 +02:00
Daniel Dietzler 3f7af51531 fix: version check (#28746) 2026-06-01 13:41:08 -04:00
Brandon Wees 4eb100327e fix: disallow cross origin/non http protocols for continueUrl on login (#28706)
* fix: disallow cross origin/non http protocols for continueUrl on login

* chore: use Route helper

* fix: also use Route.continue in pin code prompt

* fix: typecheck
2026-06-01 13:38:26 -04:00
bo0tzz 69b1946484 feat: handle prereleases in publish workflows (#28701) 2026-06-01 17:11:45 +02:00
bo0tzz 61cd69a286 fix: strip rc suffix from iOS marketing version (#28741) 2026-06-01 09:56:43 -05:00
Daniel Dietzler c8a1d0e400 feat: release candidate support (#28665)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-06-01 16:10:07 +02:00
Paul Makles d120444a87 fix(devcontainer): update build cache volume (#28736) 2026-06-01 12:41:53 +00:00
shenlong 2382894fa2 fix: auto route rebuild on settings change (#28717)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-05-31 09:00:37 -05:00
Alex a52e7dc11a chore: feat iOS run debug build (#28712)
# Conflicts:
#	mobile/ios/Runner.xcodeproj/project.pbxproj
2026-05-31 04:10:03 +00:00
Alex 206992605e feat: upload local assets to album from bottom sheet (#28531)
* feat: upload local assets to album from bottom sheet

* Cancel token

* refactor

* refactor

* Update mobile/lib/domain/services/remote_album.service.dart

Co-authored-by: shenlong <139912620+shenlong-tanwen@users.noreply.github.com>

---------

Co-authored-by: shenlong <139912620+shenlong-tanwen@users.noreply.github.com>
2026-05-30 13:14:49 -05:00
Mert 65611bb860 chore(mobile): make openapi requests abortable (#28692)
make open-api requests abortable
2026-05-30 10:31:17 -05:00
shenlong 14aff51da9 refactor: rename metadata to settings (#28691)
rename metadata to settings

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-05-30 10:27:55 -05:00
shenlong c42cea5ca9 refactor: use widget previews for ui showcase (#28548)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-05-29 20:22:47 +00:00
Jason Rasmussen da8505f61d feat: more plugin triggers and methods (#28690) 2026-05-29 14:02:07 -04:00
Alex 58586483dc feat: render album's name in workflow step card (#28680)
* feat: render album name in step card body

* clean up

* i18n
2026-05-29 10:37:37 -05:00
pneuly a838167f11 fix(ml): pass model_root_dir to OcrOptions for RapidOCR compatibility (#28610)
* fix(ml): pass model_root_dir to OcrOptions for RapidOCR compatibility

Fix a TypeError (Path(None)) when the OCR model is invoked, caused by an upstream change in RapidOCR v3.8.1 (RapidAI/RapidOCR@8ea9626).
RapidOCR now internally calls `Path(cfg.get("model_root_dir"))`. Since `model_root_dir` was missing from `OcrOptions`, it evaluated to `None` and triggered a `TypeError: argument should be a str or an os.PathLike`.
This fix adds the missing `model_root_dir` argument to prevent the error.
Ref: #28331

* fix(ml-test): update OCR tests for RapidOCR schema change

* chore(ml-test): remove unused `cache_dir` parameter from `TextRecognizer`

* Revert "chore(ml-test): remove unused `cache_dir` parameter from `TextRecognizer`"

This reverts commit 007ad7b3f2.

* fix(ml): use self.cache_dir for model_root_dir in OcrOptions
2026-05-28 22:54:04 -04:00
Mert b189fc571c fix: make ts a peer dependency for swagger (#28677)
make ts a peer dependency
2026-05-28 22:04:25 +00:00
Jason Rasmussen 96923f6115 refactor: plugin sdk types (#28674) 2026-05-28 22:04:15 +00:00
shenlong 0d6cce4a5b fix: api repositories using stale endpoint (#28667)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-05-28 16:44:11 -05:00
shenlong 55947cb227 refactor: drop metadata scope (#28668)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2026-05-28 16:42:59 -05:00
Jason Rasmussen 8783180cf3 refactor: plugin manifest (#28673) 2026-05-28 17:23:49 -04:00
Jason Rasmussen 134c0d4dfb feat: search by album name and id (#28672) 2026-05-28 17:01:47 -04:00
Alex aecf8ec88b fix: timeline scroll flicker (#28653)
* test: fix scroll flicker

* lint
2026-05-28 08:20:54 -05:00
Daniel Dietzler bcff1d42b0 chore: migrate more make targets (#28663) 2026-05-28 08:33:57 -04:00
299 changed files with 20398 additions and 4870 deletions
@@ -15,7 +15,7 @@ services:
volumes:
- ${UPLOAD_LOCATION:-upload-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/data
- /etc/localtime:/etc/localtime:ro
- pnpm_store_server:/buildcache/pnpm-store
- build_cache:/buildcache
- ../packages/plugin-core:/build/plugins/immich-plugin-core
immich-web:
env_file: !reset []
+4 -2
View File
@@ -49,7 +49,9 @@ jobs:
- name: Publish
if: ${{ github.event_name == 'release' }}
run: mise run ci-publish
env:
NPM_TAG: ${{ github.event.release.prerelease && 'rc' || 'latest' }}
run: mise run ci-publish -- --tag "$NPM_TAG"
docker:
name: Docker
@@ -102,7 +104,7 @@ jobs:
name=ghcr.io/${{ github.repository_owner }}/immich-cli
tags: |
type=raw,value=${{ steps.package-version.outputs.version }},enable=${{ github.event_name == 'release' }}
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
type=raw,value=latest,enable=${{ github.event_name == 'release' && !github.event.release.prerelease }}
- name: Build and push image
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
+4 -4
View File
@@ -132,7 +132,7 @@ jobs:
suffixes: '-rocm'
platforms: linux/amd64
runner-mapping: '{"linux/amd64": "pokedex-large"}'
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@5813c7c4f7016c748ae7ac5d5f684846649d4d20 # multi-runner-build-workflow-v2.4.0
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@db54dcf16fbb12c43479a23749ceea0ad1b4a704 # multi-runner-build-workflow-v3.0.0
permissions:
contents: read
actions: read
@@ -147,7 +147,7 @@ jobs:
platforms: ${{ matrix.platforms }}
runner-mapping: ${{ matrix.runner-mapping }}
suffixes: ${{ matrix.suffixes }}
dockerhub-push: ${{ github.event_name == 'release' }}
dockerhub-push: ${{ github.event_name == 'release' && !github.event.release.prerelease }}
build-args: |
DEVICE=${{ matrix.device }}
@@ -155,7 +155,7 @@ jobs:
name: Build and Push Server
needs: pre-job
if: ${{ fromJSON(needs.pre-job.outputs.should_run).server == true }}
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@5813c7c4f7016c748ae7ac5d5f684846649d4d20 # multi-runner-build-workflow-v2.4.0
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@db54dcf16fbb12c43479a23749ceea0ad1b4a704 # multi-runner-build-workflow-v3.0.0
permissions:
contents: read
actions: read
@@ -167,7 +167,7 @@ jobs:
image: immich-server
context: .
dockerfile: server/Dockerfile
dockerhub-push: ${{ github.event_name == 'release' }}
dockerhub-push: ${{ github.event_name == 'release' && !github.event.release.prerelease }}
build-args: |
DEVICE=cpu
+10 -2
View File
@@ -98,9 +98,16 @@ jobs:
shouldDeploy: true
};
} else if (eventType == "release") {
const tag = context.payload.workflow_run.head_branch;
const { data: release } = await github.rest.repos.getReleaseByTag({
owner: context.repo.owner,
repo: context.repo.repo,
tag,
});
parameters = {
event: "release",
name: context.payload.workflow_run.head_branch,
name: tag,
prerelease: release.prerelease,
shouldDeploy: !isFork
};
}
@@ -146,6 +153,7 @@ jobs:
const parameters = JSON.parse(process.env.PARAM_JSON);
core.setOutput("event", parameters.event);
core.setOutput("name", parameters.name);
core.setOutput("prerelease", parameters.prerelease);
core.setOutput("shouldDeploy", parameters.shouldDeploy);
- name: Download artifact
@@ -203,7 +211,7 @@ jobs:
run: mise run //docs:deploy
- name: Deploy Docs Release Domain
if: ${{ steps.parameters.outputs.event == 'release' }}
if: ${{ steps.parameters.outputs.event == 'release' && steps.parameters.outputs.prerelease != 'true' }}
env:
TF_VAR_prefix_name: ${{ steps.parameters.outputs.name}}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
+1 -1
View File
@@ -137,7 +137,7 @@ jobs:
github-token: ${{ steps.generate-token.outputs.token }}
- name: Create draft release
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
with:
draft: true
tag_name: ${{ needs.bump_version.outputs.version }}
+3 -1
View File
@@ -39,4 +39,6 @@ jobs:
run: pnpm --filter @immich/sdk build
- name: Publish
run: pnpm --filter @immich/sdk publish --provenance --no-git-checks
env:
NPM_TAG: ${{ github.event.release.prerelease && 'rc' || 'latest' }}
run: pnpm --filter @immich/sdk publish --provenance --no-git-checks --tag "$NPM_TAG"
-4
View File
@@ -72,10 +72,6 @@ jobs:
run: flutter pub get
working-directory: ./mobile/packages/ui
- name: Install dependencies for UI Showcase
run: flutter pub get
working-directory: ./mobile/packages/ui/showcase
- name: Generate translation files
run: mise //mobile:codegen:translation
+2 -2
View File
@@ -374,7 +374,7 @@ jobs:
token: ${{ steps.token.outputs.token }}
- name: Setup pnpm
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
- name: Setup Node
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
@@ -451,7 +451,7 @@ jobs:
token: ${{ steps.token.outputs.token }}
- name: Setup pnpm
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
- name: Setup Node
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+15 -24
View File
@@ -1,46 +1,46 @@
dev:
@printf "This command has been removed. Please use:\n\n mise dev # or mise //:dev from another directory\n\n"\n\n >&2 && exit 1
@printf "This command has been removed. Please use:\n\n mise dev # or mise //:dev from another directory\n\n" >&2 && exit 1
dev-down:
@printf "This command has been removed. Please use:\n\n mise dev-down # or mise //:dev-down from another directory\n\n"\n\n >&2 && exit 1
@printf "This command has been removed. Please use:\n\n mise dev-down # or mise //:dev-down from another directory\n\n" >&2 && exit 1
dev-update:
@printf "This command has been removed. Please use:\n\n mise dev-update # or mise //:dev-update from another directory\n\n"\n\n >&2 && exit 1
@printf "This command has been removed. Please use:\n\n mise dev-update # or mise //:dev-update from another directory\n\n" >&2 && exit 1
dev-scale:
@printf "This command has been removed. Please use:\n\n mise dev-scale # or mise //:dev-scale from another directory\n\n"\n\n >&2 && exit 1
@printf "This command has been removed. Please use:\n\n mise dev-scale # or mise //:dev-scale from another directory\n\n" >&2 && exit 1
dev-docs:
npm --prefix docs run start
.PHONY: e2e
e2e:
@printf "This command has been removed. Please use:\n\n mise e2e # or mise //:e2e from another directory\n\n"\n\n >&2 && exit 1
@printf "This command has been removed. Please use:\n\n mise e2e # or mise //:e2e from another directory\n\n" >&2 && exit 1
e2e-dev:
@printf "This command has been removed. Please use:\n\n mise e2e-dev # or mise //:e2e-dev from another directory\n\n"\n\n >&2 && exit 1
@printf "This command has been removed. Please use:\n\n mise e2e-dev # or mise //:e2e-dev from another directory\n\n" >&2 && exit 1
e2e-update:
@printf "This command has been removed. Please use:\n\n mise e2e-update # or mise //:e2e-update from another directory\n\n"\n\n >&2 && exit 1
@printf "This command has been removed. Please use:\n\n mise e2e-update # or mise //:e2e-update from another directory\n\n" >&2 && exit 1
e2e-down:
@printf "This command has been removed. Please use:\n\n mise e2e-down # or mise //:e2e-down from another directory\n\n"\n\n >&2 && exit 1
@printf "This command has been removed. Please use:\n\n mise e2e-down # or mise //:e2e-down from another directory\n\n" >&2 && exit 1
prod:
@printf "This command has been removed. Please use:\n\n mise prod # or mise //:prod from another directory\n\n"\n\n >&2 && exit 1
@printf "This command has been removed. Please use:\n\n mise prod # or mise //:prod from another directory\n\n" >&2 && exit 1
prod-down:
@printf "This command has been removed. Please use:\n\n mise prod-down # or mise //:prod-down from another directory\n\n"\n\n >&2 && exit 1
@printf "This command has been removed. Please use:\n\n mise prod-down # or mise //:prod-down from another directory\n\n" >&2 && exit 1
prod-scale:
@printf "This command has been removed. Please use:\n\n mise prod-scale # or mise //:prod-scale from another directory\n\n"\n\n >&2 && exit 1
@printf "This command has been removed. Please use:\n\n mise prod-scale # or mise //:prod-scale from another directory\n\n" >&2 && exit 1
.PHONY: open-api
open-api:
@printf "This command has been removed. Please use:\n\n mise open-api # or mise //:open-api from another directory\n\n"\n\n >&2 && exit 1
@printf "This command has been removed. Please use:\n\n mise open-api # or mise //:open-api from another directory\n\n" >&2 && exit 1
sql:
@printf "This command has been removed. Please use:\n\n mise sql # or mise //:sql from another directory\n\n"\n\n >&2 && exit 1
@printf "This command has been removed. Please use:\n\n mise sql # or mise //:sql from another directory\n\n" >&2 && exit 1
renovate:
@@ -52,16 +52,7 @@ renovate:
MODULES = e2e server web cli sdk docs .github
test-e2e:
docker compose -f ./e2e/docker-compose.yml build
pnpm --filter immich-e2e run test
pnpm --filter immich-e2e run test:web
@printf "This command has been removed. Please use:\n\n mise //e2e:test # or mise //e2e:test-web for web tests, respectively\n\n" >&2 && exit 1
clean:
find . -name "node_modules" -type d -prune -exec rm -rf {} +
find . -name "dist" -type d -prune -exec rm -rf '{}' +
find . -name "build" -type d -prune -exec rm -rf '{}' +
find . -name ".svelte-kit" -type d -prune -exec rm -rf '{}' +
find . -name "coverage" -type d -prune -exec rm -rf '{}' +
find . -name ".pnpm-store" -type d -prune -exec rm -rf '{}' +
command -v docker >/dev/null 2>&1 && docker compose -f ./docker/docker-compose.dev.yml down -v --remove-orphans || true
command -v docker >/dev/null 2>&1 && docker compose -f ./e2e/docker-compose.yml down -v --remove-orphans || true
@printf "This command has been removed. Please use:\n\n mise clean # or mise //:clean from another directory\n\n" >&2 && exit 1
+18
View File
@@ -109,6 +109,24 @@ mise //mobile:translation
The mobile app asks you what backend to connect to. You can utilize the demo backend (https://demo.immich.app/) if you don't need to change server code or upload photos. Alternatively, you can run the server yourself per the instructions above.
#### UI components and widget previews
Shared design-system widgets (buttons, inputs, forms) live in the
[`immich_ui` package](https://github.com/immich-app/immich/tree/main/mobile/packages/ui/)
under `mobile/packages/ui/`. Components are defined in `lib/src/components/`
and have matching previews in `lib/src/previews/`.
To inspect a component in isolation with a light/dark toggle and hot reload,
launch [Flutter's Widget Previewer](https://docs.flutter.dev/tools/widget-previewer):
```bash
cd mobile/packages/ui
flutter widget-preview start
```
In VS Code or Android Studio with the Flutter plugin, the previewer
auto-starts when you open the **Flutter Widget Preview** tab in the sidebar.
## IDE setup
### Lint / format extensions
+16 -1
View File
@@ -1,11 +1,21 @@
[tasks.install]
run = "pnpm install --filter immich-e2e --frozen-lockfile"
[tasks.build]
dir = "{{ config_root }}"
run = "docker compose build"
[tasks.test]
depends = ["//e2e:build", "//e2e:ci-setup"]
env._.path = "./node_modules/.bin"
run = "vitest --run"
[tasks.playwright-install]
env._.path = "./node_modules/.bin"
run = "playwright install"
[tasks."test-web"]
depends = ["//e2e:build", "//e2e:ci-setup", "//e2e:playwright-install"]
env._.path = "./node_modules/.bin"
run = "playwright test"
@@ -30,7 +40,12 @@ run = "tsc --noEmit"
[tasks.ci-setup]
depends = ["//:sdk:install", "//:sdk:build", "//cli:install", "//cli:build"]
depends = [
"//:sdk:install",
"//:sdk:build",
"//packages/cli:install",
"//packages/cli:build",
]
run = { task = ":install" }
@@ -95,6 +95,7 @@ describe('/server', () => {
major: expect.any(Number),
minor: expect.any(Number),
patch: expect.any(Number),
prerelease: null,
});
});
});
@@ -21,18 +21,18 @@ describe('/system-config', () => {
const response1 = await request(app)
.put('/system-config')
.set('Authorization', `Bearer ${admin.accessToken}`)
.send({ ...config, newVersionCheck: { enabled: false } });
.send({ ...config, newVersionCheck: { enabled: false, channel: 'stable' } });
expect(response1.status).toBe(200);
expect(response1.body).toEqual({ ...config, newVersionCheck: { enabled: false } });
expect(response1.body).toEqual({ ...config, newVersionCheck: { enabled: false, channel: 'stable' } });
const response2 = await request(app)
.put('/system-config')
.set('Authorization', `Bearer ${admin.accessToken}`)
.send({ ...config, newVersionCheck: { enabled: true } });
.send({ ...config, newVersionCheck: { enabled: true, channel: 'stable' } });
expect(response2.status).toBe(200);
expect(response2.body).toEqual({ ...config, newVersionCheck: { enabled: true } });
expect(response2.body).toEqual({ ...config, newVersionCheck: { enabled: true, channel: 'stable' } });
});
it('should reject an invalid config entry', async () => {
+9
View File
@@ -305,6 +305,8 @@
"refreshing_all_libraries": "Refreshing all libraries",
"registration": "Admin Registration",
"registration_description": "Since you are the first user on the system, you will be assigned as the Admin and are responsible for administrative tasks, and additional users will be created by you.",
"release_channel_release_candidate": "Release candidate",
"release_channel_stable": "Stable",
"remove_failed_jobs": "Remove failed jobs",
"require_password_change_on_login": "Require user to change password on first login",
"reset_settings_to_default": "Reset settings to default",
@@ -399,6 +401,10 @@
"transcoding_preferred_hardware_device_description": "Applies only to VAAPI and QSV. Sets the dri node used for hardware transcoding.",
"transcoding_preset_preset": "Preset (-preset)",
"transcoding_preset_preset_description": "Compression speed. Slower presets produce smaller files, and increase quality when targeting a certain bitrate. VP9 ignores speeds above 'faster'.",
"transcoding_realtime": "Real-time Transcoding [EXPERIMENTAL]",
"transcoding_realtime_description": "Allows transcoding to be performed in real-time as the video is being streamed. Enables quality switching, but may cause higher playback latency and stuttering depending on server capabilities.",
"transcoding_realtime_enabled": "Enable real-time transcoding",
"transcoding_realtime_enabled_description": "If disabled, the server will refuse to start new real-time transcoding sessions.",
"transcoding_reference_frames": "Reference frames",
"transcoding_reference_frames_description": "The number of frames to reference when compressing a given frame. Higher values improve compression efficiency, but slow down encoding. 0 sets this value automatically.",
"transcoding_required_description": "Only videos not in an accepted format",
@@ -442,6 +448,8 @@
"user_settings_description": "Manage user settings",
"user_successfully_removed": "User {email} has been successfully removed.",
"users_page_description": "Admin users page",
"version_check_channel": "Release channel",
"version_check_channel_description": "Pick the release channel you want to get version announcements for",
"version_check_enabled_description": "Enable version check",
"version_check_implications": "The version check feature relies on periodic communication with {server}",
"version_check_settings": "Version Check",
@@ -2233,6 +2241,7 @@
"slideshow_repeat": "Repeat slideshow",
"slideshow_repeat_description": "Loop back to beginning when slideshow ends",
"slideshow_settings": "Slideshow settings",
"smart_album": "Smart album",
"sort_albums_by": "Sort albums by...",
"sort_created": "Date created",
"sort_items": "Number of items",
@@ -64,6 +64,7 @@ class TextRecognizer(InferenceModel):
rec_batch_num=max_batch_size if max_batch_size else 6,
rec_img_shape=(3, 48, 320),
lang_type=self.language,
model_root_dir=self.cache_dir,
)
)
return session
+18 -3
View File
@@ -1028,7 +1028,12 @@ class TestOcr:
text_recognizer.load()
rapid_recognizer.assert_called_once_with(
OcrOptions(session=ort_session.return_value, rec_batch_num=6, rec_img_shape=(3, 48, 320))
OcrOptions(
session=ort_session.return_value,
rec_batch_num=6,
rec_img_shape=(3, 48, 320),
model_root_dir=text_recognizer.cache_dir,
)
)
def test_set_custom_max_batch_size(self, ort_session: mock.Mock, path: mock.Mock, mocker: MockerFixture) -> None:
@@ -1041,7 +1046,12 @@ class TestOcr:
text_recognizer.load()
rapid_recognizer.assert_called_once_with(
OcrOptions(session=ort_session.return_value, rec_batch_num=4, rec_img_shape=(3, 48, 320))
OcrOptions(
session=ort_session.return_value,
rec_batch_num=4,
rec_img_shape=(3, 48, 320),
model_root_dir=text_recognizer.cache_dir,
)
)
def test_ignore_other_custom_max_batch_size(
@@ -1056,7 +1066,12 @@ class TestOcr:
text_recognizer.load()
rapid_recognizer.assert_called_once_with(
OcrOptions(session=ort_session.return_value, rec_batch_num=6, rec_img_shape=(3, 48, 320))
OcrOptions(
session=ort_session.return_value,
rec_batch_num=6,
rec_img_shape=(3, 48, 320),
model_root_dir=text_recognizer.cache_dir,
)
)
+14 -3
View File
@@ -54,8 +54,8 @@ lockfile = true
[tasks.plugins]
run = [
"pnpm --filter @immich/plugin-sdk --filter @immich/plugin-core install --frozen-lockfile",
"pnpm --filter @immich/plugin-sdk --filter @immich/plugin-core build",
"pnpm --filter @immich/sdk --filter @immich/plugin-sdk --filter @immich/plugin-core install --frozen-lockfile",
"pnpm --filter @immich/sdk --filter @immich/plugin-sdk --filter @immich/plugin-core build",
]
[tasks.open-api-typescript]
@@ -108,7 +108,7 @@ depends = "//:plugins"
dir = "docker"
interactive = true
env = { COMPOSE_BAKE = true }
run = "docker compose -f ./docker-compose.prod.yml up --remove-orphans"
run = "docker compose -f ./docker-compose.prod.yml up --build --remove-orphans"
depends_post = "//:prod-down"
[tasks.prod-scale]
@@ -165,3 +165,14 @@ run = "pnpm format"
[tasks."i18n:format-fix"]
run = "pnpm format:fix"
[tasks.clean]
run = [
"find . -name 'node_modules' -type d -prune -exec rm -rf '{}' +",
"find . -name 'dist' -type d -prune -exec rm -rf '{}' +",
"find . -name 'build' -type d -prune -exec rm -rf '{}' +",
"find . -name '.svelte-kit' -type d -prune -exec rm -rf '{}' +",
"find . -name 'coverage' -type d -prune -exec rm -rf '{}' +",
"find . -name '.pnpm-store' -type d -prune -exec rm -rf '{}' +",
{ task = "//:*-down" },
]
File diff suppressed because it is too large Load Diff
+93 -79
View File
@@ -9,7 +9,7 @@
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
3B6A31FED0FC846D6BD69BBC /* Pods_ShareExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 357FC57E54FD0F51795CF28A /* Pods_ShareExtension.framework */; };
467DA6EAF83F3481F8BD94AB /* Pods_ShareExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8AB817AA297EDEC88B23F3F6 /* Pods_ShareExtension.framework */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
@@ -22,7 +22,7 @@
B2EE00022E72CA15008B6CA7 /* PermissionApi.g.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2EE00012E72CA15008B6CA7 /* PermissionApi.g.swift */; };
B2EE00042E72CA15008B6CA7 /* PermissionApiImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2EE00032E72CA15008B6CA7 /* PermissionApiImpl.swift */; };
B2BE315F2E5E5229006EEF88 /* BackgroundWorker.g.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2BE315E2E5E5229006EEF88 /* BackgroundWorker.g.swift */; };
D218389C4A4C4693F141F7D1 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 886774DBDDE6B35BF2B4F2CD /* Pods_Runner.framework */; };
D3BED739C0BC29BB32E18EB2 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CC499FBCE6B29B2DAFED7130 /* Pods_Runner.framework */; };
F02538E92DFBCBDD008C3FA3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
F0B57D3A2DF764BD00DC5BCC /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0B57D392DF764BD00DC5BCC /* WidgetKit.framework */; };
F0B57D3C2DF764BD00DC5BCC /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0B57D3B2DF764BD00DC5BCC /* SwiftUI.framework */; };
@@ -85,16 +85,18 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
10B378D23F917891A0F23E33 /* Pods-ShareExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.release.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.release.xcconfig"; sourceTree = "<group>"; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
2E3441B73560D0F6FD25E04F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
357FC57E54FD0F51795CF28A /* Pods_ShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
571EAA93D77181C7C98C2EA6 /* Pods-ShareExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.release.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.release.xcconfig"; sourceTree = "<group>"; };
614A7F5DC5DB09E89E4FCBE8 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
681FBA560D5D2ADDE4F0B59E /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
6D160F04A389B9FFBC557803 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
886774DBDDE6B35BF2B4F2CD /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8AB817AA297EDEC88B23F3F6 /* Pods_ShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
937632897A02DE9C249F20A6 /* Pods-ShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.debug.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Immich-Debug.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Immich-Debug.app"; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -103,7 +105,6 @@
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
A01DD6982F7F43B40049AB63 /* ImageRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageRequest.swift; sourceTree = "<group>"; };
B1FBA9EE014DE20271B0FE77 /* Pods-ShareExtension.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.profile.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.profile.xcconfig"; sourceTree = "<group>"; };
B21E34A92E5AFD210031FDB9 /* BackgroundWorkerApiImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundWorkerApiImpl.swift; sourceTree = "<group>"; };
B21E34AB2E5B09100031FDB9 /* BackgroundWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundWorker.swift; sourceTree = "<group>"; };
B25D37782E72CA15008B6CA7 /* Connectivity.g.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Connectivity.g.swift; sourceTree = "<group>"; };
@@ -111,12 +112,11 @@
B2EE00012E72CA15008B6CA7 /* PermissionApi.g.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionApi.g.swift; sourceTree = "<group>"; };
B2EE00032E72CA15008B6CA7 /* PermissionApiImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionApiImpl.swift; sourceTree = "<group>"; };
B2BE315E2E5E5229006EEF88 /* BackgroundWorker.g.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundWorker.g.swift; sourceTree = "<group>"; };
E0E99CDC17B3EB7FA8BA2332 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
C4A6A71F33CE37B3C913115C /* Pods-ShareExtension.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.profile.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.profile.xcconfig"; sourceTree = "<group>"; };
CC499FBCE6B29B2DAFED7130 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
F0B57D382DF764BD00DC5BCC /* WidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
F0B57D392DF764BD00DC5BCC /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
F0B57D3B2DF764BD00DC5BCC /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
F7101BB0391A314774615E89 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
F8A35EA3C3E01BD66AFDE0E5 /* Pods-ShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.debug.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
FA9973382CF6DF4B000EF859 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
FAC6F8902D287C890078CB2F /* ShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
FAC6F8B12D287F120078CB2F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -199,7 +199,7 @@
FEE084F82EC172460045228E /* SQLiteData in Frameworks */,
FEE084FB2EC1725A0045228E /* RawStructuredFieldValues in Frameworks */,
FEE084FD2EC1725A0045228E /* StructuredFieldValues in Frameworks */,
D218389C4A4C4693F141F7D1 /* Pods_Runner.framework in Frameworks */,
D3BED739C0BC29BB32E18EB2 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -216,7 +216,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
3B6A31FED0FC846D6BD69BBC /* Pods_ShareExtension.framework in Frameworks */,
467DA6EAF83F3481F8BD94AB /* Pods_ShareExtension.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -226,12 +226,12 @@
0FB772A5B9601143383626CA /* Pods */ = {
isa = PBXGroup;
children = (
2E3441B73560D0F6FD25E04F /* Pods-Runner.debug.xcconfig */,
E0E99CDC17B3EB7FA8BA2332 /* Pods-Runner.release.xcconfig */,
F7101BB0391A314774615E89 /* Pods-Runner.profile.xcconfig */,
F8A35EA3C3E01BD66AFDE0E5 /* Pods-ShareExtension.debug.xcconfig */,
571EAA93D77181C7C98C2EA6 /* Pods-ShareExtension.release.xcconfig */,
B1FBA9EE014DE20271B0FE77 /* Pods-ShareExtension.profile.xcconfig */,
614A7F5DC5DB09E89E4FCBE8 /* Pods-Runner.debug.xcconfig */,
6D160F04A389B9FFBC557803 /* Pods-Runner.release.xcconfig */,
681FBA560D5D2ADDE4F0B59E /* Pods-Runner.profile.xcconfig */,
937632897A02DE9C249F20A6 /* Pods-ShareExtension.debug.xcconfig */,
10B378D23F917891A0F23E33 /* Pods-ShareExtension.release.xcconfig */,
C4A6A71F33CE37B3C913115C /* Pods-ShareExtension.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
@@ -239,10 +239,10 @@
1754452DD81DA6620E279E51 /* Frameworks */ = {
isa = PBXGroup;
children = (
886774DBDDE6B35BF2B4F2CD /* Pods_Runner.framework */,
357FC57E54FD0F51795CF28A /* Pods_ShareExtension.framework */,
F0B57D392DF764BD00DC5BCC /* WidgetKit.framework */,
F0B57D3B2DF764BD00DC5BCC /* SwiftUI.framework */,
CC499FBCE6B29B2DAFED7130 /* Pods_Runner.framework */,
8AB817AA297EDEC88B23F3F6 /* Pods_ShareExtension.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -370,7 +370,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
4044AF030EF7D8721844FFBA /* [CP] Check Pods Manifest.lock */,
BAEA01ACA3F5C9CD3D732370 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
@@ -378,8 +378,8 @@
9705A1C41CF9048500538489 /* Embed Frameworks */,
FAC6F89A2D287C890078CB2F /* Embed Foundation Extensions */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
D218A34AEE62BC1EF119F5B0 /* [CP] Embed Pods Frameworks */,
6724EEB7D74949FA08581154 /* [CP] Copy Pods Resources */,
513DA7292DED6106813332F4 /* [CP] Embed Pods Frameworks */,
2FA39DEC809D6D7C4A01EFCB /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -393,6 +393,9 @@
FEE084F22EC172080045228E /* Schemas */,
);
name = Runner;
packageProductDependencies = (
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */,
);
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Immich-Debug.app */;
productType = "com.apple.product-type.application";
@@ -421,7 +424,7 @@
isa = PBXNativeTarget;
buildConfigurationList = FAC6F8A02D287C890078CB2F /* Build configuration list for PBXNativeTarget "ShareExtension" */;
buildPhases = (
3BEF3D71D97E337D921C0EB5 /* [CP] Check Pods Manifest.lock */,
8EC9CF3E20AF32BF24D4F3E1 /* [CP] Check Pods Manifest.lock */,
FAC6F88C2D287C890078CB2F /* Sources */,
FAC6F88D2D287C890078CB2F /* Frameworks */,
FAC6F88E2D287C890078CB2F /* Resources */,
@@ -470,7 +473,7 @@
);
mainGroup = 97C146E51CF9000F007C117D;
packageReferences = (
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */,
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */,
FEE084F62EC172460045228E /* XCRemoteSwiftPackageReference "sqlite-data" */,
FEE084F92EC1725A0045228E /* XCRemoteSwiftPackageReference "swift-http-structured-headers" */,
);
@@ -517,6 +520,23 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
2FA39DEC809D6D7C4A01EFCB /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@@ -533,7 +553,24 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
3BEF3D71D97E337D921C0EB5 /* [CP] Check Pods Manifest.lock */ = {
513DA7292DED6106813332F4 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
8EC9CF3E20AF32BF24D4F3E1 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -555,7 +592,22 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
4044AF030EF7D8721844FFBA /* [CP] Check Pods Manifest.lock */ = {
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
};
BAEA01ACA3F5C9CD3D732370 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -577,55 +629,6 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
6724EEB7D74949FA08581154 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
};
D218A34AEE62BC1EF119F5B0 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -1092,7 +1095,7 @@
};
FAC6F89C2D287C890078CB2F /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F8A35EA3C3E01BD66AFDE0E5 /* Pods-ShareExtension.debug.xcconfig */;
baseConfigurationReference = 937632897A02DE9C249F20A6 /* Pods-ShareExtension.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
@@ -1135,7 +1138,7 @@
};
FAC6F89D2D287C890078CB2F /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 571EAA93D77181C7C98C2EA6 /* Pods-ShareExtension.release.xcconfig */;
baseConfigurationReference = 10B378D23F917891A0F23E33 /* Pods-ShareExtension.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
@@ -1175,7 +1178,7 @@
};
FAC6F89E2D287C890078CB2F /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = B1FBA9EE014DE20271B0FE77 /* Pods-ShareExtension.profile.xcconfig */;
baseConfigurationReference = C4A6A71F33CE37B3C913115C /* Pods-ShareExtension.profile.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
@@ -1258,6 +1261,13 @@
};
/* End XCConfigurationList section */
/* Begin XCLocalSwiftPackageReference section */
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = {
isa = XCLocalSwiftPackageReference;
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
};
/* End XCLocalSwiftPackageReference section */
/* Begin XCRemoteSwiftPackageReference section */
FEE084F62EC172460045228E /* XCRemoteSwiftPackageReference "sqlite-data" */ = {
isa = XCRemoteSwiftPackageReference;
@@ -1278,6 +1288,10 @@
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = {
isa = XCSwiftPackageProductDependency;
productName = FlutterGeneratedPluginSwiftPackage;
};
FEE084F72EC172460045228E /* SQLiteData */ = {
isa = XCSwiftPackageProductDependency;
package = FEE084F62EC172460045228E /* XCRemoteSwiftPackageReference "sqlite-data" */;
@@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/combine-schedulers",
"state" : {
"revision" : "5928286acce13def418ec36d05a001a9641086f2",
"version" : "1.0.3"
"revision" : "fd16d76fd8b9a976d88bfb6cacc05ca8d19c91b6",
"version" : "1.1.0"
}
},
{
+1 -1
View File
@@ -49,7 +49,7 @@ def get_version_from_pubspec
pubspec = YAML.load_file(pubspec_path)
version_string = pubspec['version']
version_string ? version_string.split('+').first : nil
version_string ? version_string.split('+').first.split('-').first : nil
end
# Helper method to configure code signing for all targets
@@ -12,7 +12,7 @@ import 'package:immich_mobile/domain/models/config/theme_config.dart';
import 'package:immich_mobile/domain/models/config/timeline_config.dart';
import 'package:immich_mobile/domain/models/config/viewer_config.dart';
import 'package:immich_mobile/domain/models/log.model.dart';
import 'package:immich_mobile/domain/models/metadata_key.dart';
import 'package:immich_mobile/domain/models/settings_key.dart';
import 'package:immich_mobile/domain/models/timeline.model.dart';
import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart';
@@ -95,7 +95,7 @@ class AppConfig {
String toString() =>
'AppConfig(logLevel: $logLevel, theme: $theme, cleanup: $cleanup, map: $map, timeline: $timeline, image: $image, viewer: $viewer, slideshow: $slideshow, album: $album, backup: $backup, network: $network)';
T read<T extends Object>(MetadataKey<T> key) =>
T read<T extends Object>(SettingsKey<T> key) =>
(switch (key) {
.logLevel => logLevel,
.themePrimaryColor => theme.primaryColor,
@@ -143,15 +143,10 @@ class AppConfig {
})
as T;
factory AppConfig.fromEntries(Map<MetadataKey<Object>, Object> entries) {
var config = const AppConfig();
for (final MapEntry(key: key, value: value) in entries.entries) {
config = config.write(key, value);
}
return config;
}
factory AppConfig.fromEntries(Map<SettingsKey<Object>, Object> overrides) =>
overrides.entries.fold(const AppConfig(), (config, entry) => config.write(entry.key, entry.value));
AppConfig write<T extends Object>(MetadataKey<T> key, T value) {
AppConfig write<T extends Object>(SettingsKey<T> key, T value) {
return switch (key) {
.logLevel => copyWith(logLevel: value as LogLevel),
.themePrimaryColor => copyWith(theme: theme.copyWith(primaryColor: value as ImmichColorPreset)),
@@ -7,14 +7,7 @@ import 'package:immich_mobile/domain/models/log.model.dart';
import 'package:immich_mobile/domain/models/timeline.model.dart';
import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart';
enum MetadataScope {
user, // keys with this scope are deleted on logout
system;
const MetadataScope();
}
enum MetadataKey<T extends Object> {
enum SettingsKey<T extends Object> {
// Theme
themePrimaryColor<ImmichColorPreset>(codec: _EnumCodec(ImmichColorPreset.values)),
themeMode<ThemeMode>(codec: _EnumCodec(ThemeMode.values)),
@@ -32,14 +25,11 @@ enum MetadataKey<T extends Object> {
viewerTapToNavigate<bool>(),
// Network
networkAutoEndpointSwitching<bool>(scope: .system),
networkPreferredWifiName<String>(scope: .system),
networkLocalEndpoint<String>(scope: .system),
networkExternalEndpointList<List<String>>(scope: .system, codec: _ListCodec(_PrimitiveCodec.string)),
networkCustomHeaders<Map<String, String>>(
scope: .system,
codec: _MapCodec(_PrimitiveCodec.string, _PrimitiveCodec.string),
),
networkAutoEndpointSwitching<bool>(),
networkPreferredWifiName<String>(),
networkLocalEndpoint<String>(),
networkExternalEndpointList<List<String>>(codec: _ListCodec(_PrimitiveCodec.string)),
networkCustomHeaders<Map<String, String>>(codec: _MapCodec(_PrimitiveCodec.string, _PrimitiveCodec.string)),
// Album
albumSortMode<AlbumSortMode>(codec: _EnumCodec(AlbumSortMode.values)),
@@ -60,7 +50,7 @@ enum MetadataKey<T extends Object> {
timelineStorageIndicator<bool>(),
// Log
logLevel<LogLevel>(scope: .system, codec: _EnumCodec(LogLevel.values)),
logLevel<LogLevel>(codec: _EnumCodec(LogLevel.values)),
// Map
mapShowFavoriteOnly<bool>(),
@@ -83,25 +73,24 @@ enum MetadataKey<T extends Object> {
slideshowLook<SlideshowLook>(codec: _EnumCodec(SlideshowLook.values)),
slideshowDirection<SlideshowDirection>(codec: _EnumCodec(SlideshowDirection.values));
final MetadataScope scope;
final _MetadataCodec<T>? _codecOverride;
final _SettingsCodec<T>? _codecOverride;
const MetadataKey({this.scope = .user, _MetadataCodec<T>? codec}) : _codecOverride = codec;
const SettingsKey({_SettingsCodec<T>? codec}) : _codecOverride = codec;
_MetadataCodec<T> get _codec => _codecOverride ?? _MetadataCodec.forType(T);
_SettingsCodec<T> get _codec => _codecOverride ?? _SettingsCodec.forType(T);
String encode(T value) => _codec.encode(value);
T decode(String raw) => _codec.decode(raw);
}
sealed class _MetadataCodec<T extends Object> {
const _MetadataCodec();
sealed class _SettingsCodec<T extends Object> {
const _SettingsCodec();
String encode(T value);
T decode(String raw);
static const Map<Type, _MetadataCodec<Object>> _primitives = {
static const Map<Type, _SettingsCodec<Object>> _primitives = {
int: _PrimitiveCodec.integer,
double: _PrimitiveCodec.real,
bool: _PrimitiveCodec.boolean,
@@ -109,16 +98,16 @@ sealed class _MetadataCodec<T extends Object> {
DateTime: _DateTimeCodec(),
};
static _MetadataCodec<T> forType<T extends Object>(Type runtimeType) {
static _SettingsCodec<T> forType<T extends Object>(Type runtimeType) {
final codec = _primitives[runtimeType];
if (codec == null) {
throw StateError('No primitive codec for $runtimeType. Provide an explicit codec when defining the MetadataKey.');
throw StateError('No primitive codec for $runtimeType. Provide an explicit codec when defining the SettingsKey.');
}
return codec as _MetadataCodec<T>;
return codec as _SettingsCodec<T>;
}
}
final class _EnumCodec<T extends Enum> extends _MetadataCodec<T> {
final class _EnumCodec<T extends Enum> extends _SettingsCodec<T> {
final List<T> values;
const _EnumCodec(this.values);
@@ -130,7 +119,7 @@ final class _EnumCodec<T extends Enum> extends _MetadataCodec<T> {
T decode(String raw) => values.firstWhere((v) => v.name == raw);
}
final class _DateTimeCodec extends _MetadataCodec<DateTime> {
final class _DateTimeCodec extends _SettingsCodec<DateTime> {
const _DateTimeCodec();
@override
@@ -140,9 +129,9 @@ final class _DateTimeCodec extends _MetadataCodec<DateTime> {
DateTime decode(String raw) => DateTime.parse(raw);
}
final class _MapCodec<K extends Object, V extends Object> extends _MetadataCodec<Map<K, V>> {
final _MetadataCodec<K> _keyCodec;
final _MetadataCodec<V> _valueCodec;
final class _MapCodec<K extends Object, V extends Object> extends _SettingsCodec<Map<K, V>> {
final _SettingsCodec<K> _keyCodec;
final _SettingsCodec<V> _valueCodec;
const _MapCodec(this._keyCodec, this._valueCodec);
@@ -178,8 +167,8 @@ final class _MapCodec<K extends Object, V extends Object> extends _MetadataCodec
}
}
final class _ListCodec<T extends Object> extends _MetadataCodec<List<T>> {
final _MetadataCodec<T> _elementCodec;
final class _ListCodec<T extends Object> extends _SettingsCodec<List<T>> {
final _SettingsCodec<T> _elementCodec;
const _ListCodec(this._elementCodec);
@@ -208,7 +197,7 @@ final class _ListCodec<T extends Object> extends _MetadataCodec<List<T>> {
}
}
final class _PrimitiveCodec<T extends Object> extends _MetadataCodec<T> {
final class _PrimitiveCodec<T extends Object> extends _SettingsCodec<T> {
final T Function(String) _parse;
const _PrimitiveCodec._(this._parse);
@@ -11,7 +11,7 @@ import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/extensions/platform_extensions.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/platform/background_worker_api.g.dart';
import 'package:immich_mobile/platform/background_worker_lock_api.g.dart';
import 'package:immich_mobile/providers/background_sync.provider.dart';
@@ -39,7 +39,7 @@ class BackgroundWorkerFgService {
_foregroundHostApi.saveNotificationMessage(title, body);
Future<void> configure({int? minimumDelaySeconds, bool? requireCharging}) {
final backup = MetadataRepository.instance.appConfig.backup;
final backup = SettingsRepository.instance.appConfig.backup;
return _foregroundHostApi.configure(
BackgroundWorkerSettings(
minimumDelaySeconds: minimumDelaySeconds ?? backup.triggerDelay,
@@ -67,7 +67,7 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi {
BackgroundWorkerFlutterApi.setUp(this);
}
bool get _isBackupEnabled => MetadataRepository.instance.appConfig.backup.enabled;
bool get _isBackupEnabled => SettingsRepository.instance.appConfig.backup.enabled;
Future<void> init() async {
try {
+11 -11
View File
@@ -2,9 +2,9 @@ import 'dart:async';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/models/log.model.dart';
import 'package:immich_mobile/domain/models/metadata_key.dart';
import 'package:immich_mobile/domain/models/settings_key.dart';
import 'package:immich_mobile/infrastructure/repositories/log.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/utils/debug_print.dart';
import 'package:logging/logging.dart';
@@ -12,10 +12,10 @@ import 'package:logging/logging.dart';
///
/// It listens to Dart's [Logger.root], buffers logs in memory (optionally),
/// writes them to a persistent [LogRepository], and manages log levels via
/// [MetadataRepository].
/// [SettingsRepository].
class LogService {
final LogRepository _logRepository;
final MetadataRepository _metadataRepository;
final SettingsRepository _settingsRepository;
final List<LogMessage> _msgBuffer = [];
@@ -38,12 +38,12 @@ class LogService {
static Future<LogService> init({
required LogRepository logRepository,
required MetadataRepository metadataRepository,
required SettingsRepository settingsRepository,
bool shouldBuffer = true,
}) async {
_instance ??= await create(
logRepository: logRepository,
metadataRepository: metadataRepository,
settingsRepository: settingsRepository,
shouldBuffer: shouldBuffer,
);
return _instance!;
@@ -51,17 +51,17 @@ class LogService {
static Future<LogService> create({
required LogRepository logRepository,
required MetadataRepository metadataRepository,
required SettingsRepository settingsRepository,
bool shouldBuffer = true,
}) async {
final instance = LogService._(logRepository, metadataRepository, shouldBuffer);
final instance = LogService._(logRepository, settingsRepository, shouldBuffer);
await logRepository.truncate(limit: kLogTruncateLimit);
final level = instance._metadataRepository.appConfig.logLevel;
final level = instance._settingsRepository.appConfig.logLevel;
Logger.root.level = Level.LEVELS.elementAtOrNull(level.index) ?? Level.INFO;
return instance;
}
LogService._(this._logRepository, this._metadataRepository, this._shouldBuffer) {
LogService._(this._logRepository, this._settingsRepository, this._shouldBuffer) {
_logSubscription = Logger.root.onRecord.listen(_handleLogRecord);
}
@@ -91,7 +91,7 @@ class LogService {
}
Future<void> setLogLevel(LogLevel level) async {
await _metadataRepository.write(MetadataKey.logLevel, level);
await _settingsRepository.write(SettingsKey.logLevel, level);
Logger.root.level = level.toLevel();
}
@@ -192,43 +192,30 @@ class RemoteAlbumService {
required UserDto uploader,
required AlbumAssetCandidates candidates,
UploadCallbacks uploadCallbacks = const UploadCallbacks(),
Completer<void>? cancelToken,
}) async {
int addedCount = 0;
if (candidates.remoteAssetIds.isNotEmpty) {
addedCount += await addAssets(albumId: albumId, assetIds: candidates.remoteAssetIds);
}
if (candidates.localAssetsToUpload.isNotEmpty) {
addedCount += await _uploadAndAddLocals(albumId, uploader, candidates.localAssetsToUpload, uploadCallbacks);
addedCount += await _uploadAndAddLocals(
albumId,
uploader,
candidates.localAssetsToUpload,
uploadCallbacks,
cancelToken,
);
}
return addedCount;
}
/// Creates an album, seeding it with already-remote asset IDs, then uploads
/// local-only assets and links each one as it finishes.
Future<RemoteAlbum> createAlbumWithAssets({
required String title,
required UserDto owner,
String? description,
AlbumAssetCandidates candidates = const AlbumAssetCandidates(remoteAssetIds: [], localAssetsToUpload: []),
UploadCallbacks uploadCallbacks = const UploadCallbacks(),
}) async {
final album = await createAlbum(
title: title,
owner: owner,
description: description,
assetIds: candidates.remoteAssetIds,
);
if (candidates.localAssetsToUpload.isNotEmpty) {
await _uploadAndAddLocals(album.id, owner, candidates.localAssetsToUpload, uploadCallbacks);
}
return album;
}
Future<int> _uploadAndAddLocals(
String albumId,
UserDto uploader,
List<LocalAsset> localAssets,
UploadCallbacks userCallbacks,
Completer<void>? cancelToken,
) async {
int addedCount = 0;
final pendingAdds = <Future<void>>[];
@@ -258,7 +245,7 @@ class RemoteAlbumService {
return;
}
pendingAdds.add(
_linkUploadedAssetToAlbum(albumId, remoteId, uploader, source)
linkUploadedAssetToAlbum(albumId, remoteId, uploader, source)
.then<void>((added) {
addedCount += added;
})
@@ -269,7 +256,7 @@ class RemoteAlbumService {
},
);
await _uploadService.uploadManual(localAssets, callbacks: wrappedCallbacks);
await _uploadService.uploadManual(localAssets, callbacks: wrappedCallbacks, cancelToken: cancelToken);
await Future.wait(pendingAdds);
return addedCount;
}
@@ -288,7 +275,7 @@ class RemoteAlbumService {
/// `remote_asset_entity` row from the local source so the FK-protected
/// junction insert succeeds. Sync overwrites the placeholder later with
/// the authoritative server data.
Future<int> _linkUploadedAssetToAlbum(String albumId, String remoteId, UserDto uploader, LocalAsset source) async {
Future<int> linkUploadedAssetToAlbum(String albumId, String remoteId, UserDto uploader, LocalAsset source) async {
final result = await _albumApiRepository.addAssets(albumId, [remoteId]);
if (result.added.isEmpty) {
return 0;
@@ -7,7 +7,7 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/events.model.dart';
import 'package:immich_mobile/domain/models/timeline.model.dart';
import 'package:immich_mobile/domain/utils/event_stream.dart';
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart';
import 'package:immich_mobile/utils/async_mutex.dart';
@@ -39,12 +39,12 @@ enum TimelineOrigin {
class TimelineFactory {
final DriftTimelineRepository _timelineRepository;
final MetadataRepository _metadataRepository;
final SettingsRepository _settingsRepository;
const TimelineFactory({required this._timelineRepository, required this._metadataRepository});
const TimelineFactory({required this._timelineRepository, required this._settingsRepository});
GroupAssetsBy get groupBy {
final group = _metadataRepository.appConfig.timeline.groupAssetsBy;
final group = _settingsRepository.appConfig.timeline.groupAssetsBy;
// We do not support auto grouping in the new timeline yet, fallback to day grouping
return group == GroupAssetsBy.auto ? GroupAssetsBy.day : group;
}
@@ -1,8 +1,8 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
class MetadataEntity extends Table with DriftDefaultsMixin {
const MetadataEntity();
class SettingsEntity extends Table with DriftDefaultsMixin {
const SettingsEntity();
TextColumn get key => text()();
@@ -14,5 +14,5 @@ class MetadataEntity extends Table with DriftDefaultsMixin {
Set<Column> get primaryKey => {key};
@override
String get tableName => "metadata";
String get tableName => "settings";
}
@@ -1,28 +1,28 @@
// dart format width=80
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:immich_mobile/infrastructure/entities/metadata.entity.drift.dart'
import 'package:immich_mobile/infrastructure/entities/settings.entity.drift.dart'
as i1;
import 'package:immich_mobile/infrastructure/entities/metadata.entity.dart'
import 'package:immich_mobile/infrastructure/entities/settings.entity.dart'
as i2;
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i3;
typedef $$MetadataEntityTableCreateCompanionBuilder =
i1.MetadataEntityCompanion Function({
typedef $$SettingsEntityTableCreateCompanionBuilder =
i1.SettingsEntityCompanion Function({
required String key,
required String value,
i0.Value<DateTime> updatedAt,
});
typedef $$MetadataEntityTableUpdateCompanionBuilder =
i1.MetadataEntityCompanion Function({
typedef $$SettingsEntityTableUpdateCompanionBuilder =
i1.SettingsEntityCompanion Function({
i0.Value<String> key,
i0.Value<String> value,
i0.Value<DateTime> updatedAt,
});
class $$MetadataEntityTableFilterComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$MetadataEntityTable> {
$$MetadataEntityTableFilterComposer({
class $$SettingsEntityTableFilterComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$SettingsEntityTable> {
$$SettingsEntityTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
@@ -45,9 +45,9 @@ class $$MetadataEntityTableFilterComposer
);
}
class $$MetadataEntityTableOrderingComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$MetadataEntityTable> {
$$MetadataEntityTableOrderingComposer({
class $$SettingsEntityTableOrderingComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$SettingsEntityTable> {
$$SettingsEntityTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
@@ -70,9 +70,9 @@ class $$MetadataEntityTableOrderingComposer
);
}
class $$MetadataEntityTableAnnotationComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$MetadataEntityTable> {
$$MetadataEntityTableAnnotationComposer({
class $$SettingsEntityTableAnnotationComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$SettingsEntityTable> {
$$SettingsEntityTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
@@ -89,47 +89,47 @@ class $$MetadataEntityTableAnnotationComposer
$composableBuilder(column: $table.updatedAt, builder: (column) => column);
}
class $$MetadataEntityTableTableManager
class $$SettingsEntityTableTableManager
extends
i0.RootTableManager<
i0.GeneratedDatabase,
i1.$MetadataEntityTable,
i1.MetadataEntityData,
i1.$$MetadataEntityTableFilterComposer,
i1.$$MetadataEntityTableOrderingComposer,
i1.$$MetadataEntityTableAnnotationComposer,
$$MetadataEntityTableCreateCompanionBuilder,
$$MetadataEntityTableUpdateCompanionBuilder,
i1.$SettingsEntityTable,
i1.SettingsEntityData,
i1.$$SettingsEntityTableFilterComposer,
i1.$$SettingsEntityTableOrderingComposer,
i1.$$SettingsEntityTableAnnotationComposer,
$$SettingsEntityTableCreateCompanionBuilder,
$$SettingsEntityTableUpdateCompanionBuilder,
(
i1.MetadataEntityData,
i1.SettingsEntityData,
i0.BaseReferences<
i0.GeneratedDatabase,
i1.$MetadataEntityTable,
i1.MetadataEntityData
i1.$SettingsEntityTable,
i1.SettingsEntityData
>,
),
i1.MetadataEntityData,
i1.SettingsEntityData,
i0.PrefetchHooks Function()
> {
$$MetadataEntityTableTableManager(
$$SettingsEntityTableTableManager(
i0.GeneratedDatabase db,
i1.$MetadataEntityTable table,
i1.$SettingsEntityTable table,
) : super(
i0.TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
i1.$$MetadataEntityTableFilterComposer($db: db, $table: table),
i1.$$SettingsEntityTableFilterComposer($db: db, $table: table),
createOrderingComposer: () =>
i1.$$MetadataEntityTableOrderingComposer($db: db, $table: table),
i1.$$SettingsEntityTableOrderingComposer($db: db, $table: table),
createComputedFieldComposer: () => i1
.$$MetadataEntityTableAnnotationComposer($db: db, $table: table),
.$$SettingsEntityTableAnnotationComposer($db: db, $table: table),
updateCompanionCallback:
({
i0.Value<String> key = const i0.Value.absent(),
i0.Value<String> value = const i0.Value.absent(),
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
}) => i1.MetadataEntityCompanion(
}) => i1.SettingsEntityCompanion(
key: key,
value: value,
updatedAt: updatedAt,
@@ -139,7 +139,7 @@ class $$MetadataEntityTableTableManager
required String key,
required String value,
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
}) => i1.MetadataEntityCompanion.insert(
}) => i1.SettingsEntityCompanion.insert(
key: key,
value: value,
updatedAt: updatedAt,
@@ -152,34 +152,34 @@ class $$MetadataEntityTableTableManager
);
}
typedef $$MetadataEntityTableProcessedTableManager =
typedef $$SettingsEntityTableProcessedTableManager =
i0.ProcessedTableManager<
i0.GeneratedDatabase,
i1.$MetadataEntityTable,
i1.MetadataEntityData,
i1.$$MetadataEntityTableFilterComposer,
i1.$$MetadataEntityTableOrderingComposer,
i1.$$MetadataEntityTableAnnotationComposer,
$$MetadataEntityTableCreateCompanionBuilder,
$$MetadataEntityTableUpdateCompanionBuilder,
i1.$SettingsEntityTable,
i1.SettingsEntityData,
i1.$$SettingsEntityTableFilterComposer,
i1.$$SettingsEntityTableOrderingComposer,
i1.$$SettingsEntityTableAnnotationComposer,
$$SettingsEntityTableCreateCompanionBuilder,
$$SettingsEntityTableUpdateCompanionBuilder,
(
i1.MetadataEntityData,
i1.SettingsEntityData,
i0.BaseReferences<
i0.GeneratedDatabase,
i1.$MetadataEntityTable,
i1.MetadataEntityData
i1.$SettingsEntityTable,
i1.SettingsEntityData
>,
),
i1.MetadataEntityData,
i1.SettingsEntityData,
i0.PrefetchHooks Function()
>;
class $MetadataEntityTable extends i2.MetadataEntity
with i0.TableInfo<$MetadataEntityTable, i1.MetadataEntityData> {
class $SettingsEntityTable extends i2.SettingsEntity
with i0.TableInfo<$SettingsEntityTable, i1.SettingsEntityData> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
$MetadataEntityTable(this.attachedDatabase, [this._alias]);
$SettingsEntityTable(this.attachedDatabase, [this._alias]);
static const i0.VerificationMeta _keyMeta = const i0.VerificationMeta('key');
@override
late final i0.GeneratedColumn<String> key = i0.GeneratedColumn<String>(
@@ -219,10 +219,10 @@ class $MetadataEntityTable extends i2.MetadataEntity
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'metadata';
static const String $name = 'settings';
@override
i0.VerificationContext validateIntegrity(
i0.Insertable<i1.MetadataEntityData> instance, {
i0.Insertable<i1.SettingsEntityData> instance, {
bool isInserting = false,
}) {
final context = i0.VerificationContext();
@@ -255,9 +255,9 @@ class $MetadataEntityTable extends i2.MetadataEntity
@override
Set<i0.GeneratedColumn> get $primaryKey => {key};
@override
i1.MetadataEntityData map(Map<String, dynamic> data, {String? tablePrefix}) {
i1.SettingsEntityData map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.MetadataEntityData(
return i1.SettingsEntityData(
key: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}key'],
@@ -274,8 +274,8 @@ class $MetadataEntityTable extends i2.MetadataEntity
}
@override
$MetadataEntityTable createAlias(String alias) {
return $MetadataEntityTable(attachedDatabase, alias);
$SettingsEntityTable createAlias(String alias) {
return $SettingsEntityTable(attachedDatabase, alias);
}
@override
@@ -284,12 +284,12 @@ class $MetadataEntityTable extends i2.MetadataEntity
bool get isStrict => true;
}
class MetadataEntityData extends i0.DataClass
implements i0.Insertable<i1.MetadataEntityData> {
class SettingsEntityData extends i0.DataClass
implements i0.Insertable<i1.SettingsEntityData> {
final String key;
final String value;
final DateTime updatedAt;
const MetadataEntityData({
const SettingsEntityData({
required this.key,
required this.value,
required this.updatedAt,
@@ -303,12 +303,12 @@ class MetadataEntityData extends i0.DataClass
return map;
}
factory MetadataEntityData.fromJson(
factory SettingsEntityData.fromJson(
Map<String, dynamic> json, {
i0.ValueSerializer? serializer,
}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return MetadataEntityData(
return SettingsEntityData(
key: serializer.fromJson<String>(json['key']),
value: serializer.fromJson<String>(json['value']),
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
@@ -324,17 +324,17 @@ class MetadataEntityData extends i0.DataClass
};
}
i1.MetadataEntityData copyWith({
i1.SettingsEntityData copyWith({
String? key,
String? value,
DateTime? updatedAt,
}) => i1.MetadataEntityData(
}) => i1.SettingsEntityData(
key: key ?? this.key,
value: value ?? this.value,
updatedAt: updatedAt ?? this.updatedAt,
);
MetadataEntityData copyWithCompanion(i1.MetadataEntityCompanion data) {
return MetadataEntityData(
SettingsEntityData copyWithCompanion(i1.SettingsEntityCompanion data) {
return SettingsEntityData(
key: data.key.present ? data.key.value : this.key,
value: data.value.present ? data.value.value : this.value,
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
@@ -343,7 +343,7 @@ class MetadataEntityData extends i0.DataClass
@override
String toString() {
return (StringBuffer('MetadataEntityData(')
return (StringBuffer('SettingsEntityData(')
..write('key: $key, ')
..write('value: $value, ')
..write('updatedAt: $updatedAt')
@@ -356,29 +356,29 @@ class MetadataEntityData extends i0.DataClass
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.MetadataEntityData &&
(other is i1.SettingsEntityData &&
other.key == this.key &&
other.value == this.value &&
other.updatedAt == this.updatedAt);
}
class MetadataEntityCompanion
extends i0.UpdateCompanion<i1.MetadataEntityData> {
class SettingsEntityCompanion
extends i0.UpdateCompanion<i1.SettingsEntityData> {
final i0.Value<String> key;
final i0.Value<String> value;
final i0.Value<DateTime> updatedAt;
const MetadataEntityCompanion({
const SettingsEntityCompanion({
this.key = const i0.Value.absent(),
this.value = const i0.Value.absent(),
this.updatedAt = const i0.Value.absent(),
});
MetadataEntityCompanion.insert({
SettingsEntityCompanion.insert({
required String key,
required String value,
this.updatedAt = const i0.Value.absent(),
}) : key = i0.Value(key),
value = i0.Value(value);
static i0.Insertable<i1.MetadataEntityData> custom({
static i0.Insertable<i1.SettingsEntityData> custom({
i0.Expression<String>? key,
i0.Expression<String>? value,
i0.Expression<DateTime>? updatedAt,
@@ -390,12 +390,12 @@ class MetadataEntityCompanion
});
}
i1.MetadataEntityCompanion copyWith({
i1.SettingsEntityCompanion copyWith({
i0.Value<String>? key,
i0.Value<String>? value,
i0.Value<DateTime>? updatedAt,
}) {
return i1.MetadataEntityCompanion(
return i1.SettingsEntityCompanion(
key: key ?? this.key,
value: value ?? this.value,
updatedAt: updatedAt ?? this.updatedAt,
@@ -419,7 +419,7 @@ class MetadataEntityCompanion
@override
String toString() {
return (StringBuffer('MetadataEntityCompanion(')
return (StringBuffer('SettingsEntityCompanion(')
..write('key: $key, ')
..write('value: $value, ')
..write('updatedAt: $updatedAt')
@@ -13,7 +13,7 @@ import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/memory.entity.dart';
import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/metadata.entity.dart';
import 'package:immich_mobile/infrastructure/entities/settings.entity.dart';
import 'package:immich_mobile/infrastructure/entities/partner.entity.dart';
import 'package:immich_mobile/infrastructure/entities/person.entity.dart';
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart';
@@ -55,7 +55,7 @@ import 'package:logging/logging.dart';
StoreEntity,
TrashedLocalAssetEntity,
AssetEditEntity,
MetadataEntity,
SettingsEntity,
],
include: {'package:immich_mobile/infrastructure/entities/merged_asset.drift'},
)
@@ -98,7 +98,7 @@ class Drift extends $Drift {
}
@override
int get schemaVersion => 26;
int get schemaVersion => 27;
@override
MigrationStrategy get migration => MigrationStrategy(
@@ -276,6 +276,9 @@ class Drift extends $Drift {
from25To26: (m, v26) async {
await m.addColumn(v26.remoteAssetEntity, v26.remoteAssetEntity.uploadedAt);
},
from26To27: (m, v27) async {
await customStatement('ALTER TABLE metadata RENAME TO settings');
},
),
);
@@ -43,7 +43,7 @@ import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity
as i20;
import 'package:immich_mobile/infrastructure/entities/asset_edit.entity.drift.dart'
as i21;
import 'package:immich_mobile/infrastructure/entities/metadata.entity.drift.dart'
import 'package:immich_mobile/infrastructure/entities/settings.entity.drift.dart'
as i22;
import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart'
as i23;
@@ -91,7 +91,7 @@ abstract class $Drift extends i0.GeneratedDatabase {
.$TrashedLocalAssetEntityTable(this);
late final i21.$AssetEditEntityTable assetEditEntity = i21
.$AssetEditEntityTable(this);
late final i22.$MetadataEntityTable metadataEntity = i22.$MetadataEntityTable(
late final i22.$SettingsEntityTable settingsEntity = i22.$SettingsEntityTable(
this,
);
i23.MergedAssetDrift get mergedAssetDrift => i24.ReadDatabaseContainer(
@@ -132,7 +132,7 @@ abstract class $Drift extends i0.GeneratedDatabase {
storeEntity,
trashedLocalAssetEntity,
assetEditEntity,
metadataEntity,
settingsEntity,
i10.idxPartnerSharedWithId,
i11.idxLatLng,
i11.idxRemoteExifCity,
@@ -395,6 +395,6 @@ class $DriftManager {
);
i21.$$AssetEditEntityTableTableManager get assetEditEntity =>
i21.$$AssetEditEntityTableTableManager(_db, _db.assetEditEntity);
i22.$$MetadataEntityTableTableManager get metadataEntity =>
i22.$$MetadataEntityTableTableManager(_db, _db.metadataEntity);
i22.$$SettingsEntityTableTableManager get settingsEntity =>
i22.$$SettingsEntityTableTableManager(_db, _db.settingsEntity);
}
@@ -13539,6 +13539,550 @@ i1.GeneratedColumn<String> _column_212(String aliasedName) =>
type: i1.DriftSqlType.string,
$customConstraints: 'NULL',
);
final class Schema27 extends i0.VersionedSchema {
Schema27({required super.database}) : super(version: 27);
@override
late final List<i1.DatabaseSchemaEntity> entities = [
userEntity,
remoteAssetEntity,
stackEntity,
localAssetEntity,
remoteAlbumEntity,
localAlbumEntity,
localAlbumAssetEntity,
idxLocalAlbumAssetAlbumAsset,
idxLocalAssetChecksum,
idxLocalAssetCloudId,
idxStackPrimaryAssetId,
uQRemoteAssetsOwnerChecksum,
uQRemoteAssetsOwnerLibraryChecksum,
idxRemoteAssetChecksum,
idxRemoteAssetStackId,
idxRemoteAssetOwnerVisibilityDeletedCreated,
authUserEntity,
userMetadataEntity,
partnerEntity,
remoteExifEntity,
remoteAlbumAssetEntity,
remoteAlbumUserEntity,
remoteAssetCloudIdEntity,
memoryEntity,
memoryAssetEntity,
personEntity,
assetFaceEntity,
storeEntity,
trashedLocalAssetEntity,
assetEditEntity,
settings,
idxPartnerSharedWithId,
idxLatLng,
idxRemoteExifCity,
idxRemoteAlbumAssetAlbumAsset,
idxRemoteAssetCloudId,
idxPersonOwnerId,
idxAssetFacePersonId,
idxAssetFaceAssetId,
idxAssetFaceVisiblePerson,
idxTrashedLocalAssetChecksum,
idxTrashedLocalAssetAlbum,
idxAssetEditAssetId,
];
late final Shape33 userEntity = Shape33(
source: i0.VersionedTable(
entityName: 'user_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(id)'],
columns: [
_column_107,
_column_108,
_column_109,
_column_110,
_column_111,
_column_112,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape50 remoteAssetEntity = Shape50(
source: i0.VersionedTable(
entityName: 'remote_asset_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(id)'],
columns: [
_column_108,
_column_113,
_column_114,
_column_115,
_column_116,
_column_117,
_column_118,
_column_107,
_column_119,
_column_120,
_column_121,
_column_122,
_column_123,
_column_124,
_column_212,
_column_125,
_column_126,
_column_127,
_column_128,
_column_129,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape35 stackEntity = Shape35(
source: i0.VersionedTable(
entityName: 'stack_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(id)'],
columns: [
_column_107,
_column_114,
_column_115,
_column_121,
_column_130,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape36 localAssetEntity = Shape36(
source: i0.VersionedTable(
entityName: 'local_asset_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(id)'],
columns: [
_column_108,
_column_113,
_column_114,
_column_115,
_column_116,
_column_117,
_column_118,
_column_107,
_column_131,
_column_120,
_column_132,
_column_133,
_column_134,
_column_135,
_column_136,
_column_137,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape48 remoteAlbumEntity = Shape48(
source: i0.VersionedTable(
entityName: 'remote_album_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(id)'],
columns: [
_column_107,
_column_108,
_column_138,
_column_114,
_column_115,
_column_139,
_column_140,
_column_141,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape38 localAlbumEntity = Shape38(
source: i0.VersionedTable(
entityName: 'local_album_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(id)'],
columns: [
_column_107,
_column_108,
_column_115,
_column_142,
_column_143,
_column_144,
_column_145,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape39 localAlbumAssetEntity = Shape39(
source: i0.VersionedTable(
entityName: 'local_album_asset_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(asset_id, album_id)'],
columns: [_column_146, _column_147, _column_145],
attachedDatabase: database,
),
alias: null,
);
final i1.Index idxLocalAlbumAssetAlbumAsset = i1.Index(
'idx_local_album_asset_album_asset',
'CREATE INDEX IF NOT EXISTS idx_local_album_asset_album_asset ON local_album_asset_entity (album_id, asset_id)',
);
final i1.Index idxLocalAssetChecksum = i1.Index(
'idx_local_asset_checksum',
'CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)',
);
final i1.Index idxLocalAssetCloudId = i1.Index(
'idx_local_asset_cloud_id',
'CREATE INDEX IF NOT EXISTS idx_local_asset_cloud_id ON local_asset_entity (i_cloud_id)',
);
final i1.Index idxStackPrimaryAssetId = i1.Index(
'idx_stack_primary_asset_id',
'CREATE INDEX IF NOT EXISTS idx_stack_primary_asset_id ON stack_entity (primary_asset_id)',
);
final i1.Index uQRemoteAssetsOwnerChecksum = i1.Index(
'UQ_remote_assets_owner_checksum',
'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)',
);
final i1.Index uQRemoteAssetsOwnerLibraryChecksum = i1.Index(
'UQ_remote_assets_owner_library_checksum',
'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)',
);
final i1.Index idxRemoteAssetChecksum = i1.Index(
'idx_remote_asset_checksum',
'CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)',
);
final i1.Index idxRemoteAssetStackId = i1.Index(
'idx_remote_asset_stack_id',
'CREATE INDEX IF NOT EXISTS idx_remote_asset_stack_id ON remote_asset_entity (stack_id)',
);
final i1.Index idxRemoteAssetOwnerVisibilityDeletedCreated = i1.Index(
'idx_remote_asset_owner_visibility_deleted_created',
'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_visibility_deleted_created ON remote_asset_entity (owner_id, visibility, deleted_at, created_at DESC)',
);
late final Shape40 authUserEntity = Shape40(
source: i0.VersionedTable(
entityName: 'auth_user_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(id)'],
columns: [
_column_107,
_column_108,
_column_109,
_column_148,
_column_110,
_column_111,
_column_149,
_column_150,
_column_151,
_column_152,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape4 userMetadataEntity = Shape4(
source: i0.VersionedTable(
entityName: 'user_metadata_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(user_id, "key")'],
columns: [_column_153, _column_154, _column_155],
attachedDatabase: database,
),
alias: null,
);
late final Shape41 partnerEntity = Shape41(
source: i0.VersionedTable(
entityName: 'partner_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'],
columns: [_column_156, _column_157, _column_158],
attachedDatabase: database,
),
alias: null,
);
late final Shape42 remoteExifEntity = Shape42(
source: i0.VersionedTable(
entityName: 'remote_exif_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(asset_id)'],
columns: [
_column_159,
_column_160,
_column_161,
_column_162,
_column_163,
_column_164,
_column_117,
_column_116,
_column_165,
_column_166,
_column_167,
_column_168,
_column_135,
_column_136,
_column_169,
_column_170,
_column_171,
_column_172,
_column_173,
_column_174,
_column_175,
_column_176,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape7 remoteAlbumAssetEntity = Shape7(
source: i0.VersionedTable(
entityName: 'remote_album_asset_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(asset_id, album_id)'],
columns: [_column_159, _column_177],
attachedDatabase: database,
),
alias: null,
);
late final Shape10 remoteAlbumUserEntity = Shape10(
source: i0.VersionedTable(
entityName: 'remote_album_user_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(album_id, user_id)'],
columns: [_column_177, _column_153, _column_178],
attachedDatabase: database,
),
alias: null,
);
late final Shape43 remoteAssetCloudIdEntity = Shape43(
source: i0.VersionedTable(
entityName: 'remote_asset_cloud_id_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(asset_id)'],
columns: [
_column_159,
_column_179,
_column_180,
_column_134,
_column_135,
_column_136,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape44 memoryEntity = Shape44(
source: i0.VersionedTable(
entityName: 'memory_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(id)'],
columns: [
_column_107,
_column_114,
_column_115,
_column_124,
_column_121,
_column_113,
_column_181,
_column_182,
_column_183,
_column_184,
_column_185,
_column_186,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape12 memoryAssetEntity = Shape12(
source: i0.VersionedTable(
entityName: 'memory_asset_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'],
columns: [_column_159, _column_187],
attachedDatabase: database,
),
alias: null,
);
late final Shape45 personEntity = Shape45(
source: i0.VersionedTable(
entityName: 'person_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(id)'],
columns: [
_column_107,
_column_114,
_column_115,
_column_121,
_column_108,
_column_188,
_column_189,
_column_190,
_column_191,
_column_192,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape46 assetFaceEntity = Shape46(
source: i0.VersionedTable(
entityName: 'asset_face_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(id)'],
columns: [
_column_107,
_column_159,
_column_193,
_column_194,
_column_195,
_column_196,
_column_197,
_column_198,
_column_199,
_column_200,
_column_201,
_column_124,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape18 storeEntity = Shape18(
source: i0.VersionedTable(
entityName: 'store_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(id)'],
columns: [_column_202, _column_203, _column_204],
attachedDatabase: database,
),
alias: null,
);
late final Shape47 trashedLocalAssetEntity = Shape47(
source: i0.VersionedTable(
entityName: 'trashed_local_asset_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(id, album_id)'],
columns: [
_column_108,
_column_113,
_column_114,
_column_115,
_column_116,
_column_117,
_column_118,
_column_107,
_column_205,
_column_131,
_column_120,
_column_132,
_column_206,
_column_137,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape32 assetEditEntity = Shape32(
source: i0.VersionedTable(
entityName: 'asset_edit_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(id)'],
columns: [
_column_107,
_column_159,
_column_207,
_column_208,
_column_209,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape49 settings = Shape49(
source: i0.VersionedTable(
entityName: 'settings',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY("key")'],
columns: [_column_210, _column_211, _column_115],
attachedDatabase: database,
),
alias: null,
);
final i1.Index idxPartnerSharedWithId = i1.Index(
'idx_partner_shared_with_id',
'CREATE INDEX IF NOT EXISTS idx_partner_shared_with_id ON partner_entity (shared_with_id)',
);
final i1.Index idxLatLng = i1.Index(
'idx_lat_lng',
'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)',
);
final i1.Index idxRemoteExifCity = i1.Index(
'idx_remote_exif_city',
'CREATE INDEX IF NOT EXISTS idx_remote_exif_city ON remote_exif_entity (city) WHERE city IS NOT NULL',
);
final i1.Index idxRemoteAlbumAssetAlbumAsset = i1.Index(
'idx_remote_album_asset_album_asset',
'CREATE INDEX IF NOT EXISTS idx_remote_album_asset_album_asset ON remote_album_asset_entity (album_id, asset_id)',
);
final i1.Index idxRemoteAssetCloudId = i1.Index(
'idx_remote_asset_cloud_id',
'CREATE INDEX IF NOT EXISTS idx_remote_asset_cloud_id ON remote_asset_cloud_id_entity (cloud_id)',
);
final i1.Index idxPersonOwnerId = i1.Index(
'idx_person_owner_id',
'CREATE INDEX IF NOT EXISTS idx_person_owner_id ON person_entity (owner_id)',
);
final i1.Index idxAssetFacePersonId = i1.Index(
'idx_asset_face_person_id',
'CREATE INDEX IF NOT EXISTS idx_asset_face_person_id ON asset_face_entity (person_id)',
);
final i1.Index idxAssetFaceAssetId = i1.Index(
'idx_asset_face_asset_id',
'CREATE INDEX IF NOT EXISTS idx_asset_face_asset_id ON asset_face_entity (asset_id)',
);
final i1.Index idxAssetFaceVisiblePerson = i1.Index(
'idx_asset_face_visible_person',
'CREATE INDEX IF NOT EXISTS idx_asset_face_visible_person ON asset_face_entity (person_id, asset_id) WHERE is_visible = 1 AND deleted_at IS NULL',
);
final i1.Index idxTrashedLocalAssetChecksum = i1.Index(
'idx_trashed_local_asset_checksum',
'CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_checksum ON trashed_local_asset_entity (checksum)',
);
final i1.Index idxTrashedLocalAssetAlbum = i1.Index(
'idx_trashed_local_asset_album',
'CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_album ON trashed_local_asset_entity (album_id)',
);
final i1.Index idxAssetEditAssetId = i1.Index(
'idx_asset_edit_asset_id',
'CREATE INDEX IF NOT EXISTS idx_asset_edit_asset_id ON asset_edit_entity (asset_id)',
);
}
i0.MigrationStepWithVersion migrationSteps({
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
@@ -13565,6 +14109,7 @@ i0.MigrationStepWithVersion migrationSteps({
required Future<void> Function(i1.Migrator m, Schema24 schema) from23To24,
required Future<void> Function(i1.Migrator m, Schema25 schema) from24To25,
required Future<void> Function(i1.Migrator m, Schema26 schema) from25To26,
required Future<void> Function(i1.Migrator m, Schema27 schema) from26To27,
}) {
return (currentVersion, database) async {
switch (currentVersion) {
@@ -13693,6 +14238,11 @@ i0.MigrationStepWithVersion migrationSteps({
final migrator = i1.Migrator(database, schema);
await from25To26(migrator, schema);
return 26;
case 26:
final schema = Schema27(database: database);
final migrator = i1.Migrator(database, schema);
await from26To27(migrator, schema);
return 27;
default:
throw ArgumentError.value('Unknown migration from $currentVersion');
}
@@ -13725,6 +14275,7 @@ i1.OnUpgrade stepByStep({
required Future<void> Function(i1.Migrator m, Schema24 schema) from23To24,
required Future<void> Function(i1.Migrator m, Schema25 schema) from24To25,
required Future<void> Function(i1.Migrator m, Schema26 schema) from25To26,
required Future<void> Function(i1.Migrator m, Schema27 schema) from26To27,
}) => i0.VersionedSchema.stepByStepHelper(
step: migrationSteps(
from1To2: from1To2,
@@ -13752,5 +14303,6 @@ i1.OnUpgrade stepByStep({
from23To24: from23To24,
from24To25: from24To25,
from25To26: from25To26,
from26To27: from26To27,
),
);
@@ -1,72 +0,0 @@
import 'package:collection/collection.dart';
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/config/app_config.dart';
import 'package:immich_mobile/domain/models/metadata_key.dart';
import 'package:immich_mobile/infrastructure/entities/metadata.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
class MetadataRepository extends DriftDatabaseRepository {
final Drift _db;
MetadataRepository._(this._db) : super(_db);
static MetadataRepository? _instance;
static MetadataRepository get instance {
final instance = _instance;
if (instance == null) {
throw StateError('MetadataRepository not initialized. Call ensureInitialized() first');
}
return instance;
}
AppConfig _appConfig = const .new();
AppConfig get appConfig => _appConfig;
static Future<MetadataRepository> ensureInitialized(Drift db) async {
if (_instance == null) {
final instance = MetadataRepository._(db);
await instance.refresh();
_instance = instance;
}
return _instance!;
}
Future<void> refresh() async => _applyOverrides(await _db.select(_db.metadataEntity).get());
Future<void> write<T extends Object, U extends T>(MetadataKey<T> key, U value) async {
if (value == _appConfig.read(key)) {
return;
}
if (value == defaultConfig.read(key)) {
await (_db.delete(_db.metadataEntity)..where((t) => t.key.equals(key.name))).go();
} else {
await _db
.into(_db.metadataEntity)
.insertOnConflictUpdate(
MetadataEntityCompanion.insert(key: key.name, value: key.encode(value), updatedAt: Value(DateTime.now())),
);
}
_appConfig = _appConfig.write(key, value);
}
Stream<AppConfig> watchConfig() => _db.select(_db.metadataEntity).watch().map((rows) {
_applyOverrides(rows);
return _appConfig;
});
void _applyOverrides(List<MetadataEntityData> rows) {
_appConfig = AppConfig.fromEntries(
rows.fold({}, (overrides, row) {
final metadataKey = MetadataKey.values.firstWhereOrNull((key) => key.name == row.key);
if (metadataKey == null) {
return overrides;
}
return {...overrides, metadataKey: metadataKey.decode(row.value)};
}),
);
}
}
@@ -0,0 +1,84 @@
import 'package:collection/collection.dart';
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/config/app_config.dart';
import 'package:immich_mobile/domain/models/settings_key.dart';
import 'package:immich_mobile/infrastructure/entities/settings.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
class SettingsRepository extends DriftDatabaseRepository {
final Drift _db;
SettingsRepository._(this._db) : super(_db);
static SettingsRepository? _instance;
static SettingsRepository get instance {
final instance = _instance;
if (instance == null) {
throw StateError('SettingsRepository not initialized. Call ensureInitialized() first');
}
return instance;
}
AppConfig _appConfig = const .new();
AppConfig get appConfig => _appConfig;
static Future<SettingsRepository> ensureInitialized(Drift db) async {
if (_instance == null) {
final instance = SettingsRepository._(db);
await instance.refresh();
_instance = instance;
}
return _instance!;
}
Future<void> refresh() async => _applyOverrides(await _db.select(_db.settingsEntity).get());
Future<void> clear(Iterable<SettingsKey> keys) async {
if (keys.isEmpty) {
return;
}
final names = keys.map((key) => key.name).toList();
await (_db.delete(_db.settingsEntity)..where((row) => row.key.isIn(names))).go();
for (final key in keys) {
_appConfig = _appConfig.write(key, defaultConfig.read(key));
}
}
Future<void> write<T extends Object, U extends T>(SettingsKey<T> key, U value) async {
if (value == _appConfig.read(key)) {
return;
}
if (value == defaultConfig.read(key)) {
return clear([key]);
}
await _db
.into(_db.settingsEntity)
.insertOnConflictUpdate(
SettingsEntityCompanion.insert(key: key.name, value: key.encode(value), updatedAt: Value(DateTime.now())),
);
_appConfig = _appConfig.write(key, value);
}
Stream<AppConfig> watchConfig() => _db.select(_db.settingsEntity).watch().map((rows) {
_applyOverrides(rows);
return _appConfig;
});
void _applyOverrides(List<SettingsEntityData> rows) {
_appConfig = AppConfig.fromEntries(
rows.fold({}, (overrides, row) {
final metadataKey = SettingsKey.values.firstWhereOrNull((key) => key.name == row.key);
if (metadataKey == null) {
return overrides;
}
return {...overrides, metadataKey: metadataKey.decode(row.value)};
}),
);
}
}
+1 -1
View File
@@ -25,7 +25,7 @@ import 'package:immich_mobile/platform/background_worker_lock_api.g.dart';
import 'package:immich_mobile/providers/app_life_cycle.provider.dart';
import 'package:immich_mobile/providers/asset_viewer/share_intent_upload.provider.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
import 'package:immich_mobile/providers/locale_provider.dart';
import 'package:immich_mobile/providers/routes.provider.dart';
@@ -2,16 +2,12 @@ import 'package:immich_mobile/utils/semver.dart';
import 'package:openapi/api.dart';
class ServerVersion extends SemVer {
const ServerVersion({required super.major, required super.minor, required super.patch});
const ServerVersion({required super.major, required super.minor, required super.patch, super.prerelease});
@override
String toString() {
return 'ServerVersion(major: $major, minor: $minor, patch: $patch)';
}
ServerVersion.fromDto(ServerVersionResponseDto dto)
: super(major: dto.major, minor: dto.minor, patch: dto.patch_, prerelease: dto.prerelease);
ServerVersion.fromDto(ServerVersionResponseDto dto) : super(major: dto.major, minor: dto.minor, patch: dto.patch_);
bool isAtLeast({int major = 0, int minor = 0, int patch = 0}) {
return this >= SemVer(major: major, minor: minor, patch: patch);
bool isAtLeast({int major = 0, int minor = 0, int patch = 0, int? prerelease}) {
return this >= SemVer(major: major, minor: minor, patch: patch, prerelease: prerelease);
}
}
@@ -8,11 +8,11 @@ import 'package:immich_mobile/domain/models/album/local_album.model.dart';
import 'package:immich_mobile/domain/services/sync_linked_album.service.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/providers/background_sync.provider.dart';
import 'package:immich_mobile/providers/backup/backup_album.provider.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart';
@@ -103,7 +103,7 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbum
return;
}
final isBackupEnabled = MetadataRepository.instance.appConfig.backup.enabled;
final isBackupEnabled = SettingsRepository.instance.appConfig.backup.enabled;
await ref.read(driftBackupProvider.notifier).getBackupStatus(user.id);
final currentTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount));
final totalChanged = currentTotalAssetCount != _initialTotalAssetCount;
@@ -4,10 +4,10 @@ import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/providers/background_sync.provider.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/widgets/settings/backup_settings/drift_backup_settings.dart';
import 'package:logging/logging.dart';
@@ -27,7 +27,7 @@ class DriftBackupOptionsPage extends ConsumerWidget {
// There is an issue with Flutter where the pop event
// can be triggered multiple times, so we guard it with _hasPopped
final currentBackup = ref.read(metadataProvider).appConfig.backup;
final currentBackup = ref.read(appConfigProvider).backup;
final currentCellularForVideos = currentBackup.useCellularForVideos;
final currentCellularForPhotos = currentBackup.useCellularForPhotos;
@@ -45,7 +45,7 @@ class DriftBackupOptionsPage extends ConsumerWidget {
}
await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id);
final isBackupEnabled = MetadataRepository.instance.appConfig.backup.enabled;
final isBackupEnabled = SettingsRepository.instance.appConfig.backup.enabled;
if (!isBackupEnabled) {
return;
}
@@ -3,10 +3,9 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/metadata_key.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/providers/api.provider.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
class SettingsHeader {
String key = "";
@@ -22,7 +21,7 @@ class HeaderSettingsPage extends HookConsumerWidget {
final headers = useState<List<SettingsHeader>>([]);
final setInitialHeaders = useState(false);
final storedHeaders = ref.read(metadataProvider).appConfig.network.customHeaders;
final storedHeaders = ref.read(appConfigProvider).network.customHeaders;
if (!setInitialHeaders.value) {
storedHeaders.forEach((k, v) {
final header = SettingsHeader();
@@ -94,7 +93,7 @@ class HeaderSettingsPage extends HookConsumerWidget {
headersMap[key] = value;
}
await ref.read(metadataProvider).write(MetadataKey.networkCustomHeaders, headersMap);
await ref.read(settingsProvider).write(.networkCustomHeaders, headersMap);
await ref.read(apiServiceProvider).updateHeaders();
}
}
@@ -12,7 +12,7 @@ import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/generated/codegen_loader.g.dart';
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/background_sync.provider.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
@@ -341,7 +341,7 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
await backgroundManager.hashAssets();
}
if (MetadataRepository.instance.appConfig.backup.syncAlbums) {
if (SettingsRepository.instance.appConfig.backup.syncAlbums) {
await backgroundManager.syncLinkedAlbum();
}
} catch (e) {
@@ -370,7 +370,7 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
}
Future<void> _resumeBackup(DriftBackupNotifier notifier) async {
final isEnableBackup = MetadataRepository.instance.appConfig.backup.enabled;
final isEnableBackup = SettingsRepository.instance.appConfig.backup.enabled;
if (isEnableBackup) {
final currentUser = Store.tryGet(StoreKey.currentUser);
@@ -17,7 +17,7 @@ import 'package:immich_mobile/presentation/widgets/asset_viewer/video_viewer.wid
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
import 'package:immich_mobile/providers/asset_viewer/video_player_provider.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart';
import 'package:immich_mobile/widgets/photo_view/photo_view.dart';
@@ -6,6 +6,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_bu
import 'package:immich_mobile/presentation/widgets/action_buttons/unarchive_action_button.widget.dart';
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
import 'package:immich_mobile/presentation/widgets/album/album_selector.widget.dart';
import 'package:immich_mobile/providers/infrastructure/action.provider.dart';
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
import 'package:immich_mobile/providers/routes.provider.dart';
import 'package:immich_mobile/widgets/common/immich_toast.dart';
@@ -142,13 +143,18 @@ class _AddActionButtonState extends ConsumerState<AddActionButton> {
return;
}
final addedCount = await ref.read(remoteAlbumProvider.notifier).addAssets(album.id, [latest.remoteId!]);
final result = await ref.read(actionProvider.notifier).addToAlbum(ActionSource.viewer, album);
if (!context.mounted) {
return;
}
if (addedCount == 0) {
if (!result.success) {
ImmichToast.show(context: context, msg: 'scaffold_body_error_occurred'.tr(), toastType: ToastType.error);
return;
}
if (result.count == 0) {
ImmichToast.show(
context: context,
msg: 'add_to_album_bottom_sheet_already_exists'.tr(namedArgs: {'album': album.name}),
@@ -159,7 +165,7 @@ class _AddActionButtonState extends ConsumerState<AddActionButton> {
msg: 'add_to_album_bottom_sheet_added'.tr(namedArgs: {'album': album.name}),
);
// Invalidate using the asset's remote ID to refresh the "Appears in" list
// Refresh the "Appears in" list on the asset's info panel.
ref.invalidate(albumsContainingAssetProvider(latest.remoteId!));
}
@@ -7,7 +7,6 @@ import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
@@ -15,12 +14,11 @@ import 'package:immich_mobile/models/albums/album_search.model.dart';
import 'package:immich_mobile/presentation/widgets/album/album_tile.dart';
import 'package:immich_mobile/presentation/widgets/album/new_album_name_modal.widget.dart';
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
import 'package:immich_mobile/domain/models/metadata_key.dart';
import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart';
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/routing/router.dart';
@@ -58,7 +56,7 @@ class _AlbumSelectorState extends ConsumerState<AlbumSelector> {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
final albumConfig = ref.read(metadataProvider).appConfig.album;
final albumConfig = ref.read(appConfigProvider).album;
setState(() {
sort = AlbumSort(mode: albumConfig.sortMode, isReverse: albumConfig.isReverse);
@@ -94,7 +92,7 @@ class _AlbumSelectorState extends ConsumerState<AlbumSelector> {
setState(() {
isGrid = !isGrid;
});
ref.read(metadataProvider).write(MetadataKey.albumIsGrid, isGrid);
ref.read(settingsProvider).write(.albumIsGrid, isGrid);
}
void changeFilter(QuickFilterMode mode) {
@@ -110,9 +108,9 @@ class _AlbumSelectorState extends ConsumerState<AlbumSelector> {
this.sort = sort;
});
final metadata = ref.read(metadataProvider);
await metadata.write(MetadataKey.albumSortMode, sort.mode);
await metadata.write(MetadataKey.albumIsReverse, sort.isReverse);
final metadata = ref.read(settingsProvider);
await metadata.write(.albumSortMode, sort.mode);
await metadata.write(.albumIsReverse, sort.isReverse);
await sortAlbums();
}
@@ -747,12 +745,10 @@ class AddToAlbumHeader extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
Future<void> onCreateAlbum() async {
final selectedAssets = ref.read(multiSelectProvider).selectedAssets;
final newAlbum = await ref
.read(remoteAlbumProvider.notifier)
.createAlbum(
title: "Untitled Album",
assetIds: ref.read(multiSelectProvider).selectedAssets.map((e) => (e as RemoteAsset).id).toList(),
);
.createAlbumWithAssets(title: "Untitled Album", assets: selectedAssets);
if (newAlbum == null) {
ImmichToast.show(context: context, toastType: ToastType.error, msg: 'errors.failed_to_create_album'.tr());
@@ -5,6 +5,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
import 'package:immich_mobile/providers/album/pending_album_uploads.provider.dart';
import 'package:immich_mobile/providers/backup/asset_upload_progress.provider.dart';
/// Pinned banner sliver that surfaces in-flight album uploads directly under
/// the album app bar. Renders nothing while the queue is empty. Tapping the
@@ -165,6 +166,8 @@ class _PendingUploadsSheet extends ConsumerWidget {
}
final failedCount = pending.where((p) => p.failed).length;
final inFlightCount = pending.length - failedCount;
final canAbort = inFlightCount > 0 && ref.watch(manualUploadCancelTokenProvider) != null;
return SafeArea(
child: Padding(
@@ -183,7 +186,21 @@ class _PendingUploadsSheet extends ConsumerWidget {
style: context.textTheme.titleMedium,
),
),
if (failedCount > 0)
if (canAbort)
TextButton.icon(
onPressed: () {
final cancelToken = ref.read(manualUploadCancelTokenProvider);
if (cancelToken != null && !cancelToken.isCompleted) {
cancelToken.complete();
}
ref.read(manualUploadCancelTokenProvider.notifier).state = null;
ref.read(pendingAlbumUploadsProvider(albumId).notifier).clear();
},
icon: const Icon(Icons.stop_circle_outlined, size: 18),
label: Text('cancel'.t(context: context)),
style: TextButton.styleFrom(foregroundColor: context.colorScheme.error),
)
else if (failedCount > 0)
TextButton.icon(
onPressed: () => ref.read(pendingAlbumUploadsProvider(albumId).notifier).clearFailed(),
icon: const Icon(Icons.clear_rounded, size: 18),
@@ -19,7 +19,7 @@ import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart';
import 'package:immich_mobile/widgets/photo_view/photo_view.dart';
@@ -241,7 +241,7 @@ class _AssetPageState extends ConsumerState<AssetPage> {
return;
}
final tapToNavigate = ref.read(metadataProvider).appConfig.viewer.tapToNavigate;
final tapToNavigate = ref.read(appConfigProvider).viewer.tapToNavigate;
if (!tapToNavigate) {
_viewer.toggleControls();
return;
@@ -63,16 +63,19 @@ class SheetTile extends ConsumerWidget {
subtitleWidget = null;
}
return ListTile(
dense: true,
visualDensity: VisualDensity.compact,
title: GestureDetector(onLongPress: () => copyTitle(context, ref), child: titleWidget),
titleAlignment: ListTileTitleAlignment.center,
leading: leading,
trailing: trailing,
contentPadding: leading == null ? null : const EdgeInsets.only(left: 25),
subtitle: subtitleWidget,
onTap: onTap,
return Material(
type: MaterialType.transparency,
child: ListTile(
dense: true,
visualDensity: VisualDensity.compact,
title: GestureDetector(onLongPress: () => copyTitle(context, ref), child: titleWidget),
titleAlignment: ListTileTitleAlignment.center,
leading: leading,
trailing: trailing,
contentPadding: leading == null ? null : const EdgeInsets.only(left: 25),
subtitle: subtitleWidget,
onTap: onTap,
),
);
}
}
@@ -12,7 +12,7 @@ import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.pro
import 'package:immich_mobile/providers/asset_viewer/video_player_provider.dart';
import 'package:immich_mobile/providers/cast.provider.dart';
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/services/api.service.dart';
import 'package:logging/logging.dart';
import 'package:native_video_player/native_video_player.dart';
@@ -128,7 +128,7 @@ class _NativeVideoViewerState extends ConsumerState<NativeVideoViewer> with Widg
final remoteId = (videoAsset as RemoteAsset).id;
final serverEndpoint = Store.get(StoreKey.serverEndpoint);
final isOriginalVideo = ref.read(metadataProvider).appConfig.viewer.loadOriginalVideo;
final isOriginalVideo = ref.read(appConfigProvider).viewer.loadOriginalVideo;
final String postfixUrl = isOriginalVideo ? 'original' : 'video/playback';
final String videoUrl = videoAsset.livePhotoVideoId != null
? '$serverEndpoint/assets/${videoAsset.livePhotoVideoId}/$postfixUrl'
@@ -161,7 +161,7 @@ class _NativeVideoViewerState extends ConsumerState<NativeVideoViewer> with Widg
return;
}
final autoPlayVideo = ref.read(metadataProvider).appConfig.viewer.autoPlayVideo;
final autoPlayVideo = ref.read(appConfigProvider).viewer.autoPlayVideo;
if (autoPlayVideo || widget.asset.isMotionPhoto) {
await _notifier.play();
}
@@ -212,7 +212,7 @@ class _NativeVideoViewerState extends ConsumerState<NativeVideoViewer> with Widg
}
await _notifier.load(source);
final loopVideo = ref.read(metadataProvider).appConfig.viewer.loopVideo;
final loopVideo = ref.read(appConfigProvider).viewer.loopVideo;
await _notifier.setLoop(!widget.asset.isMotionPhoto && loopVideo);
await _notifier.setVolume(1);
}
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/setting.model.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
import 'package:immich_mobile/providers/cast.provider.dart';
@@ -34,7 +33,7 @@ class ViewerKebabMenu extends ConsumerWidget {
final isInLockedView = ref.watch(inLockedViewProvider);
final currentAlbum = ref.watch(currentRemoteAlbumProvider);
final isArchived = asset is RemoteAsset && asset.visibility == AssetVisibility.archive;
final advancedTroubleshooting = ref.watch(settingsProvider.notifier).get(Setting.advancedTroubleshooting);
final advancedTroubleshooting = ref.watch(settingsProvider.notifier).get(.advancedTroubleshooting);
final actionContext = ActionButtonContext(
asset: asset,
@@ -1,10 +1,9 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/metadata_key.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
class BackupToggleButton extends ConsumerStatefulWidget {
final VoidCallback onStart;
@@ -31,7 +30,7 @@ class BackupToggleButtonState extends ConsumerState<BackupToggleButton> with Sin
end: 1,
).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeInOut));
_isEnabled = ref.read(metadataProvider).appConfig.backup.enabled;
_isEnabled = ref.read(appConfigProvider).backup.enabled;
}
@override
@@ -41,7 +40,7 @@ class BackupToggleButtonState extends ConsumerState<BackupToggleButton> with Sin
}
Future<void> _onToggle(bool value) async {
await ref.read(metadataProvider).write(MetadataKey.backupEnabled, value);
await ref.read(settingsProvider).write(.backupEnabled, value);
setState(() {
_isEnabled = value;
@@ -3,9 +3,7 @@ import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/setting.model.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/advanced_info_action_button.widget.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/bulk_tag_assets_action_button.widget.dart';
@@ -25,7 +23,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/unstack_action
import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart';
import 'package:immich_mobile/presentation/widgets/album/album_selector.widget.dart';
import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart';
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
import 'package:immich_mobile/providers/infrastructure/action.provider.dart';
import 'package:immich_mobile/providers/infrastructure/setting.provider.dart';
import 'package:immich_mobile/providers/infrastructure/user_metadata.provider.dart';
import 'package:immich_mobile/providers/server_info.provider.dart';
@@ -63,37 +61,23 @@ class _GeneralBottomSheetState extends ConsumerState<GeneralBottomSheet> {
userMetadataPreferencesProvider.select((value) => value.valueOrNull?.tagsEnabled ?? false),
);
Future<void> addAssetsToAlbum(RemoteAlbum album) async {
final selectedAssets = multiselect.selectedAssets;
if (selectedAssets.isEmpty) {
Future<void> addToAlbum(RemoteAlbum album) async {
final result = await ref.read(actionProvider.notifier).addToAlbum(ActionSource.timeline, album);
if (!context.mounted) {
return;
}
final remoteAssets = selectedAssets.whereType<RemoteAsset>();
final addedCount = await ref
.read(remoteAlbumProvider.notifier)
.addAssets(album.id, remoteAssets.map((e) => e.id).toList());
if (selectedAssets.length != remoteAssets.length) {
ImmichToast.show(
context: context,
msg: 'add_to_album_bottom_sheet_some_local_assets'.t(context: context),
);
if (!result.success) {
ImmichToast.show(context: context, msg: 'scaffold_body_error_occurred'.tr(), toastType: ToastType.error);
return;
}
if (addedCount != remoteAssets.length) {
ImmichToast.show(
context: context,
msg: 'add_to_album_bottom_sheet_already_exists'.tr(namedArgs: {"album": album.name}),
);
} else {
ImmichToast.show(
context: context,
msg: 'add_to_album_bottom_sheet_added'.tr(namedArgs: {"album": album.name}),
);
}
ref.read(multiSelectProvider.notifier).reset();
ImmichToast.show(
context: context,
msg: result.count == 0
? 'add_to_album_bottom_sheet_already_exists'.tr(namedArgs: {'album': album.name})
: 'add_to_album_bottom_sheet_added'.tr(namedArgs: {'album': album.name}),
);
}
Future<void> onKeyboardExpand() {
@@ -131,12 +115,10 @@ class _GeneralBottomSheetState extends ConsumerState<GeneralBottomSheet> {
const DeleteLocalActionButton(source: ActionSource.timeline),
if (multiselect.onlyLocal) const UploadActionButton(source: ActionSource.timeline),
],
slivers: multiselect.hasRemote
? [
const AddToAlbumHeader(),
AlbumSelector(onAlbumSelected: addAssetsToAlbum, onKeyboardExpanded: onKeyboardExpand),
]
: [],
slivers: [
const AddToAlbumHeader(),
AlbumSelector(onAlbumSelected: addToAlbum, onKeyboardExpanded: onKeyboardExpand),
],
);
}
}
@@ -1,25 +1,78 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_button.widget.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart';
import 'package:immich_mobile/presentation/widgets/album/album_selector.widget.dart';
import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart';
import 'package:immich_mobile/providers/infrastructure/action.provider.dart';
import 'package:immich_mobile/widgets/common/immich_toast.dart';
class LocalAlbumBottomSheet extends ConsumerWidget {
class LocalAlbumBottomSheet extends ConsumerStatefulWidget {
const LocalAlbumBottomSheet({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return const BaseBottomSheet(
ConsumerState<LocalAlbumBottomSheet> createState() => _LocalAlbumBottomSheetState();
}
class _LocalAlbumBottomSheetState extends ConsumerState<LocalAlbumBottomSheet> {
late final DraggableScrollableController sheetController;
@override
void initState() {
super.initState();
sheetController = DraggableScrollableController();
}
@override
void dispose() {
sheetController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
Future<void> addToAlbum(RemoteAlbum album) async {
final result = await ref.read(actionProvider.notifier).addToAlbum(ActionSource.timeline, album);
if (!context.mounted) {
return;
}
if (!result.success) {
ImmichToast.show(context: context, msg: 'scaffold_body_error_occurred'.tr(), toastType: ToastType.error);
return;
}
ImmichToast.show(
context: context,
msg: result.count == 0
? 'add_to_album_bottom_sheet_already_exists'.tr(namedArgs: {'album': album.name})
: 'add_to_album_bottom_sheet_added'.tr(namedArgs: {'album': album.name}),
);
}
Future<void> onKeyboardExpand() {
return sheetController.animateTo(0.85, duration: const Duration(milliseconds: 200), curve: Curves.easeInOut);
}
return BaseBottomSheet(
controller: sheetController,
initialChildSize: 0.25,
maxChildSize: 0.4,
maxChildSize: 0.85,
shouldCloseOnMinExtent: false,
actions: [
actions: const [
ShareActionButton(source: ActionSource.timeline),
DeleteLocalActionButton(source: ActionSource.timeline),
UploadActionButton(source: ActionSource.timeline),
],
slivers: [
const AddToAlbumHeader(),
AlbumSelector(onAlbumSelected: addToAlbum, onKeyboardExpanded: onKeyboardExpand),
],
);
}
}
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart';
@@ -21,7 +20,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_b
import 'package:immich_mobile/presentation/widgets/action_buttons/unstack_action_button.widget.dart';
import 'package:immich_mobile/presentation/widgets/album/album_selector.widget.dart';
import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart';
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
import 'package:immich_mobile/providers/infrastructure/action.provider.dart';
import 'package:immich_mobile/providers/server_info.provider.dart';
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
@@ -56,29 +55,28 @@ class _RemoteAlbumBottomSheetState extends ConsumerState<RemoteAlbumBottomSheet>
final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash));
final ownsAlbum = ref.watch(currentUserProvider)?.id == widget.album.ownerId;
Future<void> addAssetsToAlbum(RemoteAlbum album) async {
final selectedAssets = multiselect.selectedAssets;
if (selectedAssets.isEmpty) {
Future<void> addToAlbum(RemoteAlbum album) async {
final result = await ref.read(actionProvider.notifier).addToAlbum(ActionSource.timeline, album);
if (!context.mounted) {
return;
}
final addedCount = await ref
.read(remoteAlbumProvider.notifier)
.addAssets(album.id, selectedAssets.map((e) => (e as RemoteAsset).id).toList());
if (addedCount != selectedAssets.length) {
if (!result.success) {
ImmichToast.show(
context: context,
msg: 'add_to_album_bottom_sheet_already_exists'.t(context: context, args: {"album": album.name}),
);
} else {
ImmichToast.show(
context: context,
msg: 'add_to_album_bottom_sheet_added'.t(context: context, args: {"album": album.name}),
msg: 'scaffold_body_error_occurred'.t(context: context),
toastType: ToastType.error,
);
return;
}
ref.read(multiSelectProvider.notifier).reset();
ImmichToast.show(
context: context,
msg: result.count == 0
? 'add_to_album_bottom_sheet_already_exists'.t(context: context, args: {"album": album.name})
: 'add_to_album_bottom_sheet_added'.t(context: context, args: {"album": album.name}),
);
}
Future<void> onKeyboardExpand() {
@@ -118,10 +116,7 @@ class _RemoteAlbumBottomSheetState extends ConsumerState<RemoteAlbumBottomSheet>
SetAlbumCoverActionButton(source: ActionSource.timeline, albumId: widget.album.id),
],
slivers: ownsAlbum
? [
const AddToAlbumHeader(),
AlbumSelector(onAlbumSelected: addAssetsToAlbum, onKeyboardExpanded: onKeyboardExpand),
]
? [const AddToAlbumHeader(), AlbumSelector(onAlbumSelected: addToAlbum, onKeyboardExpanded: onKeyboardExpand)]
: null,
);
}
@@ -4,7 +4,7 @@ import 'package:async/async.dart';
import 'package:flutter/widgets.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/infrastructure/loaders/image_request.dart';
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/presentation/widgets/images/local_image_provider.dart';
import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart';
import 'package:immich_mobile/presentation/widgets/timeline/constants.dart';
@@ -189,5 +189,5 @@ ImageProvider? getThumbnailImageProvider(BaseAsset asset, {Size size = kThumbnai
bool _shouldUseLocalAsset(BaseAsset asset) =>
asset.hasLocal &&
(!asset.hasRemote || !MetadataRepository.instance.appConfig.image.preferRemote) &&
(!asset.hasRemote || !SettingsRepository.instance.appConfig.image.preferRemote) &&
!asset.isEdited;
@@ -2,7 +2,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/infrastructure/loaders/image_request.dart';
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/presentation/widgets/images/animated_image_stream_completer.dart';
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
import 'package:immich_mobile/presentation/widgets/images/one_frame_multi_image_stream_completer.dart';
@@ -104,7 +104,7 @@ class LocalFullImageProvider extends CancellableImageProvider<LocalFullImageProv
return;
}
final loadOriginal = MetadataRepository.instance.appConfig.image.loadOriginal;
final loadOriginal = SettingsRepository.instance.appConfig.image.loadOriginal;
final devicePixelRatio = PlatformDispatcher.instance.views.first.devicePixelRatio;
var request = this.request = LocalImageRequest(
localId: key.id,
@@ -2,7 +2,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/infrastructure/loaders/image_request.dart';
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/presentation/widgets/images/animated_image_stream_completer.dart';
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
import 'package:immich_mobile/presentation/widgets/images/one_frame_multi_image_stream_completer.dart';
@@ -122,7 +122,7 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
edited: key.edited,
),
);
final loadOriginal = assetType == AssetType.image && MetadataRepository.instance.appConfig.image.loadOriginal;
final loadOriginal = assetType == AssetType.image && SettingsRepository.instance.appConfig.image.loadOriginal;
yield* loadRequest(previewRequest, decode, isFinal: !loadOriginal);
if (!loadOriginal) {
@@ -9,7 +9,7 @@ import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'
import 'package:immich_mobile/presentation/widgets/timeline/constants.dart';
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
import 'package:immich_mobile/providers/backup/asset_upload_progress.provider.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
class ThumbnailTile extends ConsumerStatefulWidget {
@@ -1,11 +1,10 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/events.model.dart';
import 'package:immich_mobile/domain/models/metadata_key.dart';
import 'package:immich_mobile/domain/utils/event_stream.dart';
import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart';
import 'package:immich_mobile/providers/infrastructure/map.provider.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/map/map_state.provider.dart';
import 'package:maplibre_gl/maplibre_gl.dart';
@@ -81,25 +80,25 @@ class MapStateNotifier extends Notifier<MapState> {
}
void switchFavoriteOnly(bool isFavoriteOnly) {
ref.read(metadataProvider).write(MetadataKey.mapShowFavoriteOnly, isFavoriteOnly);
ref.read(settingsProvider).write(.mapShowFavoriteOnly, isFavoriteOnly);
state = state.copyWith(onlyFavorites: isFavoriteOnly);
EventStream.shared.emit(const MapMarkerReloadEvent());
}
void switchIncludeArchived(bool isIncludeArchived) {
ref.read(metadataProvider).write(MetadataKey.mapIncludeArchived, isIncludeArchived);
ref.read(settingsProvider).write(.mapIncludeArchived, isIncludeArchived);
state = state.copyWith(includeArchived: isIncludeArchived);
EventStream.shared.emit(const MapMarkerReloadEvent());
}
void switchWithPartners(bool isWithPartners) {
ref.read(metadataProvider).write(MetadataKey.mapWithPartners, isWithPartners);
ref.read(settingsProvider).write(.mapWithPartners, isWithPartners);
state = state.copyWith(withPartners: isWithPartners);
EventStream.shared.emit(const MapMarkerReloadEvent());
}
void setRelativeTime(int relativeDays) {
ref.read(metadataProvider).write(MetadataKey.mapRelativeDate, relativeDays);
ref.read(settingsProvider).write(.mapRelativeDate, relativeDays);
state = state.copyWith(relativeDays: relativeDays);
EventStream.shared.emit(const MapMarkerReloadEvent());
}
@@ -5,7 +5,7 @@ import 'package:immich_mobile/domain/models/timeline.model.dart';
import 'package:immich_mobile/presentation/widgets/timeline/constants.dart';
import 'package:immich_mobile/presentation/widgets/timeline/fixed/segment_builder.dart';
import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
class TimelineArgs {
@@ -10,7 +10,6 @@ import 'package:flutter/rendering.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/events.model.dart';
import 'package:immich_mobile/domain/models/metadata_key.dart';
import 'package:immich_mobile/domain/models/timeline.model.dart';
import 'package:immich_mobile/domain/utils/event_stream.dart';
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
@@ -22,7 +21,7 @@ import 'package:immich_mobile/presentation/widgets/timeline/scrubber.widget.dart
import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart';
import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart';
import 'package:immich_mobile/presentation/widgets/timeline/timeline_drag_region.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
@@ -459,7 +458,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
_restoreAssetIndex = targetAssetIndex;
});
ref.read(metadataProvider).write(MetadataKey.timelineTilesPerRow, _perRow);
ref.read(settingsProvider).write(.timelineTilesPerRow, _perRow);
}
};
},
@@ -67,6 +67,11 @@ class AlbumPendingUploadsNotifier extends AutoDisposeFamilyNotifier<List<Pending
_syncKeepAlive();
}
void clear() {
state = const [];
_syncKeepAlive();
}
void _syncKeepAlive() {
if (state.isEmpty) {
_keepAliveLink?.close();
@@ -9,8 +9,8 @@ import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/background_sync.provider.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/gallery_permission.provider.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/notification_permission.provider.dart';
import 'package:immich_mobile/providers/server_info.provider.dart';
import 'package:immich_mobile/providers/websocket.provider.dart';
@@ -107,7 +107,7 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
await Future.delayed(const Duration(milliseconds: 500));
final backgroundManager = _ref.read(backgroundSyncProvider);
final isAlbumLinkedSyncEnable = _ref.read(metadataProvider).appConfig.backup.syncAlbums;
final isAlbumLinkedSyncEnable = _ref.read(appConfigProvider).backup.syncAlbums;
try {
bool syncSuccess = false;
@@ -137,7 +137,7 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
}
Future<void> _resumeBackup() async {
final isEnableBackup = _ref.read(metadataProvider).appConfig.backup.enabled;
final isEnableBackup = _ref.read(appConfigProvider).backup.enabled;
if (isEnableBackup) {
final currentUser = Store.tryGet(StoreKey.currentUser);
+6 -7
View File
@@ -3,7 +3,6 @@ import 'dart:convert';
import 'package:flutter_udid/flutter_udid.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/models/metadata_key.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/domain/services/user.service.dart';
@@ -11,7 +10,7 @@ import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/models/auth/auth_state.model.dart';
import 'package:immich_mobile/models/auth/login_response.model.dart';
import 'package:immich_mobile/providers/api.provider.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
import 'package:immich_mobile/services/api.service.dart';
import 'package:immich_mobile/services/auth.service.dart';
@@ -130,7 +129,7 @@ class AuthNotifier extends StateNotifier<AuthState> {
await _apiService.updateHeaders();
final serverEndpoint = Store.get(StoreKey.serverEndpoint);
final headerMap = _ref.read(metadataProvider).appConfig.network.customHeaders;
final headerMap = _ref.read(appConfigProvider).network.customHeaders;
final customHeaders = headerMap.isEmpty ? null : jsonEncode(headerMap);
await _widgetService.writeCredentials(serverEndpoint, accessToken, customHeaders);
@@ -179,19 +178,19 @@ class AuthNotifier extends StateNotifier<AuthState> {
}
Future<void> saveWifiName(String wifiName) async {
await _ref.read(metadataProvider).write(MetadataKey.networkPreferredWifiName, wifiName);
await _ref.read(settingsProvider).write(.networkPreferredWifiName, wifiName);
}
Future<void> saveLocalEndpoint(String url) async {
await _ref.read(metadataProvider).write(MetadataKey.networkLocalEndpoint, url);
await _ref.read(settingsProvider).write(.networkLocalEndpoint, url);
}
String? getSavedWifiName() {
return _ref.read(metadataProvider).appConfig.network.preferredWifiName;
return _ref.read(appConfigProvider).network.preferredWifiName;
}
String? getSavedLocalEndpoint() {
return _ref.read(metadataProvider).appConfig.network.localEndpoint;
return _ref.read(appConfigProvider).network.localEndpoint;
}
/// Returns the current server endpoint (with /api) URL from the store
+12 -12
View File
@@ -1,8 +1,8 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/services/cleanup.service.dart';
@@ -54,21 +54,21 @@ final cleanupProvider = StateNotifierProvider<CleanupNotifier, CleanupState>((re
return CleanupNotifier(
ref.watch(cleanupServiceProvider),
ref.watch(currentUserProvider)?.id,
ref.watch(metadataProvider),
ref.watch(settingsProvider),
);
});
class CleanupNotifier extends StateNotifier<CleanupState> {
final CleanupService _cleanupService;
final String? _userId;
final MetadataRepository _metadataRepository;
final SettingsRepository _settingsRepository;
CleanupNotifier(this._cleanupService, this._userId, this._metadataRepository) : super(const CleanupState()) {
CleanupNotifier(this._cleanupService, this._userId, this._settingsRepository) : super(const CleanupState()) {
_loadPersistedSettings();
}
void _loadPersistedSettings() {
final cleanup = _metadataRepository.appConfig.cleanup;
final cleanup = _settingsRepository.appConfig.cleanup;
final keepFavorites = cleanup.keepFavorites;
final keepMediaType = cleanup.keepMediaType;
final keepAlbumIds = cleanup.keepAlbumIds.toSet();
@@ -87,18 +87,18 @@ class CleanupNotifier extends StateNotifier<CleanupState> {
state = state.copyWith(selectedDate: date, assetsToDelete: []);
if (date != null) {
final daysAgo = DateTime.now().difference(date).inDays;
_metadataRepository.write(.cleanupCutoffDaysAgo, daysAgo);
_settingsRepository.write(.cleanupCutoffDaysAgo, daysAgo);
}
}
void setKeepMediaType(AssetKeepType keepMediaType) {
state = state.copyWith(keepMediaType: keepMediaType, assetsToDelete: []);
_metadataRepository.write(.cleanupKeepMediaType, keepMediaType);
_settingsRepository.write(.cleanupKeepMediaType, keepMediaType);
}
void setKeepFavorites(bool keepFavorites) {
state = state.copyWith(keepFavorites: keepFavorites, assetsToDelete: []);
_metadataRepository.write(.cleanupKeepFavorites, keepFavorites);
_settingsRepository.write(.cleanupKeepFavorites, keepFavorites);
}
void toggleKeepAlbum(String albumId) {
@@ -118,7 +118,7 @@ class CleanupNotifier extends StateNotifier<CleanupState> {
}
void _persistExcludedAlbumIds(Set<String> albumIds) {
_metadataRepository.write(.cleanupKeepAlbumIds, albumIds.toList());
_settingsRepository.write(.cleanupKeepAlbumIds, albumIds.toList());
}
void cleanupStaleAlbumIds(Set<String> existingAlbumIds) {
@@ -131,7 +131,7 @@ class CleanupNotifier extends StateNotifier<CleanupState> {
}
void applyDefaultAlbumSelections(List<(String id, String name)> albums) {
final isInitialized = _metadataRepository.appConfig.cleanup.defaultsInitialized;
final isInitialized = _settingsRepository.appConfig.cleanup.defaultsInitialized;
if (isInitialized) {
return;
}
@@ -144,7 +144,7 @@ class CleanupNotifier extends StateNotifier<CleanupState> {
_persistExcludedAlbumIds(keepAlbumIds);
}
_metadataRepository.write(.cleanupDefaultsInitialized, true);
_settingsRepository.write(.cleanupDefaultsInitialized, true);
}
Future<void> scanAssets() async {
@@ -5,12 +5,15 @@ import 'package:background_downloader/background_downloader.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/asset_edit.model.dart';
import 'package:immich_mobile/domain/services/asset.service.dart';
import 'package:immich_mobile/domain/services/remote_album.service.dart';
import 'package:immich_mobile/models/download/livephotos_medatada.model.dart';
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
import 'package:immich_mobile/providers/backup/asset_upload_progress.provider.dart';
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
import 'package:immich_mobile/providers/infrastructure/asset_viewer/asset.provider.dart' show assetExifProvider;
import 'package:immich_mobile/providers/infrastructure/tag.provider.dart';
@@ -373,6 +376,52 @@ class ActionNotifier extends Notifier<void> {
}
}
Future<ActionResult> addToAlbum(ActionSource source, RemoteAlbum album) async {
final selected = _getAssets(source).toList(growable: false);
if (selected.isEmpty) {
return const ActionResult(count: 0, success: true);
}
final candidates = RemoteAlbumService.categorizeCandidates(selected);
final remoteIds = candidates.remoteAssetIds;
final localAssets = candidates.localAssetsToUpload;
final albumNotifier = ref.read(remoteAlbumProvider.notifier);
int addedRemote = 0;
if (remoteIds.isNotEmpty) {
try {
addedRemote = await albumNotifier.addAssets(album.id, remoteIds);
} catch (error, stack) {
_logger.severe('Failed to add assets to album ${album.id}', error, stack);
return ActionResult(count: 0, success: false, error: error.toString());
}
}
// Keep the selection available for retry if the remote add fails. Once the
// album mutation succeeds, clear timeline selection so upload overlays can render.
if (source == ActionSource.timeline) {
ref.read(multiSelectProvider.notifier).reset();
}
if (localAssets.isEmpty) {
return ActionResult(count: addedRemote, success: true);
}
final uploadResult = await upload(
source,
assets: localAssets,
onAssetUploaded: (asset, remoteId) async {
await albumNotifier.linkUploadedAssetToAlbum(album.id, asset, remoteId);
},
);
return ActionResult(
count: addedRemote + uploadResult.count,
success: uploadResult.success,
error: uploadResult.error,
);
}
Future<ActionResult> removeFromAlbum(ActionSource source, String albumId) async {
final ids = _getRemoteIdsForSource(source);
try {
@@ -495,8 +544,16 @@ class ActionNotifier extends Notifier<void> {
}
}
Future<ActionResult> upload(ActionSource source, {List<LocalAsset>? assets}) async {
Future<ActionResult> upload(
ActionSource source, {
List<LocalAsset>? assets,
FutureOr<void> Function(LocalAsset asset, String remoteId)? onAssetUploaded,
}) async {
final assetsToUpload = assets ?? _getAssets(source).whereType<LocalAsset>().toList();
final assetById = {for (final a in assetsToUpload) a.id: a};
final uploadedAssetIds = <String>{};
final failedAssetIds = <String>{};
final postUploadTasks = <Future<void>>[];
final progressNotifier = ref.read(assetUploadProgressProvider.notifier);
final cancelToken = Completer<void>();
@@ -518,16 +575,43 @@ class ActionNotifier extends Notifier<void> {
},
onSuccess: (localAssetId, remoteAssetId) {
progressNotifier.remove(localAssetId);
uploadedAssetIds.add(localAssetId);
final asset = assetById[localAssetId];
final callback = onAssetUploaded;
if (asset != null && callback != null) {
postUploadTasks.add(
Future.sync(() => callback(asset, remoteAssetId)).catchError((Object error, StackTrace stack) {
failedAssetIds.add(localAssetId);
progressNotifier.setError(localAssetId);
_logger.warning('Post-upload callback failed for $localAssetId', error, stack);
}),
);
}
},
onError: (localAssetId, errorMessage) {
failedAssetIds.add(localAssetId);
progressNotifier.setError(localAssetId);
},
),
);
return ActionResult(count: assetsToUpload.length, success: true);
await Future.wait(postUploadTasks);
final successCount = uploadedAssetIds.difference(failedAssetIds).length;
final isSuccess = successCount == assetsToUpload.length && failedAssetIds.isEmpty;
return ActionResult(
count: successCount,
success: isSuccess,
error: isSuccess ? null : 'Failed to upload ${assetsToUpload.length - successCount} assets',
);
} catch (error, stack) {
_logger.severe('Failed manually upload assets', error, stack);
return ActionResult(count: assetsToUpload.length, success: false, error: error.toString());
return ActionResult(
count: uploadedAssetIds.difference(failedAssetIds).length,
success: false,
error: error.toString(),
);
} finally {
ref.read(manualUploadCancelTokenProvider.notifier).state = null;
Future.delayed(const Duration(seconds: 2), () {
@@ -9,6 +9,7 @@ import 'package:immich_mobile/domain/services/remote_album.service.dart';
import 'package:immich_mobile/models/albums/album_search.model.dart';
import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart';
import 'package:immich_mobile/providers/album/pending_album_uploads.provider.dart';
import 'package:immich_mobile/providers/backup/asset_upload_progress.provider.dart';
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/services/foreground_upload.service.dart';
@@ -207,6 +208,22 @@ class RemoteAlbumNotifier extends Notifier<RemoteAlbumState> {
return added;
}
/// Links a freshly-uploaded local asset to an album using its new remote ID,
/// upserting a placeholder remote asset row so the local DB join survives
/// until the next sync catches up.
Future<int> linkUploadedAssetToAlbum(String albumId, LocalAsset source, String remoteId) async {
final currentUser = ref.read(currentUserProvider);
if (currentUser == null) {
throw Exception('User not logged in');
}
final added = await _remoteAlbumService.linkUploadedAssetToAlbum(albumId, remoteId, currentUser, source);
if (added > 0) {
await _refreshAlbumInState(albumId);
}
return added;
}
/// Adds a heterogeneous asset selection to an album. Already-remote assets
/// are linked immediately; local-only assets are queued in
/// [pendingAlbumUploadsProvider] (so the album page can show them with
@@ -221,11 +238,18 @@ class RemoteAlbumNotifier extends Notifier<RemoteAlbumState> {
final pendingNotifier = ref.read(pendingAlbumUploadsProvider(albumId).notifier);
pendingNotifier.enqueue(candidates.localAssetsToUpload);
Completer<void>? cancelToken;
if (candidates.localAssetsToUpload.isNotEmpty) {
cancelToken = Completer<void>();
ref.read(manualUploadCancelTokenProvider.notifier).state = cancelToken;
}
try {
final added = await _remoteAlbumService.addAssetsToAlbum(
albumId: albumId,
uploader: currentUser,
candidates: candidates,
cancelToken: cancelToken,
uploadCallbacks: UploadCallbacks(
onProgress: (localAssetId, _, bytes, totalBytes) {
final progress = totalBytes > 0 ? bytes / totalBytes : 0.0;
@@ -245,6 +269,15 @@ class RemoteAlbumNotifier extends Notifier<RemoteAlbumState> {
}
_logger.severe('Failed to add assets to album $albumId', error, stack);
rethrow;
} finally {
if (cancelToken != null) {
if (cancelToken.isCompleted) {
pendingNotifier.clear();
}
if (ref.read(manualUploadCancelTokenProvider) == cancelToken) {
ref.read(manualUploadCancelTokenProvider.notifier).state = null;
}
}
}
}
@@ -1,11 +1,11 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/config/app_config.dart';
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
final metadataProvider = Provider.autoDispose<MetadataRepository>((_) => MetadataRepository.instance);
final settingsProvider = Provider.autoDispose<SettingsRepository>((_) => SettingsRepository.instance);
final appConfigProvider = Provider.autoDispose<AppConfig>((ref) {
final repo = ref.watch(metadataProvider);
final repo = ref.watch(settingsProvider);
final subscription = repo.watchConfig().listen((event) => ref.state = event);
ref.onDispose(subscription.cancel);
return repo.appConfig;
@@ -3,7 +3,7 @@ import 'package:immich_mobile/domain/services/timeline.service.dart';
import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart';
import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
final timelineRepositoryProvider = Provider<DriftTimelineRepository>(
@@ -29,7 +29,7 @@ final timelineServiceProvider = Provider<TimelineService>(
final timelineFactoryProvider = Provider<TimelineFactory>(
(ref) => TimelineFactory(
timelineRepository: ref.watch(timelineRepositoryProvider),
metadataRepository: ref.watch(metadataProvider),
settingsRepository: ref.watch(settingsProvider),
),
);
@@ -1,8 +1,7 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/metadata_key.dart';
import 'package:immich_mobile/models/map/map_state.model.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/server_info.provider.dart';
final mapStateNotifierProvider = NotifierProvider<MapStateNotifier, MapState>(MapStateNotifier.new);
@@ -27,12 +26,12 @@ class MapStateNotifier extends Notifier<MapState> {
}
void switchTheme(ThemeMode mode) {
ref.read(metadataProvider).write(MetadataKey.mapThemeMode, mode);
ref.read(settingsProvider).write(.mapThemeMode, mode);
state = state.copyWith(themeMode: mode);
}
void switchFavoriteOnly(bool isFavoriteOnly) {
ref.read(metadataProvider).write(MetadataKey.mapShowFavoriteOnly, isFavoriteOnly);
ref.read(settingsProvider).write(.mapShowFavoriteOnly, isFavoriteOnly);
state = state.copyWith(showFavoriteOnly: isFavoriteOnly, shouldRefetchMarkers: true);
}
@@ -41,17 +40,17 @@ class MapStateNotifier extends Notifier<MapState> {
}
void switchIncludeArchived(bool isIncludeArchived) {
ref.read(metadataProvider).write(MetadataKey.mapIncludeArchived, isIncludeArchived);
ref.read(settingsProvider).write(.mapIncludeArchived, isIncludeArchived);
state = state.copyWith(includeArchived: isIncludeArchived, shouldRefetchMarkers: true);
}
void switchWithPartners(bool isWithPartners) {
ref.read(metadataProvider).write(MetadataKey.mapWithPartners, isWithPartners);
ref.read(settingsProvider).write(.mapWithPartners, isWithPartners);
state = state.copyWith(withPartners: isWithPartners, shouldRefetchMarkers: true);
}
void setRelativeTime(int relativeTime) {
ref.read(metadataProvider).write(MetadataKey.mapRelativeDate, relativeTime);
ref.read(settingsProvider).write(.mapRelativeDate, relativeTime);
state = state.copyWith(relativeTime: relativeTime, shouldRefetchMarkers: true);
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/theme/color_scheme.dart';
import 'package:immich_mobile/theme/dynamic_theme.dart';
import 'package:immich_mobile/theme/theme_data.dart';
+3 -3
View File
@@ -7,7 +7,7 @@ import 'package:immich_mobile/infrastructure/repositories/network.repository.dar
import 'package:immich_mobile/models/server_info/server_version.model.dart';
import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/background_sync.provider.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/server_info.provider.dart';
import 'package:immich_mobile/utils/debounce.dart';
import 'package:immich_mobile/utils/debug_print.dart';
@@ -193,7 +193,7 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
return;
}
final isSyncAlbumEnabled = _ref.read(metadataProvider).appConfig.backup.syncAlbums;
final isSyncAlbumEnabled = _ref.read(appConfigProvider).backup.syncAlbums;
try {
unawaited(
_ref.read(backgroundSyncProvider).syncWebsocketBatchV1(_batchedAssetUploadReady.toList()).then((_) {
@@ -214,7 +214,7 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
return;
}
final isSyncAlbumEnabled = _ref.read(metadataProvider).appConfig.backup.syncAlbums;
final isSyncAlbumEnabled = _ref.read(appConfigProvider).backup.syncAlbums;
try {
unawaited(
_ref.read(backgroundSyncProvider).syncWebsocketBatchV2(_batchedAssetUploadReady.toList()).then((_) {
+11 -9
View File
@@ -1,38 +1,40 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/config/app_config.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart';
import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
final authRepositoryProvider = Provider<AuthRepository>(
(ref) => AuthRepository(ref.watch(driftProvider), ref.watch(appConfigProvider)),
(ref) => AuthRepository(ref.watch(driftProvider), ref.watch(settingsProvider)),
);
class AuthRepository {
final Drift _drift;
final AppConfig _config;
final SettingsRepository _settings;
const AuthRepository(this._drift, this._config);
const AuthRepository(this._drift, this._settings);
Future<void> clearLocalData() async {
await SyncStreamRepository(_drift).reset();
}
bool getEndpointSwitchingFeature() {
return _config.network.autoEndpointSwitching;
return _settings.appConfig.network.autoEndpointSwitching;
}
String? getPreferredWifiName() {
return _config.network.preferredWifiName;
return _settings.appConfig.network.preferredWifiName;
}
String? getLocalEndpoint() {
return _config.network.localEndpoint;
return _settings.appConfig.network.localEndpoint;
}
List<AuxilaryEndpoint> getExternalEndpointList() {
return _config.network.externalEndpointList.map((url) => AuxilaryEndpoint(url: url, status: .valid)).toList();
return _settings.appConfig.network.externalEndpointList
.map((url) => AuxilaryEndpoint(url: url, status: .valid))
.toList();
}
}
+5 -5
View File
@@ -5,7 +5,7 @@ import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/network.repository.dart';
import 'package:immich_mobile/utils/debug_print.dart';
import 'package:immich_mobile/utils/url_helper.dart';
@@ -13,7 +13,7 @@ import 'package:logging/logging.dart';
import 'package:openapi/api.dart';
class ApiService {
late ApiClient _apiClient;
final ApiClient _apiClient = ApiClient(basePath: '');
late UsersApi usersApi;
late AuthenticationApi authenticationApi;
@@ -54,7 +54,7 @@ class ApiService {
}
setEndpoint(String endpoint) {
_apiClient = ApiClient(basePath: endpoint);
_apiClient.basePath = endpoint;
_apiClient.client = NetworkRepository.client;
usersApi = UsersApi(_apiClient);
authenticationApi = AuthenticationApi(_apiClient);
@@ -177,7 +177,7 @@ class ApiService {
if (serverEndpoint != null && serverEndpoint.isNotEmpty) {
urls.add(serverEndpoint);
}
final network = MetadataRepository.instance.appConfig.network;
final network = SettingsRepository.instance.appConfig.network;
final localEndpoint = network.localEndpoint;
if (localEndpoint.isNotEmpty) {
urls.add(localEndpoint);
@@ -191,7 +191,7 @@ class ApiService {
}
static Map<String, String> getRequestHeaders() {
return MetadataRepository.instance.appConfig.network.customHeaders;
return SettingsRepository.instance.appConfig.network.customHeaders;
}
ApiClient get apiClient => _apiClient;
+10 -4
View File
@@ -1,11 +1,11 @@
import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/metadata_key.dart';
import 'package:immich_mobile/domain/models/settings_key.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/utils/background_sync.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/network.repository.dart';
import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart';
import 'package:immich_mobile/models/auth/login_response.model.dart';
@@ -100,7 +100,7 @@ class AuthService {
_log.severe("Error clearing local data", error, stackTrace);
});
await MetadataRepository.instance.write(MetadataKey.backupEnabled, false);
await SettingsRepository.instance.write(SettingsKey.backupEnabled, false);
}
}
@@ -110,7 +110,7 @@ class AuthService {
/// - Authentication repository data
/// - Current user information
/// - Access token
/// - Asset ETag
/// - Server-specific endpoint configuration
///
/// All deletions are executed in parallel using [Future.wait].
Future<void> clearLocalData() async {
@@ -120,6 +120,12 @@ class AuthService {
_authRepository.clearLocalData(),
Store.delete(StoreKey.currentUser),
Store.delete(StoreKey.accessToken),
SettingsRepository.instance.clear(const [
.networkAutoEndpointSwitching,
.networkPreferredWifiName,
.networkLocalEndpoint,
.networkExternalEndpointList,
]),
]);
}
@@ -13,7 +13,7 @@ import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/extensions/platform_extensions.dart';
import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
import 'package:immich_mobile/providers/infrastructure/storage.provider.dart';
@@ -359,7 +359,7 @@ class BackgroundUploadService {
}
bool _shouldRequireWiFi(LocalAsset asset) {
final backup = MetadataRepository.instance.appConfig.backup;
final backup = SettingsRepository.instance.appConfig.backup;
if (asset.isVideo && backup.useCellularForVideos) {
return false;
}
@@ -11,7 +11,7 @@ import 'package:immich_mobile/extensions/network_capability_extensions.dart';
import 'package:immich_mobile/extensions/platform_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
import 'package:immich_mobile/platform/connectivity_api.g.dart';
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
@@ -451,7 +451,7 @@ class ForegroundUploadService {
}
bool _shouldRequireWiFi(LocalAsset asset) {
final backup = MetadataRepository.instance.appConfig.backup;
final backup = SettingsRepository.instance.appConfig.backup;
if (asset.isVideo && backup.useCellularForVideos) {
return false;
}
+3 -3
View File
@@ -6,7 +6,7 @@ import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/log.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/network.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
import 'package:photo_manager/photo_manager.dart';
@@ -49,11 +49,11 @@ abstract final class Bootstrap {
await StoreService.init(storeRepository: storeRepo, listenUpdates: listenStoreUpdates);
final metadataRepo = await MetadataRepository.ensureInitialized(drift);
final settingsRepo = await SettingsRepository.ensureInitialized(drift);
await LogService.init(
logRepository: LogRepository(logDb),
metadataRepository: metadataRepo,
settingsRepository: settingsRepo,
shouldBuffer: shouldBufferLogs,
);
+49 -49
View File
@@ -8,11 +8,11 @@ import 'package:immich_mobile/constants/colors.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/config/app_config.dart';
import 'package:immich_mobile/domain/models/log.model.dart';
import 'package:immich_mobile/domain/models/metadata_key.dart';
import 'package:immich_mobile/domain/models/settings_key.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/models/timeline.model.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/infrastructure/entities/metadata.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/settings.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/network.repository.dart';
import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart';
@@ -74,65 +74,65 @@ Future<void> _migrateTo25() async {
Future<void> _migrateTo26(Drift drift) async {
final migrator = _StoreMigrator(drift);
await migrator.migrateEnumIndex(StoreKey.legacyLogLevel, MetadataKey.logLevel, LogLevel.values);
await migrator.migrateEnumIndex(StoreKey.legacyLogLevel, SettingsKey.logLevel, LogLevel.values);
// Theme
await migrator.migrateEnumName(StoreKey.legacyThemeMode, MetadataKey.themeMode, ThemeMode.values);
await migrator.migrateEnumName(StoreKey.legacyPrimaryColor, MetadataKey.themePrimaryColor, ImmichColorPreset.values);
await migrator.migrateBool(StoreKey.legacyDynamicTheme, MetadataKey.themeDynamic);
await migrator.migrateBool(StoreKey.legacyColorfulInterface, MetadataKey.themeColorfulInterface);
await migrator.migrateEnumName(StoreKey.legacyThemeMode, SettingsKey.themeMode, ThemeMode.values);
await migrator.migrateEnumName(StoreKey.legacyPrimaryColor, SettingsKey.themePrimaryColor, ImmichColorPreset.values);
await migrator.migrateBool(StoreKey.legacyDynamicTheme, SettingsKey.themeDynamic);
await migrator.migrateBool(StoreKey.legacyColorfulInterface, SettingsKey.themeColorfulInterface);
// Cleanup
final cleanupKeepAlbumIds = await migrator.readLegacyStoreString(StoreKey.legacyCleanupKeepAlbumIds.id);
if (cleanupKeepAlbumIds != null) {
final ids = cleanupKeepAlbumIds.split(',').where((id) => id.isNotEmpty).toList();
migrator.stage(StoreKey.legacyCleanupKeepAlbumIds, MetadataKey.cleanupKeepAlbumIds, ids);
migrator.stage(StoreKey.legacyCleanupKeepAlbumIds, SettingsKey.cleanupKeepAlbumIds, ids);
}
await migrator.migrateBool(StoreKey.legacyCleanupKeepFavorites, MetadataKey.cleanupKeepFavorites);
await migrator.migrateBool(StoreKey.legacyCleanupKeepFavorites, SettingsKey.cleanupKeepFavorites);
await migrator.migrateEnumIndex(
StoreKey.legacyCleanupKeepMediaType,
MetadataKey.cleanupKeepMediaType,
SettingsKey.cleanupKeepMediaType,
AssetKeepType.values,
);
await migrator.migrateInt(StoreKey.legacyCleanupCutoffDaysAgo, MetadataKey.cleanupCutoffDaysAgo);
await migrator.migrateBool(StoreKey.legacyCleanupDefaultsInitialized, MetadataKey.cleanupDefaultsInitialized);
await migrator.migrateInt(StoreKey.legacyCleanupCutoffDaysAgo, SettingsKey.cleanupCutoffDaysAgo);
await migrator.migrateBool(StoreKey.legacyCleanupDefaultsInitialized, SettingsKey.cleanupDefaultsInitialized);
// Map
await migrator.migrateBool(StoreKey.legacyMapShowFavoriteOnly, MetadataKey.mapShowFavoriteOnly);
await migrator.migrateInt(StoreKey.legacyMapRelativeDate, MetadataKey.mapRelativeDate);
await migrator.migrateBool(StoreKey.legacyMapIncludeArchived, MetadataKey.mapIncludeArchived);
await migrator.migrateEnumIndex(StoreKey.legacyMapThemeMode, MetadataKey.mapThemeMode, ThemeMode.values);
await migrator.migrateBool(StoreKey.legacyMapwithPartners, MetadataKey.mapWithPartners);
await migrator.migrateBool(StoreKey.legacyMapShowFavoriteOnly, SettingsKey.mapShowFavoriteOnly);
await migrator.migrateInt(StoreKey.legacyMapRelativeDate, SettingsKey.mapRelativeDate);
await migrator.migrateBool(StoreKey.legacyMapIncludeArchived, SettingsKey.mapIncludeArchived);
await migrator.migrateEnumIndex(StoreKey.legacyMapThemeMode, SettingsKey.mapThemeMode, ThemeMode.values);
await migrator.migrateBool(StoreKey.legacyMapwithPartners, SettingsKey.mapWithPartners);
// Timeline
await migrator.migrateInt(StoreKey.legacyTilesPerRow, MetadataKey.timelineTilesPerRow);
await migrator.migrateInt(StoreKey.legacyTilesPerRow, SettingsKey.timelineTilesPerRow);
await migrator.migrateEnumIndex(
StoreKey.legacyGroupAssetsBy,
MetadataKey.timelineGroupAssetsBy,
SettingsKey.timelineGroupAssetsBy,
GroupAssetsBy.values,
);
await migrator.migrateBool(StoreKey.legacyStorageIndicator, MetadataKey.timelineStorageIndicator);
await migrator.migrateBool(StoreKey.legacyStorageIndicator, SettingsKey.timelineStorageIndicator);
// Image
await migrator.migrateBool(StoreKey.legacyPreferRemoteImage, MetadataKey.imagePreferRemote);
await migrator.migrateBool(StoreKey.legacyLoadOriginal, MetadataKey.imageLoadOriginal);
await migrator.migrateBool(StoreKey.legacyPreferRemoteImage, SettingsKey.imagePreferRemote);
await migrator.migrateBool(StoreKey.legacyLoadOriginal, SettingsKey.imageLoadOriginal);
// Viewer
await migrator.migrateBool(StoreKey.legacyLoopVideo, MetadataKey.viewerLoopVideo);
await migrator.migrateBool(StoreKey.legacyLoadOriginalVideo, MetadataKey.viewerLoadOriginalVideo);
await migrator.migrateBool(StoreKey.legacyAutoPlayVideo, MetadataKey.viewerAutoPlayVideo);
await migrator.migrateBool(StoreKey.legacyTapToNavigate, MetadataKey.viewerTapToNavigate);
await migrator.migrateBool(StoreKey.legacyLoopVideo, SettingsKey.viewerLoopVideo);
await migrator.migrateBool(StoreKey.legacyLoadOriginalVideo, SettingsKey.viewerLoadOriginalVideo);
await migrator.migrateBool(StoreKey.legacyAutoPlayVideo, SettingsKey.viewerAutoPlayVideo);
await migrator.migrateBool(StoreKey.legacyTapToNavigate, SettingsKey.viewerTapToNavigate);
// Network
await migrator.migrateBool(StoreKey.legacyAutoEndpointSwitching, MetadataKey.networkAutoEndpointSwitching);
await migrator.migrateString(StoreKey.legacyPreferredWifiName, MetadataKey.networkPreferredWifiName);
await migrator.migrateString(StoreKey.legacyLocalEndpoint, MetadataKey.networkLocalEndpoint);
await migrator.migrateBool(StoreKey.legacyAutoEndpointSwitching, SettingsKey.networkAutoEndpointSwitching);
await migrator.migrateString(StoreKey.legacyPreferredWifiName, SettingsKey.networkPreferredWifiName);
await migrator.migrateString(StoreKey.legacyLocalEndpoint, SettingsKey.networkLocalEndpoint);
await _migrateExternalEndpointList(migrator);
await _migrateCustomHeaders(migrator);
// Album
await _migrateAlbumSortMode(migrator);
await migrator.migrateBool(StoreKey.legacySelectedAlbumSortReverse, MetadataKey.albumIsReverse);
await migrator.migrateBool(StoreKey.legacyAlbumGridView, MetadataKey.albumIsGrid);
await migrator.migrateBool(StoreKey.legacySelectedAlbumSortReverse, SettingsKey.albumIsReverse);
await migrator.migrateBool(StoreKey.legacyAlbumGridView, SettingsKey.albumIsGrid);
// Backup
await migrator.migrateBool(StoreKey.legacyEnableBackup, MetadataKey.backupEnabled);
await migrator.migrateBool(StoreKey.legacyUseWifiForUploadVideos, MetadataKey.backupUseCellularForVideos);
await migrator.migrateBool(StoreKey.legacyUseWifiForUploadPhotos, MetadataKey.backupUseCellularForPhotos);
await migrator.migrateBool(StoreKey.legacyBackupRequireCharging, MetadataKey.backupRequireCharging);
await migrator.migrateInt(StoreKey.legacyBackupTriggerDelay, MetadataKey.backupTriggerDelay);
await migrator.migrateBool(StoreKey.legacySyncAlbums, MetadataKey.backupSyncAlbums);
await migrator.migrateBool(StoreKey.legacyEnableBackup, SettingsKey.backupEnabled);
await migrator.migrateBool(StoreKey.legacyUseWifiForUploadVideos, SettingsKey.backupUseCellularForVideos);
await migrator.migrateBool(StoreKey.legacyUseWifiForUploadPhotos, SettingsKey.backupUseCellularForPhotos);
await migrator.migrateBool(StoreKey.legacyBackupRequireCharging, SettingsKey.backupRequireCharging);
await migrator.migrateInt(StoreKey.legacyBackupTriggerDelay, SettingsKey.backupTriggerDelay);
await migrator.migrateBool(StoreKey.legacySyncAlbums, SettingsKey.backupSyncAlbums);
await migrator.complete();
}
@@ -143,7 +143,7 @@ Future<void> _migrateAlbumSortMode(_StoreMigrator migrator) async {
return;
}
migrator.stage(StoreKey.legacySelectedAlbumSortOrder, MetadataKey.albumSortMode, mode);
migrator.stage(StoreKey.legacySelectedAlbumSortOrder, SettingsKey.albumSortMode, mode);
}
Future<void> _migrateExternalEndpointList(_StoreMigrator migrator) async {
@@ -167,7 +167,7 @@ Future<void> _migrateExternalEndpointList(_StoreMigrator migrator) async {
// ignore invalid entries
}
migrator.stage(StoreKey.legacyExternalEndpointList, MetadataKey.networkExternalEndpointList, urls);
migrator.stage(StoreKey.legacyExternalEndpointList, SettingsKey.networkExternalEndpointList, urls);
}
Future<void> _migrateCustomHeaders(_StoreMigrator migrator) async {
@@ -190,17 +190,17 @@ Future<void> _migrateCustomHeaders(_StoreMigrator migrator) async {
// ignore invalid entries
}
migrator.stage(StoreKey.legacyCustomHeaders, MetadataKey.networkCustomHeaders, headers);
migrator.stage(StoreKey.legacyCustomHeaders, SettingsKey.networkCustomHeaders, headers);
}
class _StoreMigrator {
final Drift _db;
final Map<MetadataKey<Object>, Object> _cache = {};
final Map<SettingsKey<Object>, Object> _cache = {};
final List<int> _migratedStoreIds = [];
_StoreMigrator(this._db);
Future<void> migrateEnumIndex<T extends Enum>(StoreKey<int> legacyKey, MetadataKey<T> newKey, List<T> values) async {
Future<void> migrateEnumIndex<T extends Enum>(StoreKey<int> legacyKey, SettingsKey<T> newKey, List<T> values) async {
final index = await readLegacyStoreInt(legacyKey.id);
if (index == null) {
return;
@@ -217,7 +217,7 @@ class _StoreMigrator {
Future<void> migrateEnumName<T extends Enum>(
StoreKey<String> legacyKey,
MetadataKey<T> newKey,
SettingsKey<T> newKey,
List<T> values,
) async {
final name = await readLegacyStoreString(legacyKey.id);
@@ -234,7 +234,7 @@ class _StoreMigrator {
_migratedStoreIds.add(legacyKey.id);
}
Future<void> migrateBool(StoreKey<bool> legacyKey, MetadataKey<bool> newKey) async {
Future<void> migrateBool(StoreKey<bool> legacyKey, SettingsKey<bool> newKey) async {
final intValue = await readLegacyStoreInt(legacyKey.id);
if (intValue == null) {
return;
@@ -245,7 +245,7 @@ class _StoreMigrator {
_migratedStoreIds.add(legacyKey.id);
}
Future<void> migrateInt(StoreKey<int> legacyKey, MetadataKey<int> newKey) async {
Future<void> migrateInt(StoreKey<int> legacyKey, SettingsKey<int> newKey) async {
final intValue = await readLegacyStoreInt(legacyKey.id);
if (intValue == null) {
return;
@@ -255,7 +255,7 @@ class _StoreMigrator {
_migratedStoreIds.add(legacyKey.id);
}
Future<void> migrateString(StoreKey<String> legacyKey, MetadataKey<String> newKey) async {
Future<void> migrateString(StoreKey<String> legacyKey, SettingsKey<String> newKey) async {
final value = await readLegacyStoreString(legacyKey.id);
if (value == null) {
return;
@@ -265,7 +265,7 @@ class _StoreMigrator {
_migratedStoreIds.add(legacyKey.id);
}
void stage<T extends Object>(StoreKey legacyKey, MetadataKey<T> newKey, T value) {
void stage<T extends Object>(StoreKey legacyKey, SettingsKey<T> newKey, T value) {
_cache[newKey] = value;
_migratedStoreIds.add(legacyKey.id);
}
@@ -277,8 +277,8 @@ class _StoreMigrator {
continue;
}
batch.insert(
_db.metadataEntity,
MetadataEntityCompanion(key: Value(entry.key.name), value: Value(entry.key.encode(entry.value))),
_db.settingsEntity,
SettingsEntityCompanion(key: Value(entry.key.name), value: Value(entry.key.encode(entry.value))),
mode: InsertMode.insertOrReplace,
);
}
+52 -20
View File
@@ -1,36 +1,42 @@
enum SemVerType { major, minor, patch }
enum SemVerType { major, minor, patch, prerelease }
class SemVer {
final int major;
final int minor;
final int patch;
final int? prerelease;
const SemVer({required this.major, required this.minor, required this.patch});
const SemVer({required this.major, required this.minor, required this.patch, this.prerelease});
@override
String toString() {
return '$major.$minor.$patch';
return '$major.$minor.$patch${prerelease == null ? '' : '-rc.$prerelease'}';
}
SemVer copyWith({int? major, int? minor, int? patch}) {
return SemVer(major: major ?? this.major, minor: minor ?? this.minor, patch: patch ?? this.patch);
SemVer copyWith({int? major, int? minor, int? patch, int? prerelease}) {
return SemVer(
major: major ?? this.major,
minor: minor ?? this.minor,
patch: patch ?? this.patch,
prerelease: prerelease ?? this.prerelease,
);
}
static final _pattern = RegExp(r'^v?(\d+)\.(\d+)\.(\d+)(?:-rc\.(\d+))?(?:[-+].*)?$', caseSensitive: false);
factory SemVer.fromString(String version) {
if (version.toLowerCase().startsWith("v")) {
version = version.substring(1);
}
final parts = version.split("-")[0].split('.');
if (parts.length != 3) {
final match = _pattern.firstMatch(version);
if (match == null) {
throw FormatException('Invalid semantic version string: $version');
}
try {
return SemVer(major: int.parse(parts[0]), minor: int.parse(parts[1]), patch: int.parse(parts[2]));
} catch (e) {
throw FormatException('Invalid semantic version string: $version');
}
final prerelease = match.group(4);
return SemVer(
major: int.parse(match.group(1)!),
minor: int.parse(match.group(2)!),
patch: int.parse(match.group(3)!),
prerelease: prerelease == null ? null : int.parse(prerelease),
);
}
bool operator >(SemVer other) {
@@ -40,7 +46,10 @@ class SemVer {
if (minor != other.minor) {
return minor > other.minor;
}
return patch > other.patch;
if (patch != other.patch) {
return patch > other.patch;
}
return _comparePrerelease(other) > 0;
}
bool operator <(SemVer other) {
@@ -50,7 +59,23 @@ class SemVer {
if (minor != other.minor) {
return minor < other.minor;
}
return patch < other.patch;
if (patch != other.patch) {
return patch < other.patch;
}
return _comparePrerelease(other) < 0;
}
int _comparePrerelease(SemVer other) {
if (prerelease == other.prerelease) {
return 0;
}
if (prerelease == null) {
return 1;
}
if (other.prerelease == null) {
return -1;
}
return prerelease!.compareTo(other.prerelease!);
}
bool operator >=(SemVer other) {
@@ -67,7 +92,11 @@ class SemVer {
return true;
}
return other is SemVer && other.major == major && other.minor == minor && other.patch == patch;
return other is SemVer &&
other.major == major &&
other.minor == minor &&
other.patch == patch &&
other.prerelease == prerelease;
}
SemVerType? differenceType(SemVer other) {
@@ -80,10 +109,13 @@ class SemVer {
if (patch != other.patch) {
return SemVerType.patch;
}
if (prerelease != other.prerelease) {
return SemVerType.prerelease;
}
return null;
}
@override
int get hashCode => major.hashCode ^ minor.hashCode ^ patch.hashCode;
int get hashCode => major.hashCode ^ minor.hashCode ^ patch.hashCode ^ prerelease.hashCode;
}
@@ -50,9 +50,7 @@ class AppBarServerInfo extends HookConsumerWidget {
divider,
_ServerInfoItem(
label: "server_version".tr(),
text: serverInfoState.serverVersion.major > 0
? "${serverInfoState.serverVersion.major}.${serverInfoState.serverVersion.minor}.${serverInfoState.serverVersion.patch}"
: "--",
text: serverInfoState.serverVersion.major > 0 ? "${serverInfoState.serverVersion}" : "--",
),
divider,
_ServerInfoItem(label: "server_info_box_server_url".tr(), text: getServerUrl() ?? '--', tooltip: true),
@@ -60,9 +58,7 @@ class AppBarServerInfo extends HookConsumerWidget {
divider,
_ServerInfoItem(
label: "latest_version".tr(),
text: serverInfoState.latestVersion!.major > 0
? "${serverInfoState.latestVersion!.major}.${serverInfoState.latestVersion!.minor}.${serverInfoState.latestVersion!.patch}"
: "--",
text: serverInfoState.latestVersion!.major > 0 ? "${serverInfoState.latestVersion!}" : "--",
tooltip: true,
icon: serverInfoState.versionStatus == VersionStatus.serverOutOfDate
? const Icon(Icons.info, color: Color.fromARGB(255, 243, 188, 106), size: 12)
@@ -10,7 +10,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/models/server_info/server_info.model.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/cast.provider.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
import 'package:immich_mobile/providers/server_info.provider.dart';
import 'package:immich_mobile/providers/sync_status.provider.dart';
@@ -15,7 +15,7 @@ import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/background_sync.provider.dart';
import 'package:immich_mobile/providers/gallery_permission.provider.dart';
@@ -187,7 +187,7 @@ class LoginForm extends HookConsumerWidget {
await backgroundManager.syncRemote();
await backgroundManager.hashAssets();
if (MetadataRepository.instance.appConfig.backup.syncAlbums) {
if (SettingsRepository.instance.appConfig.backup.syncAlbums) {
await backgroundManager.syncLinkedAlbum();
}
}
@@ -7,7 +7,7 @@ import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/services/log.service.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
import 'package:immich_mobile/repositories/permission.repository.dart';
@@ -35,7 +35,7 @@ class AdvancedSettings extends HookConsumerWidget {
final preferRemote = useState(ref.read(appConfigProvider).image.preferRemote);
useValueChanged(
preferRemote.value,
(_, __) => ref.read(metadataProvider).write(.imagePreferRemote, preferRemote.value),
(_, __) => ref.read(settingsProvider).write(.imagePreferRemote, preferRemote.value),
);
final readonlyModeEnabled = useAppSettingsState(AppSettingsEnum.readonlyModeEnabled);
@@ -3,11 +3,10 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/metadata_key.dart';
import 'package:immich_mobile/domain/models/timeline.model.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/providers/app_settings.provider.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/widgets/settings/setting_group_title.dart';
import 'package:immich_mobile/widgets/settings/settings_radio_list_tile.dart';
@@ -19,7 +18,7 @@ class GroupSettings extends HookConsumerWidget {
final groupBy = useValueNotifier(ref.watch(appConfigProvider.select((s) => s.timeline.groupAssetsBy)));
Future<void> updateAppSettings(GroupAssetsBy groupBy) async {
await ref.read(metadataProvider).write(MetadataKey.timelineGroupAssetsBy, groupBy);
await ref.read(settingsProvider).write(.timelineGroupAssetsBy, groupBy);
ref.invalidate(appSettingsServiceProvider);
}
@@ -2,10 +2,9 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/metadata_key.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/providers/app_settings.provider.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/widgets/settings/setting_group_title.dart';
import 'package:immich_mobile/widgets/settings/settings_slider_list_tile.dart';
@@ -16,7 +15,7 @@ class LayoutSettings extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final tilesPerRow = useState(ref.read(appConfigProvider.select((s) => s.timeline.tilesPerRow)));
useValueChanged<int, void>(tilesPerRow.value, (_, __) {
ref.read(metadataProvider).write(MetadataKey.timelineTilesPerRow, tilesPerRow.value);
ref.read(settingsProvider).write(.timelineTilesPerRow, tilesPerRow.value);
});
return Column(
@@ -2,10 +2,8 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/metadata_key.dart';
import 'package:immich_mobile/providers/app_settings.provider.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/setting.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/widgets/settings/asset_list_settings/asset_list_group_settings.dart';
import 'package:immich_mobile/widgets/settings/asset_list_settings/asset_list_layout_settings.dart';
import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart';
@@ -23,7 +21,7 @@ class AssetListSettings extends HookConsumerWidget {
valueNotifier: storageIndicator,
title: 'theme_setting_asset_list_storage_indicator_title'.tr(),
onChanged: (value) {
ref.read(metadataProvider).write(MetadataKey.timelineStorageIndicator, value);
ref.read(settingsProvider).write(.timelineStorageIndicator, value);
ref.invalidate(appSettingsServiceProvider);
ref.invalidate(settingsProvider);
},
@@ -3,7 +3,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/providers/app_settings.provider.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/widgets/settings/setting_group_title.dart';
import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart';
@@ -14,7 +14,7 @@ class ImageViewerQualitySetting extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final isOriginal = useState(ref.read(appConfigProvider).image.loadOriginal);
useValueChanged<bool, void>(isOriginal.value, (_, __) {
ref.read(metadataProvider).write(.imageLoadOriginal, isOriginal.value);
ref.read(settingsProvider).write(.imageLoadOriginal, isOriginal.value);
});
return Column(
@@ -2,7 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/widgets/settings/settings_sub_title.dart';
import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart';
@@ -13,7 +13,7 @@ class ImageViewerTapToNavigateSetting extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final tapToNavigate = useState(ref.read(appConfigProvider).viewer.tapToNavigate);
useValueChanged<bool, void>(tapToNavigate.value, (_, __) {
ref.read(metadataProvider).write(.viewerTapToNavigate, tapToNavigate.value);
ref.read(settingsProvider).write(.viewerTapToNavigate, tapToNavigate.value);
});
return Column(
@@ -3,7 +3,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/widgets/settings/setting_group_title.dart';
import 'package:immich_mobile/widgets/settings/settings_radio_list_tile.dart';
import 'package:immich_mobile/widgets/settings/settings_slider_list_tile.dart';
@@ -23,19 +23,19 @@ class SlideshowSettings extends HookConsumerWidget {
final useDirection = useState(slideshow.direction);
useValueChanged<bool, void>(useTransition.value, (_, __) {
ref.read(metadataProvider).write(.slideshowTransition, useTransition.value);
ref.read(settingsProvider).write(.slideshowTransition, useTransition.value);
});
useValueChanged<bool, void>(useRepeat.value, (_, __) {
ref.read(metadataProvider).write(.slideshowRepeat, useRepeat.value);
ref.read(settingsProvider).write(.slideshowRepeat, useRepeat.value);
});
useValueChanged<int, void>(useDuration.value, (_, __) {
ref.read(metadataProvider).write(.slideshowDuration, useDuration.value);
ref.read(settingsProvider).write(.slideshowDuration, useDuration.value);
});
useValueChanged<SlideshowLook, void>(useLook.value, (_, __) {
ref.read(metadataProvider).write(.slideshowLook, useLook.value);
ref.read(settingsProvider).write(.slideshowLook, useLook.value);
});
useValueChanged<SlideshowDirection, void>(useDirection.value, (_, __) {
ref.read(metadataProvider).write(.slideshowDirection, useDirection.value);
ref.read(settingsProvider).write(.slideshowDirection, useDirection.value);
});
return Column(
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/widgets/settings/setting_group_title.dart';
import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart';
@@ -17,13 +17,13 @@ class VideoViewerSettings extends HookConsumerWidget {
final useOriginalVideo = useState(viewer.loadOriginalVideo);
useValueChanged<bool, void>(useAutoPlayVideo.value, (_, __) {
ref.read(metadataProvider).write(.viewerAutoPlayVideo, useAutoPlayVideo.value);
ref.read(settingsProvider).write(.viewerAutoPlayVideo, useAutoPlayVideo.value);
});
useValueChanged<bool, void>(useLoopVideo.value, (_, __) {
ref.read(metadataProvider).write(.viewerLoopVideo, useLoopVideo.value);
ref.read(settingsProvider).write(.viewerLoopVideo, useLoopVideo.value);
});
useValueChanged<bool, void>(useOriginalVideo.value, (_, __) {
ref.read(metadataProvider).write(.viewerLoadOriginalVideo, useOriginalVideo.value);
ref.read(settingsProvider).write(.viewerLoadOriginalVideo, useOriginalVideo.value);
});
return Column(
@@ -5,15 +5,15 @@ import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
import 'package:immich_mobile/domain/models/config/app_config.dart';
import 'package:immich_mobile/domain/models/metadata_key.dart';
import 'package:immich_mobile/domain/models/settings_key.dart';
import 'package:immich_mobile/domain/services/sync_linked_album.service.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/platform_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/providers/background_sync.provider.dart';
import 'package:immich_mobile/providers/backup/backup_album.provider.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/widgets/settings/setting_group_title.dart';
import 'package:immich_mobile/widgets/settings/setting_list_tile.dart';
@@ -112,7 +112,7 @@ class _AlbumSyncActionButtonState extends ConsumerState<_AlbumSyncActionButton>
trailing: Switch(
value: albumSyncEnable,
onChanged: (bool newValue) async {
await ref.read(metadataProvider).write(MetadataKey.backupSyncAlbums, newValue);
await ref.read(settingsProvider).write(.backupSyncAlbums, newValue);
if (newValue == true) {
await _manageLinkedAlbums();
@@ -158,7 +158,7 @@ class _AlbumSyncActionButtonState extends ConsumerState<_AlbumSyncActionButton>
}
class _BackupSwitchTile extends ConsumerWidget {
final MetadataKey<bool> metadataKey;
final SettingsKey<bool> metadataKey;
final bool Function(AppConfig) selector;
final String titleKey;
final String subtitleKey;
@@ -183,7 +183,7 @@ class _BackupSwitchTile extends ConsumerWidget {
trailing: Switch(
value: value,
onChanged: (bool newValue) async {
await ref.read(metadataProvider).write(metadataKey, newValue);
await ref.read(settingsProvider).write(metadataKey, newValue);
onChanged?.call(newValue);
},
),
@@ -198,7 +198,7 @@ class _UseCellularForVideosButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return _BackupSwitchTile(
metadataKey: MetadataKey.backupUseCellularForVideos,
metadataKey: SettingsKey.backupUseCellularForVideos,
selector: (c) => c.backup.useCellularForVideos,
titleKey: "videos",
subtitleKey: "network_requirement_videos_upload",
@@ -212,7 +212,7 @@ class _UseCellularForPhotosButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return _BackupSwitchTile(
metadataKey: MetadataKey.backupUseCellularForPhotos,
metadataKey: SettingsKey.backupUseCellularForPhotos,
selector: (c) => c.backup.useCellularForPhotos,
titleKey: "photos",
subtitleKey: "network_requirement_photos_upload",
@@ -227,7 +227,7 @@ class _BackupOnlyWhenChargingButton extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final fgService = ref.read(backgroundWorkerFgServiceProvider);
return _BackupSwitchTile(
metadataKey: MetadataKey.backupRequireCharging,
metadataKey: SettingsKey.backupRequireCharging,
selector: (c) => c.backup.requireCharging,
titleKey: "charging",
subtitleKey: "charging_requirement_mobile_backup",
@@ -282,11 +282,11 @@ class _BackupDelaySlider extends ConsumerWidget {
value: currentValue.toDouble(),
onChanged: (double v) async {
final seconds = backupDelayToSeconds(v.toInt());
await ref.read(metadataProvider).write(MetadataKey.backupTriggerDelay, seconds);
await ref.read(settingsProvider).write(SettingsKey.backupTriggerDelay, seconds);
},
onChangeEnd: (double v) async {
final seconds = backupDelayToSeconds(v.toInt());
await ref.read(metadataProvider).write(MetadataKey.backupTriggerDelay, seconds);
await ref.read(settingsProvider).write(SettingsKey.backupTriggerDelay, seconds);
},
max: 3.0,
min: 0.0,
@@ -1,11 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/metadata_key.dart';
import 'package:immich_mobile/domain/models/settings_key.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/widgets/settings/networking_settings/endpoint_input.dart';
class ExternalNetworkPreference extends HookConsumerWidget {
@@ -26,7 +26,7 @@ class ExternalNetworkPreference extends HookConsumerWidget {
.map((e) => e.url)
.toList();
ref.read(metadataProvider).write(MetadataKey.networkExternalEndpointList, urls);
ref.read(settingsProvider).write(SettingsKey.networkExternalEndpointList, urls);
}
updateValidationStatus(String url, int index, AuxCheckStatus status) {
@@ -5,7 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/network.provider.dart';
import 'package:immich_mobile/utils/url_helper.dart';
import 'package:immich_mobile/widgets/settings/networking_settings/external_network_preference.dart';
@@ -21,7 +21,7 @@ class NetworkingSettings extends HookConsumerWidget {
final currentEndpoint = getServerUrl();
final featureEnabled = useState(ref.read(appConfigProvider).network.autoEndpointSwitching);
useValueChanged<bool, void>(featureEnabled.value, (_, __) {
ref.read(metadataProvider).write(.networkAutoEndpointSwitching, featureEnabled.value);
ref.read(settingsProvider).write(.networkAutoEndpointSwitching, featureEnabled.value);
});
Future<void> checkWifiReadPermission() async {
@@ -4,7 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/colors.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/theme_extensions.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/theme.provider.dart';
import 'package:immich_mobile/theme/color_scheme.dart';
import 'package:immich_mobile/theme/dynamic_theme.dart';
@@ -26,16 +26,16 @@ class PrimaryColorSetting extends HookConsumerWidget {
}
onUseSystemColorChange(bool newValue) {
ref.read(metadataProvider).write(.themeDynamic, newValue);
ref.read(settingsProvider).write(.themeDynamic, newValue);
popBottomSheet();
}
onPrimaryColorChange(ImmichColorPreset colorPreset) {
ref.read(metadataProvider).write(.themePrimaryColor, colorPreset);
ref.read(settingsProvider).write(.themePrimaryColor, colorPreset);
//turn off system color setting
if (themeConfig.dynamicTheme) {
ref.read(metadataProvider).write(.themeDynamic, false);
ref.read(settingsProvider).write(.themeDynamic, false);
}
popBottomSheet();
}
@@ -3,7 +3,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/widgets/settings/preference_settings/primary_color_setting.dart';
import 'package:immich_mobile/widgets/settings/setting_group_title.dart';
import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart';
@@ -22,7 +22,7 @@ class ThemeSetting extends HookConsumerWidget {
void onThemeChange(bool isDark) {
currentTheme.value = isDark ? ThemeMode.dark : ThemeMode.light;
ref.read(metadataProvider).write(.themeMode, currentTheme.value);
ref.read(settingsProvider).write(.themeMode, currentTheme.value);
}
void onSystemThemeChange(bool isSystem) {
@@ -39,11 +39,11 @@ class ThemeSetting extends HookConsumerWidget {
currentTheme.value = ThemeMode.dark;
}
}
ref.read(metadataProvider).write(.themeMode, currentTheme.value);
ref.read(settingsProvider).write(.themeMode, currentTheme.value);
}
void onSurfaceColorSettingChange(bool useColorfulInterface) {
ref.read(metadataProvider).write(.themeColorfulInterface, useColorfulInterface);
ref.read(settingsProvider).write(.themeColorfulInterface, useColorfulInterface);
colorfulInterface.value = useColorfulInterface;
}
+8
View File
@@ -103,12 +103,16 @@ Class | Method | HTTP request | Description
*AssetsApi* | [**deleteBulkAssetMetadata**](doc//AssetsApi.md#deletebulkassetmetadata) | **DELETE** /assets/metadata | Delete asset metadata
*AssetsApi* | [**downloadAsset**](doc//AssetsApi.md#downloadasset) | **GET** /assets/{id}/original | Download original asset
*AssetsApi* | [**editAsset**](doc//AssetsApi.md#editasset) | **PUT** /assets/{id}/edits | Apply edits to an existing asset
*AssetsApi* | [**endSession**](doc//AssetsApi.md#endsession) | **DELETE** /assets/{id}/video/stream/{sessionId} | End HLS streaming session
*AssetsApi* | [**getAssetEdits**](doc//AssetsApi.md#getassetedits) | **GET** /assets/{id}/edits | Retrieve edits for an existing asset
*AssetsApi* | [**getAssetInfo**](doc//AssetsApi.md#getassetinfo) | **GET** /assets/{id} | Retrieve an asset
*AssetsApi* | [**getAssetMetadata**](doc//AssetsApi.md#getassetmetadata) | **GET** /assets/{id}/metadata | Get asset metadata
*AssetsApi* | [**getAssetMetadataByKey**](doc//AssetsApi.md#getassetmetadatabykey) | **GET** /assets/{id}/metadata/{key} | Retrieve asset metadata by key
*AssetsApi* | [**getAssetOcr**](doc//AssetsApi.md#getassetocr) | **GET** /assets/{id}/ocr | Retrieve asset OCR data
*AssetsApi* | [**getAssetStatistics**](doc//AssetsApi.md#getassetstatistics) | **GET** /assets/statistics | Get asset statistics
*AssetsApi* | [**getMainPlaylist**](doc//AssetsApi.md#getmainplaylist) | **GET** /assets/{id}/video/stream/main.m3u8 | Get HLS main playlist
*AssetsApi* | [**getMediaPlaylist**](doc//AssetsApi.md#getmediaplaylist) | **GET** /assets/{id}/video/stream/{sessionId}/{variantIndex}/playlist.m3u8 | Get HLS media playlist
*AssetsApi* | [**getSegment**](doc//AssetsApi.md#getsegment) | **GET** /assets/{id}/video/stream/{sessionId}/{variantIndex}/{filename} | Get HLS segment or init file
*AssetsApi* | [**playAssetVideo**](doc//AssetsApi.md#playassetvideo) | **GET** /assets/{id}/video/playback | Play asset video
*AssetsApi* | [**removeAssetEdits**](doc//AssetsApi.md#removeassetedits) | **DELETE** /assets/{id}/edits | Remove edits from an existing asset
*AssetsApi* | [**runAssetJobs**](doc//AssetsApi.md#runassetjobs) | **POST** /assets/jobs | Run an asset job
@@ -513,6 +517,9 @@ Class | Method | HTTP request | Description
- [RatingsUpdate](doc//RatingsUpdate.md)
- [ReactionLevel](doc//ReactionLevel.md)
- [ReactionType](doc//ReactionType.md)
- [ReleaseChannel](doc//ReleaseChannel.md)
- [ReleaseEventV1](doc//ReleaseEventV1.md)
- [ReleaseType](doc//ReleaseType.md)
- [ReverseGeocodingStateResponseDto](doc//ReverseGeocodingStateResponseDto.md)
- [RotateParameters](doc//RotateParameters.md)
- [SearchAlbumResponseDto](doc//SearchAlbumResponseDto.md)
@@ -597,6 +604,7 @@ Class | Method | HTTP request | Description
- [SystemConfigBackupsDto](doc//SystemConfigBackupsDto.md)
- [SystemConfigDto](doc//SystemConfigDto.md)
- [SystemConfigFFmpegDto](doc//SystemConfigFFmpegDto.md)
- [SystemConfigFFmpegRealtimeDto](doc//SystemConfigFFmpegRealtimeDto.md)
- [SystemConfigFacesDto](doc//SystemConfigFacesDto.md)
- [SystemConfigGeneratedFullsizeImageDto](doc//SystemConfigGeneratedFullsizeImageDto.md)
- [SystemConfigGeneratedImageDto](doc//SystemConfigGeneratedImageDto.md)
+4
View File
@@ -258,6 +258,9 @@ part 'model/ratings_response.dart';
part 'model/ratings_update.dart';
part 'model/reaction_level.dart';
part 'model/reaction_type.dart';
part 'model/release_channel.dart';
part 'model/release_event_v1.dart';
part 'model/release_type.dart';
part 'model/reverse_geocoding_state_response_dto.dart';
part 'model/rotate_parameters.dart';
part 'model/search_album_response_dto.dart';
@@ -342,6 +345,7 @@ part 'model/sync_user_v1.dart';
part 'model/system_config_backups_dto.dart';
part 'model/system_config_dto.dart';
part 'model/system_config_f_fmpeg_dto.dart';
part 'model/system_config_f_fmpeg_realtime_dto.dart';
part 'model/system_config_faces_dto.dart';
part 'model/system_config_generated_fullsize_image_dto.dart';
part 'model/system_config_generated_image_dto.dart';
+16 -12
View File
@@ -25,7 +25,7 @@ class ActivitiesApi {
/// Parameters:
///
/// * [ActivityCreateDto] activityCreateDto (required):
Future<Response> createActivityWithHttpInfo(ActivityCreateDto activityCreateDto,) async {
Future<Response> createActivityWithHttpInfo(ActivityCreateDto activityCreateDto, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/activities';
@@ -47,6 +47,7 @@ class ActivitiesApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -57,8 +58,8 @@ class ActivitiesApi {
/// Parameters:
///
/// * [ActivityCreateDto] activityCreateDto (required):
Future<ActivityResponseDto?> createActivity(ActivityCreateDto activityCreateDto,) async {
final response = await createActivityWithHttpInfo(activityCreateDto,);
Future<ActivityResponseDto?> createActivity(ActivityCreateDto activityCreateDto, { Future<void>? abortTrigger, }) async {
final response = await createActivityWithHttpInfo(activityCreateDto, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -81,7 +82,7 @@ class ActivitiesApi {
/// Parameters:
///
/// * [String] id (required):
Future<Response> deleteActivityWithHttpInfo(String id,) async {
Future<Response> deleteActivityWithHttpInfo(String id, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/activities/{id}'
.replaceAll('{id}', id);
@@ -104,6 +105,7 @@ class ActivitiesApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -114,8 +116,8 @@ class ActivitiesApi {
/// Parameters:
///
/// * [String] id (required):
Future<void> deleteActivity(String id,) async {
final response = await deleteActivityWithHttpInfo(id,);
Future<void> deleteActivity(String id, { Future<void>? abortTrigger, }) async {
final response = await deleteActivityWithHttpInfo(id, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -141,7 +143,7 @@ class ActivitiesApi {
///
/// * [String] userId:
/// Filter by user ID
Future<Response> getActivitiesWithHttpInfo(String albumId, { String? assetId, ReactionLevel? level, ReactionType? type, String? userId, }) async {
Future<Response> getActivitiesWithHttpInfo(String albumId, { String? assetId, ReactionLevel? level, ReactionType? type, String? userId, Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/activities';
@@ -177,6 +179,7 @@ class ActivitiesApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -198,8 +201,8 @@ class ActivitiesApi {
///
/// * [String] userId:
/// Filter by user ID
Future<List<ActivityResponseDto>?> getActivities(String albumId, { String? assetId, ReactionLevel? level, ReactionType? type, String? userId, }) async {
final response = await getActivitiesWithHttpInfo(albumId, assetId: assetId, level: level, type: type, userId: userId, );
Future<List<ActivityResponseDto>?> getActivities(String albumId, { String? assetId, ReactionLevel? level, ReactionType? type, String? userId, Future<void>? abortTrigger, }) async {
final response = await getActivitiesWithHttpInfo(albumId, assetId: assetId, level: level, type: type, userId: userId, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -229,7 +232,7 @@ class ActivitiesApi {
///
/// * [String] assetId:
/// Asset ID (if activity is for an asset)
Future<Response> getActivityStatisticsWithHttpInfo(String albumId, { String? assetId, }) async {
Future<Response> getActivityStatisticsWithHttpInfo(String albumId, { String? assetId, Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/activities/statistics';
@@ -256,6 +259,7 @@ class ActivitiesApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -270,8 +274,8 @@ class ActivitiesApi {
///
/// * [String] assetId:
/// Asset ID (if activity is for an asset)
Future<ActivityStatisticsResponseDto?> getActivityStatistics(String albumId, { String? assetId, }) async {
final response = await getActivityStatisticsWithHttpInfo(albumId, assetId: assetId, );
Future<ActivityStatisticsResponseDto?> getActivityStatistics(String albumId, { String? assetId, Future<void>? abortTrigger, }) async {
final response = await getActivityStatisticsWithHttpInfo(albumId, assetId: assetId, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
+70 -39
View File
@@ -27,7 +27,7 @@ class AlbumsApi {
/// * [String] id (required):
///
/// * [BulkIdsDto] bulkIdsDto (required):
Future<Response> addAssetsToAlbumWithHttpInfo(String id, BulkIdsDto bulkIdsDto,) async {
Future<Response> addAssetsToAlbumWithHttpInfo(String id, BulkIdsDto bulkIdsDto, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/albums/{id}/assets'
.replaceAll('{id}', id);
@@ -50,6 +50,7 @@ class AlbumsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -62,8 +63,8 @@ class AlbumsApi {
/// * [String] id (required):
///
/// * [BulkIdsDto] bulkIdsDto (required):
Future<List<BulkIdResponseDto>?> addAssetsToAlbum(String id, BulkIdsDto bulkIdsDto,) async {
final response = await addAssetsToAlbumWithHttpInfo(id, bulkIdsDto,);
Future<List<BulkIdResponseDto>?> addAssetsToAlbum(String id, BulkIdsDto bulkIdsDto, { Future<void>? abortTrigger, }) async {
final response = await addAssetsToAlbumWithHttpInfo(id, bulkIdsDto, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -89,7 +90,7 @@ class AlbumsApi {
/// Parameters:
///
/// * [AlbumsAddAssetsDto] albumsAddAssetsDto (required):
Future<Response> addAssetsToAlbumsWithHttpInfo(AlbumsAddAssetsDto albumsAddAssetsDto,) async {
Future<Response> addAssetsToAlbumsWithHttpInfo(AlbumsAddAssetsDto albumsAddAssetsDto, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/albums/assets';
@@ -111,6 +112,7 @@ class AlbumsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -121,8 +123,8 @@ class AlbumsApi {
/// Parameters:
///
/// * [AlbumsAddAssetsDto] albumsAddAssetsDto (required):
Future<AlbumsAddAssetsResponseDto?> addAssetsToAlbums(AlbumsAddAssetsDto albumsAddAssetsDto,) async {
final response = await addAssetsToAlbumsWithHttpInfo(albumsAddAssetsDto,);
Future<AlbumsAddAssetsResponseDto?> addAssetsToAlbums(AlbumsAddAssetsDto albumsAddAssetsDto, { Future<void>? abortTrigger, }) async {
final response = await addAssetsToAlbumsWithHttpInfo(albumsAddAssetsDto, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -147,7 +149,7 @@ class AlbumsApi {
/// * [String] id (required):
///
/// * [AddUsersDto] addUsersDto (required):
Future<Response> addUsersToAlbumWithHttpInfo(String id, AddUsersDto addUsersDto,) async {
Future<Response> addUsersToAlbumWithHttpInfo(String id, AddUsersDto addUsersDto, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/albums/{id}/users'
.replaceAll('{id}', id);
@@ -170,6 +172,7 @@ class AlbumsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -182,8 +185,8 @@ class AlbumsApi {
/// * [String] id (required):
///
/// * [AddUsersDto] addUsersDto (required):
Future<AlbumResponseDto?> addUsersToAlbum(String id, AddUsersDto addUsersDto,) async {
final response = await addUsersToAlbumWithHttpInfo(id, addUsersDto,);
Future<AlbumResponseDto?> addUsersToAlbum(String id, AddUsersDto addUsersDto, { Future<void>? abortTrigger, }) async {
final response = await addUsersToAlbumWithHttpInfo(id, addUsersDto, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -206,7 +209,7 @@ class AlbumsApi {
/// Parameters:
///
/// * [CreateAlbumDto] createAlbumDto (required):
Future<Response> createAlbumWithHttpInfo(CreateAlbumDto createAlbumDto,) async {
Future<Response> createAlbumWithHttpInfo(CreateAlbumDto createAlbumDto, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/albums';
@@ -228,6 +231,7 @@ class AlbumsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -238,8 +242,8 @@ class AlbumsApi {
/// Parameters:
///
/// * [CreateAlbumDto] createAlbumDto (required):
Future<AlbumResponseDto?> createAlbum(CreateAlbumDto createAlbumDto,) async {
final response = await createAlbumWithHttpInfo(createAlbumDto,);
Future<AlbumResponseDto?> createAlbum(CreateAlbumDto createAlbumDto, { Future<void>? abortTrigger, }) async {
final response = await createAlbumWithHttpInfo(createAlbumDto, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -262,7 +266,7 @@ class AlbumsApi {
/// Parameters:
///
/// * [String] id (required):
Future<Response> deleteAlbumWithHttpInfo(String id,) async {
Future<Response> deleteAlbumWithHttpInfo(String id, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/albums/{id}'
.replaceAll('{id}', id);
@@ -285,6 +289,7 @@ class AlbumsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -295,8 +300,8 @@ class AlbumsApi {
/// Parameters:
///
/// * [String] id (required):
Future<void> deleteAlbum(String id,) async {
final response = await deleteAlbumWithHttpInfo(id,);
Future<void> deleteAlbum(String id, { Future<void>? abortTrigger, }) async {
final response = await deleteAlbumWithHttpInfo(id, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -315,7 +320,7 @@ class AlbumsApi {
/// * [String] key:
///
/// * [String] slug:
Future<Response> getAlbumInfoWithHttpInfo(String id, { String? key, String? slug, }) async {
Future<Response> getAlbumInfoWithHttpInfo(String id, { String? key, String? slug, Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/albums/{id}'
.replaceAll('{id}', id);
@@ -345,6 +350,7 @@ class AlbumsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -359,8 +365,8 @@ class AlbumsApi {
/// * [String] key:
///
/// * [String] slug:
Future<AlbumResponseDto?> getAlbumInfo(String id, { String? key, String? slug, }) async {
final response = await getAlbumInfoWithHttpInfo(id, key: key, slug: slug, );
Future<AlbumResponseDto?> getAlbumInfo(String id, { String? key, String? slug, Future<void>? abortTrigger, }) async {
final response = await getAlbumInfoWithHttpInfo(id, key: key, slug: slug, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -387,7 +393,7 @@ class AlbumsApi {
/// * [String] key:
///
/// * [String] slug:
Future<Response> getAlbumMapMarkersWithHttpInfo(String id, { String? key, String? slug, }) async {
Future<Response> getAlbumMapMarkersWithHttpInfo(String id, { String? key, String? slug, Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/albums/{id}/map-markers'
.replaceAll('{id}', id);
@@ -417,6 +423,7 @@ class AlbumsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -431,8 +438,8 @@ class AlbumsApi {
/// * [String] key:
///
/// * [String] slug:
Future<List<MapMarkerResponseDto>?> getAlbumMapMarkers(String id, { String? key, String? slug, }) async {
final response = await getAlbumMapMarkersWithHttpInfo(id, key: key, slug: slug, );
Future<List<MapMarkerResponseDto>?> getAlbumMapMarkers(String id, { String? key, String? slug, Future<void>? abortTrigger, }) async {
final response = await getAlbumMapMarkersWithHttpInfo(id, key: key, slug: slug, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -454,7 +461,7 @@ class AlbumsApi {
/// Returns statistics about the albums available to the authenticated user.
///
/// Note: This method returns the HTTP [Response].
Future<Response> getAlbumStatisticsWithHttpInfo() async {
Future<Response> getAlbumStatisticsWithHttpInfo({ Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/albums/statistics';
@@ -476,14 +483,15 @@ class AlbumsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
/// Retrieve album statistics
///
/// Returns statistics about the albums available to the authenticated user.
Future<AlbumStatisticsResponseDto?> getAlbumStatistics() async {
final response = await getAlbumStatisticsWithHttpInfo();
Future<AlbumStatisticsResponseDto?> getAlbumStatistics({ Future<void>? abortTrigger, }) async {
final response = await getAlbumStatisticsWithHttpInfo(abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -508,12 +516,18 @@ class AlbumsApi {
/// * [String] assetId:
/// Filter albums containing this asset ID (ignores other parameters)
///
/// * [String] id:
/// Album ID
///
/// * [bool] isOwned:
/// Filter by ownership: true = only owned, false = only shared-with-me, undefined = no filter
///
/// * [bool] isShared:
/// Filter by shared status: true = only shared, false = not shared, undefined = no filter
Future<Response> getAllAlbumsWithHttpInfo({ String? assetId, bool? isOwned, bool? isShared, }) async {
///
/// * [String] name:
/// Album name (exact match)
Future<Response> getAllAlbumsWithHttpInfo({ String? assetId, String? id, bool? isOwned, bool? isShared, String? name, Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/albums';
@@ -527,12 +541,18 @@ class AlbumsApi {
if (assetId != null) {
queryParams.addAll(_queryParams('', 'assetId', assetId));
}
if (id != null) {
queryParams.addAll(_queryParams('', 'id', id));
}
if (isOwned != null) {
queryParams.addAll(_queryParams('', 'isOwned', isOwned));
}
if (isShared != null) {
queryParams.addAll(_queryParams('', 'isShared', isShared));
}
if (name != null) {
queryParams.addAll(_queryParams('', 'name', name));
}
const contentTypes = <String>[];
@@ -545,6 +565,7 @@ class AlbumsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -557,13 +578,19 @@ class AlbumsApi {
/// * [String] assetId:
/// Filter albums containing this asset ID (ignores other parameters)
///
/// * [String] id:
/// Album ID
///
/// * [bool] isOwned:
/// Filter by ownership: true = only owned, false = only shared-with-me, undefined = no filter
///
/// * [bool] isShared:
/// Filter by shared status: true = only shared, false = not shared, undefined = no filter
Future<List<AlbumResponseDto>?> getAllAlbums({ String? assetId, bool? isOwned, bool? isShared, }) async {
final response = await getAllAlbumsWithHttpInfo( assetId: assetId, isOwned: isOwned, isShared: isShared, );
///
/// * [String] name:
/// Album name (exact match)
Future<List<AlbumResponseDto>?> getAllAlbums({ String? assetId, String? id, bool? isOwned, bool? isShared, String? name, Future<void>? abortTrigger, }) async {
final response = await getAllAlbumsWithHttpInfo(assetId: assetId, id: id, isOwned: isOwned, isShared: isShared, name: name, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -591,7 +618,7 @@ class AlbumsApi {
/// * [String] id (required):
///
/// * [BulkIdsDto] bulkIdsDto (required):
Future<Response> removeAssetFromAlbumWithHttpInfo(String id, BulkIdsDto bulkIdsDto,) async {
Future<Response> removeAssetFromAlbumWithHttpInfo(String id, BulkIdsDto bulkIdsDto, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/albums/{id}/assets'
.replaceAll('{id}', id);
@@ -614,6 +641,7 @@ class AlbumsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -626,8 +654,8 @@ class AlbumsApi {
/// * [String] id (required):
///
/// * [BulkIdsDto] bulkIdsDto (required):
Future<List<BulkIdResponseDto>?> removeAssetFromAlbum(String id, BulkIdsDto bulkIdsDto,) async {
final response = await removeAssetFromAlbumWithHttpInfo(id, bulkIdsDto,);
Future<List<BulkIdResponseDto>?> removeAssetFromAlbum(String id, BulkIdsDto bulkIdsDto, { Future<void>? abortTrigger, }) async {
final response = await removeAssetFromAlbumWithHttpInfo(id, bulkIdsDto, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -655,7 +683,7 @@ class AlbumsApi {
/// * [String] id (required):
///
/// * [String] userId (required):
Future<Response> removeUserFromAlbumWithHttpInfo(String id, String userId,) async {
Future<Response> removeUserFromAlbumWithHttpInfo(String id, String userId, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/albums/{id}/user/{userId}'
.replaceAll('{id}', id)
@@ -679,6 +707,7 @@ class AlbumsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -691,8 +720,8 @@ class AlbumsApi {
/// * [String] id (required):
///
/// * [String] userId (required):
Future<void> removeUserFromAlbum(String id, String userId,) async {
final response = await removeUserFromAlbumWithHttpInfo(id, userId,);
Future<void> removeUserFromAlbum(String id, String userId, { Future<void>? abortTrigger, }) async {
final response = await removeUserFromAlbumWithHttpInfo(id, userId, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -709,7 +738,7 @@ class AlbumsApi {
/// * [String] id (required):
///
/// * [UpdateAlbumDto] updateAlbumDto (required):
Future<Response> updateAlbumInfoWithHttpInfo(String id, UpdateAlbumDto updateAlbumDto,) async {
Future<Response> updateAlbumInfoWithHttpInfo(String id, UpdateAlbumDto updateAlbumDto, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/albums/{id}'
.replaceAll('{id}', id);
@@ -732,6 +761,7 @@ class AlbumsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -744,8 +774,8 @@ class AlbumsApi {
/// * [String] id (required):
///
/// * [UpdateAlbumDto] updateAlbumDto (required):
Future<AlbumResponseDto?> updateAlbumInfo(String id, UpdateAlbumDto updateAlbumDto,) async {
final response = await updateAlbumInfoWithHttpInfo(id, updateAlbumDto,);
Future<AlbumResponseDto?> updateAlbumInfo(String id, UpdateAlbumDto updateAlbumDto, { Future<void>? abortTrigger, }) async {
final response = await updateAlbumInfoWithHttpInfo(id, updateAlbumDto, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -772,7 +802,7 @@ class AlbumsApi {
/// * [String] userId (required):
///
/// * [UpdateAlbumUserDto] updateAlbumUserDto (required):
Future<Response> updateAlbumUserWithHttpInfo(String id, String userId, UpdateAlbumUserDto updateAlbumUserDto,) async {
Future<Response> updateAlbumUserWithHttpInfo(String id, String userId, UpdateAlbumUserDto updateAlbumUserDto, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/albums/{id}/user/{userId}'
.replaceAll('{id}', id)
@@ -796,6 +826,7 @@ class AlbumsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -810,8 +841,8 @@ class AlbumsApi {
/// * [String] userId (required):
///
/// * [UpdateAlbumUserDto] updateAlbumUserDto (required):
Future<void> updateAlbumUser(String id, String userId, UpdateAlbumUserDto updateAlbumUserDto,) async {
final response = await updateAlbumUserWithHttpInfo(id, userId, updateAlbumUserDto,);
Future<void> updateAlbumUser(String id, String userId, UpdateAlbumUserDto updateAlbumUserDto, { Future<void>? abortTrigger, }) async {
final response = await updateAlbumUserWithHttpInfo(id, userId, updateAlbumUserDto, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
+24 -18
View File
@@ -25,7 +25,7 @@ class APIKeysApi {
/// Parameters:
///
/// * [ApiKeyCreateDto] apiKeyCreateDto (required):
Future<Response> createApiKeyWithHttpInfo(ApiKeyCreateDto apiKeyCreateDto,) async {
Future<Response> createApiKeyWithHttpInfo(ApiKeyCreateDto apiKeyCreateDto, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/api-keys';
@@ -47,6 +47,7 @@ class APIKeysApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -57,8 +58,8 @@ class APIKeysApi {
/// Parameters:
///
/// * [ApiKeyCreateDto] apiKeyCreateDto (required):
Future<ApiKeyCreateResponseDto?> createApiKey(ApiKeyCreateDto apiKeyCreateDto,) async {
final response = await createApiKeyWithHttpInfo(apiKeyCreateDto,);
Future<ApiKeyCreateResponseDto?> createApiKey(ApiKeyCreateDto apiKeyCreateDto, { Future<void>? abortTrigger, }) async {
final response = await createApiKeyWithHttpInfo(apiKeyCreateDto, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -81,7 +82,7 @@ class APIKeysApi {
/// Parameters:
///
/// * [String] id (required):
Future<Response> deleteApiKeyWithHttpInfo(String id,) async {
Future<Response> deleteApiKeyWithHttpInfo(String id, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/api-keys/{id}'
.replaceAll('{id}', id);
@@ -104,6 +105,7 @@ class APIKeysApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -114,8 +116,8 @@ class APIKeysApi {
/// Parameters:
///
/// * [String] id (required):
Future<void> deleteApiKey(String id,) async {
final response = await deleteApiKeyWithHttpInfo(id,);
Future<void> deleteApiKey(String id, { Future<void>? abortTrigger, }) async {
final response = await deleteApiKeyWithHttpInfo(id, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -130,7 +132,7 @@ class APIKeysApi {
/// Parameters:
///
/// * [String] id (required):
Future<Response> getApiKeyWithHttpInfo(String id,) async {
Future<Response> getApiKeyWithHttpInfo(String id, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/api-keys/{id}'
.replaceAll('{id}', id);
@@ -153,6 +155,7 @@ class APIKeysApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -163,8 +166,8 @@ class APIKeysApi {
/// Parameters:
///
/// * [String] id (required):
Future<ApiKeyResponseDto?> getApiKey(String id,) async {
final response = await getApiKeyWithHttpInfo(id,);
Future<ApiKeyResponseDto?> getApiKey(String id, { Future<void>? abortTrigger, }) async {
final response = await getApiKeyWithHttpInfo(id, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -183,7 +186,7 @@ class APIKeysApi {
/// Retrieve all API keys of the current user.
///
/// Note: This method returns the HTTP [Response].
Future<Response> getApiKeysWithHttpInfo() async {
Future<Response> getApiKeysWithHttpInfo({ Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/api-keys';
@@ -205,14 +208,15 @@ class APIKeysApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
/// List all API keys
///
/// Retrieve all API keys of the current user.
Future<List<ApiKeyResponseDto>?> getApiKeys() async {
final response = await getApiKeysWithHttpInfo();
Future<List<ApiKeyResponseDto>?> getApiKeys({ Future<void>? abortTrigger, }) async {
final response = await getApiKeysWithHttpInfo(abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -234,7 +238,7 @@ class APIKeysApi {
/// Retrieve the API key that is used to access this endpoint.
///
/// Note: This method returns the HTTP [Response].
Future<Response> getMyApiKeyWithHttpInfo() async {
Future<Response> getMyApiKeyWithHttpInfo({ Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/api-keys/me';
@@ -256,14 +260,15 @@ class APIKeysApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
/// Retrieve the current API key
///
/// Retrieve the API key that is used to access this endpoint.
Future<ApiKeyResponseDto?> getMyApiKey() async {
final response = await getMyApiKeyWithHttpInfo();
Future<ApiKeyResponseDto?> getMyApiKey({ Future<void>? abortTrigger, }) async {
final response = await getMyApiKeyWithHttpInfo(abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -288,7 +293,7 @@ class APIKeysApi {
/// * [String] id (required):
///
/// * [ApiKeyUpdateDto] apiKeyUpdateDto (required):
Future<Response> updateApiKeyWithHttpInfo(String id, ApiKeyUpdateDto apiKeyUpdateDto,) async {
Future<Response> updateApiKeyWithHttpInfo(String id, ApiKeyUpdateDto apiKeyUpdateDto, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/api-keys/{id}'
.replaceAll('{id}', id);
@@ -311,6 +316,7 @@ class APIKeysApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -323,8 +329,8 @@ class APIKeysApi {
/// * [String] id (required):
///
/// * [ApiKeyUpdateDto] apiKeyUpdateDto (required):
Future<ApiKeyResponseDto?> updateApiKey(String id, ApiKeyUpdateDto apiKeyUpdateDto,) async {
final response = await updateApiKeyWithHttpInfo(id, apiKeyUpdateDto,);
Future<ApiKeyResponseDto?> updateApiKey(String id, ApiKeyUpdateDto apiKeyUpdateDto, { Future<void>? abortTrigger, }) async {
final response = await updateApiKeyWithHttpInfo(id, apiKeyUpdateDto, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
+402 -66
View File
@@ -25,7 +25,7 @@ class AssetsApi {
/// Parameters:
///
/// * [AssetBulkUploadCheckDto] assetBulkUploadCheckDto (required):
Future<Response> checkBulkUploadWithHttpInfo(AssetBulkUploadCheckDto assetBulkUploadCheckDto,) async {
Future<Response> checkBulkUploadWithHttpInfo(AssetBulkUploadCheckDto assetBulkUploadCheckDto, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/bulk-upload-check';
@@ -47,6 +47,7 @@ class AssetsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -57,8 +58,8 @@ class AssetsApi {
/// Parameters:
///
/// * [AssetBulkUploadCheckDto] assetBulkUploadCheckDto (required):
Future<AssetBulkUploadCheckResponseDto?> checkBulkUpload(AssetBulkUploadCheckDto assetBulkUploadCheckDto,) async {
final response = await checkBulkUploadWithHttpInfo(assetBulkUploadCheckDto,);
Future<AssetBulkUploadCheckResponseDto?> checkBulkUpload(AssetBulkUploadCheckDto assetBulkUploadCheckDto, { Future<void>? abortTrigger, }) async {
final response = await checkBulkUploadWithHttpInfo(assetBulkUploadCheckDto, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -81,7 +82,7 @@ class AssetsApi {
/// Parameters:
///
/// * [AssetCopyDto] assetCopyDto (required):
Future<Response> copyAssetWithHttpInfo(AssetCopyDto assetCopyDto,) async {
Future<Response> copyAssetWithHttpInfo(AssetCopyDto assetCopyDto, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/copy';
@@ -103,6 +104,7 @@ class AssetsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -113,8 +115,8 @@ class AssetsApi {
/// Parameters:
///
/// * [AssetCopyDto] assetCopyDto (required):
Future<void> copyAsset(AssetCopyDto assetCopyDto,) async {
final response = await copyAssetWithHttpInfo(assetCopyDto,);
Future<void> copyAsset(AssetCopyDto assetCopyDto, { Future<void>? abortTrigger, }) async {
final response = await copyAssetWithHttpInfo(assetCopyDto, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -133,7 +135,7 @@ class AssetsApi {
///
/// * [String] key (required):
/// Metadata key
Future<Response> deleteAssetMetadataWithHttpInfo(String id, String key,) async {
Future<Response> deleteAssetMetadataWithHttpInfo(String id, String key, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/{id}/metadata/{key}'
.replaceAll('{id}', id)
@@ -157,6 +159,7 @@ class AssetsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -171,8 +174,8 @@ class AssetsApi {
///
/// * [String] key (required):
/// Metadata key
Future<void> deleteAssetMetadata(String id, String key,) async {
final response = await deleteAssetMetadataWithHttpInfo(id, key,);
Future<void> deleteAssetMetadata(String id, String key, { Future<void>? abortTrigger, }) async {
final response = await deleteAssetMetadataWithHttpInfo(id, key, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -187,7 +190,7 @@ class AssetsApi {
/// Parameters:
///
/// * [AssetBulkDeleteDto] assetBulkDeleteDto (required):
Future<Response> deleteAssetsWithHttpInfo(AssetBulkDeleteDto assetBulkDeleteDto,) async {
Future<Response> deleteAssetsWithHttpInfo(AssetBulkDeleteDto assetBulkDeleteDto, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets';
@@ -209,6 +212,7 @@ class AssetsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -219,8 +223,8 @@ class AssetsApi {
/// Parameters:
///
/// * [AssetBulkDeleteDto] assetBulkDeleteDto (required):
Future<void> deleteAssets(AssetBulkDeleteDto assetBulkDeleteDto,) async {
final response = await deleteAssetsWithHttpInfo(assetBulkDeleteDto,);
Future<void> deleteAssets(AssetBulkDeleteDto assetBulkDeleteDto, { Future<void>? abortTrigger, }) async {
final response = await deleteAssetsWithHttpInfo(assetBulkDeleteDto, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -235,7 +239,7 @@ class AssetsApi {
/// Parameters:
///
/// * [AssetMetadataBulkDeleteDto] assetMetadataBulkDeleteDto (required):
Future<Response> deleteBulkAssetMetadataWithHttpInfo(AssetMetadataBulkDeleteDto assetMetadataBulkDeleteDto,) async {
Future<Response> deleteBulkAssetMetadataWithHttpInfo(AssetMetadataBulkDeleteDto assetMetadataBulkDeleteDto, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/metadata';
@@ -257,6 +261,7 @@ class AssetsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -267,8 +272,8 @@ class AssetsApi {
/// Parameters:
///
/// * [AssetMetadataBulkDeleteDto] assetMetadataBulkDeleteDto (required):
Future<void> deleteBulkAssetMetadata(AssetMetadataBulkDeleteDto assetMetadataBulkDeleteDto,) async {
final response = await deleteBulkAssetMetadataWithHttpInfo(assetMetadataBulkDeleteDto,);
Future<void> deleteBulkAssetMetadata(AssetMetadataBulkDeleteDto assetMetadataBulkDeleteDto, { Future<void>? abortTrigger, }) async {
final response = await deleteBulkAssetMetadataWithHttpInfo(assetMetadataBulkDeleteDto, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -290,7 +295,7 @@ class AssetsApi {
/// * [String] key:
///
/// * [String] slug:
Future<Response> downloadAssetWithHttpInfo(String id, { bool? edited, String? key, String? slug, }) async {
Future<Response> downloadAssetWithHttpInfo(String id, { bool? edited, String? key, String? slug, Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/{id}/original'
.replaceAll('{id}', id);
@@ -323,6 +328,7 @@ class AssetsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -340,8 +346,8 @@ class AssetsApi {
/// * [String] key:
///
/// * [String] slug:
Future<MultipartFile?> downloadAsset(String id, { bool? edited, String? key, String? slug, }) async {
final response = await downloadAssetWithHttpInfo(id, edited: edited, key: key, slug: slug, );
Future<MultipartFile?> downloadAsset(String id, { bool? edited, String? key, String? slug, Future<void>? abortTrigger, }) async {
final response = await downloadAssetWithHttpInfo(id, edited: edited, key: key, slug: slug, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -366,7 +372,7 @@ class AssetsApi {
/// * [String] id (required):
///
/// * [AssetEditsCreateDto] assetEditsCreateDto (required):
Future<Response> editAssetWithHttpInfo(String id, AssetEditsCreateDto assetEditsCreateDto,) async {
Future<Response> editAssetWithHttpInfo(String id, AssetEditsCreateDto assetEditsCreateDto, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/{id}/edits'
.replaceAll('{id}', id);
@@ -389,6 +395,7 @@ class AssetsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -401,8 +408,8 @@ class AssetsApi {
/// * [String] id (required):
///
/// * [AssetEditsCreateDto] assetEditsCreateDto (required):
Future<AssetEditsResponseDto?> editAsset(String id, AssetEditsCreateDto assetEditsCreateDto,) async {
final response = await editAssetWithHttpInfo(id, assetEditsCreateDto,);
Future<AssetEditsResponseDto?> editAsset(String id, AssetEditsCreateDto assetEditsCreateDto, { Future<void>? abortTrigger, }) async {
final response = await editAssetWithHttpInfo(id, assetEditsCreateDto, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -416,6 +423,76 @@ class AssetsApi {
return null;
}
/// End HLS streaming session
///
/// Releases server resources for the streaming session.
///
/// Note: This method returns the HTTP [Response].
///
/// Parameters:
///
/// * [String] id (required):
///
/// * [String] sessionId (required):
///
/// * [String] key:
///
/// * [String] slug:
Future<Response> endSessionWithHttpInfo(String id, String sessionId, { String? key, String? slug, Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/{id}/video/stream/{sessionId}'
.replaceAll('{id}', id)
.replaceAll('{sessionId}', sessionId);
// ignore: prefer_final_locals
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
if (key != null) {
queryParams.addAll(_queryParams('', 'key', key));
}
if (slug != null) {
queryParams.addAll(_queryParams('', 'slug', slug));
}
const contentTypes = <String>[];
return apiClient.invokeAPI(
apiPath,
'DELETE',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
/// End HLS streaming session
///
/// Releases server resources for the streaming session.
///
/// Parameters:
///
/// * [String] id (required):
///
/// * [String] sessionId (required):
///
/// * [String] key:
///
/// * [String] slug:
Future<void> endSession(String id, String sessionId, { String? key, String? slug, Future<void>? abortTrigger, }) async {
final response = await endSessionWithHttpInfo(id, sessionId, key: key, slug: slug, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
}
/// Retrieve edits for an existing asset
///
/// Retrieve a series of edit actions (crop, rotate, mirror) associated with the specified asset.
@@ -425,7 +502,7 @@ class AssetsApi {
/// Parameters:
///
/// * [String] id (required):
Future<Response> getAssetEditsWithHttpInfo(String id,) async {
Future<Response> getAssetEditsWithHttpInfo(String id, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/{id}/edits'
.replaceAll('{id}', id);
@@ -448,6 +525,7 @@ class AssetsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -458,8 +536,8 @@ class AssetsApi {
/// Parameters:
///
/// * [String] id (required):
Future<AssetEditsResponseDto?> getAssetEdits(String id,) async {
final response = await getAssetEditsWithHttpInfo(id,);
Future<AssetEditsResponseDto?> getAssetEdits(String id, { Future<void>? abortTrigger, }) async {
final response = await getAssetEditsWithHttpInfo(id, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -486,7 +564,7 @@ class AssetsApi {
/// * [String] key:
///
/// * [String] slug:
Future<Response> getAssetInfoWithHttpInfo(String id, { String? key, String? slug, }) async {
Future<Response> getAssetInfoWithHttpInfo(String id, { String? key, String? slug, Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/{id}'
.replaceAll('{id}', id);
@@ -516,6 +594,7 @@ class AssetsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -530,8 +609,8 @@ class AssetsApi {
/// * [String] key:
///
/// * [String] slug:
Future<AssetResponseDto?> getAssetInfo(String id, { String? key, String? slug, }) async {
final response = await getAssetInfoWithHttpInfo(id, key: key, slug: slug, );
Future<AssetResponseDto?> getAssetInfo(String id, { String? key, String? slug, Future<void>? abortTrigger, }) async {
final response = await getAssetInfoWithHttpInfo(id, key: key, slug: slug, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -554,7 +633,7 @@ class AssetsApi {
/// Parameters:
///
/// * [String] id (required):
Future<Response> getAssetMetadataWithHttpInfo(String id,) async {
Future<Response> getAssetMetadataWithHttpInfo(String id, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/{id}/metadata'
.replaceAll('{id}', id);
@@ -577,6 +656,7 @@ class AssetsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -587,8 +667,8 @@ class AssetsApi {
/// Parameters:
///
/// * [String] id (required):
Future<List<AssetMetadataResponseDto>?> getAssetMetadata(String id,) async {
final response = await getAssetMetadataWithHttpInfo(id,);
Future<List<AssetMetadataResponseDto>?> getAssetMetadata(String id, { Future<void>? abortTrigger, }) async {
final response = await getAssetMetadataWithHttpInfo(id, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -618,7 +698,7 @@ class AssetsApi {
///
/// * [String] key (required):
/// Metadata key
Future<Response> getAssetMetadataByKeyWithHttpInfo(String id, String key,) async {
Future<Response> getAssetMetadataByKeyWithHttpInfo(String id, String key, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/{id}/metadata/{key}'
.replaceAll('{id}', id)
@@ -642,6 +722,7 @@ class AssetsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -656,8 +737,8 @@ class AssetsApi {
///
/// * [String] key (required):
/// Metadata key
Future<AssetMetadataResponseDto?> getAssetMetadataByKey(String id, String key,) async {
final response = await getAssetMetadataByKeyWithHttpInfo(id, key,);
Future<AssetMetadataResponseDto?> getAssetMetadataByKey(String id, String key, { Future<void>? abortTrigger, }) async {
final response = await getAssetMetadataByKeyWithHttpInfo(id, key, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -680,7 +761,7 @@ class AssetsApi {
/// Parameters:
///
/// * [String] id (required):
Future<Response> getAssetOcrWithHttpInfo(String id,) async {
Future<Response> getAssetOcrWithHttpInfo(String id, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/{id}/ocr'
.replaceAll('{id}', id);
@@ -703,6 +784,7 @@ class AssetsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -713,8 +795,8 @@ class AssetsApi {
/// Parameters:
///
/// * [String] id (required):
Future<List<AssetOcrResponseDto>?> getAssetOcr(String id,) async {
final response = await getAssetOcrWithHttpInfo(id,);
Future<List<AssetOcrResponseDto>?> getAssetOcr(String id, { Future<void>? abortTrigger, }) async {
final response = await getAssetOcrWithHttpInfo(id, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -746,7 +828,7 @@ class AssetsApi {
/// Filter by trash status
///
/// * [AssetVisibility] visibility:
Future<Response> getAssetStatisticsWithHttpInfo({ bool? isFavorite, bool? isTrashed, AssetVisibility? visibility, }) async {
Future<Response> getAssetStatisticsWithHttpInfo({ bool? isFavorite, bool? isTrashed, AssetVisibility? visibility, Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/statistics';
@@ -778,6 +860,7 @@ class AssetsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -794,8 +877,8 @@ class AssetsApi {
/// Filter by trash status
///
/// * [AssetVisibility] visibility:
Future<AssetStatsResponseDto?> getAssetStatistics({ bool? isFavorite, bool? isTrashed, AssetVisibility? visibility, }) async {
final response = await getAssetStatisticsWithHttpInfo( isFavorite: isFavorite, isTrashed: isTrashed, visibility: visibility, );
Future<AssetStatsResponseDto?> getAssetStatistics({ bool? isFavorite, bool? isTrashed, AssetVisibility? visibility, Future<void>? abortTrigger, }) async {
final response = await getAssetStatisticsWithHttpInfo(isFavorite: isFavorite, isTrashed: isTrashed, visibility: visibility, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -809,6 +892,250 @@ class AssetsApi {
return null;
}
/// Get HLS main playlist
///
/// Returns an HLS main playlist with all available variants for the asset.
///
/// Note: This method returns the HTTP [Response].
///
/// Parameters:
///
/// * [String] id (required):
///
/// * [String] key:
///
/// * [String] slug:
Future<Response> getMainPlaylistWithHttpInfo(String id, { String? key, String? slug, Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/{id}/video/stream/main.m3u8'
.replaceAll('{id}', id);
// ignore: prefer_final_locals
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
if (key != null) {
queryParams.addAll(_queryParams('', 'key', key));
}
if (slug != null) {
queryParams.addAll(_queryParams('', 'slug', slug));
}
const contentTypes = <String>[];
return apiClient.invokeAPI(
apiPath,
'GET',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
/// Get HLS main playlist
///
/// Returns an HLS main playlist with all available variants for the asset.
///
/// Parameters:
///
/// * [String] id (required):
///
/// * [String] key:
///
/// * [String] slug:
Future<String?> getMainPlaylist(String id, { String? key, String? slug, Future<void>? abortTrigger, }) async {
final response = await getMainPlaylistWithHttpInfo(id, key: key, slug: slug, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'String',) as String;
}
return null;
}
/// Get HLS media playlist
///
/// Returns an HLS media playlist for one variant of the streaming session.
///
/// Note: This method returns the HTTP [Response].
///
/// Parameters:
///
/// * [String] id (required):
///
/// * [String] sessionId (required):
///
/// * [int] variantIndex (required):
///
/// * [String] key:
///
/// * [String] slug:
Future<Response> getMediaPlaylistWithHttpInfo(String id, String sessionId, int variantIndex, { String? key, String? slug, Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/{id}/video/stream/{sessionId}/{variantIndex}/playlist.m3u8'
.replaceAll('{id}', id)
.replaceAll('{sessionId}', sessionId)
.replaceAll('{variantIndex}', variantIndex.toString());
// ignore: prefer_final_locals
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
if (key != null) {
queryParams.addAll(_queryParams('', 'key', key));
}
if (slug != null) {
queryParams.addAll(_queryParams('', 'slug', slug));
}
const contentTypes = <String>[];
return apiClient.invokeAPI(
apiPath,
'GET',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
/// Get HLS media playlist
///
/// Returns an HLS media playlist for one variant of the streaming session.
///
/// Parameters:
///
/// * [String] id (required):
///
/// * [String] sessionId (required):
///
/// * [int] variantIndex (required):
///
/// * [String] key:
///
/// * [String] slug:
Future<String?> getMediaPlaylist(String id, String sessionId, int variantIndex, { String? key, String? slug, Future<void>? abortTrigger, }) async {
final response = await getMediaPlaylistWithHttpInfo(id, sessionId, variantIndex, key: key, slug: slug, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'String',) as String;
}
return null;
}
/// Get HLS segment or init file
///
/// Streams an HLS init segment (init.mp4) or media segment (seg_N.m4s).
///
/// Note: This method returns the HTTP [Response].
///
/// Parameters:
///
/// * [String] filename (required):
///
/// * [String] id (required):
///
/// * [String] sessionId (required):
///
/// * [int] variantIndex (required):
///
/// * [String] key:
///
/// * [String] slug:
Future<Response> getSegmentWithHttpInfo(String filename, String id, String sessionId, int variantIndex, { String? key, String? slug, Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/{id}/video/stream/{sessionId}/{variantIndex}/{filename}'
.replaceAll('{filename}', filename)
.replaceAll('{id}', id)
.replaceAll('{sessionId}', sessionId)
.replaceAll('{variantIndex}', variantIndex.toString());
// ignore: prefer_final_locals
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
if (key != null) {
queryParams.addAll(_queryParams('', 'key', key));
}
if (slug != null) {
queryParams.addAll(_queryParams('', 'slug', slug));
}
const contentTypes = <String>[];
return apiClient.invokeAPI(
apiPath,
'GET',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
/// Get HLS segment or init file
///
/// Streams an HLS init segment (init.mp4) or media segment (seg_N.m4s).
///
/// Parameters:
///
/// * [String] filename (required):
///
/// * [String] id (required):
///
/// * [String] sessionId (required):
///
/// * [int] variantIndex (required):
///
/// * [String] key:
///
/// * [String] slug:
Future<MultipartFile?> getSegment(String filename, String id, String sessionId, int variantIndex, { String? key, String? slug, Future<void>? abortTrigger, }) async {
final response = await getSegmentWithHttpInfo(filename, id, sessionId, variantIndex, key: key, slug: slug, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'MultipartFile',) as MultipartFile;
}
return null;
}
/// Play asset video
///
/// Streams the video file for the specified asset. This endpoint also supports byte range requests.
@@ -822,7 +1149,7 @@ class AssetsApi {
/// * [String] key:
///
/// * [String] slug:
Future<Response> playAssetVideoWithHttpInfo(String id, { String? key, String? slug, }) async {
Future<Response> playAssetVideoWithHttpInfo(String id, { String? key, String? slug, Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/{id}/video/playback'
.replaceAll('{id}', id);
@@ -852,6 +1179,7 @@ class AssetsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -866,8 +1194,8 @@ class AssetsApi {
/// * [String] key:
///
/// * [String] slug:
Future<MultipartFile?> playAssetVideo(String id, { String? key, String? slug, }) async {
final response = await playAssetVideoWithHttpInfo(id, key: key, slug: slug, );
Future<MultipartFile?> playAssetVideo(String id, { String? key, String? slug, Future<void>? abortTrigger, }) async {
final response = await playAssetVideoWithHttpInfo(id, key: key, slug: slug, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -890,7 +1218,7 @@ class AssetsApi {
/// Parameters:
///
/// * [String] id (required):
Future<Response> removeAssetEditsWithHttpInfo(String id,) async {
Future<Response> removeAssetEditsWithHttpInfo(String id, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/{id}/edits'
.replaceAll('{id}', id);
@@ -913,6 +1241,7 @@ class AssetsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -923,8 +1252,8 @@ class AssetsApi {
/// Parameters:
///
/// * [String] id (required):
Future<void> removeAssetEdits(String id,) async {
final response = await removeAssetEditsWithHttpInfo(id,);
Future<void> removeAssetEdits(String id, { Future<void>? abortTrigger, }) async {
final response = await removeAssetEditsWithHttpInfo(id, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -939,7 +1268,7 @@ class AssetsApi {
/// Parameters:
///
/// * [AssetJobsDto] assetJobsDto (required):
Future<Response> runAssetJobsWithHttpInfo(AssetJobsDto assetJobsDto,) async {
Future<Response> runAssetJobsWithHttpInfo(AssetJobsDto assetJobsDto, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/jobs';
@@ -961,6 +1290,7 @@ class AssetsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -971,8 +1301,8 @@ class AssetsApi {
/// Parameters:
///
/// * [AssetJobsDto] assetJobsDto (required):
Future<void> runAssetJobs(AssetJobsDto assetJobsDto,) async {
final response = await runAssetJobsWithHttpInfo(assetJobsDto,);
Future<void> runAssetJobs(AssetJobsDto assetJobsDto, { Future<void>? abortTrigger, }) async {
final response = await runAssetJobsWithHttpInfo(assetJobsDto, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -989,7 +1319,7 @@ class AssetsApi {
/// * [String] id (required):
///
/// * [UpdateAssetDto] updateAssetDto (required):
Future<Response> updateAssetWithHttpInfo(String id, UpdateAssetDto updateAssetDto,) async {
Future<Response> updateAssetWithHttpInfo(String id, UpdateAssetDto updateAssetDto, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/{id}'
.replaceAll('{id}', id);
@@ -1012,6 +1342,7 @@ class AssetsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -1024,8 +1355,8 @@ class AssetsApi {
/// * [String] id (required):
///
/// * [UpdateAssetDto] updateAssetDto (required):
Future<AssetResponseDto?> updateAsset(String id, UpdateAssetDto updateAssetDto,) async {
final response = await updateAssetWithHttpInfo(id, updateAssetDto,);
Future<AssetResponseDto?> updateAsset(String id, UpdateAssetDto updateAssetDto, { Future<void>? abortTrigger, }) async {
final response = await updateAssetWithHttpInfo(id, updateAssetDto, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -1050,7 +1381,7 @@ class AssetsApi {
/// * [String] id (required):
///
/// * [AssetMetadataUpsertDto] assetMetadataUpsertDto (required):
Future<Response> updateAssetMetadataWithHttpInfo(String id, AssetMetadataUpsertDto assetMetadataUpsertDto,) async {
Future<Response> updateAssetMetadataWithHttpInfo(String id, AssetMetadataUpsertDto assetMetadataUpsertDto, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/{id}/metadata'
.replaceAll('{id}', id);
@@ -1073,6 +1404,7 @@ class AssetsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -1085,8 +1417,8 @@ class AssetsApi {
/// * [String] id (required):
///
/// * [AssetMetadataUpsertDto] assetMetadataUpsertDto (required):
Future<List<AssetMetadataResponseDto>?> updateAssetMetadata(String id, AssetMetadataUpsertDto assetMetadataUpsertDto,) async {
final response = await updateAssetMetadataWithHttpInfo(id, assetMetadataUpsertDto,);
Future<List<AssetMetadataResponseDto>?> updateAssetMetadata(String id, AssetMetadataUpsertDto assetMetadataUpsertDto, { Future<void>? abortTrigger, }) async {
final response = await updateAssetMetadataWithHttpInfo(id, assetMetadataUpsertDto, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -1112,7 +1444,7 @@ class AssetsApi {
/// Parameters:
///
/// * [AssetBulkUpdateDto] assetBulkUpdateDto (required):
Future<Response> updateAssetsWithHttpInfo(AssetBulkUpdateDto assetBulkUpdateDto,) async {
Future<Response> updateAssetsWithHttpInfo(AssetBulkUpdateDto assetBulkUpdateDto, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets';
@@ -1134,6 +1466,7 @@ class AssetsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -1144,8 +1477,8 @@ class AssetsApi {
/// Parameters:
///
/// * [AssetBulkUpdateDto] assetBulkUpdateDto (required):
Future<void> updateAssets(AssetBulkUpdateDto assetBulkUpdateDto,) async {
final response = await updateAssetsWithHttpInfo(assetBulkUpdateDto,);
Future<void> updateAssets(AssetBulkUpdateDto assetBulkUpdateDto, { Future<void>? abortTrigger, }) async {
final response = await updateAssetsWithHttpInfo(assetBulkUpdateDto, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -1160,7 +1493,7 @@ class AssetsApi {
/// Parameters:
///
/// * [AssetMetadataBulkUpsertDto] assetMetadataBulkUpsertDto (required):
Future<Response> updateBulkAssetMetadataWithHttpInfo(AssetMetadataBulkUpsertDto assetMetadataBulkUpsertDto,) async {
Future<Response> updateBulkAssetMetadataWithHttpInfo(AssetMetadataBulkUpsertDto assetMetadataBulkUpsertDto, { Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/metadata';
@@ -1182,6 +1515,7 @@ class AssetsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -1192,8 +1526,8 @@ class AssetsApi {
/// Parameters:
///
/// * [AssetMetadataBulkUpsertDto] assetMetadataBulkUpsertDto (required):
Future<List<AssetMetadataBulkResponseDto>?> updateBulkAssetMetadata(AssetMetadataBulkUpsertDto assetMetadataBulkUpsertDto,) async {
final response = await updateBulkAssetMetadataWithHttpInfo(assetMetadataBulkUpsertDto,);
Future<List<AssetMetadataBulkResponseDto>?> updateBulkAssetMetadata(AssetMetadataBulkUpsertDto assetMetadataBulkUpsertDto, { Future<void>? abortTrigger, }) async {
final response = await updateBulkAssetMetadataWithHttpInfo(assetMetadataBulkUpsertDto, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -1253,7 +1587,7 @@ class AssetsApi {
/// Sidecar file data
///
/// * [AssetVisibility] visibility:
Future<Response> uploadAssetWithHttpInfo(MultipartFile assetData, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, int? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, List<AssetMetadataUpsertItemDto>? metadata, MultipartFile? sidecarData, AssetVisibility? visibility, }) async {
Future<Response> uploadAssetWithHttpInfo(MultipartFile assetData, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, int? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, List<AssetMetadataUpsertItemDto>? metadata, MultipartFile? sidecarData, AssetVisibility? visibility, Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets';
@@ -1333,6 +1667,7 @@ class AssetsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -1377,8 +1712,8 @@ class AssetsApi {
/// Sidecar file data
///
/// * [AssetVisibility] visibility:
Future<AssetMediaResponseDto?> uploadAsset(MultipartFile assetData, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, int? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, List<AssetMetadataUpsertItemDto>? metadata, MultipartFile? sidecarData, AssetVisibility? visibility, }) async {
final response = await uploadAssetWithHttpInfo(assetData, fileCreatedAt, fileModifiedAt, key: key, slug: slug, xImmichChecksum: xImmichChecksum, duration: duration, filename: filename, isFavorite: isFavorite, livePhotoVideoId: livePhotoVideoId, metadata: metadata, sidecarData: sidecarData, visibility: visibility, );
Future<AssetMediaResponseDto?> uploadAsset(MultipartFile assetData, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, int? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, List<AssetMetadataUpsertItemDto>? metadata, MultipartFile? sidecarData, AssetVisibility? visibility, Future<void>? abortTrigger, }) async {
final response = await uploadAssetWithHttpInfo(assetData, fileCreatedAt, fileModifiedAt, key: key, slug: slug, xImmichChecksum: xImmichChecksum, duration: duration, filename: filename, isFavorite: isFavorite, livePhotoVideoId: livePhotoVideoId, metadata: metadata, sidecarData: sidecarData, visibility: visibility, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -1410,7 +1745,7 @@ class AssetsApi {
/// * [AssetMediaSize] size:
///
/// * [String] slug:
Future<Response> viewAssetWithHttpInfo(String id, { bool? edited, String? key, AssetMediaSize? size, String? slug, }) async {
Future<Response> viewAssetWithHttpInfo(String id, { bool? edited, String? key, AssetMediaSize? size, String? slug, Future<void>? abortTrigger, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/{id}/thumbnail'
.replaceAll('{id}', id);
@@ -1446,6 +1781,7 @@ class AssetsApi {
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
abortTrigger: abortTrigger,
);
}
@@ -1465,8 +1801,8 @@ class AssetsApi {
/// * [AssetMediaSize] size:
///
/// * [String] slug:
Future<MultipartFile?> viewAsset(String id, { bool? edited, String? key, AssetMediaSize? size, String? slug, }) async {
final response = await viewAssetWithHttpInfo(id, edited: edited, key: key, size: size, slug: slug, );
Future<MultipartFile?> viewAsset(String id, { bool? edited, String? key, AssetMediaSize? size, String? slug, Future<void>? abortTrigger, }) async {
final response = await viewAssetWithHttpInfo(id, edited: edited, key: key, size: size, slug: slug, abortTrigger: abortTrigger,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}

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