mirror of
https://github.com/immich-app/immich.git
synced 2026-05-26 09:32:29 -04:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7e0dd0f61b | |||
| 815ff677fc | |||
| 915d865ce2 | |||
| c28e5f90b6 | |||
| 4383473ed6 | |||
| 77701dd5a3 |
@@ -91,7 +91,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -159,14 +159,14 @@ jobs:
|
|||||||
|
|
||||||
- name: Comment APK download link on PR
|
- name: Comment APK download link on PR
|
||||||
if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }}
|
if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }}
|
||||||
uses: mshick/add-pr-comment@8e4927817251f1ff60c001f04568532b38e0b4a0 # v3.11.0
|
uses: immich-app/devtools/actions/sticky-comment@0135acd12ad9f3369b94a2aa3c0ae8c835a4e926 # sticky-comment-action-v1.0.0
|
||||||
env:
|
env:
|
||||||
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
APK_URL: ${{ steps.upload-apk.outputs.artifact-url }}
|
APK_URL: ${{ steps.upload-apk.outputs.artifact-url }}
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
id: mobile-android-apk
|
||||||
message-id: 'mobile-android-apk'
|
token: ${{ steps.token.outputs.token }}
|
||||||
message: |
|
body: |
|
||||||
📱 **Android release APK (universal)** — `${{ env.HEAD_SHA }}`
|
📱 **Android release APK (universal)** — `${{ env.HEAD_SHA }}`
|
||||||
|
|
||||||
Download: ${{ env.APK_URL }}
|
Download: ${{ env.APK_URL }}
|
||||||
@@ -216,7 +216,7 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -231,7 +231,7 @@ jobs:
|
|||||||
run: mise //mobile:codegen:pigeon
|
run: mise //mobile:codegen:pigeon
|
||||||
|
|
||||||
- name: Setup Ruby
|
- name: Setup Ruby
|
||||||
uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1.306.0
|
uses: ruby/setup-ruby@6aaa311d81eba98ae12eaffbcb63296ace0efcde # v1.307.0
|
||||||
with:
|
with:
|
||||||
ruby-version: '3.3'
|
ruby-version: '3.3'
|
||||||
bundler-cache: true
|
bundler-cache: true
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Check for breaking API changes
|
- name: Check for breaking API changes
|
||||||
uses: oasdiff/oasdiff-action/breaking@26ccb332c67a45ca649de9faf60552ef1b8260d9 # v0.0.46
|
uses: oasdiff/oasdiff-action/breaking@6147a58e5d1249a12f42fc864ab791d571a30015 # v0.0.47
|
||||||
with:
|
with:
|
||||||
base: https://raw.githubusercontent.com/${{ github.repository }}/main/open-api/immich-openapi-specs.json
|
base: https://raw.githubusercontent.com/${{ github.repository }}/main/open-api/immich-openapi-specs.json
|
||||||
revision: open-api/immich-openapi-specs.json
|
revision: open-api/immich-openapi-specs.json
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3
|
uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@@ -70,7 +70,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3
|
uses: github/codeql-action/autobuild@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||||
@@ -83,6 +83,6 @@ jobs:
|
|||||||
# ./location_of_script_within_repo/buildscript.sh
|
# ./location_of_script_within_repo/buildscript.sh
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3
|
uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
|
||||||
with:
|
with:
|
||||||
category: '/language:${{matrix.language}}'
|
category: '/language:${{matrix.language}}'
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -213,12 +213,11 @@ jobs:
|
|||||||
run: 'mise run //deployment:tf apply'
|
run: 'mise run //deployment:tf apply'
|
||||||
|
|
||||||
- name: Comment
|
- name: Comment
|
||||||
uses: actions-cool/maintain-one-comment@909842216bc8e8658364c572ec52100f4c2cc50a # v3.3.0
|
uses: immich-app/devtools/actions/sticky-comment@0135acd12ad9f3369b94a2aa3c0ae8c835a4e926 # sticky-comment-action-v1.0.0
|
||||||
if: ${{ steps.parameters.outputs.event == 'pr' }}
|
if: ${{ steps.parameters.outputs.event == 'pr' }}
|
||||||
with:
|
with:
|
||||||
|
id: docs-pr-url
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
number: ${{ fromJson(needs.checks.outputs.parameters).pr_number }}
|
number: ${{ fromJson(needs.checks.outputs.parameters).pr_number }}
|
||||||
body: |
|
body: |
|
||||||
📖 Documentation deployed to [${{ steps.docs-output.outputs.subdomain }}](https://${{ steps.docs-output.outputs.subdomain }})
|
📖 Documentation deployed to [${{ steps.docs-output.outputs.subdomain }}](https://${{ steps.docs-output.outputs.subdomain }})
|
||||||
emojis: 'rocket'
|
|
||||||
body-include: '<!-- Docs PR URL -->'
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -44,9 +44,8 @@ jobs:
|
|||||||
run: 'mise run //deployment:tf destroy -- -refresh=false'
|
run: 'mise run //deployment:tf destroy -- -refresh=false'
|
||||||
|
|
||||||
- name: Comment
|
- name: Comment
|
||||||
uses: actions-cool/maintain-one-comment@909842216bc8e8658364c572ec52100f4c2cc50a # v3.3.0
|
uses: immich-app/devtools/actions/sticky-comment@0135acd12ad9f3369b94a2aa3c0ae8c835a4e926 # sticky-comment-action-v1.0.0
|
||||||
with:
|
with:
|
||||||
|
id: docs-pr-url
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
number: ${{ github.event.number }}
|
|
||||||
delete: true
|
delete: true
|
||||||
body-include: '<!-- Docs PR URL -->'
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ jobs:
|
|||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate_token
|
id: generate_token
|
||||||
if: ${{ inputs.skip != true }}
|
if: ${{ inputs.skip != true }}
|
||||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
|
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|||||||
@@ -13,3 +13,4 @@ jobs:
|
|||||||
actions: read
|
actions: read
|
||||||
contents: read
|
contents: read
|
||||||
security-events: write
|
security-events: write
|
||||||
|
secrets: inherit
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ jobs:
|
|||||||
ref: main
|
ref: main
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
|
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||||
with:
|
with:
|
||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|||||||
@@ -19,11 +19,11 @@ jobs:
|
|||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- uses: mshick/add-pr-comment@8e4927817251f1ff60c001f04568532b38e0b4a0 # v3.11.0
|
- uses: immich-app/devtools/actions/sticky-comment@0135acd12ad9f3369b94a2aa3c0ae8c835a4e926 # sticky-comment-action-v1.0.0
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
id: preview-status
|
||||||
message-id: 'preview-status'
|
token: ${{ steps.token.outputs.token }}
|
||||||
message: 'Deploying preview environment to https://pr-${{ github.event.pull_request.number }}.preview.internal.immich.build/'
|
body: 'Deploying preview environment to https://pr-${{ github.event.pull_request.number }}.preview.internal.immich.build/'
|
||||||
|
|
||||||
remove-label:
|
remove-label:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -48,16 +48,16 @@ jobs:
|
|||||||
name: 'preview'
|
name: 'preview'
|
||||||
})
|
})
|
||||||
|
|
||||||
- uses: mshick/add-pr-comment@8e4927817251f1ff60c001f04568532b38e0b4a0 # v3.11.0
|
- uses: immich-app/devtools/actions/sticky-comment@0135acd12ad9f3369b94a2aa3c0ae8c835a4e926 # sticky-comment-action-v1.0.0
|
||||||
if: ${{ github.event.pull_request.head.repo.fork }}
|
if: ${{ github.event.pull_request.head.repo.fork }}
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
id: preview-status
|
||||||
message-id: 'preview-status'
|
token: ${{ steps.token.outputs.token }}
|
||||||
message: 'PRs from forks cannot have preview environments.'
|
body: 'PRs from forks cannot have preview environments.'
|
||||||
|
|
||||||
- uses: mshick/add-pr-comment@8e4927817251f1ff60c001f04568532b38e0b4a0 # v3.11.0
|
- uses: immich-app/devtools/actions/sticky-comment@0135acd12ad9f3369b94a2aa3c0ae8c835a4e926 # sticky-comment-action-v1.0.0
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
id: preview-status
|
||||||
message-id: 'preview-status'
|
token: ${{ steps.token.outputs.token }}
|
||||||
message: 'Preview environment has been removed.'
|
body: 'Preview environment has been removed.'
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
|
|||||||
+13
-13
@@ -76,7 +76,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -182,7 +182,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -220,7 +220,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -248,7 +248,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -298,7 +298,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -331,7 +331,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -550,7 +550,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -587,7 +587,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -618,7 +618,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -669,7 +669,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
@@ -727,7 +727,7 @@ jobs:
|
|||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Setup Mise
|
- name: Setup Mise
|
||||||
uses: immich-app/devtools/actions/use-mise@cf6e190bacde3d7bda59372a786b36ac7d01536a # use-mise-action-v2.0.1
|
uses: immich-app/devtools/actions/use-mise@7b8610a904d57da241e4ddba17fa62b62b15aed4 # use-mise-action-v2.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ After making any changes in the `server/src/schema`, a database migration need t
|
|||||||
1. Run the command
|
1. Run the command
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm run migrations:generate <migration-name>
|
mise //server:migrations generate <migration-name>
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Check if the migration file makes sense.
|
2. Check if the migration file makes sense.
|
||||||
@@ -18,7 +18,7 @@ The server will automatically detect `*.ts` file changes and restart. Part of th
|
|||||||
If you need to undo the most recently applied migration—for example, when developing or testing on schema changes—run:
|
If you need to undo the most recently applied migration—for example, when developing or testing on schema changes—run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm run migrations:revert
|
mise //server:migrations revert
|
||||||
```
|
```
|
||||||
|
|
||||||
This command rolls back the latest migration and brings the database schema back to its previous state.
|
This command rolls back the latest migration and brings the database schema back to its previous state.
|
||||||
|
|||||||
@@ -252,44 +252,33 @@ To connect the mobile app to your Dev Container:
|
|||||||
|
|
||||||
The Dev Container supports multiple ways to run tests:
|
The Dev Container supports multiple ways to run tests:
|
||||||
|
|
||||||
#### Using Mise Commands (Recommended)
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Run tests for specific components
|
# Server
|
||||||
mise run checklist # in `server/`, `web/`, `packages/cli`
|
mise //server:test # unit tests
|
||||||
|
mise //server:test-medium # medium / integration tests
|
||||||
|
|
||||||
|
# Web
|
||||||
|
mise //web:test # unit tests
|
||||||
|
|
||||||
|
# E2E
|
||||||
|
mise //e2e:test # API tests
|
||||||
|
mise //e2e:test-web # web UI tests (Playwright)
|
||||||
|
|
||||||
|
# Run all checks for a component
|
||||||
|
mise //server:checklist
|
||||||
|
mise //web:checklist
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Using PNPM Directly
|
### Additional Commands
|
||||||
|
|
||||||
```bash
|
|
||||||
# Server tests
|
|
||||||
cd /workspaces/immich/server
|
|
||||||
pnpm test # Run all tests
|
|
||||||
pnpm run test:medium # Medium tests (integration tests)
|
|
||||||
pnpm run test:watch # Watch mode
|
|
||||||
pnpm run test:cov # Coverage report
|
|
||||||
|
|
||||||
# Web tests
|
|
||||||
cd /workspaces/immich/web
|
|
||||||
pnpm test # Run all tests
|
|
||||||
pnpm run test:watch # Watch mode
|
|
||||||
|
|
||||||
# E2E tests
|
|
||||||
cd /workspaces/immich/e2e
|
|
||||||
pnpm run test # Run API tests
|
|
||||||
pnpm run test:web # Run web UI tests
|
|
||||||
```
|
|
||||||
|
|
||||||
### Additional Make Commands
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# API generation
|
# API generation
|
||||||
make open-api # Generate OpenAPI specs
|
mise //:open-api # Generate OpenAPI specs
|
||||||
make open-api-typescript # Generate TypeScript SDK
|
mise //:open-api-typescript # Generate TypeScript SDK
|
||||||
make open-api-dart # Generate Dart SDK
|
mise //:open-api-dart # Generate Dart SDK
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
mise sql # Sync database schema
|
mise //server:sql # Sync database schema
|
||||||
```
|
```
|
||||||
|
|
||||||
### Debugging
|
### Debugging
|
||||||
|
|||||||
@@ -8,34 +8,42 @@ When contributing code through a pull request, please check the following:
|
|||||||
|
|
||||||
## Web Checks
|
## Web Checks
|
||||||
|
|
||||||
- [ ] `pnpm run lint` (linting via ESLint)
|
- [ ] `mise //web:lint` (linting via ESLint)
|
||||||
- [ ] `pnpm run format` (formatting via Prettier)
|
- [ ] `mise //web:format` (formatting via Prettier)
|
||||||
- [ ] `pnpm run check:svelte` (Type checking via SvelteKit)
|
- [ ] `mise //web:check-svelte` (type checking via SvelteKit)
|
||||||
- [ ] `pnpm run check:typescript` (check typescript)
|
- [ ] `mise //web:check-typescript` (type checking via `tsc`)
|
||||||
- [ ] `pnpm test` (unit tests)
|
- [ ] `mise //web:test` (unit tests)
|
||||||
|
|
||||||
:::tip AIO
|
:::tip AIO
|
||||||
Run all web checks with `pnpm run check:all`
|
Run all web checks with `mise //web:checklist`
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::tip Auto Fix
|
||||||
|
Use `mise //web:lint-fix` and `mise //web:format-fix` to automatically correct some issues.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
- [ ] `pnpm run format` (formatting via Prettier)
|
- [ ] `mise //docs:format` (formatting via Prettier)
|
||||||
- [ ] Update the `_redirects` file if you have renamed a page or removed it from the documentation.
|
- [ ] Update the `_redirects` file if you have renamed a page or removed it from the documentation.
|
||||||
|
|
||||||
|
:::tip Auto Fix
|
||||||
|
Use `mise //docs:format-fix` to automatically fix formatting.
|
||||||
|
:::
|
||||||
|
|
||||||
## Server Checks
|
## Server Checks
|
||||||
|
|
||||||
- [ ] `pnpm run lint` (linting via ESLint)
|
- [ ] `mise //server:lint` (linting via ESLint)
|
||||||
- [ ] `pnpm run format` (formatting via Prettier)
|
- [ ] `mise //server:format` (formatting via Prettier)
|
||||||
- [ ] `pnpm run check` (Type checking via `tsc`)
|
- [ ] `mise //server:check` (type checking via `tsc`)
|
||||||
- [ ] `pnpm test` (unit tests)
|
- [ ] `mise //server:test` (unit tests)
|
||||||
|
|
||||||
:::tip AIO
|
:::tip AIO
|
||||||
Run all server checks with `pnpm run check:all`
|
Run all server checks with `mise //server:checklist`
|
||||||
:::
|
:::
|
||||||
|
|
||||||
:::tip Auto Fix
|
:::tip Auto Fix
|
||||||
You can use `pnpm run __:fix` to potentially correct some issues automatically for `pnpm run format` and `lint`.
|
Use `mise //server:lint-fix` and `mise //server:format-fix` to automatically correct some issues.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## Mobile Checklist
|
## Mobile Checklist
|
||||||
@@ -53,6 +61,17 @@ Run all these commands at once with `mise //mobile:checklist`
|
|||||||
You can use `mise //mobile:lint-fix` to potentially correct some issues automatically for `mise //mobile:lint`.
|
You can use `mise //mobile:lint-fix` to potentially correct some issues automatically for `mise //mobile:lint`.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
## Machine Learning Checklist
|
||||||
|
|
||||||
|
- [ ] `mise //machine-learning:lint` (linting via ruff)
|
||||||
|
- [ ] `mise //machine-learning:format` (formatting via ruff)
|
||||||
|
- [ ] `mise //machine-learning:check` (type checking via mypy)
|
||||||
|
- [ ] `mise //machine-learning:test` (unit tests via pytest)
|
||||||
|
|
||||||
|
:::tip AIO
|
||||||
|
Run all machine learning checks with `mise //machine-learning:checklist`
|
||||||
|
:::
|
||||||
|
|
||||||
## OpenAPI
|
## OpenAPI
|
||||||
|
|
||||||
The OpenAPI client libraries need to be regenerated whenever there are changes to the `immich-openapi-specs.json` file. Note that you should not modify this file directly as it is auto-generated. See [OpenAPI](/api.md) for more details.
|
The OpenAPI client libraries need to be regenerated whenever there are changes to the `immich-openapi-specs.json` file. Note that you should not modify this file directly as it is auto-generated. See [OpenAPI](/api.md) for more details.
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ This environment includes the services below. Additional details are available i
|
|||||||
|
|
||||||
All the services are packaged to run as with single Docker Compose command.
|
All the services are packaged to run as with single Docker Compose command.
|
||||||
|
|
||||||
|
:::tip mise
|
||||||
|
[mise](https://mise.jdx.dev) is used throughout the project to manage tool versions and run tasks. [Install mise](https://mise.jdx.dev/installing-mise.html), then from the repo root run `mise trust` and `mise install` to get all required tools. Tasks for each service can be run from the repo root using `mise //namespace:task` (e.g. `mise //server:lint`). To list all available tasks, run `mise tasks ls --all`.
|
||||||
|
:::
|
||||||
|
|
||||||
### Server and web apps
|
### Server and web apps
|
||||||
|
|
||||||
1. Clone the project repo.
|
1. Clone the project repo.
|
||||||
@@ -56,22 +60,23 @@ You can access the web from `http://your-machine-ip:3000` or `http://localhost:3
|
|||||||
|
|
||||||
#### Connect web to a remote backend
|
#### Connect web to a remote backend
|
||||||
|
|
||||||
If you only want to do web development connected to an existing, remote backend, follow these steps:
|
If you only want to do web development connected to an existing, remote backend, run from the repo root:
|
||||||
|
|
||||||
1. Build the Immich SDK - `pnpm --filter @immich/sdk install && pnpm --filter @immich/sdk build`
|
|
||||||
2. Enter the web directory - `cd web/`
|
|
||||||
3. Install web dependencies - `pnpm i`
|
|
||||||
4. Start the web development server
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
IMMICH_SERVER_URL=https://demo.immich.app/ pnpm run dev
|
IMMICH_SERVER_URL=https://demo.immich.app/ mise //web:start
|
||||||
|
```
|
||||||
|
|
||||||
|
This will install all dependencies (including the SDK) and start the dev server in one step. To connect to the hosted demo server specifically, use the shorthand:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mise //web:start-demo
|
||||||
```
|
```
|
||||||
|
|
||||||
If you're using PowerShell on Windows you may need to set the env var separately like so:
|
If you're using PowerShell on Windows you may need to set the env var separately like so:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
$env:IMMICH_SERVER_URL = "https://demo.immich.app/"
|
$env:IMMICH_SERVER_URL = "https://demo.immich.app/"
|
||||||
pnpm run dev
|
mise //web:start
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `@immich/ui`
|
#### `@immich/ui`
|
||||||
@@ -90,20 +95,16 @@ To see local changes to `@immich/ui` in Immich, do the following:
|
|||||||
|
|
||||||
#### Setup
|
#### Setup
|
||||||
|
|
||||||
1. [Install mise](https://mise.jdx.dev/installing-mise.html).
|
1. Run `mise //mobile:install` to install Flutter dependencies.
|
||||||
2. Change to the immich (root) directory and trust the mise config with `mise trust`.
|
2. Run `mise //mobile:translation` to generate the translation file.
|
||||||
3. Install tools with mise: `mise install`.
|
3. Change to the `mobile/` directory and run `flutter run` to start the app.
|
||||||
4. Change to the `mobile/` directory.
|
|
||||||
5. Run `flutter pub get` to install the dependencies.
|
|
||||||
6. Run `make translation` to generate the translation file.
|
|
||||||
7. Run `flutter run` to start the app.
|
|
||||||
|
|
||||||
#### Translation
|
#### Translation
|
||||||
|
|
||||||
To add a new translation text, enter the key-value pair in the `i18n/en.json` in the root of the immich project. Then, from the `mobile/` directory, run
|
To add a new translation text, enter the key-value pair in the `i18n/en.json` in the root of the immich project. Then run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make translation
|
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.
|
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.
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
### Unit tests
|
### Unit tests
|
||||||
|
|
||||||
Unit are run by calling `pnpm run test` from the `server/` directory.
|
Unit tests are run with `mise //server:test`.
|
||||||
You need to run `pnpm install` (in `server/`) before _once_.
|
You need to run `mise //server:install` before _once_.
|
||||||
|
|
||||||
### End to end tests
|
### End to end tests
|
||||||
|
|
||||||
@@ -17,8 +17,7 @@ make e2e
|
|||||||
|
|
||||||
Before you can run the tests, you need to run the following commands _once_:
|
Before you can run the tests, you need to run the following commands _once_:
|
||||||
|
|
||||||
- `pnpm install`
|
- `mise //e2e:ci-setup` (installs e2e, SDK, and CLI dependencies)
|
||||||
- `pnpm --filter @immich/sdk --filter @immich/cli build`
|
|
||||||
- `mise //:open-api`
|
- `mise //:open-api`
|
||||||
|
|
||||||
Once the test environment is running, the e2e tests can be run via:
|
Once the test environment is running, the e2e tests can be run via:
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ run = "pnpm install --filter documentation --frozen-lockfile"
|
|||||||
|
|
||||||
[tasks.start]
|
[tasks.start]
|
||||||
env._.path = "./node_modules/.bin"
|
env._.path = "./node_modules/.bin"
|
||||||
run = "docusaurus --port 3005"
|
run = "docusaurus start --port 3005"
|
||||||
|
|
||||||
[tasks.build]
|
[tasks.build]
|
||||||
env._.path = "./node_modules/.bin"
|
env._.path = "./node_modules/.bin"
|
||||||
|
|||||||
@@ -89,6 +89,13 @@ flutter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
constraints {
|
||||||
|
implementation("androidx.glance:glance-appwidget") {
|
||||||
|
version { strictly libs.versions.glance.get() }
|
||||||
|
because 'home_widget requests 1.+ which can resolve to pre-releases incompatible with our compileSdk/AGP'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
implementation libs.okhttp
|
implementation libs.okhttp
|
||||||
implementation libs.cronet.embedded
|
implementation libs.cronet.embedded
|
||||||
implementation libs.media3.datasource.okhttp
|
implementation libs.media3.datasource.okhttp
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:immich_mobile/domain/models/config/album_config.dart';
|
import 'package:immich_mobile/domain/models/config/album_config.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/config/backup_config.dart';
|
||||||
import 'package:immich_mobile/domain/models/config/cleanup_config.dart';
|
import 'package:immich_mobile/domain/models/config/cleanup_config.dart';
|
||||||
import 'package:immich_mobile/domain/models/config/image_config.dart';
|
import 'package:immich_mobile/domain/models/config/image_config.dart';
|
||||||
import 'package:immich_mobile/domain/models/config/map_config.dart';
|
import 'package:immich_mobile/domain/models/config/map_config.dart';
|
||||||
@@ -16,6 +17,7 @@ class AppConfig {
|
|||||||
final ViewerConfig viewer;
|
final ViewerConfig viewer;
|
||||||
final SlideshowConfig slideshow;
|
final SlideshowConfig slideshow;
|
||||||
final AlbumConfig album;
|
final AlbumConfig album;
|
||||||
|
final BackupConfig backup;
|
||||||
|
|
||||||
const AppConfig({
|
const AppConfig({
|
||||||
this.theme = const .new(),
|
this.theme = const .new(),
|
||||||
@@ -26,6 +28,7 @@ class AppConfig {
|
|||||||
this.viewer = const .new(),
|
this.viewer = const .new(),
|
||||||
this.slideshow = const .new(),
|
this.slideshow = const .new(),
|
||||||
this.album = const .new(),
|
this.album = const .new(),
|
||||||
|
this.backup = const .new(),
|
||||||
});
|
});
|
||||||
|
|
||||||
AppConfig copyWith({
|
AppConfig copyWith({
|
||||||
@@ -37,6 +40,7 @@ class AppConfig {
|
|||||||
ViewerConfig? viewer,
|
ViewerConfig? viewer,
|
||||||
SlideshowConfig? slideshow,
|
SlideshowConfig? slideshow,
|
||||||
AlbumConfig? album,
|
AlbumConfig? album,
|
||||||
|
BackupConfig? backup,
|
||||||
}) => .new(
|
}) => .new(
|
||||||
theme: theme ?? this.theme,
|
theme: theme ?? this.theme,
|
||||||
cleanup: cleanup ?? this.cleanup,
|
cleanup: cleanup ?? this.cleanup,
|
||||||
@@ -46,6 +50,7 @@ class AppConfig {
|
|||||||
viewer: viewer ?? this.viewer,
|
viewer: viewer ?? this.viewer,
|
||||||
slideshow: slideshow ?? this.slideshow,
|
slideshow: slideshow ?? this.slideshow,
|
||||||
album: album ?? this.album,
|
album: album ?? this.album,
|
||||||
|
backup: backup ?? this.backup,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -59,12 +64,13 @@ class AppConfig {
|
|||||||
other.image == image &&
|
other.image == image &&
|
||||||
other.viewer == viewer &&
|
other.viewer == viewer &&
|
||||||
other.slideshow == slideshow &&
|
other.slideshow == slideshow &&
|
||||||
other.album == album);
|
other.album == album &&
|
||||||
|
other.backup == backup);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(theme, cleanup, map, timeline, image, viewer, slideshow, album);
|
int get hashCode => Object.hash(theme, cleanup, map, timeline, image, viewer, slideshow, album, backup);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() =>
|
String toString() =>
|
||||||
'AppConfig(theme: $theme, cleanup: $cleanup, map: $map, timeline: $timeline, image: $image, viewer: $viewer, slideshow: $slideshow, album: $album)';
|
'AppConfig(theme: $theme, cleanup: $cleanup, map: $map, timeline: $timeline, image: $image, viewer: $viewer, slideshow: $slideshow, album: $album, backup: $backup)';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
class BackupConfig {
|
||||||
|
final bool enabled;
|
||||||
|
final bool useCellularForVideos;
|
||||||
|
final bool useCellularForPhotos;
|
||||||
|
final bool requireCharging;
|
||||||
|
final int triggerDelay;
|
||||||
|
final bool syncAlbums;
|
||||||
|
|
||||||
|
const BackupConfig({
|
||||||
|
this.enabled = false,
|
||||||
|
this.useCellularForVideos = false,
|
||||||
|
this.useCellularForPhotos = false,
|
||||||
|
this.requireCharging = false,
|
||||||
|
this.triggerDelay = 30,
|
||||||
|
this.syncAlbums = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
BackupConfig copyWith({
|
||||||
|
bool? enabled,
|
||||||
|
bool? useCellularForVideos,
|
||||||
|
bool? useCellularForPhotos,
|
||||||
|
bool? requireCharging,
|
||||||
|
int? triggerDelay,
|
||||||
|
bool? syncAlbums,
|
||||||
|
}) => BackupConfig(
|
||||||
|
enabled: enabled ?? this.enabled,
|
||||||
|
useCellularForVideos: useCellularForVideos ?? this.useCellularForVideos,
|
||||||
|
useCellularForPhotos: useCellularForPhotos ?? this.useCellularForPhotos,
|
||||||
|
requireCharging: requireCharging ?? this.requireCharging,
|
||||||
|
triggerDelay: triggerDelay ?? this.triggerDelay,
|
||||||
|
syncAlbums: syncAlbums ?? this.syncAlbums,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
(other is BackupConfig &&
|
||||||
|
other.enabled == enabled &&
|
||||||
|
other.useCellularForVideos == useCellularForVideos &&
|
||||||
|
other.useCellularForPhotos == useCellularForPhotos &&
|
||||||
|
other.requireCharging == requireCharging &&
|
||||||
|
other.triggerDelay == triggerDelay &&
|
||||||
|
other.syncAlbums == syncAlbums);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
Object.hash(enabled, useCellularForVideos, useCellularForPhotos, requireCharging, triggerDelay, syncAlbums);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() =>
|
||||||
|
'BackupConfig(enabled: $enabled, useCellularForVideos: $useCellularForVideos, useCellularForPhotos: $useCellularForPhotos, requireCharging: $requireCharging, triggerDelay: $triggerDelay, syncAlbums: $syncAlbums)';
|
||||||
|
}
|
||||||
@@ -62,6 +62,14 @@ enum MetadataKey<T extends Object> {
|
|||||||
albumIsReverse<bool>(.appConfig, 'album.isReverse', true),
|
albumIsReverse<bool>(.appConfig, 'album.isReverse', true),
|
||||||
albumIsGrid<bool>(.appConfig, 'album.isGrid', false),
|
albumIsGrid<bool>(.appConfig, 'album.isGrid', false),
|
||||||
|
|
||||||
|
// Backup
|
||||||
|
backupEnabled<bool>(.appConfig, 'backup.enabled', false),
|
||||||
|
backupUseCellularForVideos<bool>(.appConfig, 'backup.useCellularForVideos', false),
|
||||||
|
backupUseCellularForPhotos<bool>(.appConfig, 'backup.useCellularForPhotos', false),
|
||||||
|
backupRequireCharging<bool>(.appConfig, 'backup.requireCharging', false),
|
||||||
|
backupTriggerDelay<int>(.appConfig, 'backup.triggerDelay', 30),
|
||||||
|
backupSyncAlbums<bool>(.appConfig, 'backup.syncAlbums', false),
|
||||||
|
|
||||||
// Timeline
|
// Timeline
|
||||||
timelineTilesPerRow<int>(.appConfig, 'timeline.tilesPerRow', 4),
|
timelineTilesPerRow<int>(.appConfig, 'timeline.tilesPerRow', 4),
|
||||||
timelineGroupAssetsBy<GroupAssetsBy>(
|
timelineGroupAssetsBy<GroupAssetsBy>(
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
|
|
||||||
enum Setting<T> {
|
enum Setting<T> {
|
||||||
advancedTroubleshooting<bool>(StoreKey.advancedTroubleshooting, false),
|
advancedTroubleshooting<bool>(StoreKey.advancedTroubleshooting, false);
|
||||||
enableBackup<bool>(StoreKey.enableBackup, false);
|
|
||||||
|
|
||||||
const Setting(this.storeKey, this.defaultValue);
|
const Setting(this.storeKey, this.defaultValue);
|
||||||
|
|
||||||
|
|||||||
@@ -6,26 +6,25 @@ enum StoreKey<T> {
|
|||||||
version<int>._(0),
|
version<int>._(0),
|
||||||
currentUser<UserDto>._(2),
|
currentUser<UserDto>._(2),
|
||||||
deviceId<String>._(4),
|
deviceId<String>._(4),
|
||||||
backupRequireCharging<bool>._(7),
|
|
||||||
backupTriggerDelay<int>._(8),
|
|
||||||
serverUrl<String>._(10),
|
serverUrl<String>._(10),
|
||||||
accessToken<String>._(11),
|
accessToken<String>._(11),
|
||||||
serverEndpoint<String>._(12),
|
serverEndpoint<String>._(12),
|
||||||
advancedTroubleshooting<bool>._(114),
|
advancedTroubleshooting<bool>._(114),
|
||||||
enableHapticFeedback<bool>._(126),
|
enableHapticFeedback<bool>._(126),
|
||||||
syncAlbums<bool>._(131),
|
|
||||||
|
|
||||||
manageLocalMediaAndroid<bool>._(137),
|
manageLocalMediaAndroid<bool>._(137),
|
||||||
// Read-only Mode settings
|
// Read-only Mode settings
|
||||||
readonlyModeEnabled<bool>._(138),
|
readonlyModeEnabled<bool>._(138),
|
||||||
|
|
||||||
// Experimental stuff
|
|
||||||
enableBackup<bool>._(1003),
|
|
||||||
useWifiForUploadVideos<bool>._(1004),
|
|
||||||
useWifiForUploadPhotos<bool>._(1005),
|
|
||||||
syncMigrationStatus<String>._(1013),
|
syncMigrationStatus<String>._(1013),
|
||||||
|
|
||||||
// Legacy keys that have been migrated to the new metadata store
|
// Legacy keys that have been migrated to the new metadata store
|
||||||
|
legacyBackupRequireCharging<bool>._(7),
|
||||||
|
legacyBackupTriggerDelay<int>._(8),
|
||||||
|
legacySyncAlbums<bool>._(131),
|
||||||
|
legacyEnableBackup<bool>._(1003),
|
||||||
|
legacyUseWifiForUploadVideos<bool>._(1004),
|
||||||
|
legacyUseWifiForUploadPhotos<bool>._(1005),
|
||||||
legacySelectedAlbumSortOrder<int>._(113),
|
legacySelectedAlbumSortOrder<int>._(113),
|
||||||
legacySelectedAlbumSortReverse<bool>._(123),
|
legacySelectedAlbumSortReverse<bool>._(123),
|
||||||
legacyAlbumGridView<bool>._(140),
|
legacyAlbumGridView<bool>._(140),
|
||||||
|
|||||||
@@ -11,15 +11,14 @@ import 'package:immich_mobile/entities/store.entity.dart';
|
|||||||
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.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/logger_db.repository.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
|
||||||
import 'package:immich_mobile/platform/background_worker_api.g.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/platform/background_worker_lock_api.g.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/background_sync.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/backup/drift_backup.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart' show nativeSyncApiProvider;
|
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart' show nativeSyncApiProvider;
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
||||||
import 'package:immich_mobile/services/auth.service.dart';
|
import 'package:immich_mobile/services/auth.service.dart';
|
||||||
import 'package:immich_mobile/services/foreground_upload.service.dart';
|
import 'package:immich_mobile/services/foreground_upload.service.dart';
|
||||||
import 'package:immich_mobile/services/localization.service.dart';
|
import 'package:immich_mobile/services/localization.service.dart';
|
||||||
@@ -39,16 +38,15 @@ class BackgroundWorkerFgService {
|
|||||||
Future<void> saveNotificationMessage(String title, String body) =>
|
Future<void> saveNotificationMessage(String title, String body) =>
|
||||||
_foregroundHostApi.saveNotificationMessage(title, body);
|
_foregroundHostApi.saveNotificationMessage(title, body);
|
||||||
|
|
||||||
Future<void> configure({int? minimumDelaySeconds, bool? requireCharging}) => _foregroundHostApi.configure(
|
Future<void> configure({int? minimumDelaySeconds, bool? requireCharging}) {
|
||||||
BackgroundWorkerSettings(
|
final backup = MetadataRepository.instance.appConfig.backup;
|
||||||
minimumDelaySeconds:
|
return _foregroundHostApi.configure(
|
||||||
minimumDelaySeconds ??
|
BackgroundWorkerSettings(
|
||||||
Store.get(AppSettingsEnum.backupTriggerDelay.storeKey, AppSettingsEnum.backupTriggerDelay.defaultValue),
|
minimumDelaySeconds: minimumDelaySeconds ?? backup.triggerDelay,
|
||||||
requiresCharging:
|
requiresCharging: requireCharging ?? backup.requireCharging,
|
||||||
requireCharging ??
|
),
|
||||||
Store.get(AppSettingsEnum.backupRequireCharging.storeKey, AppSettingsEnum.backupRequireCharging.defaultValue),
|
);
|
||||||
),
|
}
|
||||||
);
|
|
||||||
|
|
||||||
Future<void> disable() => _foregroundHostApi.disable();
|
Future<void> disable() => _foregroundHostApi.disable();
|
||||||
}
|
}
|
||||||
@@ -71,7 +69,7 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi {
|
|||||||
BackgroundWorkerFlutterApi.setUp(this);
|
BackgroundWorkerFlutterApi.setUp(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get _isBackupEnabled => _ref?.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup) ?? false;
|
bool get _isBackupEnabled => MetadataRepository.instance.appConfig.backup.enabled;
|
||||||
|
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -152,6 +152,14 @@ extension<T extends Object> on MetadataDomain<T> {
|
|||||||
isReverse: repo._read(.albumIsReverse),
|
isReverse: repo._read(.albumIsReverse),
|
||||||
isGrid: repo._read(.albumIsGrid),
|
isGrid: repo._read(.albumIsGrid),
|
||||||
),
|
),
|
||||||
|
backup: .new(
|
||||||
|
enabled: repo._read(.backupEnabled),
|
||||||
|
useCellularForVideos: repo._read(.backupUseCellularForVideos),
|
||||||
|
useCellularForPhotos: repo._read(.backupUseCellularForPhotos),
|
||||||
|
requireCharging: repo._read(.backupRequireCharging),
|
||||||
|
triggerDelay: repo._read(.backupTriggerDelay),
|
||||||
|
syncAlbums: repo._read(.backupSyncAlbums),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
case .systemConfig:
|
case .systemConfig:
|
||||||
repo._systemConfig = .new(
|
repo._systemConfig = .new(
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ 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/domain/services/sync_linked_album.service.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
|
||||||
import 'package:immich_mobile/providers/background_sync.provider.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/backup_album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/backup/drift_backup.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/platform.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
||||||
import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart';
|
import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart';
|
||||||
import 'package:immich_mobile/widgets/common/search_field.dart';
|
import 'package:immich_mobile/widgets/common/search_field.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
@@ -43,7 +43,7 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbum
|
|||||||
_searchController = TextEditingController();
|
_searchController = TextEditingController();
|
||||||
_searchFocusNode = FocusNode();
|
_searchFocusNode = FocusNode();
|
||||||
|
|
||||||
_enableSyncUploadAlbum.value = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums);
|
_enableSyncUploadAlbum.value = ref.read(metadataProvider).appConfig.backup.syncAlbums;
|
||||||
ref.read(backupAlbumProvider.notifier).getAll();
|
ref.read(backupAlbumProvider.notifier).getAll();
|
||||||
|
|
||||||
_initialTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount));
|
_initialTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount));
|
||||||
@@ -55,7 +55,7 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbum
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final enableSyncUploadAlbum = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums);
|
final enableSyncUploadAlbum = ref.read(metadataProvider).appConfig.backup.syncAlbums;
|
||||||
final selectedAlbums = ref
|
final selectedAlbums = ref
|
||||||
.read(backupAlbumProvider)
|
.read(backupAlbumProvider)
|
||||||
.where((a) => a.backupSelection == BackupSelection.selected)
|
.where((a) => a.backupSelection == BackupSelection.selected)
|
||||||
@@ -103,7 +103,7 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbum
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final isBackupEnabled = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup);
|
final isBackupEnabled = MetadataRepository.instance.appConfig.backup.enabled;
|
||||||
await ref.read(driftBackupProvider.notifier).getBackupStatus(user.id);
|
await ref.read(driftBackupProvider.notifier).getBackupStatus(user.id);
|
||||||
final currentTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount));
|
final currentTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount));
|
||||||
final totalChanged = currentTotalAssetCount != _initialTotalAssetCount;
|
final totalChanged = currentTotalAssetCount != _initialTotalAssetCount;
|
||||||
|
|||||||
@@ -3,14 +3,12 @@ import 'dart:async';
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
|
||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
|
||||||
import 'package:immich_mobile/providers/background_sync.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/backup/drift_backup.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
||||||
import 'package:immich_mobile/widgets/settings/backup_settings/drift_backup_settings.dart';
|
import 'package:immich_mobile/widgets/settings/backup_settings/drift_backup_settings.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
@@ -21,18 +19,20 @@ class DriftBackupOptionsPage extends ConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
bool hasPopped = false;
|
bool hasPopped = false;
|
||||||
final previousWifiReqForVideos = Store.tryGet(StoreKey.useWifiForUploadVideos) ?? false;
|
final previousBackup = ref.read(metadataProvider).appConfig.backup;
|
||||||
final previousWifiReqForPhotos = Store.tryGet(StoreKey.useWifiForUploadPhotos) ?? false;
|
final previousCellularForVideos = previousBackup.useCellularForVideos;
|
||||||
|
final previousCellularForPhotos = previousBackup.useCellularForPhotos;
|
||||||
return PopScope(
|
return PopScope(
|
||||||
onPopInvokedWithResult: (didPop, result) async {
|
onPopInvokedWithResult: (didPop, result) async {
|
||||||
// There is an issue with Flutter where the pop event
|
// There is an issue with Flutter where the pop event
|
||||||
// can be triggered multiple times, so we guard it with _hasPopped
|
// can be triggered multiple times, so we guard it with _hasPopped
|
||||||
|
|
||||||
final currentWifiReqForVideos = Store.tryGet(StoreKey.useWifiForUploadVideos) ?? false;
|
final currentBackup = ref.read(metadataProvider).appConfig.backup;
|
||||||
final currentWifiReqForPhotos = Store.tryGet(StoreKey.useWifiForUploadPhotos) ?? false;
|
final currentCellularForVideos = currentBackup.useCellularForVideos;
|
||||||
|
final currentCellularForPhotos = currentBackup.useCellularForPhotos;
|
||||||
|
|
||||||
if (currentWifiReqForVideos == previousWifiReqForVideos &&
|
if (currentCellularForVideos == previousCellularForVideos &&
|
||||||
currentWifiReqForPhotos == previousWifiReqForPhotos) {
|
currentCellularForPhotos == previousCellularForPhotos) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ class DriftBackupOptionsPage extends ConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id);
|
await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id);
|
||||||
final isBackupEnabled = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup);
|
final isBackupEnabled = MetadataRepository.instance.appConfig.backup.enabled;
|
||||||
if (!isBackupEnabled) {
|
if (!isBackupEnabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import 'package:immich_mobile/domain/models/store.model.dart';
|
|||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/generated/codegen_loader.g.dart';
|
import 'package:immich_mobile/generated/codegen_loader.g.dart';
|
||||||
import 'package:immich_mobile/generated/translations.g.dart';
|
import 'package:immich_mobile/generated/translations.g.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
|
||||||
import 'package:immich_mobile/providers/auth.provider.dart';
|
import 'package:immich_mobile/providers/auth.provider.dart';
|
||||||
import 'package:immich_mobile/providers/background_sync.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/backup/drift_backup.provider.dart';
|
||||||
@@ -340,7 +341,7 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
|
|||||||
await backgroundManager.hashAssets();
|
await backgroundManager.hashAssets();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Store.get(StoreKey.syncAlbums, false)) {
|
if (MetadataRepository.instance.appConfig.backup.syncAlbums) {
|
||||||
await backgroundManager.syncLinkedAlbum();
|
await backgroundManager.syncLinkedAlbum();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -369,7 +370,7 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _resumeBackup(DriftBackupNotifier notifier) async {
|
Future<void> _resumeBackup(DriftBackupNotifier notifier) async {
|
||||||
final isEnableBackup = Store.get(StoreKey.enableBackup, false);
|
final isEnableBackup = MetadataRepository.instance.appConfig.backup.enabled;
|
||||||
|
|
||||||
if (isEnableBackup) {
|
if (isEnableBackup) {
|
||||||
final currentUser = Store.tryGet(StoreKey.currentUser);
|
final currentUser = Store.tryGet(StoreKey.currentUser);
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/metadata_key.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
|
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
|
||||||
|
|
||||||
class BackupToggleButton extends ConsumerStatefulWidget {
|
class BackupToggleButton extends ConsumerStatefulWidget {
|
||||||
final VoidCallback onStart;
|
final VoidCallback onStart;
|
||||||
@@ -31,7 +31,7 @@ class BackupToggleButtonState extends ConsumerState<BackupToggleButton> with Sin
|
|||||||
end: 1,
|
end: 1,
|
||||||
).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeInOut));
|
).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeInOut));
|
||||||
|
|
||||||
_isEnabled = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup);
|
_isEnabled = ref.read(metadataProvider).appConfig.backup.enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -41,7 +41,7 @@ class BackupToggleButtonState extends ConsumerState<BackupToggleButton> with Sin
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onToggle(bool value) async {
|
Future<void> _onToggle(bool value) async {
|
||||||
await ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.enableBackup, value);
|
await ref.read(metadataProvider).write(MetadataKey.backupEnabled, value);
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_isEnabled = value;
|
_isEnabled = value;
|
||||||
|
|||||||
@@ -5,16 +5,15 @@ import 'package:immich_mobile/domain/models/store.model.dart';
|
|||||||
import 'package:immich_mobile/domain/services/log.service.dart';
|
import 'package:immich_mobile/domain/services/log.service.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/auth.provider.dart';
|
import 'package:immich_mobile/providers/auth.provider.dart';
|
||||||
import 'package:immich_mobile/providers/background_sync.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/backup/drift_backup.provider.dart';
|
||||||
import 'package:immich_mobile/providers/gallery_permission.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/platform.provider.dart';
|
||||||
import 'package:immich_mobile/providers/notification_permission.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/server_info.provider.dart';
|
||||||
import 'package:immich_mobile/providers/websocket.provider.dart';
|
import 'package:immich_mobile/providers/websocket.provider.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
enum AppLifeCycleEnum { active, inactive, paused, resumed, detached, hidden }
|
enum AppLifeCycleEnum { active, inactive, paused, resumed, detached, hidden }
|
||||||
@@ -108,7 +107,7 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
|
|||||||
await Future.delayed(const Duration(milliseconds: 500));
|
await Future.delayed(const Duration(milliseconds: 500));
|
||||||
|
|
||||||
final backgroundManager = _ref.read(backgroundSyncProvider);
|
final backgroundManager = _ref.read(backgroundSyncProvider);
|
||||||
final isAlbumLinkedSyncEnable = _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums);
|
final isAlbumLinkedSyncEnable = _ref.read(metadataProvider).appConfig.backup.syncAlbums;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
bool syncSuccess = false;
|
bool syncSuccess = false;
|
||||||
@@ -138,7 +137,7 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _resumeBackup() async {
|
Future<void> _resumeBackup() async {
|
||||||
final isEnableBackup = _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup);
|
final isEnableBackup = _ref.read(metadataProvider).appConfig.backup.enabled;
|
||||||
|
|
||||||
if (isEnableBackup) {
|
if (isEnableBackup) {
|
||||||
final currentUser = Store.tryGet(StoreKey.currentUser);
|
final currentUser = Store.tryGet(StoreKey.currentUser);
|
||||||
|
|||||||
@@ -7,6 +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/models/server_info/server_version.model.dart';
|
||||||
import 'package:immich_mobile/providers/auth.provider.dart';
|
import 'package:immich_mobile/providers/auth.provider.dart';
|
||||||
import 'package:immich_mobile/providers/background_sync.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/server_info.provider.dart';
|
import 'package:immich_mobile/providers/server_info.provider.dart';
|
||||||
import 'package:immich_mobile/utils/debounce.dart';
|
import 'package:immich_mobile/utils/debounce.dart';
|
||||||
import 'package:immich_mobile/utils/debug_print.dart';
|
import 'package:immich_mobile/utils/debug_print.dart';
|
||||||
@@ -192,7 +193,7 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final isSyncAlbumEnabled = Store.get(StoreKey.syncAlbums, false);
|
final isSyncAlbumEnabled = _ref.read(metadataProvider).appConfig.backup.syncAlbums;
|
||||||
try {
|
try {
|
||||||
unawaited(
|
unawaited(
|
||||||
_ref.read(backgroundSyncProvider).syncWebsocketBatchV1(_batchedAssetUploadReady.toList()).then((_) {
|
_ref.read(backgroundSyncProvider).syncWebsocketBatchV1(_batchedAssetUploadReady.toList()).then((_) {
|
||||||
@@ -213,7 +214,7 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final isSyncAlbumEnabled = Store.get(StoreKey.syncAlbums, false);
|
final isSyncAlbumEnabled = _ref.read(metadataProvider).appConfig.backup.syncAlbums;
|
||||||
try {
|
try {
|
||||||
unawaited(
|
unawaited(
|
||||||
_ref.read(backgroundSyncProvider).syncWebsocketBatchV2(_batchedAssetUploadReady.toList()).then((_) {
|
_ref.read(backgroundSyncProvider).syncWebsocketBatchV2(_batchedAssetUploadReady.toList()).then((_) {
|
||||||
|
|||||||
@@ -5,13 +5,7 @@ enum AppSettingsEnum<T> {
|
|||||||
advancedTroubleshooting<bool>(StoreKey.advancedTroubleshooting, null, false),
|
advancedTroubleshooting<bool>(StoreKey.advancedTroubleshooting, null, false),
|
||||||
manageLocalMediaAndroid<bool>(StoreKey.manageLocalMediaAndroid, null, false),
|
manageLocalMediaAndroid<bool>(StoreKey.manageLocalMediaAndroid, null, false),
|
||||||
enableHapticFeedback<bool>(StoreKey.enableHapticFeedback, null, true),
|
enableHapticFeedback<bool>(StoreKey.enableHapticFeedback, null, true),
|
||||||
syncAlbums<bool>(StoreKey.syncAlbums, null, false),
|
readonlyModeEnabled<bool>(StoreKey.readonlyModeEnabled, "readonlyModeEnabled", false);
|
||||||
enableBackup<bool>(StoreKey.enableBackup, null, false),
|
|
||||||
useCellularForUploadVideos<bool>(StoreKey.useWifiForUploadVideos, null, false),
|
|
||||||
useCellularForUploadPhotos<bool>(StoreKey.useWifiForUploadPhotos, null, false),
|
|
||||||
readonlyModeEnabled<bool>(StoreKey.readonlyModeEnabled, "readonlyModeEnabled", false),
|
|
||||||
backupRequireCharging<bool>(StoreKey.backupRequireCharging, null, false),
|
|
||||||
backupTriggerDelay<int>(StoreKey.backupTriggerDelay, null, 30);
|
|
||||||
|
|
||||||
const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue);
|
const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue);
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.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/store.model.dart';
|
||||||
import 'package:immich_mobile/domain/utils/background_sync.dart';
|
import 'package:immich_mobile/domain/utils/background_sync.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/network.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/auxilary_endpoint.model.dart';
|
||||||
import 'package:immich_mobile/models/auth/login_response.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/api.provider.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/auth.repository.dart';
|
import 'package:immich_mobile/repositories/auth.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/auth_api.repository.dart';
|
import 'package:immich_mobile/repositories/auth_api.repository.dart';
|
||||||
import 'package:immich_mobile/services/api.service.dart';
|
import 'package:immich_mobile/services/api.service.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
||||||
import 'package:immich_mobile/services/network.service.dart';
|
import 'package:immich_mobile/services/network.service.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
@@ -25,7 +25,6 @@ final authServiceProvider = Provider(
|
|||||||
ref.watch(apiServiceProvider),
|
ref.watch(apiServiceProvider),
|
||||||
ref.watch(networkServiceProvider),
|
ref.watch(networkServiceProvider),
|
||||||
ref.watch(backgroundSyncProvider),
|
ref.watch(backgroundSyncProvider),
|
||||||
ref.watch(appSettingsServiceProvider),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -35,7 +34,6 @@ class AuthService {
|
|||||||
final ApiService _apiService;
|
final ApiService _apiService;
|
||||||
final NetworkService _networkService;
|
final NetworkService _networkService;
|
||||||
final BackgroundSyncManager _backgroundSyncManager;
|
final BackgroundSyncManager _backgroundSyncManager;
|
||||||
final AppSettingsService _appSettingsService;
|
|
||||||
final _log = Logger("AuthService");
|
final _log = Logger("AuthService");
|
||||||
|
|
||||||
AuthService(
|
AuthService(
|
||||||
@@ -44,7 +42,6 @@ class AuthService {
|
|||||||
this._apiService,
|
this._apiService,
|
||||||
this._networkService,
|
this._networkService,
|
||||||
this._backgroundSyncManager,
|
this._backgroundSyncManager,
|
||||||
this._appSettingsService,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Validates the provided server URL by resolving and setting the endpoint.
|
/// Validates the provided server URL by resolving and setting the endpoint.
|
||||||
@@ -103,7 +100,7 @@ class AuthService {
|
|||||||
_log.severe("Error clearing local data", error, stackTrace);
|
_log.severe("Error clearing local data", error, stackTrace);
|
||||||
});
|
});
|
||||||
|
|
||||||
await _appSettingsService.setSetting(AppSettingsEnum.enableBackup, false);
|
await MetadataRepository.instance.write(MetadataKey.backupEnabled, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,14 +13,13 @@ import 'package:immich_mobile/entities/store.entity.dart';
|
|||||||
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/backup.repository.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/local_asset.repository.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/storage.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/storage.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/asset_media.repository.dart';
|
import 'package:immich_mobile/repositories/asset_media.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/upload.repository.dart';
|
import 'package:immich_mobile/repositories/upload.repository.dart';
|
||||||
import 'package:immich_mobile/services/api.service.dart';
|
import 'package:immich_mobile/services/api.service.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
||||||
import 'package:immich_mobile/utils/debug_print.dart';
|
import 'package:immich_mobile/utils/debug_print.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
@@ -31,7 +30,6 @@ final backgroundUploadServiceProvider = Provider((ref) {
|
|||||||
ref.watch(storageRepositoryProvider),
|
ref.watch(storageRepositoryProvider),
|
||||||
ref.watch(localAssetRepository),
|
ref.watch(localAssetRepository),
|
||||||
ref.watch(backupRepositoryProvider),
|
ref.watch(backupRepositoryProvider),
|
||||||
ref.watch(appSettingsServiceProvider),
|
|
||||||
ref.watch(assetMediaRepositoryProvider),
|
ref.watch(assetMediaRepositoryProvider),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -105,7 +103,6 @@ class BackgroundUploadService {
|
|||||||
this._storageRepository,
|
this._storageRepository,
|
||||||
this._localAssetRepository,
|
this._localAssetRepository,
|
||||||
this._backupRepository,
|
this._backupRepository,
|
||||||
this._appSettingsService,
|
|
||||||
this._assetMediaRepository,
|
this._assetMediaRepository,
|
||||||
) {
|
) {
|
||||||
_uploadRepository.onUploadStatus = _onUploadCallback;
|
_uploadRepository.onUploadStatus = _onUploadCallback;
|
||||||
@@ -116,7 +113,6 @@ class BackgroundUploadService {
|
|||||||
final StorageRepository _storageRepository;
|
final StorageRepository _storageRepository;
|
||||||
final DriftLocalAssetRepository _localAssetRepository;
|
final DriftLocalAssetRepository _localAssetRepository;
|
||||||
final DriftBackupRepository _backupRepository;
|
final DriftBackupRepository _backupRepository;
|
||||||
final AppSettingsService _appSettingsService;
|
|
||||||
final AssetMediaRepository _assetMediaRepository;
|
final AssetMediaRepository _assetMediaRepository;
|
||||||
final Logger _logger = Logger('BackgroundUploadService');
|
final Logger _logger = Logger('BackgroundUploadService');
|
||||||
|
|
||||||
@@ -363,15 +359,14 @@ class BackgroundUploadService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _shouldRequireWiFi(LocalAsset asset) {
|
bool _shouldRequireWiFi(LocalAsset asset) {
|
||||||
bool requiresWiFi = true;
|
final backup = MetadataRepository.instance.appConfig.backup;
|
||||||
|
if (asset.isVideo && backup.useCellularForVideos) {
|
||||||
if (asset.isVideo && _appSettingsService.getSetting(AppSettingsEnum.useCellularForUploadVideos)) {
|
return false;
|
||||||
requiresWiFi = false;
|
|
||||||
} else if (!asset.isVideo && _appSettingsService.getSetting(AppSettingsEnum.useCellularForUploadPhotos)) {
|
|
||||||
requiresWiFi = false;
|
|
||||||
}
|
}
|
||||||
|
if (!asset.isVideo && backup.useCellularForPhotos) {
|
||||||
return requiresWiFi;
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<UploadTask> buildUploadTask(
|
Future<UploadTask> buildUploadTask(
|
||||||
|
|||||||
@@ -7,18 +7,17 @@ import 'package:immich_mobile/domain/models/asset/asset_metadata.model.dart';
|
|||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
|
||||||
import 'package:immich_mobile/extensions/network_capability_extensions.dart';
|
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/extensions/translate_extensions.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/backup.repository.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/storage.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
||||||
import 'package:immich_mobile/platform/connectivity_api.g.dart';
|
import 'package:immich_mobile/platform/connectivity_api.g.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/storage.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/storage.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/asset_media.repository.dart';
|
import 'package:immich_mobile/repositories/asset_media.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/upload.repository.dart';
|
import 'package:immich_mobile/repositories/upload.repository.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
import 'package:photo_manager/photo_manager.dart' show PMProgressHandler;
|
import 'package:photo_manager/photo_manager.dart' show PMProgressHandler;
|
||||||
@@ -39,7 +38,6 @@ final foregroundUploadServiceProvider = Provider((ref) {
|
|||||||
ref.watch(storageRepositoryProvider),
|
ref.watch(storageRepositoryProvider),
|
||||||
ref.watch(backupRepositoryProvider),
|
ref.watch(backupRepositoryProvider),
|
||||||
ref.watch(connectivityApiProvider),
|
ref.watch(connectivityApiProvider),
|
||||||
ref.watch(appSettingsServiceProvider),
|
|
||||||
ref.watch(assetMediaRepositoryProvider),
|
ref.watch(assetMediaRepositoryProvider),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -55,7 +53,6 @@ class ForegroundUploadService {
|
|||||||
this._storageRepository,
|
this._storageRepository,
|
||||||
this._backupRepository,
|
this._backupRepository,
|
||||||
this._connectivityApi,
|
this._connectivityApi,
|
||||||
this._appSettingsService,
|
|
||||||
this._assetMediaRepository,
|
this._assetMediaRepository,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -63,7 +60,6 @@ class ForegroundUploadService {
|
|||||||
final StorageRepository _storageRepository;
|
final StorageRepository _storageRepository;
|
||||||
final DriftBackupRepository _backupRepository;
|
final DriftBackupRepository _backupRepository;
|
||||||
final ConnectivityApi _connectivityApi;
|
final ConnectivityApi _connectivityApi;
|
||||||
final AppSettingsService _appSettingsService;
|
|
||||||
final AssetMediaRepository _assetMediaRepository;
|
final AssetMediaRepository _assetMediaRepository;
|
||||||
final Logger _logger = Logger('ForegroundUploadService');
|
final Logger _logger = Logger('ForegroundUploadService');
|
||||||
|
|
||||||
@@ -455,14 +451,13 @@ class ForegroundUploadService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _shouldRequireWiFi(LocalAsset asset) {
|
bool _shouldRequireWiFi(LocalAsset asset) {
|
||||||
bool requiresWiFi = true;
|
final backup = MetadataRepository.instance.appConfig.backup;
|
||||||
|
if (asset.isVideo && backup.useCellularForVideos) {
|
||||||
if (asset.isVideo && _appSettingsService.getSetting(AppSettingsEnum.useCellularForUploadVideos)) {
|
return false;
|
||||||
requiresWiFi = false;
|
|
||||||
} else if (!asset.isVideo && _appSettingsService.getSetting(AppSettingsEnum.useCellularForUploadPhotos)) {
|
|
||||||
requiresWiFi = false;
|
|
||||||
}
|
}
|
||||||
|
if (!asset.isVideo && backup.useCellularForPhotos) {
|
||||||
return requiresWiFi;
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,6 +124,13 @@ Future<void> _migrateTo26(Drift drift) async {
|
|||||||
await _migrateAlbumSortMode(migrator);
|
await _migrateAlbumSortMode(migrator);
|
||||||
await migrator.migrateBool(StoreKey.legacySelectedAlbumSortReverse, MetadataKey.albumIsReverse);
|
await migrator.migrateBool(StoreKey.legacySelectedAlbumSortReverse, MetadataKey.albumIsReverse);
|
||||||
await migrator.migrateBool(StoreKey.legacyAlbumGridView, MetadataKey.albumIsGrid);
|
await migrator.migrateBool(StoreKey.legacyAlbumGridView, MetadataKey.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.complete();
|
await migrator.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,13 +6,12 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/domain/models/setting.model.dart';
|
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/models/server_info/server_info.model.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/backup/drift_backup.provider.dart';
|
||||||
import 'package:immich_mobile/providers/cast.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/readonly_mode.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/setting.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/server_info.provider.dart';
|
import 'package:immich_mobile/providers/server_info.provider.dart';
|
||||||
import 'package:immich_mobile/providers/sync_status.provider.dart';
|
import 'package:immich_mobile/providers/sync_status.provider.dart';
|
||||||
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||||
@@ -193,64 +192,51 @@ class _BackupIndicator extends ConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget? _getBackupBadgeIcon(BuildContext context, WidgetRef ref) {
|
Widget? _getBackupBadgeIcon(BuildContext context, WidgetRef ref) {
|
||||||
final backupStateStream = ref.watch(settingsProvider).watch(Setting.enableBackup);
|
final backupEnabled = ref.watch(appConfigProvider.select((c) => c.backup.enabled));
|
||||||
final hasError = ref.watch(driftBackupProvider.select((state) => state.error != BackupError.none));
|
final hasError = ref.watch(driftBackupProvider.select((state) => state.error != BackupError.none));
|
||||||
final isDarkTheme = context.isDarkTheme;
|
final isDarkTheme = context.isDarkTheme;
|
||||||
final iconColor = isDarkTheme ? Colors.white : Colors.black;
|
final iconColor = isDarkTheme ? Colors.white : Colors.black;
|
||||||
final isUploading = ref.watch(driftBackupProvider.select((state) => state.uploadItems.isNotEmpty));
|
final isUploading = ref.watch(driftBackupProvider.select((state) => state.uploadItems.isNotEmpty));
|
||||||
|
|
||||||
return StreamBuilder(
|
if (!backupEnabled) {
|
||||||
stream: backupStateStream,
|
return _BadgeLabel(
|
||||||
initialData: false,
|
Icon(Icons.cloud_off_rounded, size: 9, color: iconColor, semanticLabel: 'backup_controller_page_backup'.tr()),
|
||||||
builder: (ctx, snapshot) {
|
);
|
||||||
final backupEnabled = snapshot.data ?? false;
|
}
|
||||||
|
|
||||||
if (!backupEnabled) {
|
if (hasError) {
|
||||||
return _BadgeLabel(
|
return _BadgeLabel(
|
||||||
Icon(
|
Icon(
|
||||||
Icons.cloud_off_rounded,
|
Icons.warning_rounded,
|
||||||
size: 9,
|
size: 12,
|
||||||
color: iconColor,
|
color: context.colorScheme.error,
|
||||||
semanticLabel: 'backup_controller_page_backup'.tr(),
|
semanticLabel: 'backup_controller_page_backup'.tr(),
|
||||||
|
),
|
||||||
|
backgroundColor: context.colorScheme.errorContainer,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isUploading) {
|
||||||
|
return _BadgeLabel(
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(3.5),
|
||||||
|
child: Theme(
|
||||||
|
data: context.themeData.copyWith(
|
||||||
|
progressIndicatorTheme: context.themeData.progressIndicatorTheme.copyWith(year2023: true),
|
||||||
),
|
),
|
||||||
);
|
child: CircularProgressIndicator(
|
||||||
}
|
strokeWidth: 2,
|
||||||
|
strokeCap: StrokeCap.round,
|
||||||
if (hasError) {
|
valueColor: AlwaysStoppedAnimation<Color>(iconColor),
|
||||||
return _BadgeLabel(
|
semanticsLabel: 'backup_controller_page_backup'.tr(),
|
||||||
Icon(
|
|
||||||
Icons.warning_rounded,
|
|
||||||
size: 12,
|
|
||||||
color: context.colorScheme.error,
|
|
||||||
semanticLabel: 'backup_controller_page_backup'.tr(),
|
|
||||||
),
|
),
|
||||||
backgroundColor: context.colorScheme.errorContainer,
|
),
|
||||||
);
|
),
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (isUploading) {
|
return _BadgeLabel(
|
||||||
return _BadgeLabel(
|
Icon(Icons.check_outlined, size: 9, color: iconColor, semanticLabel: 'backup_controller_page_backup'.tr()),
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(3.5),
|
|
||||||
child: Theme(
|
|
||||||
data: context.themeData.copyWith(
|
|
||||||
progressIndicatorTheme: context.themeData.progressIndicatorTheme.copyWith(year2023: true),
|
|
||||||
),
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
strokeWidth: 2,
|
|
||||||
strokeCap: StrokeCap.round,
|
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(iconColor),
|
|
||||||
semanticsLabel: 'backup_controller_page_backup'.tr(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _BadgeLabel(
|
|
||||||
Icon(Icons.check_outlined, size: 9, color: iconColor, semanticLabel: 'backup_controller_page_backup'.tr()),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import 'package:immich_mobile/domain/models/store.model.dart';
|
|||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
|
||||||
import 'package:immich_mobile/providers/auth.provider.dart';
|
import 'package:immich_mobile/providers/auth.provider.dart';
|
||||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
||||||
import 'package:immich_mobile/providers/gallery_permission.provider.dart';
|
import 'package:immich_mobile/providers/gallery_permission.provider.dart';
|
||||||
@@ -186,7 +187,7 @@ class LoginForm extends HookConsumerWidget {
|
|||||||
await backgroundManager.syncRemote();
|
await backgroundManager.syncRemote();
|
||||||
await backgroundManager.hashAssets();
|
await backgroundManager.hashAssets();
|
||||||
|
|
||||||
if (Store.get(StoreKey.syncAlbums, false)) {
|
if (MetadataRepository.instance.appConfig.backup.syncAlbums) {
|
||||||
await backgroundManager.syncLinkedAlbum();
|
await backgroundManager.syncLinkedAlbum();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,18 +4,17 @@ import 'package:easy_localization/easy_localization.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/store.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/services/sync_linked_album.service.dart';
|
import 'package:immich_mobile/domain/services/sync_linked_album.service.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/background_sync.provider.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/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/platform.provider.dart';
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
||||||
import 'package:immich_mobile/widgets/settings/setting_group_title.dart';
|
import 'package:immich_mobile/widgets/settings/setting_group_title.dart';
|
||||||
import 'package:immich_mobile/widgets/settings/setting_list_tile.dart';
|
import 'package:immich_mobile/widgets/settings/setting_list_tile.dart';
|
||||||
import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart';
|
import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart';
|
||||||
@@ -31,8 +30,8 @@ class DriftBackupSettings extends ConsumerWidget {
|
|||||||
title: "network_requirements".t(context: context),
|
title: "network_requirements".t(context: context),
|
||||||
icon: Icons.cell_tower,
|
icon: Icons.cell_tower,
|
||||||
),
|
),
|
||||||
const _UseWifiForUploadVideosButton(),
|
const _UseCellularForVideosButton(),
|
||||||
const _UseWifiForUploadPhotosButton(),
|
const _UseCellularForPhotosButton(),
|
||||||
if (CurrentPlatform.isAndroid) ...[
|
if (CurrentPlatform.isAndroid) ...[
|
||||||
const Divider(),
|
const Divider(),
|
||||||
SettingGroupTitle(
|
SettingGroupTitle(
|
||||||
@@ -99,64 +98,58 @@ class _AlbumSyncActionButtonState extends ConsumerState<_AlbumSyncActionButton>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final albumSyncEnable = ref.watch(appConfigProvider.select((c) => c.backup.syncAlbums));
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(left: 8.0),
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
child: ListView(
|
child: ListView(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
children: [
|
children: [
|
||||||
StreamBuilder(
|
Column(
|
||||||
stream: Store.watch(StoreKey.syncAlbums),
|
children: [
|
||||||
initialData: Store.tryGet(StoreKey.syncAlbums) ?? false,
|
SettingListTile(
|
||||||
builder: (context, snapshot) {
|
title: "sync_albums".t(context: context),
|
||||||
final albumSyncEnable = snapshot.data ?? false;
|
subtitle: "sync_upload_album_setting_subtitle".t(context: context),
|
||||||
return Column(
|
trailing: Switch(
|
||||||
children: [
|
value: albumSyncEnable,
|
||||||
SettingListTile(
|
onChanged: (bool newValue) async {
|
||||||
title: "sync_albums".t(context: context),
|
await ref.read(metadataProvider).write(MetadataKey.backupSyncAlbums, newValue);
|
||||||
subtitle: "sync_upload_album_setting_subtitle".t(context: context),
|
|
||||||
trailing: Switch(
|
|
||||||
value: albumSyncEnable,
|
|
||||||
onChanged: (bool newValue) async {
|
|
||||||
await ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.syncAlbums, newValue);
|
|
||||||
|
|
||||||
if (newValue == true) {
|
if (newValue == true) {
|
||||||
await _manageLinkedAlbums();
|
await _manageLinkedAlbums();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
AnimatedSize(
|
AnimatedSize(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
child: AnimatedOpacity(
|
child: AnimatedOpacity(
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
opacity: albumSyncEnable ? 1.0 : 0.0,
|
opacity: albumSyncEnable ? 1.0 : 0.0,
|
||||||
child: albumSyncEnable
|
child: albumSyncEnable
|
||||||
? SettingListTile(
|
? SettingListTile(
|
||||||
onTap: _manualSyncAlbums,
|
onTap: _manualSyncAlbums,
|
||||||
contentPadding: const EdgeInsets.only(left: 32, right: 16),
|
contentPadding: const EdgeInsets.only(left: 32, right: 16),
|
||||||
title: "organize_into_albums".t(context: context),
|
title: "organize_into_albums".t(context: context),
|
||||||
subtitle: "organize_into_albums_description".t(context: context),
|
subtitle: "organize_into_albums_description".t(context: context),
|
||||||
trailing: isAlbumSyncInProgress
|
trailing: isAlbumSyncInProgress
|
||||||
? const SizedBox(
|
? const SizedBox(
|
||||||
width: 32,
|
width: 32,
|
||||||
height: 32,
|
height: 32,
|
||||||
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
|
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
|
||||||
)
|
)
|
||||||
: IconButton(
|
: IconButton(
|
||||||
onPressed: _manualSyncAlbums,
|
onPressed: _manualSyncAlbums,
|
||||||
icon: const Icon(Icons.sync_rounded),
|
icon: const Icon(Icons.sync_rounded),
|
||||||
color: context.colorScheme.onSurface.withValues(alpha: 0.7),
|
color: context.colorScheme.onSurface.withValues(alpha: 0.7),
|
||||||
iconSize: 20,
|
iconSize: 20,
|
||||||
constraints: const BoxConstraints(minWidth: 32, minHeight: 32),
|
constraints: const BoxConstraints(minWidth: 32, minHeight: 32),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: const SizedBox.shrink(),
|
: const SizedBox.shrink(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -164,60 +157,34 @@ class _AlbumSyncActionButtonState extends ConsumerState<_AlbumSyncActionButton>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SettingsSwitchTile extends ConsumerStatefulWidget {
|
class _BackupSwitchTile extends ConsumerWidget {
|
||||||
final AppSettingsEnum<bool> appSettingsEnum;
|
final MetadataKey<bool> metadataKey;
|
||||||
|
final bool Function(AppConfig) selector;
|
||||||
final String titleKey;
|
final String titleKey;
|
||||||
final String subtitleKey;
|
final String subtitleKey;
|
||||||
final void Function(bool?)? onChanged;
|
final void Function(bool)? onChanged;
|
||||||
|
|
||||||
const _SettingsSwitchTile({
|
const _BackupSwitchTile({
|
||||||
required this.appSettingsEnum,
|
required this.metadataKey,
|
||||||
|
required this.selector,
|
||||||
required this.titleKey,
|
required this.titleKey,
|
||||||
required this.subtitleKey,
|
required this.subtitleKey,
|
||||||
this.onChanged,
|
this.onChanged,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState createState() => _SettingsSwitchTileState();
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
}
|
final value = ref.watch(appConfigProvider.select(selector));
|
||||||
|
|
||||||
class _SettingsSwitchTileState extends ConsumerState<_SettingsSwitchTile> {
|
|
||||||
late final Stream<bool?> valueStream;
|
|
||||||
late final StreamSubscription<bool?> subscription;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
valueStream = Store.watch(widget.appSettingsEnum.storeKey).asBroadcastStream();
|
|
||||||
subscription = valueStream.listen((value) {
|
|
||||||
widget.onChanged?.call(value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
subscription.cancel();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(left: 8.0),
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
child: SettingListTile(
|
child: SettingListTile(
|
||||||
title: widget.titleKey.t(context: context),
|
title: titleKey.t(context: context),
|
||||||
subtitle: widget.subtitleKey.t(context: context),
|
subtitle: subtitleKey.t(context: context),
|
||||||
trailing: StreamBuilder(
|
trailing: Switch(
|
||||||
stream: valueStream,
|
value: value,
|
||||||
initialData: Store.tryGet(widget.appSettingsEnum.storeKey) ?? widget.appSettingsEnum.defaultValue,
|
onChanged: (bool newValue) async {
|
||||||
builder: (context, snapshot) {
|
await ref.read(metadataProvider).write(metadataKey, newValue);
|
||||||
final value = snapshot.data ?? false;
|
onChanged?.call(newValue);
|
||||||
return Switch(
|
|
||||||
value: value,
|
|
||||||
onChanged: (bool newValue) async {
|
|
||||||
await ref.read(appSettingsServiceProvider).setSetting(widget.appSettingsEnum, newValue);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -225,26 +192,28 @@ class _SettingsSwitchTileState extends ConsumerState<_SettingsSwitchTile> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _UseWifiForUploadVideosButton extends ConsumerWidget {
|
class _UseCellularForVideosButton extends StatelessWidget {
|
||||||
const _UseWifiForUploadVideosButton();
|
const _UseCellularForVideosButton();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context) {
|
||||||
return const _SettingsSwitchTile(
|
return _BackupSwitchTile(
|
||||||
appSettingsEnum: AppSettingsEnum.useCellularForUploadVideos,
|
metadataKey: MetadataKey.backupUseCellularForVideos,
|
||||||
|
selector: (c) => c.backup.useCellularForVideos,
|
||||||
titleKey: "videos",
|
titleKey: "videos",
|
||||||
subtitleKey: "network_requirement_videos_upload",
|
subtitleKey: "network_requirement_videos_upload",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _UseWifiForUploadPhotosButton extends ConsumerWidget {
|
class _UseCellularForPhotosButton extends StatelessWidget {
|
||||||
const _UseWifiForUploadPhotosButton();
|
const _UseCellularForPhotosButton();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context) {
|
||||||
return const _SettingsSwitchTile(
|
return _BackupSwitchTile(
|
||||||
appSettingsEnum: AppSettingsEnum.useCellularForUploadPhotos,
|
metadataKey: MetadataKey.backupUseCellularForPhotos,
|
||||||
|
selector: (c) => c.backup.useCellularForPhotos,
|
||||||
titleKey: "photos",
|
titleKey: "photos",
|
||||||
subtitleKey: "network_requirement_photos_upload",
|
subtitleKey: "network_requirement_photos_upload",
|
||||||
);
|
);
|
||||||
@@ -256,29 +225,22 @@ class _BackupOnlyWhenChargingButton extends ConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
return _SettingsSwitchTile(
|
final fgService = ref.read(backgroundWorkerFgServiceProvider);
|
||||||
appSettingsEnum: AppSettingsEnum.backupRequireCharging,
|
return _BackupSwitchTile(
|
||||||
|
metadataKey: MetadataKey.backupRequireCharging,
|
||||||
|
selector: (c) => c.backup.requireCharging,
|
||||||
titleKey: "charging",
|
titleKey: "charging",
|
||||||
subtitleKey: "charging_requirement_mobile_backup",
|
subtitleKey: "charging_requirement_mobile_backup",
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
ref.read(backgroundWorkerFgServiceProvider).configure(requireCharging: value ?? false);
|
fgService.configure(requireCharging: value);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _BackupDelaySlider extends ConsumerStatefulWidget {
|
class _BackupDelaySlider extends ConsumerWidget {
|
||||||
const _BackupDelaySlider();
|
const _BackupDelaySlider();
|
||||||
|
|
||||||
@override
|
|
||||||
ConsumerState<_BackupDelaySlider> createState() => _BackupDelaySliderState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _BackupDelaySliderState extends ConsumerState<_BackupDelaySlider> {
|
|
||||||
late final Stream<int?> valueStream;
|
|
||||||
late final StreamSubscription<int?> subscription;
|
|
||||||
late int currentValue;
|
|
||||||
|
|
||||||
static int backupDelayToSliderValue(int ms) => switch (ms) {
|
static int backupDelayToSliderValue(int ms) => switch (ms) {
|
||||||
5 => 0,
|
5 => 0,
|
||||||
30 => 1,
|
30 => 1,
|
||||||
@@ -301,30 +263,9 @@ class _BackupDelaySliderState extends ConsumerState<_BackupDelaySlider> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
super.initState();
|
final triggerDelay = ref.watch(appConfigProvider.select((c) => c.backup.triggerDelay));
|
||||||
final initialValue =
|
final currentValue = backupDelayToSliderValue(triggerDelay);
|
||||||
Store.tryGet(AppSettingsEnum.backupTriggerDelay.storeKey) ?? AppSettingsEnum.backupTriggerDelay.defaultValue;
|
|
||||||
currentValue = backupDelayToSliderValue(initialValue);
|
|
||||||
|
|
||||||
valueStream = Store.watch(AppSettingsEnum.backupTriggerDelay.storeKey).asBroadcastStream();
|
|
||||||
subscription = valueStream.listen((value) {
|
|
||||||
if (mounted && value != null) {
|
|
||||||
setState(() {
|
|
||||||
currentValue = backupDelayToSliderValue(value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
subscription.cancel();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@@ -339,14 +280,13 @@ class _BackupDelaySliderState extends ConsumerState<_BackupDelaySlider> {
|
|||||||
),
|
),
|
||||||
Slider(
|
Slider(
|
||||||
value: currentValue.toDouble(),
|
value: currentValue.toDouble(),
|
||||||
onChanged: (double v) {
|
onChanged: (double v) async {
|
||||||
setState(() {
|
final seconds = backupDelayToSeconds(v.toInt());
|
||||||
currentValue = v.toInt();
|
await ref.read(metadataProvider).write(MetadataKey.backupTriggerDelay, seconds);
|
||||||
});
|
|
||||||
},
|
},
|
||||||
onChangeEnd: (double v) async {
|
onChangeEnd: (double v) async {
|
||||||
final milliseconds = backupDelayToSeconds(v.toInt());
|
final seconds = backupDelayToSeconds(v.toInt());
|
||||||
await ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.backupTriggerDelay, milliseconds);
|
await ref.read(metadataProvider).write(MetadataKey.backupTriggerDelay, seconds);
|
||||||
},
|
},
|
||||||
max: 3.0,
|
max: 3.0,
|
||||||
min: 0.0,
|
min: 0.0,
|
||||||
|
|||||||
@@ -78,6 +78,15 @@ alias = "migration"
|
|||||||
description = "Generate database migrations"
|
description = "Generate database migrations"
|
||||||
run = "dart run drift_dev make-migrations"
|
run = "dart run drift_dev make-migrations"
|
||||||
|
|
||||||
|
[tasks.install]
|
||||||
|
alias = "install"
|
||||||
|
description = "Install flutter dependencies"
|
||||||
|
run = "flutter pub get"
|
||||||
|
|
||||||
|
[tasks.start]
|
||||||
|
alias = "start"
|
||||||
|
description = "Start flutter app"
|
||||||
|
run = "flutter run"
|
||||||
|
|
||||||
# Internal tasks
|
# Internal tasks
|
||||||
[tasks."i18n:loader"]
|
[tasks."i18n:loader"]
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import 'package:mocktail/mocktail.dart';
|
|||||||
import '../../infrastructure/repository.mock.dart';
|
import '../../infrastructure/repository.mock.dart';
|
||||||
|
|
||||||
const _kAccessToken = '#ThisIsAToken';
|
const _kAccessToken = '#ThisIsAToken';
|
||||||
const _kEnableBackup = false;
|
const _kAdvancedTroubleshooting = false;
|
||||||
const _kVersion = 2;
|
const _kVersion = 2;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
@@ -22,13 +22,13 @@ void main() {
|
|||||||
mockDriftStoreRepo = MockDriftStoreRepository();
|
mockDriftStoreRepo = MockDriftStoreRepository();
|
||||||
// For generics, we need to provide fallback to each concrete type to avoid runtime errors
|
// For generics, we need to provide fallback to each concrete type to avoid runtime errors
|
||||||
registerFallbackValue(StoreKey.accessToken);
|
registerFallbackValue(StoreKey.accessToken);
|
||||||
registerFallbackValue(StoreKey.backupTriggerDelay);
|
registerFallbackValue(StoreKey.version);
|
||||||
registerFallbackValue(StoreKey.enableBackup);
|
registerFallbackValue(StoreKey.advancedTroubleshooting);
|
||||||
|
|
||||||
when(() => mockDriftStoreRepo.getAll()).thenAnswer(
|
when(() => mockDriftStoreRepo.getAll()).thenAnswer(
|
||||||
(_) async => [
|
(_) async => [
|
||||||
const StoreDto(StoreKey.accessToken, _kAccessToken),
|
const StoreDto(StoreKey.accessToken, _kAccessToken),
|
||||||
const StoreDto(StoreKey.enableBackup, _kEnableBackup),
|
const StoreDto(StoreKey.advancedTroubleshooting, _kAdvancedTroubleshooting),
|
||||||
const StoreDto(StoreKey.version, _kVersion),
|
const StoreDto(StoreKey.version, _kVersion),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -46,7 +46,7 @@ void main() {
|
|||||||
test('Populates the internal cache on init', () {
|
test('Populates the internal cache on init', () {
|
||||||
verify(() => mockDriftStoreRepo.getAll()).called(1);
|
verify(() => mockDriftStoreRepo.getAll()).called(1);
|
||||||
expect(sut.tryGet(StoreKey.accessToken), _kAccessToken);
|
expect(sut.tryGet(StoreKey.accessToken), _kAccessToken);
|
||||||
expect(sut.tryGet(StoreKey.enableBackup), _kEnableBackup);
|
expect(sut.tryGet(StoreKey.advancedTroubleshooting), _kAdvancedTroubleshooting);
|
||||||
expect(sut.tryGet(StoreKey.version), _kVersion);
|
expect(sut.tryGet(StoreKey.version), _kVersion);
|
||||||
// Other keys should be null
|
// Other keys should be null
|
||||||
expect(sut.tryGet(StoreKey.currentUser), isNull);
|
expect(sut.tryGet(StoreKey.currentUser), isNull);
|
||||||
@@ -147,7 +147,7 @@ void main() {
|
|||||||
await sut.clear();
|
await sut.clear();
|
||||||
verify(() => mockDriftStoreRepo.deleteAll()).called(1);
|
verify(() => mockDriftStoreRepo.deleteAll()).called(1);
|
||||||
expect(sut.tryGet(StoreKey.accessToken), isNull);
|
expect(sut.tryGet(StoreKey.accessToken), isNull);
|
||||||
expect(sut.tryGet(StoreKey.enableBackup), isNull);
|
expect(sut.tryGet(StoreKey.advancedTroubleshooting), isNull);
|
||||||
expect(sut.tryGet(StoreKey.version), isNull);
|
expect(sut.tryGet(StoreKey.version), isNull);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import '../../fixtures/user.stub.dart';
|
|||||||
|
|
||||||
const _kTestAccessToken = "#TestToken";
|
const _kTestAccessToken = "#TestToken";
|
||||||
const _kTestVersion = 10;
|
const _kTestVersion = 10;
|
||||||
const _kTestBackupRequireCharging = false;
|
const _kTestAdvancedTroubleshooting = false;
|
||||||
final _kTestUser = UserStub.admin;
|
final _kTestUser = UserStub.admin;
|
||||||
|
|
||||||
Future<void> _populateStore(Drift db) async {
|
Future<void> _populateStore(Drift db) async {
|
||||||
@@ -21,8 +21,8 @@ Future<void> _populateStore(Drift db) async {
|
|||||||
batch.insert(
|
batch.insert(
|
||||||
db.storeEntity,
|
db.storeEntity,
|
||||||
StoreEntityCompanion(
|
StoreEntityCompanion(
|
||||||
id: Value(StoreKey.backupRequireCharging.id),
|
id: Value(StoreKey.advancedTroubleshooting.id),
|
||||||
intValue: const Value(_kTestBackupRequireCharging ? 1 : 0),
|
intValue: const Value(_kTestAdvancedTroubleshooting ? 1 : 0),
|
||||||
stringValue: const Value(null),
|
stringValue: const Value(null),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -76,11 +76,11 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('converts bool', () async {
|
test('converts bool', () async {
|
||||||
bool? backupRequireCharging = await sut.tryGet(StoreKey.backupRequireCharging);
|
bool? advancedTroubleshooting = await sut.tryGet(StoreKey.advancedTroubleshooting);
|
||||||
expect(backupRequireCharging, isNull);
|
expect(advancedTroubleshooting, isNull);
|
||||||
await sut.upsert(StoreKey.backupRequireCharging, _kTestBackupRequireCharging);
|
await sut.upsert(StoreKey.advancedTroubleshooting, _kTestAdvancedTroubleshooting);
|
||||||
backupRequireCharging = await sut.tryGet(StoreKey.backupRequireCharging);
|
advancedTroubleshooting = await sut.tryGet(StoreKey.advancedTroubleshooting);
|
||||||
expect(backupRequireCharging, _kTestBackupRequireCharging);
|
expect(advancedTroubleshooting, _kTestAdvancedTroubleshooting);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('converts user', () async {
|
test('converts user', () async {
|
||||||
@@ -98,11 +98,11 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('delete()', () async {
|
test('delete()', () async {
|
||||||
bool? backupRequireCharging = await sut.tryGet(StoreKey.backupRequireCharging);
|
bool? advancedTroubleshooting = await sut.tryGet(StoreKey.advancedTroubleshooting);
|
||||||
expect(backupRequireCharging, isFalse);
|
expect(advancedTroubleshooting, isFalse);
|
||||||
await sut.delete(StoreKey.backupRequireCharging);
|
await sut.delete(StoreKey.advancedTroubleshooting);
|
||||||
backupRequireCharging = await sut.tryGet(StoreKey.backupRequireCharging);
|
advancedTroubleshooting = await sut.tryGet(StoreKey.advancedTroubleshooting);
|
||||||
expect(backupRequireCharging, isNull);
|
expect(advancedTroubleshooting, isNull);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('deleteAll()', () async {
|
test('deleteAll()', () async {
|
||||||
@@ -147,13 +147,13 @@ void main() {
|
|||||||
emitsInOrder([
|
emitsInOrder([
|
||||||
[
|
[
|
||||||
const StoreDto<Object>(StoreKey.version, _kTestVersion),
|
const StoreDto<Object>(StoreKey.version, _kTestVersion),
|
||||||
const StoreDto<Object>(StoreKey.backupRequireCharging, _kTestBackupRequireCharging),
|
|
||||||
const StoreDto<Object>(StoreKey.accessToken, _kTestAccessToken),
|
const StoreDto<Object>(StoreKey.accessToken, _kTestAccessToken),
|
||||||
|
const StoreDto<Object>(StoreKey.advancedTroubleshooting, _kTestAdvancedTroubleshooting),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
const StoreDto<Object>(StoreKey.version, _kTestVersion + 10),
|
const StoreDto<Object>(StoreKey.version, _kTestVersion + 10),
|
||||||
const StoreDto<Object>(StoreKey.backupRequireCharging, _kTestBackupRequireCharging),
|
|
||||||
const StoreDto<Object>(StoreKey.accessToken, _kTestAccessToken),
|
const StoreDto<Object>(StoreKey.accessToken, _kTestAccessToken),
|
||||||
|
const StoreDto<Object>(StoreKey.advancedTroubleshooting, _kTestAdvancedTroubleshooting),
|
||||||
],
|
],
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ void main() {
|
|||||||
late MockApiService apiService;
|
late MockApiService apiService;
|
||||||
late MockNetworkService networkService;
|
late MockNetworkService networkService;
|
||||||
late MockBackgroundSyncManager backgroundSyncManager;
|
late MockBackgroundSyncManager backgroundSyncManager;
|
||||||
late MockAppSettingService appSettingsService;
|
|
||||||
late Drift db;
|
late Drift db;
|
||||||
|
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
@@ -30,15 +29,12 @@ void main() {
|
|||||||
apiService = MockApiService();
|
apiService = MockApiService();
|
||||||
networkService = MockNetworkService();
|
networkService = MockNetworkService();
|
||||||
backgroundSyncManager = MockBackgroundSyncManager();
|
backgroundSyncManager = MockBackgroundSyncManager();
|
||||||
appSettingsService = MockAppSettingService();
|
|
||||||
|
|
||||||
sut = AuthService(
|
sut = AuthService(
|
||||||
authApiRepository,
|
authApiRepository,
|
||||||
authRepository,
|
authRepository,
|
||||||
apiService,
|
apiService,
|
||||||
networkService,
|
networkService,
|
||||||
backgroundSyncManager,
|
backgroundSyncManager,
|
||||||
appSettingsService,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
registerFallbackValue(Uri());
|
registerFallbackValue(Uri());
|
||||||
|
|||||||
@@ -13,11 +13,9 @@ import 'package:immich_mobile/entities/store.entity.dart';
|
|||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
||||||
import 'package:immich_mobile/services/background_upload.service.dart';
|
import 'package:immich_mobile/services/background_upload.service.dart';
|
||||||
import 'package:mocktail/mocktail.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
|
||||||
import '../domain/service.mock.dart';
|
|
||||||
import '../fixtures/asset.stub.dart';
|
import '../fixtures/asset.stub.dart';
|
||||||
import '../infrastructure/repository.mock.dart';
|
import '../infrastructure/repository.mock.dart';
|
||||||
import '../mocks/asset_entity.mock.dart';
|
import '../mocks/asset_entity.mock.dart';
|
||||||
@@ -29,13 +27,10 @@ void main() {
|
|||||||
late MockStorageRepository mockStorageRepository;
|
late MockStorageRepository mockStorageRepository;
|
||||||
late MockDriftLocalAssetRepository mockLocalAssetRepository;
|
late MockDriftLocalAssetRepository mockLocalAssetRepository;
|
||||||
late MockDriftBackupRepository mockBackupRepository;
|
late MockDriftBackupRepository mockBackupRepository;
|
||||||
late MockAppSettingsService mockAppSettingsService;
|
|
||||||
late MockAssetMediaRepository mockAssetMediaRepository;
|
late MockAssetMediaRepository mockAssetMediaRepository;
|
||||||
late Drift db;
|
late Drift db;
|
||||||
|
|
||||||
setUpAll(() async {
|
setUpAll(() async {
|
||||||
registerFallbackValue(AppSettingsEnum.useCellularForUploadPhotos);
|
|
||||||
|
|
||||||
TestWidgetsFlutterBinding.ensureInitialized();
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||||
const MethodChannel('plugins.flutter.io/path_provider'),
|
const MethodChannel('plugins.flutter.io/path_provider'),
|
||||||
@@ -54,18 +49,13 @@ void main() {
|
|||||||
mockStorageRepository = MockStorageRepository();
|
mockStorageRepository = MockStorageRepository();
|
||||||
mockLocalAssetRepository = MockDriftLocalAssetRepository();
|
mockLocalAssetRepository = MockDriftLocalAssetRepository();
|
||||||
mockBackupRepository = MockDriftBackupRepository();
|
mockBackupRepository = MockDriftBackupRepository();
|
||||||
mockAppSettingsService = MockAppSettingsService();
|
|
||||||
mockAssetMediaRepository = MockAssetMediaRepository();
|
mockAssetMediaRepository = MockAssetMediaRepository();
|
||||||
|
|
||||||
when(() => mockAppSettingsService.getSetting(AppSettingsEnum.useCellularForUploadVideos)).thenReturn(false);
|
|
||||||
when(() => mockAppSettingsService.getSetting(AppSettingsEnum.useCellularForUploadPhotos)).thenReturn(false);
|
|
||||||
|
|
||||||
sut = BackgroundUploadService(
|
sut = BackgroundUploadService(
|
||||||
mockUploadRepository,
|
mockUploadRepository,
|
||||||
mockStorageRepository,
|
mockStorageRepository,
|
||||||
mockLocalAssetRepository,
|
mockLocalAssetRepository,
|
||||||
mockBackupRepository,
|
mockBackupRepository,
|
||||||
mockAppSettingsService,
|
|
||||||
mockAssetMediaRepository,
|
mockAssetMediaRepository,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -181,7 +171,6 @@ void main() {
|
|||||||
mockStorageRepository,
|
mockStorageRepository,
|
||||||
mockLocalAssetRepository,
|
mockLocalAssetRepository,
|
||||||
mockBackupRepository,
|
mockBackupRepository,
|
||||||
mockAppSettingsService,
|
|
||||||
mockAssetMediaRepository,
|
mockAssetMediaRepository,
|
||||||
);
|
);
|
||||||
addTearDown(() => sutWithV24.dispose());
|
addTearDown(() => sutWithV24.dispose());
|
||||||
@@ -232,7 +221,6 @@ void main() {
|
|||||||
mockStorageRepository,
|
mockStorageRepository,
|
||||||
mockLocalAssetRepository,
|
mockLocalAssetRepository,
|
||||||
mockBackupRepository,
|
mockBackupRepository,
|
||||||
mockAppSettingsService,
|
|
||||||
mockAssetMediaRepository,
|
mockAssetMediaRepository,
|
||||||
);
|
);
|
||||||
addTearDown(() => sutAndroid.dispose());
|
addTearDown(() => sutAndroid.dispose());
|
||||||
@@ -273,7 +261,6 @@ void main() {
|
|||||||
mockStorageRepository,
|
mockStorageRepository,
|
||||||
mockLocalAssetRepository,
|
mockLocalAssetRepository,
|
||||||
mockBackupRepository,
|
mockBackupRepository,
|
||||||
mockAppSettingsService,
|
|
||||||
mockAssetMediaRepository,
|
mockAssetMediaRepository,
|
||||||
);
|
);
|
||||||
addTearDown(() => sutWithV24.dispose());
|
addTearDown(() => sutWithV24.dispose());
|
||||||
@@ -314,7 +301,6 @@ void main() {
|
|||||||
mockStorageRepository,
|
mockStorageRepository,
|
||||||
mockLocalAssetRepository,
|
mockLocalAssetRepository,
|
||||||
mockBackupRepository,
|
mockBackupRepository,
|
||||||
mockAppSettingsService,
|
|
||||||
mockAssetMediaRepository,
|
mockAssetMediaRepository,
|
||||||
);
|
);
|
||||||
addTearDown(() => sutWithV24.dispose());
|
addTearDown(() => sutWithV24.dispose());
|
||||||
|
|||||||
@@ -11,9 +11,6 @@
|
|||||||
"required": true,
|
"required": true,
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"description": "Album ID",
|
"description": "Album ID",
|
||||||
"x-nestjs_zod-parent-metadata": {
|
|
||||||
"description": "Activity search"
|
|
||||||
},
|
|
||||||
"schema": {
|
"schema": {
|
||||||
"format": "uuid",
|
"format": "uuid",
|
||||||
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$",
|
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$",
|
||||||
@@ -25,9 +22,6 @@
|
|||||||
"required": false,
|
"required": false,
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"description": "Asset ID (if activity is for an asset)",
|
"description": "Asset ID (if activity is for an asset)",
|
||||||
"x-nestjs_zod-parent-metadata": {
|
|
||||||
"description": "Activity search"
|
|
||||||
},
|
|
||||||
"schema": {
|
"schema": {
|
||||||
"format": "uuid",
|
"format": "uuid",
|
||||||
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$",
|
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$",
|
||||||
@@ -38,9 +32,6 @@
|
|||||||
"name": "level",
|
"name": "level",
|
||||||
"required": false,
|
"required": false,
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"x-nestjs_zod-parent-metadata": {
|
|
||||||
"description": "Activity search"
|
|
||||||
},
|
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/components/schemas/ReactionLevel"
|
"$ref": "#/components/schemas/ReactionLevel"
|
||||||
}
|
}
|
||||||
@@ -49,9 +40,6 @@
|
|||||||
"name": "type",
|
"name": "type",
|
||||||
"required": false,
|
"required": false,
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"x-nestjs_zod-parent-metadata": {
|
|
||||||
"description": "Activity search"
|
|
||||||
},
|
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/components/schemas/ReactionType"
|
"$ref": "#/components/schemas/ReactionType"
|
||||||
}
|
}
|
||||||
@@ -61,9 +49,6 @@
|
|||||||
"required": false,
|
"required": false,
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"description": "Filter by user ID",
|
"description": "Filter by user ID",
|
||||||
"x-nestjs_zod-parent-metadata": {
|
|
||||||
"description": "Activity search"
|
|
||||||
},
|
|
||||||
"schema": {
|
"schema": {
|
||||||
"format": "uuid",
|
"format": "uuid",
|
||||||
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$",
|
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$",
|
||||||
@@ -187,9 +172,6 @@
|
|||||||
"required": true,
|
"required": true,
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"description": "Album ID",
|
"description": "Album ID",
|
||||||
"x-nestjs_zod-parent-metadata": {
|
|
||||||
"description": "Activity"
|
|
||||||
},
|
|
||||||
"schema": {
|
"schema": {
|
||||||
"format": "uuid",
|
"format": "uuid",
|
||||||
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$",
|
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$",
|
||||||
@@ -201,9 +183,6 @@
|
|||||||
"required": false,
|
"required": false,
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"description": "Asset ID (if activity is for an asset)",
|
"description": "Asset ID (if activity is for an asset)",
|
||||||
"x-nestjs_zod-parent-metadata": {
|
|
||||||
"description": "Activity"
|
|
||||||
},
|
|
||||||
"schema": {
|
"schema": {
|
||||||
"format": "uuid",
|
"format": "uuid",
|
||||||
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$",
|
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$",
|
||||||
|
|||||||
@@ -36,18 +36,16 @@ const ActivityStatisticsResponseSchema = z
|
|||||||
})
|
})
|
||||||
.meta({ id: 'ActivityStatisticsResponseDto' });
|
.meta({ id: 'ActivityStatisticsResponseDto' });
|
||||||
|
|
||||||
const ActivitySchema = z
|
const ActivitySchema = z.object({
|
||||||
.object({
|
albumId: z.uuidv4().describe('Album ID'),
|
||||||
albumId: z.uuidv4().describe('Album ID'),
|
assetId: z.uuidv4().optional().describe('Asset ID (if activity is for an asset)'),
|
||||||
assetId: z.uuidv4().optional().describe('Asset ID (if activity is for an asset)'),
|
});
|
||||||
})
|
|
||||||
.describe('Activity');
|
|
||||||
|
|
||||||
const ActivitySearchSchema = ActivitySchema.extend({
|
const ActivitySearchSchema = ActivitySchema.extend({
|
||||||
type: ReactionTypeSchema.optional(),
|
type: ReactionTypeSchema.optional(),
|
||||||
level: ReactionLevelSchema.optional(),
|
level: ReactionLevelSchema.optional(),
|
||||||
userId: z.uuidv4().optional().describe('Filter by user ID'),
|
userId: z.uuidv4().optional().describe('Filter by user ID'),
|
||||||
}).describe('Activity search');
|
});
|
||||||
|
|
||||||
const ActivityCreateSchema = ActivitySchema.extend({
|
const ActivityCreateSchema = ActivitySchema.extend({
|
||||||
type: ReactionTypeSchema,
|
type: ReactionTypeSchema,
|
||||||
|
|||||||
Reference in New Issue
Block a user