Compare commits
131 Commits
v1.134.0
...
feat/maint
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab764f2423 | ||
|
|
16745e77d4 | ||
|
|
c0ed2210b4 | ||
|
|
f9ed314b37 | ||
|
|
160ca28253 | ||
|
|
9380625762 | ||
|
|
ade7cd258d | ||
|
|
63996f4dd3 | ||
|
|
360f68b86b | ||
|
|
bf212bf235 | ||
|
|
b890440f6b | ||
|
|
16f83c0aa9 | ||
|
|
2572413b2b | ||
|
|
14d785cec9 | ||
|
|
242817c49a | ||
|
|
74f79cae69 | ||
|
|
ac0e94c003 | ||
|
|
047c7821a3 | ||
|
|
ccb0e711f0 | ||
|
|
3fb2c3a7bf | ||
|
|
197a1886c3 | ||
|
|
adac30c9a1 | ||
|
|
ae04a62030 | ||
|
|
02246cdd1f | ||
|
|
2d05a5482f | ||
|
|
7b2237b86b | ||
|
|
84024f6cdc | ||
|
|
5574b2dd39 | ||
|
|
e88eb44aba | ||
|
|
75c24f0023 | ||
|
|
e376366b7b | ||
|
|
48e16f0a5a | ||
|
|
e8ba9dd208 | ||
|
|
a932cbae38 | ||
|
|
526206b2a5 | ||
|
|
de2115d11e | ||
|
|
e0ac588ca8 | ||
|
|
0c965ae2ea | ||
|
|
28e05537bd | ||
|
|
acca040524 | ||
|
|
b0a0ae6cd3 | ||
|
|
fb4be6e231 | ||
|
|
ecb16d9907 | ||
|
|
737fedd527 | ||
|
|
b557f3b7f2 | ||
|
|
ce6631f7e0 | ||
|
|
b46e066cc2 | ||
|
|
55f4e93456 | ||
|
|
81423420c8 | ||
|
|
a9bd651692 | ||
|
|
afda7b9525 | ||
|
|
86f64fd0bf | ||
|
|
19013af58f | ||
|
|
e746d27f5e | ||
|
|
90c8fdba96 | ||
|
|
e2ffc9d5a1 | ||
|
|
f64a3003af | ||
|
|
a26d703335 | ||
|
|
5d0ad853f4 | ||
|
|
19ff39c2b9 | ||
|
|
8733d1e554 | ||
|
|
1fb8861e35 | ||
|
|
70b9a4c8f1 | ||
|
|
2da94439c7 | ||
|
|
3d3e5dc547 | ||
|
|
daf1bee7ac | ||
|
|
6b4d5e3beb | ||
|
|
6b9233c71a | ||
|
|
b4a798c39f | ||
|
|
edae9c2d3d | ||
|
|
246d593c9d | ||
|
|
e4322ae0a2 | ||
|
|
e506c7fb19 | ||
|
|
393e8d50b2 | ||
|
|
74438f5bd8 | ||
|
|
e7d7886f44 | ||
|
|
97e86e409a | ||
|
|
72401aa6b1 | ||
|
|
fb94fd3132 | ||
|
|
a02e1f5e7c | ||
|
|
d544053c67 | ||
|
|
df927dd3ce | ||
|
|
d48702f943 | ||
|
|
fa22e865a4 | ||
|
|
b5c3a675b2 | ||
|
|
5589616921 | ||
|
|
a53d033622 | ||
|
|
36506250c4 | ||
|
|
31af44dd2a | ||
|
|
c89ac5b5e5 | ||
|
|
daf1a48b54 | ||
|
|
091a101f39 | ||
|
|
d118b46c3f | ||
|
|
ad3f58bcda | ||
|
|
0711a9006f | ||
|
|
9c18fef9b2 | ||
|
|
d00c872dc1 | ||
|
|
3a5fed99e1 | ||
|
|
e2defbc49a | ||
|
|
f4e4e6628e | ||
|
|
9d04853b34 | ||
|
|
97503d11c5 | ||
|
|
cbf68b006e | ||
|
|
4b9a7b2ce0 | ||
|
|
b854a3dd47 | ||
|
|
aebd68e24e | ||
|
|
0f42babb6b | ||
|
|
dbdb64f6c5 | ||
|
|
2b1b20ab0b | ||
|
|
44d49b9671 | ||
|
|
0e81c20cbb | ||
|
|
1f18a09061 | ||
|
|
0257f1a743 | ||
|
|
6f39a706b2 | ||
|
|
10181defb1 | ||
|
|
8ea40973a7 | ||
|
|
be247395db | ||
|
|
78224961d1 | ||
|
|
b054e9dc2c | ||
|
|
f0d881b4f8 | ||
|
|
9677eb37e1 | ||
|
|
dc23bc4d55 | ||
|
|
e9f8d68f62 | ||
|
|
3f08768854 | ||
|
|
f029910dc7 | ||
|
|
b5593823a2 | ||
|
|
a40d35555f | ||
|
|
0205e89e34 | ||
|
|
a231d7be64 | ||
|
|
219f5b25a4 | ||
|
|
486bb47ddb |
@@ -1,4 +1,4 @@
|
||||
ARG BASEIMAGE=mcr.microsoft.com/devcontainers/typescript-node:22@sha256:a20b8a3538313487ac9266875bbf733e544c1aa2091df2bb99ab592a6d4f7399
|
||||
ARG BASEIMAGE=mcr.microsoft.com/devcontainers/typescript-node:22@sha256:7c2e711a4f7b02f32d2da16192d5e05aa7c95279be4ce889cff5df316f251c1d
|
||||
FROM ${BASEIMAGE}
|
||||
|
||||
# Flutter SDK
|
||||
|
||||
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -9,6 +9,9 @@ mobile/lib/**/*.g.dart linguist-generated=true
|
||||
mobile/lib/**/*.drift.dart -diff -merge
|
||||
mobile/lib/**/*.drift.dart linguist-generated=true
|
||||
|
||||
mobile/drift_schemas/main/drift_schema_*.json -diff -merge
|
||||
mobile/drift_schemas/main/drift_schema_*.json linguist-generated=true
|
||||
|
||||
open-api/typescript-sdk/fetch-client.ts -diff -merge
|
||||
open-api/typescript-sdk/fetch-client.ts linguist-generated=true
|
||||
|
||||
|
||||
118
.github/actions/image-build/action.yml
vendored
118
.github/actions/image-build/action.yml
vendored
@@ -1,118 +0,0 @@
|
||||
name: 'Single arch image build'
|
||||
description: 'Build single-arch image on platform appropriate runner'
|
||||
inputs:
|
||||
image:
|
||||
description: 'Name of the image to build'
|
||||
required: true
|
||||
ghcr-token:
|
||||
description: 'GitHub Container Registry token'
|
||||
required: true
|
||||
platform:
|
||||
description: 'Platform to build for'
|
||||
required: true
|
||||
artifact-key-base:
|
||||
description: 'Base key for artifact name'
|
||||
required: true
|
||||
context:
|
||||
description: 'Path to build context'
|
||||
required: true
|
||||
dockerfile:
|
||||
description: 'Path to Dockerfile'
|
||||
required: true
|
||||
build-args:
|
||||
description: 'Docker build arguments'
|
||||
required: false
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Prepare
|
||||
id: prepare
|
||||
shell: bash
|
||||
env:
|
||||
PLATFORM: ${{ inputs.platform }}
|
||||
run: |
|
||||
echo "platform-pair=${PLATFORM//\//-}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ inputs.ghcr-token }}
|
||||
|
||||
- name: Generate cache key suffix
|
||||
id: cache-key-suffix
|
||||
shell: bash
|
||||
env:
|
||||
REF: ${{ github.ref_name }}
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
echo "cache-key-suffix=pr-${{ github.event.number }}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
SUFFIX=$(echo "${REF}" | sed 's/[^a-zA-Z0-9]/-/g')
|
||||
echo "suffix=${SUFFIX}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Generate cache target
|
||||
id: cache-target
|
||||
shell: bash
|
||||
env:
|
||||
BUILD_ARGS: ${{ inputs.build-args }}
|
||||
IMAGE: ${{ inputs.image }}
|
||||
SUFFIX: ${{ steps.cache-key-suffix.outputs.suffix }}
|
||||
PLATFORM_PAIR: ${{ steps.prepare.outputs.platform-pair }}
|
||||
run: |
|
||||
HASH=$(sha256sum <<< "${BUILD_ARGS}" | cut -d' ' -f1)
|
||||
CACHE_KEY="${PLATFORM_PAIR}-${HASH}"
|
||||
echo "cache-key-base=${CACHE_KEY}" >> $GITHUB_OUTPUT
|
||||
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
|
||||
# Essentially just ignore the cache output (forks can't write to registry cache)
|
||||
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "cache-to=type=registry,ref=${IMAGE}-build-cache:${CACHE_KEY}-${SUFFIX},mode=max,compression=zstd" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Generate docker image tags
|
||||
id: meta
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
||||
env:
|
||||
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
||||
|
||||
- name: Build and push image
|
||||
id: build
|
||||
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0
|
||||
with:
|
||||
context: ${{ inputs.context }}
|
||||
file: ${{ inputs.dockerfile }}
|
||||
platforms: ${{ inputs.platform }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
||||
cache-from: |
|
||||
type=registry,ref=${{ inputs.image }}-build-cache:${{ steps.cache-target.outputs.cache-key-base }}-${{ steps.cache-key-suffix.outputs.suffix }}
|
||||
type=registry,ref=${{ inputs.image }}-build-cache:${{ steps.cache-target.outputs.cache-key-base }}-main
|
||||
outputs: type=image,"name=${{ inputs.image }}",push-by-digest=true,name-canonical=true,push=${{ !github.event.pull_request.head.repo.fork }}
|
||||
build-args: |
|
||||
BUILD_ID=${{ github.run_id }}
|
||||
BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.meta.outputs.tags }}
|
||||
BUILD_SOURCE_REF=${{ github.ref_name }}
|
||||
BUILD_SOURCE_COMMIT=${{ github.sha }}
|
||||
${{ inputs.build-args }}
|
||||
|
||||
- name: Export digest
|
||||
shell: bash
|
||||
run: | # zizmor: ignore[template-injection]
|
||||
mkdir -p ${{ runner.temp }}/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: ${{ inputs.artifact-key-base }}-${{ steps.cache-target.outputs.cache-key-base }}
|
||||
path: ${{ runner.temp }}/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
4
.github/workflows/build-mobile.yml
vendored
4
.github/workflows/build-mobile.yml
vendored
@@ -93,6 +93,10 @@ jobs:
|
||||
run: make translation
|
||||
working-directory: ./mobile
|
||||
|
||||
- name: Generate platform APIs
|
||||
run: make pigeon
|
||||
working-directory: ./mobile
|
||||
|
||||
- name: Build Android App Bundle
|
||||
working-directory: ./mobile
|
||||
env:
|
||||
|
||||
24
.github/workflows/docker.yml
vendored
24
.github/workflows/docker.yml
vendored
@@ -131,7 +131,7 @@ jobs:
|
||||
tag-suffix: '-rocm'
|
||||
platforms: linux/amd64
|
||||
runner-mapping: '{"linux/amd64": "mich"}'
|
||||
uses: ./.github/workflows/multi-runner-build.yml
|
||||
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@094bfb927b8cd75b343abaac27b3241be0fccfe9 # multi-runner-build-workflow-0.1.0
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
@@ -154,7 +154,7 @@ jobs:
|
||||
name: Build and Push Server
|
||||
needs: pre-job
|
||||
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
||||
uses: ./.github/workflows/multi-runner-build.yml
|
||||
uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@094bfb927b8cd75b343abaac27b3241be0fccfe9 # multi-runner-build-workflow-0.1.0
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
@@ -177,13 +177,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: always()
|
||||
steps:
|
||||
- name: Any jobs failed?
|
||||
if: ${{ contains(needs.*.result, 'failure') }}
|
||||
run: exit 1
|
||||
- name: All jobs passed or skipped
|
||||
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
||||
# zizmor: ignore[template-injection]
|
||||
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
||||
- uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
|
||||
with:
|
||||
needs: ${{ toJSON(needs) }}
|
||||
|
||||
success-check-ml:
|
||||
name: Docker Build & Push ML Success
|
||||
@@ -192,10 +188,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: always()
|
||||
steps:
|
||||
- name: Any jobs failed?
|
||||
if: ${{ contains(needs.*.result, 'failure') }}
|
||||
run: exit 1
|
||||
- name: All jobs passed or skipped
|
||||
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
||||
# zizmor: ignore[template-injection]
|
||||
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
||||
- uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
|
||||
with:
|
||||
needs: ${{ toJSON(needs) }}
|
||||
|
||||
185
.github/workflows/multi-runner-build.yml
vendored
185
.github/workflows/multi-runner-build.yml
vendored
@@ -1,185 +0,0 @@
|
||||
name: 'Multi-runner container image build'
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
image:
|
||||
description: 'Name of the image'
|
||||
type: string
|
||||
required: true
|
||||
context:
|
||||
description: 'Path to build context'
|
||||
type: string
|
||||
required: true
|
||||
dockerfile:
|
||||
description: 'Path to Dockerfile'
|
||||
type: string
|
||||
required: true
|
||||
tag-suffix:
|
||||
description: 'Suffix to append to the image tag'
|
||||
type: string
|
||||
default: ''
|
||||
dockerhub-push:
|
||||
description: 'Push to Docker Hub'
|
||||
type: boolean
|
||||
default: false
|
||||
build-args:
|
||||
description: 'Docker build arguments'
|
||||
type: string
|
||||
required: false
|
||||
platforms:
|
||||
description: 'Platforms to build for'
|
||||
type: string
|
||||
runner-mapping:
|
||||
description: 'Mapping from platforms to runners'
|
||||
type: string
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME:
|
||||
required: false
|
||||
DOCKERHUB_TOKEN:
|
||||
required: false
|
||||
|
||||
env:
|
||||
GHCR_IMAGE: ghcr.io/${{ github.repository_owner }}/${{ inputs.image }}
|
||||
DOCKERHUB_IMAGE: altran1502/${{ inputs.image }}
|
||||
|
||||
jobs:
|
||||
matrix:
|
||||
name: 'Generate matrix'
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.matrix.outputs.matrix }}
|
||||
key: ${{ steps.artifact-key.outputs.base }}
|
||||
steps:
|
||||
- name: Generate build matrix
|
||||
id: matrix
|
||||
shell: bash
|
||||
env:
|
||||
PLATFORMS: ${{ inputs.platforms || 'linux/amd64,linux/arm64' }}
|
||||
RUNNER_MAPPING: ${{ inputs.runner-mapping || '{"linux/amd64":"ubuntu-latest","linux/arm64":"ubuntu-24.04-arm"}' }}
|
||||
run: |
|
||||
matrix=$(jq -R -c \
|
||||
--argjson runner_mapping "${RUNNER_MAPPING}" \
|
||||
'split(",") | map({platform: ., runner: $runner_mapping[.]})' \
|
||||
<<< "${PLATFORMS}")
|
||||
echo "${matrix}"
|
||||
echo "matrix=${matrix}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Determine artifact key
|
||||
id: artifact-key
|
||||
shell: bash
|
||||
env:
|
||||
IMAGE: ${{ inputs.image }}
|
||||
SUFFIX: ${{ inputs.tag-suffix }}
|
||||
run: |
|
||||
if [[ -n "${SUFFIX}" ]]; then
|
||||
base="${IMAGE}${SUFFIX}-digests"
|
||||
else
|
||||
base="${IMAGE}-digests"
|
||||
fi
|
||||
echo "${base}"
|
||||
echo "base=${base}" >> $GITHUB_OUTPUT
|
||||
|
||||
build:
|
||||
needs: matrix
|
||||
runs-on: ${{ matrix.runner }}
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include: ${{ fromJson(needs.matrix.outputs.matrix) }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: ./.github/actions/image-build
|
||||
with:
|
||||
context: ${{ inputs.context }}
|
||||
dockerfile: ${{ inputs.dockerfile }}
|
||||
image: ${{ env.GHCR_IMAGE }}
|
||||
ghcr-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
platform: ${{ matrix.platform }}
|
||||
artifact-key-base: ${{ needs.matrix.outputs.key }}
|
||||
build-args: ${{ inputs.build-args }}
|
||||
|
||||
merge:
|
||||
needs: [matrix, build]
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
path: ${{ runner.temp }}/digests
|
||||
pattern: ${{ needs.matrix.outputs.key }}-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Login to Docker Hub
|
||||
if: ${{ inputs.dockerhub-push }}
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
||||
|
||||
- name: Generate docker image tags
|
||||
id: meta
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
||||
env:
|
||||
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
||||
with:
|
||||
flavor: |
|
||||
# Disable latest tag
|
||||
latest=false
|
||||
suffix=${{ inputs.tag-suffix }}
|
||||
images: |
|
||||
name=${{ env.GHCR_IMAGE }}
|
||||
name=${{ env.DOCKERHUB_IMAGE }},enable=${{ inputs.dockerhub-push }}
|
||||
tags: |
|
||||
# Tag with branch name
|
||||
type=ref,event=branch
|
||||
# Tag with pr-number
|
||||
type=ref,event=pr
|
||||
# Tag with long commit sha hash
|
||||
type=sha,format=long,prefix=commit-
|
||||
# Tag with git tag on release
|
||||
type=ref,event=tag
|
||||
type=raw,value=release,enable=${{ github.event_name == 'release' }}
|
||||
|
||||
- name: Create manifest list and push
|
||||
working-directory: ${{ runner.temp }}/digests
|
||||
run: |
|
||||
# Process annotations
|
||||
declare -a ANNOTATIONS=()
|
||||
if [[ -n "$DOCKER_METADATA_OUTPUT_JSON" ]]; then
|
||||
while IFS= read -r annotation; do
|
||||
# Extract key and value by removing the manifest: prefix
|
||||
if [[ "$annotation" =~ ^manifest:(.+)=(.+)$ ]]; then
|
||||
key="${BASH_REMATCH[1]}"
|
||||
value="${BASH_REMATCH[2]}"
|
||||
# Use array to properly handle arguments with spaces
|
||||
ANNOTATIONS+=(--annotation "index:$key=$value")
|
||||
fi
|
||||
done < <(jq -r '.annotations[]' <<< "$DOCKER_METADATA_OUTPUT_JSON")
|
||||
fi
|
||||
|
||||
TAGS=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
SOURCE_ARGS=$(printf "${GHCR_IMAGE}@sha256:%s " *)
|
||||
|
||||
docker buildx imagetools create $TAGS "${ANNOTATIONS[@]}" $SOURCE_ARGS
|
||||
18
.github/workflows/static_analysis.yml
vendored
18
.github/workflows/static_analysis.yml
vendored
@@ -58,14 +58,26 @@ jobs:
|
||||
run: dart pub get
|
||||
working-directory: ./mobile
|
||||
|
||||
- name: Install DCM
|
||||
run: |
|
||||
sudo apt-get update
|
||||
wget -qO- https://dcm.dev/pgp-key.public | sudo gpg --dearmor -o /usr/share/keyrings/dcm.gpg
|
||||
echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install dcm
|
||||
|
||||
- name: Generate translation file
|
||||
run: make translation; dart format lib/generated/codegen_loader.g.dart
|
||||
run: make translation
|
||||
working-directory: ./mobile
|
||||
|
||||
- name: Run Build Runner
|
||||
run: make build
|
||||
working-directory: ./mobile
|
||||
|
||||
- name: Generate platform API
|
||||
run: make pigeon
|
||||
working-directory: ./mobile
|
||||
|
||||
- name: Find file changes
|
||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||
id: verify-changed-files
|
||||
@@ -96,6 +108,10 @@ jobs:
|
||||
run: dart run custom_lint
|
||||
working-directory: ./mobile
|
||||
|
||||
- name: Run DCM
|
||||
run: dcm analyze lib
|
||||
working-directory: ./mobile
|
||||
|
||||
zizmor:
|
||||
name: zizmor
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
12
.github/workflows/test.yml
vendored
12
.github/workflows/test.yml
vendored
@@ -479,13 +479,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: always()
|
||||
steps:
|
||||
- name: Any jobs failed?
|
||||
if: ${{ contains(needs.*.result, 'failure') }}
|
||||
run: exit 1
|
||||
- name: All jobs passed or skipped
|
||||
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
||||
# zizmor: ignore[template-injection]
|
||||
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
||||
- uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
|
||||
with:
|
||||
needs: ${{ toJSON(needs) }}
|
||||
|
||||
mobile-unit-tests:
|
||||
name: Unit Test Mobile
|
||||
@@ -648,7 +644,7 @@ jobs:
|
||||
contents: read
|
||||
services:
|
||||
postgres:
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:1076bb152a3000df23911fdec83f14ea83f0dd0c42bc7d4e14b854e9bda1b0c9
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1
|
||||
env:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_USER: postgres
|
||||
|
||||
10
.github/workflows/weblate-lock.yml
vendored
10
.github/workflows/weblate-lock.yml
vendored
@@ -52,10 +52,6 @@ jobs:
|
||||
permissions: {}
|
||||
if: always()
|
||||
steps:
|
||||
- name: Any jobs failed?
|
||||
if: ${{ contains(needs.*.result, 'failure') }}
|
||||
run: exit 1
|
||||
- name: All jobs passed or skipped
|
||||
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
||||
# zizmor: ignore[template-injection]
|
||||
run: echo "All jobs passed or skipped" && echo "${{ toJSON(needs.*.result) }}"
|
||||
- uses: immich-app/devtools/actions/success-check@6b81b1572e466f7f48ba3c823159ce3f4a4d66a6 # success-check-action-0.0.3
|
||||
with:
|
||||
needs: ${{ toJSON(needs) }}
|
||||
|
||||
280
cli/package-lock.json
generated
280
cli/package-lock.json
generated
@@ -27,7 +27,7 @@
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/micromatch": "^4.0.9",
|
||||
"@types/mock-fs": "^4.13.1",
|
||||
"@types/node": "^22.15.21",
|
||||
"@types/node": "^22.15.29",
|
||||
"@vitest/coverage-v8": "^3.0.0",
|
||||
"byte-size": "^9.0.0",
|
||||
"cli-progress": "^3.12.0",
|
||||
@@ -35,7 +35,7 @@
|
||||
"eslint": "^9.14.0",
|
||||
"eslint-config-prettier": "^10.0.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-unicorn": "^57.0.0",
|
||||
"eslint-plugin-unicorn": "^59.0.0",
|
||||
"globals": "^16.0.0",
|
||||
"mock-fs": "^5.2.0",
|
||||
"prettier": "^3.2.5",
|
||||
@@ -61,7 +61,7 @@
|
||||
"@oazapfts/runtime": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.15.21",
|
||||
"@types/node": "^22.15.29",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
},
|
||||
@@ -79,21 +79,6 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.26.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
|
||||
"integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.25.9",
|
||||
"js-tokens": "^4.0.0",
|
||||
"picocolors": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-string-parser": {
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
|
||||
@@ -1353,22 +1338,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.15.21",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz",
|
||||
"integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==",
|
||||
"version": "22.15.30",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.30.tgz",
|
||||
"integrity": "sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/normalize-package-data": {
|
||||
"version": "2.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz",
|
||||
"integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz",
|
||||
@@ -1878,9 +1856,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/builtin-modules": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-4.0.0.tgz",
|
||||
"integrity": "sha512-p1n8zyCkt1BVrKNFymOHjcDSAl7oq/gUvfgULv2EblgpPVQlQr9yHnWjg9IJ2MhfwPqiYqMMrr01OY7yQoK2yA==",
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.0.0.tgz",
|
||||
"integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -2362,50 +2340,65 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-unicorn": {
|
||||
"version": "57.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-57.0.0.tgz",
|
||||
"integrity": "sha512-zUYYa6zfNdTeG9BISWDlcLmz16c+2Ck2o5ZDHh0UzXJz3DEP7xjmlVDTzbyV0W+XksgZ0q37WEWzN2D2Ze+g9Q==",
|
||||
"version": "59.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-59.0.1.tgz",
|
||||
"integrity": "sha512-EtNXYuWPUmkgSU2E7Ttn57LbRREQesIP1BiLn7OZLKodopKfDXfBUkC/0j6mpw2JExwf43Uf3qLSvrSvppgy8Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.25.9",
|
||||
"@eslint-community/eslint-utils": "^4.4.1",
|
||||
"ci-info": "^4.1.0",
|
||||
"@eslint-community/eslint-utils": "^4.5.1",
|
||||
"@eslint/plugin-kit": "^0.2.7",
|
||||
"ci-info": "^4.2.0",
|
||||
"clean-regexp": "^1.0.0",
|
||||
"core-js-compat": "^3.40.0",
|
||||
"core-js-compat": "^3.41.0",
|
||||
"esquery": "^1.6.0",
|
||||
"globals": "^15.15.0",
|
||||
"find-up-simple": "^1.0.1",
|
||||
"globals": "^16.0.0",
|
||||
"indent-string": "^5.0.0",
|
||||
"is-builtin-module": "^4.0.0",
|
||||
"is-builtin-module": "^5.0.0",
|
||||
"jsesc": "^3.1.0",
|
||||
"pluralize": "^8.0.0",
|
||||
"read-package-up": "^11.0.0",
|
||||
"regexp-tree": "^0.1.27",
|
||||
"regjsparser": "^0.12.0",
|
||||
"semver": "^7.7.1",
|
||||
"strip-indent": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.18"
|
||||
"node": "^18.20.0 || ^20.10.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">=9.20.0"
|
||||
"eslint": ">=9.22.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-unicorn/node_modules/globals": {
|
||||
"version": "15.15.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
|
||||
"integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
|
||||
"node_modules/eslint-plugin-unicorn/node_modules/@eslint/core": {
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
|
||||
"integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.15"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-unicorn/node_modules/@eslint/plugin-kit": {
|
||||
"version": "0.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz",
|
||||
"integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@eslint/core": "^0.13.0",
|
||||
"levn": "^0.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-scope": {
|
||||
@@ -2792,19 +2785,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/hosted-git-info": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz",
|
||||
"integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"lru-cache": "^10.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.14.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/html-escaper": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
|
||||
@@ -2862,27 +2842,14 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/index-to-position": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.1.0.tgz",
|
||||
"integrity": "sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-builtin-module": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-4.0.0.tgz",
|
||||
"integrity": "sha512-rWP3AMAalQSesXO8gleROyL2iKU73SX5Er66losQn9rWOWL4Gef0a/xOEOVqjWGMuR2vHG3FJ8UUmT700O8oFg==",
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-5.0.0.tgz",
|
||||
"integrity": "sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"builtin-modules": "^4.0.0"
|
||||
"builtin-modules": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.20"
|
||||
@@ -3008,13 +2975,6 @@
|
||||
"@pkgjs/parseargs": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
@@ -3272,21 +3232,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/normalize-package-data": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz",
|
||||
"integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"hosted-git-info": "^7.0.0",
|
||||
"semver": "^7.3.5",
|
||||
"validate-npm-package-license": "^3.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.14.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/optionator": {
|
||||
"version": "0.9.4",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
||||
@@ -3357,24 +3302,6 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/parse-json": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz",
|
||||
"integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.26.2",
|
||||
"index-to-position": "^1.1.0",
|
||||
"type-fest": "^4.39.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
@@ -3573,44 +3500,6 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/read-package-up": {
|
||||
"version": "11.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz",
|
||||
"integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"find-up-simple": "^1.0.0",
|
||||
"read-pkg": "^9.0.0",
|
||||
"type-fest": "^4.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/read-pkg": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz",
|
||||
"integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/normalize-package-data": "^2.4.3",
|
||||
"normalize-package-data": "^6.0.0",
|
||||
"parse-json": "^8.0.0",
|
||||
"type-fest": "^4.6.0",
|
||||
"unicorn-magic": "^0.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
|
||||
@@ -3809,42 +3698,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/spdx-correct": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
|
||||
"integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"spdx-expression-parse": "^3.0.0",
|
||||
"spdx-license-ids": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/spdx-exceptions": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
|
||||
"integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
|
||||
"dev": true,
|
||||
"license": "CC-BY-3.0"
|
||||
},
|
||||
"node_modules/spdx-expression-parse": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
|
||||
"integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"spdx-exceptions": "^2.1.0",
|
||||
"spdx-license-ids": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/spdx-license-ids": {
|
||||
"version": "3.0.21",
|
||||
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz",
|
||||
"integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==",
|
||||
"dev": true,
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/stackback": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
|
||||
@@ -4172,19 +4025,6 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/type-fest": {
|
||||
"version": "4.40.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.40.0.tgz",
|
||||
"integrity": "sha512-ABHZ2/tS2JkvH1PEjxFDTUWC8dB5OsIGZP4IFLhR293GqT5Y5qB1WwL2kMPYhQW9DVgVD8Hd7I8gjwPIf5GFkw==",
|
||||
"dev": true,
|
||||
"license": "(MIT OR CC0-1.0)",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||
@@ -4229,19 +4069,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unicorn-magic": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz",
|
||||
"integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
|
||||
@@ -4283,17 +4110,6 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/validate-npm-package-license": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
|
||||
"integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"spdx-correct": "^3.0.0",
|
||||
"spdx-expression-parse": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "6.3.5",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/micromatch": "^4.0.9",
|
||||
"@types/mock-fs": "^4.13.1",
|
||||
"@types/node": "^22.15.21",
|
||||
"@types/node": "^22.15.29",
|
||||
"@vitest/coverage-v8": "^3.0.0",
|
||||
"byte-size": "^9.0.0",
|
||||
"cli-progress": "^3.12.0",
|
||||
@@ -29,7 +29,7 @@
|
||||
"eslint": "^9.14.0",
|
||||
"eslint-config-prettier": "^10.0.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-unicorn": "^57.0.0",
|
||||
"eslint-plugin-unicorn": "^59.0.0",
|
||||
"globals": "^16.0.0",
|
||||
"mock-fs": "^5.2.0",
|
||||
"prettier": "^3.2.5",
|
||||
|
||||
@@ -43,6 +43,7 @@ export interface UploadOptionsDto {
|
||||
concurrency: number;
|
||||
progress?: boolean;
|
||||
watch?: boolean;
|
||||
jsonOutput?: boolean;
|
||||
}
|
||||
|
||||
class UploadFile extends File {
|
||||
@@ -65,6 +66,9 @@ class UploadFile extends File {
|
||||
const uploadBatch = async (files: string[], options: UploadOptionsDto) => {
|
||||
const { newFiles, duplicates } = await checkForDuplicates(files, options);
|
||||
const newAssets = await uploadFiles(newFiles, options);
|
||||
if (options.jsonOutput) {
|
||||
console.log(JSON.stringify({ newFiles, duplicates, newAssets }, undefined, 4));
|
||||
}
|
||||
await updateAlbums([...newAssets, ...duplicates], options);
|
||||
await deleteFiles(newFiles, options);
|
||||
};
|
||||
|
||||
@@ -68,6 +68,11 @@ program
|
||||
.env('IMMICH_UPLOAD_CONCURRENCY')
|
||||
.default(4),
|
||||
)
|
||||
.addOption(
|
||||
new Option('-j, --json-output', 'Output detailed information in json format')
|
||||
.env('IMMICH_JSON_OUTPUT')
|
||||
.default(false),
|
||||
)
|
||||
.addOption(new Option('--delete', 'Delete local assets after upload').env('IMMICH_DELETE_ASSETS'))
|
||||
.addOption(new Option('--no-progress', 'Hide progress bars').env('IMMICH_PROGRESS_BAR').default(true))
|
||||
.addOption(
|
||||
|
||||
@@ -116,13 +116,13 @@ services:
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: docker.io/valkey/valkey:8-bookworm@sha256:ff21bc0f8194dc9c105b769aeabf9585fea6a8ed649c0781caeac5cb3c247884
|
||||
image: docker.io/valkey/valkey:8-bookworm@sha256:a19bebed6a91bd5e6e2106fef015f9602a3392deeb7c9ed47548378dcee3dfc2
|
||||
healthcheck:
|
||||
test: redis-cli ping || exit 1
|
||||
|
||||
database:
|
||||
container_name: immich_postgres
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0-pgvectors0.2.0@sha256:fa4f6e0971f454cd95fec5a9aaed2ed93d8f46725cc6bc61e0698e97dba96da1
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1-pgvectors0.2.0
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
|
||||
@@ -56,14 +56,14 @@ services:
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: docker.io/valkey/valkey:8-bookworm@sha256:ff21bc0f8194dc9c105b769aeabf9585fea6a8ed649c0781caeac5cb3c247884
|
||||
image: docker.io/valkey/valkey:8-bookworm@sha256:a19bebed6a91bd5e6e2106fef015f9602a3392deeb7c9ed47548378dcee3dfc2
|
||||
healthcheck:
|
||||
test: redis-cli ping || exit 1
|
||||
restart: always
|
||||
|
||||
database:
|
||||
container_name: immich_postgres
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0-pgvectors0.2.0@sha256:fa4f6e0971f454cd95fec5a9aaed2ed93d8f46725cc6bc61e0698e97dba96da1
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1-pgvectors0.2.0
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
@@ -82,7 +82,7 @@ services:
|
||||
container_name: immich_prometheus
|
||||
ports:
|
||||
- 9090:9090
|
||||
image: prom/prometheus@sha256:78ed1f9050eb9eaf766af6e580230b1c4965728650e332cd1ee918c0c4699775
|
||||
image: prom/prometheus@sha256:9abc6cf6aea7710d163dbb28d8eeb7dc5baef01e38fa4cd146a406dd9f07f70d
|
||||
volumes:
|
||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
- prometheus-data:/prometheus
|
||||
@@ -94,7 +94,7 @@ services:
|
||||
command: [ './run.sh', '-disable-reporting' ]
|
||||
ports:
|
||||
- 3000:3000
|
||||
image: grafana/grafana:11.6.1-ubuntu@sha256:6fc273288470ef499dd3c6b36aeade093170d4f608f864c5dd3a7fabeae77b50
|
||||
image: grafana/grafana:12.0.1-ubuntu@sha256:65575bb9c761335e2ff30e364f21d38632e3b2e75f5f81d83cc92f44b9bbc055
|
||||
volumes:
|
||||
- grafana-data:/var/lib/grafana
|
||||
|
||||
|
||||
@@ -49,14 +49,14 @@ services:
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: docker.io/valkey/valkey:8-bookworm@sha256:ff21bc0f8194dc9c105b769aeabf9585fea6a8ed649c0781caeac5cb3c247884
|
||||
image: docker.io/valkey/valkey:8-bookworm@sha256:a19bebed6a91bd5e6e2106fef015f9602a3392deeb7c9ed47548378dcee3dfc2
|
||||
healthcheck:
|
||||
test: redis-cli ping || exit 1
|
||||
restart: always
|
||||
|
||||
database:
|
||||
container_name: immich_postgres
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0-pgvectors0.2.0@sha256:fa4f6e0971f454cd95fec5a9aaed2ed93d8f46725cc6bc61e0698e97dba96da1
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1-pgvectors0.2.0
|
||||
environment:
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
POSTGRES_USER: ${DB_USERNAME}
|
||||
|
||||
@@ -219,3 +219,10 @@ When you turn off the storage template engine, it will leave the assets in `UPLO
|
||||
Do not touch the files inside these folders under any circumstances except taking a backup. Changing or removing an asset can cause untracked and missing files.
|
||||
You can think of it as App-Which-Must-Not-Be-Named, the only access to viewing, changing and deleting assets is only through the mobile or browser interface.
|
||||
:::
|
||||
|
||||
## Backup ordering
|
||||
|
||||
A backup of Immich should contain both the database and the asset files. When backing these up it's possible for them to get out of sync, potentially resulting in broken assets after you restore.
|
||||
The best way of dealing with this is to stop the immich-server container while you take a backup. If nothing is changing then the backup will always be in sync.
|
||||
|
||||
If stopping the container is not an option, then the recommended order is to back up the database first, and the filesystem second. This way, the worst case scenario is that there are files on the filesystem that the database doesn't know about. If necessary, these can be (re)uploaded manually after a restore. If the backup is done the other way around, with the filesystem first and the database second, it's possible for the restored database to reference files that aren't in the filesystem backup, thus resulting in broken assets.
|
||||
|
||||
@@ -93,6 +93,7 @@ The `.well-known/openid-configuration` part of the url is optional and will be a
|
||||
## Auto Launch
|
||||
|
||||
When Auto Launch is enabled, the login page will automatically redirect the user to the OAuth authorization url, to login with OAuth. To access the login screen again, use the browser's back button, or navigate directly to `/auth/login?autoLaunch=0`.
|
||||
Auto Launch can also be enabled on a per-request basis by navigating to `/auth/login?authLaunch=1`, this can be useful in situations where Immich is called from e.g. Nextcloud using the _External sites_ app and the _oidc_ app so as to enable users to directly interact with a logged-in instance of Immich.
|
||||
|
||||
## Mobile Redirect URI
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ You must install VectorChord into your instance of Postgres using their [instruc
|
||||
:::note
|
||||
Immich is known to work with Postgres versions `>= 14, < 18`.
|
||||
|
||||
Make sure the installed version of VectorChord is compatible with your version of Immich. The current accepted range for VectorChord is `>= 0.3.0, < 0.4.0`.
|
||||
Make sure the installed version of VectorChord is compatible with your version of Immich. The current accepted range for VectorChord is `>= 0.3.0, < 0.5.0`.
|
||||
:::
|
||||
|
||||
## Specifying the connection URL
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
Immich supports the Google's Cast protocol so that photos and videos can be cast to devices such as a Chromecast and a Nest Hub. This feature is considered experimental and has several important limitations listed below. Currently, this feature is only supported by the web client, support on Android and iOS is planned for the future.
|
||||
|
||||
## Enable Google Cast Support
|
||||
|
||||
Google Cast support is disabled by default. The web UI uses Google-provided scripts and must retreive them from Google servers when the page loads. This is a privacy concern for some and is thus opt-in.
|
||||
|
||||
You can enable Google Cast support through `Account Settings > Features > Cast > Google Cast`
|
||||
|
||||
<img src={require('./img/gcast-enable.webp').default} width="70%" title='Enable Google Cast Support' />
|
||||
|
||||
## Limitations
|
||||
|
||||
To use casting with Immich, there are a few prerequisites:
|
||||
|
||||
@@ -90,19 +90,22 @@ Usage: immich upload [paths...] [options]
|
||||
Upload assets
|
||||
|
||||
Arguments:
|
||||
paths One or more paths to assets to be uploaded
|
||||
paths One or more paths to assets to be uploaded
|
||||
|
||||
Options:
|
||||
-r, --recursive Recursive (default: false, env: IMMICH_RECURSIVE)
|
||||
-i, --ignore [paths...] Paths to ignore (default: [], env: IMMICH_IGNORE_PATHS)
|
||||
-h, --skip-hash Don't hash files before upload (default: false, env: IMMICH_SKIP_HASH)
|
||||
-H, --include-hidden Include hidden folders (default: false, env: IMMICH_INCLUDE_HIDDEN)
|
||||
-a, --album Automatically create albums based on folder name (default: false, env: IMMICH_AUTO_CREATE_ALBUM)
|
||||
-A, --album-name <name> Add all assets to specified album (env: IMMICH_ALBUM_NAME)
|
||||
-n, --dry-run Don't perform any actions, just show what will be done (default: false, env: IMMICH_DRY_RUN)
|
||||
-c, --concurrency <number> Number of assets to upload at the same time (default: 4, env: IMMICH_UPLOAD_CONCURRENCY)
|
||||
--delete Delete local assets after upload (env: IMMICH_DELETE_ASSETS)
|
||||
--help display help for command
|
||||
-r, --recursive Recursive (default: false, env: IMMICH_RECURSIVE)
|
||||
-i, --ignore <pattern> Pattern to ignore (env: IMMICH_IGNORE_PATHS)
|
||||
-h, --skip-hash Don't hash files before upload (default: false, env: IMMICH_SKIP_HASH)
|
||||
-H, --include-hidden Include hidden folders (default: false, env: IMMICH_INCLUDE_HIDDEN)
|
||||
-a, --album Automatically create albums based on folder name (default: false, env: IMMICH_AUTO_CREATE_ALBUM)
|
||||
-A, --album-name <name> Add all assets to specified album (env: IMMICH_ALBUM_NAME)
|
||||
-n, --dry-run Don't perform any actions, just show what will be done (default: false, env: IMMICH_DRY_RUN)
|
||||
-c, --concurrency <number> Number of assets to upload at the same time (default: 4, env: IMMICH_UPLOAD_CONCURRENCY)
|
||||
-j, --json-output Output detailed information in json format (default: false, env: IMMICH_JSON_OUTPUT)
|
||||
--delete Delete local assets after upload (env: IMMICH_DELETE_ASSETS)
|
||||
--no-progress Hide progress bars (env: IMMICH_PROGRESS_BAR)
|
||||
--watch Watch for changes and upload automatically (default: false, env: IMMICH_WATCH_CHANGES)
|
||||
--help display help for command
|
||||
```
|
||||
|
||||
</details>
|
||||
@@ -172,6 +175,16 @@ By default, hidden files are skipped. If you want to include hidden files, use t
|
||||
immich upload --include-hidden --recursive directory/
|
||||
```
|
||||
|
||||
You can use the `--json-output` option to get a json printed which includes
|
||||
three keys: `newFiles`, `duplicates` and `newAssets`. Due to some logging
|
||||
output you will need to strip the first three lines of output to get the json.
|
||||
For example to get a list of files that would be uploaded for further
|
||||
processing:
|
||||
|
||||
```bash
|
||||
immich upload --dry-run . | tail -n +4 | jq .newFiles[]
|
||||
```
|
||||
|
||||
### Obtain the API Key
|
||||
|
||||
The API key can be obtained in the user setting panel on the web interface.
|
||||
|
||||
BIN
docs/docs/features/img/gcast-enable.webp
Normal file
BIN
docs/docs/features/img/gcast-enable.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
@@ -13,6 +13,9 @@ import {
|
||||
mdiTrashCan,
|
||||
mdiWeb,
|
||||
mdiWrap,
|
||||
mdiCloudKeyOutline,
|
||||
mdiRegex,
|
||||
mdiCodeJson,
|
||||
} from '@mdi/js';
|
||||
import Layout from '@theme/Layout';
|
||||
import React from 'react';
|
||||
@@ -23,6 +26,30 @@ const withLanguage = (date: Date) => (language: string) => date.toLocaleDateStri
|
||||
type Item = Omit<TimelineItem, 'done' | 'getDateLabel'> & { date: Date };
|
||||
|
||||
const items: Item[] = [
|
||||
{
|
||||
icon: mdiRegex,
|
||||
iconColor: 'purple',
|
||||
title: 'Zitadel Actions are cursed',
|
||||
description:
|
||||
"Zitadel is cursed because its custom scripting feature is executed with a JS engine that doesn't support regex named capture groups.",
|
||||
link: {
|
||||
url: 'https://github.com/dop251/goja',
|
||||
text: 'Go JS engine',
|
||||
},
|
||||
date: new Date(2025, 5, 4),
|
||||
},
|
||||
{
|
||||
icon: mdiCloudKeyOutline,
|
||||
iconColor: '#0078d4',
|
||||
title: 'Entra is cursed',
|
||||
description:
|
||||
"Microsoft Entra supports PKCE, but doesn't include it in its OpenID discovery document. This leads to clients thinking PKCE isn't available.",
|
||||
link: {
|
||||
url: 'https://github.com/immich-app/immich/pull/18725',
|
||||
text: '#18725',
|
||||
},
|
||||
date: new Date(2025, 4, 30),
|
||||
},
|
||||
{
|
||||
icon: mdiCrop,
|
||||
iconColor: 'tomato',
|
||||
@@ -33,7 +60,18 @@ const items: Item[] = [
|
||||
url: 'https://github.com/immich-app/immich/pull/17974',
|
||||
text: '#17974',
|
||||
},
|
||||
date: new Date(2025, 5, 5),
|
||||
date: new Date(2025, 4, 5),
|
||||
},
|
||||
{
|
||||
icon: mdiCodeJson,
|
||||
iconColor: 'yellow',
|
||||
title: 'YAML whitespace is cursed',
|
||||
description: 'YAML whitespaces are often handled in unintuitive ways.',
|
||||
link: {
|
||||
url: 'https://github.com/immich-app/immich/pull/17309',
|
||||
text: '#17309',
|
||||
},
|
||||
date: new Date(2025, 3, 1),
|
||||
},
|
||||
{
|
||||
icon: mdiMicrosoftWindows,
|
||||
|
||||
@@ -28,8 +28,10 @@ services:
|
||||
extra_hosts:
|
||||
- 'auth-server:host-gateway'
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
redis:
|
||||
condition: service_started
|
||||
database:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- 2285:2285
|
||||
|
||||
@@ -37,7 +39,7 @@ services:
|
||||
image: redis:6.2-alpine@sha256:3211c33a618c457e5d241922c975dbc4f446d0bdb2dc75694f5573ef8e2d01fa
|
||||
|
||||
database:
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:e6d1209c1c13791c6f9fbf726c41865e3320dfe2445a6b4ffb03e25f904b3b37
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:9c704fb49ce27549df00f1b096cc93f8b0c959ef087507704d74954808f78a82
|
||||
command: -c fsync=off -c shared_preload_libraries=vchord.so -c config_file=/var/lib/postgresql/data/postgresql.conf
|
||||
environment:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
@@ -45,3 +47,9 @@ services:
|
||||
POSTGRES_DB: immich
|
||||
ports:
|
||||
- 5435:5432
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'pg_isready -U postgres -d immich']
|
||||
interval: 1s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
start_period: 10s
|
||||
|
||||
790
e2e/package-lock.json
generated
790
e2e/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -25,8 +25,8 @@
|
||||
"@immich/sdk": "file:../open-api/typescript-sdk",
|
||||
"@playwright/test": "^1.44.1",
|
||||
"@types/luxon": "^3.4.2",
|
||||
"@types/node": "^22.15.21",
|
||||
"@types/oidc-provider": "^8.5.1",
|
||||
"@types/node": "^22.15.29",
|
||||
"@types/oidc-provider": "^9.0.0",
|
||||
"@types/pg": "^8.15.1",
|
||||
"@types/pngjs": "^6.0.4",
|
||||
"@types/supertest": "^6.0.2",
|
||||
@@ -34,12 +34,12 @@
|
||||
"eslint": "^9.14.0",
|
||||
"eslint-config-prettier": "^10.0.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-unicorn": "^57.0.0",
|
||||
"eslint-plugin-unicorn": "^59.0.0",
|
||||
"exiftool-vendored": "^28.3.1",
|
||||
"globals": "^16.0.0",
|
||||
"jose": "^5.6.3",
|
||||
"luxon": "^3.4.4",
|
||||
"oidc-provider": "^8.5.1",
|
||||
"oidc-provider": "^9.0.0",
|
||||
"pg": "^8.11.3",
|
||||
"pngjs": "^7.0.0",
|
||||
"prettier": "^3.2.5",
|
||||
|
||||
@@ -428,6 +428,15 @@ describe('/albums', () => {
|
||||
order: AssetOrder.Desc,
|
||||
});
|
||||
});
|
||||
|
||||
it('should not be able to share album with owner', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.post('/albums')
|
||||
.send({ albumName: 'New album', albumUsers: [{ role: AlbumUserRole.Editor, userId: user1.userId }] })
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest('Cannot share album with owner'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('PUT /albums/:id/assets', () => {
|
||||
|
||||
@@ -143,7 +143,7 @@ describe('/api-keys', () => {
|
||||
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
||||
const { status, body } = await request(app)
|
||||
.put(`/api-keys/${apiKey.id}`)
|
||||
.send({ name: 'new name' })
|
||||
.send({ name: 'new name', permissions: [Permission.All] })
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest('API Key not found'));
|
||||
@@ -153,13 +153,16 @@ describe('/api-keys', () => {
|
||||
const { apiKey } = await create(user.accessToken, [Permission.All]);
|
||||
const { status, body } = await request(app)
|
||||
.put(`/api-keys/${apiKey.id}`)
|
||||
.send({ name: 'new name' })
|
||||
.send({
|
||||
name: 'new name',
|
||||
permissions: [Permission.ActivityCreate, Permission.ActivityRead, Permission.ActivityUpdate],
|
||||
})
|
||||
.set('Authorization', `Bearer ${user.accessToken}`);
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual({
|
||||
id: expect.any(String),
|
||||
name: 'new name',
|
||||
permissions: [Permission.All],
|
||||
permissions: [Permission.ActivityCreate, Permission.ActivityRead, Permission.ActivityUpdate],
|
||||
createdAt: expect.any(String),
|
||||
updatedAt: expect.any(String),
|
||||
});
|
||||
|
||||
@@ -75,8 +75,8 @@ describe('/timeline', () => {
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual(
|
||||
expect.arrayContaining([
|
||||
{ count: 3, timeBucket: '1970-02-01T00:00:00.000Z' },
|
||||
{ count: 1, timeBucket: '1970-01-01T00:00:00.000Z' },
|
||||
{ count: 3, timeBucket: '1970-02-01' },
|
||||
{ count: 1, timeBucket: '1970-01-01' },
|
||||
]),
|
||||
);
|
||||
});
|
||||
@@ -167,7 +167,8 @@ describe('/timeline', () => {
|
||||
isImage: [],
|
||||
isTrashed: [],
|
||||
livePhotoVideoId: [],
|
||||
localDateTime: [],
|
||||
fileCreatedAt: [],
|
||||
localOffsetHours: [],
|
||||
ownerId: [],
|
||||
projectionType: [],
|
||||
ratio: [],
|
||||
@@ -204,7 +205,8 @@ describe('/timeline', () => {
|
||||
isImage: [],
|
||||
isTrashed: [],
|
||||
livePhotoVideoId: [],
|
||||
localDateTime: [],
|
||||
fileCreatedAt: [],
|
||||
localOffsetHours: [],
|
||||
ownerId: [],
|
||||
projectionType: [],
|
||||
ratio: [],
|
||||
|
||||
@@ -103,6 +103,7 @@ export const loginResponseDto = {
|
||||
accessToken: expect.any(String),
|
||||
name: 'Immich Admin',
|
||||
isAdmin: true,
|
||||
isOnboarded: false,
|
||||
profileImagePath: '',
|
||||
shouldChangePassword: true,
|
||||
userEmail: 'admin@immich.cloud',
|
||||
|
||||
@@ -33,7 +33,9 @@ test.describe('Registration', () => {
|
||||
// onboarding
|
||||
await expect(page).toHaveURL('/auth/onboarding');
|
||||
await page.getByRole('button', { name: 'Theme' }).click();
|
||||
await page.getByRole('button', { name: 'Privacy' }).click();
|
||||
await page.getByRole('button', { name: 'Language' }).click();
|
||||
await page.getByRole('button', { name: 'Server Privacy' }).click();
|
||||
await page.getByRole('button', { name: 'User Privacy' }).click();
|
||||
await page.getByRole('button', { name: 'Storage Template' }).click();
|
||||
await page.getByRole('button', { name: 'Done' }).click();
|
||||
|
||||
@@ -77,6 +79,13 @@ test.describe('Registration', () => {
|
||||
await page.getByLabel('Password').fill('new-password');
|
||||
await page.getByRole('button', { name: 'Login' }).click();
|
||||
|
||||
// onboarding
|
||||
await expect(page).toHaveURL('/auth/onboarding');
|
||||
await page.getByRole('button', { name: 'Theme' }).click();
|
||||
await page.getByRole('button', { name: 'Language' }).click();
|
||||
await page.getByRole('button', { name: 'User Privacy' }).click();
|
||||
await page.getByRole('button', { name: 'Done' }).click();
|
||||
|
||||
// success
|
||||
await expect(page).toHaveURL(/\/photos/);
|
||||
});
|
||||
|
||||
101
i18n/en.json
101
i18n/en.json
@@ -22,11 +22,11 @@
|
||||
"add_partner": "Add partner",
|
||||
"add_path": "Add path",
|
||||
"add_photos": "Add photos",
|
||||
"add_tag": "Add tag",
|
||||
"add_to": "Add to…",
|
||||
"add_to_album": "Add to album",
|
||||
"add_to_album_bottom_sheet_added": "Added to {album}",
|
||||
"add_to_album_bottom_sheet_already_exists": "Already in {album}",
|
||||
"add_to_locked_folder": "Add to locked folder",
|
||||
"add_to_shared_album": "Add to shared album",
|
||||
"add_url": "Add URL",
|
||||
"added_to_archive": "Added to archive",
|
||||
@@ -44,9 +44,7 @@
|
||||
"backup_database_enable_description": "Enable database dumps",
|
||||
"backup_keep_last_amount": "Amount of previous dumps to keep",
|
||||
"backup_settings": "Database Dump Settings",
|
||||
"backup_settings_description": "Manage database dump settings. Note: These jobs are not monitored and you will not be notified of failure.",
|
||||
"check_all": "Check All",
|
||||
"cleanup": "Cleanup",
|
||||
"backup_settings_description": "Manage database dump settings.",
|
||||
"cleared_jobs": "Cleared jobs for: {job}",
|
||||
"config_set_by_file": "Config is currently set by a config file",
|
||||
"confirm_delete_library": "Are you sure you want to delete {library} library?",
|
||||
@@ -62,14 +60,12 @@
|
||||
"disable_login": "Disable login",
|
||||
"duplicate_detection_job_description": "Run machine learning on assets to detect similar images. Relies on Smart Search",
|
||||
"exclusion_pattern_description": "Exclusion patterns lets you ignore files and folders when scanning your library. This is useful if you have folders that contain files you don't want to import, such as RAW files.",
|
||||
"external_library_created_at": "External library (created on {date})",
|
||||
"external_library_management": "External Library Management",
|
||||
"face_detection": "Face detection",
|
||||
"face_detection_description": "Detect the faces in assets using machine learning. For videos, only the thumbnail is considered. \"Refresh\" (re-)processes all assets. \"Reset\" additionally clears all current face data. \"Missing\" queues assets that haven't been processed yet. Detected faces will be queued for Facial Recognition after Face Detection is complete, grouping them into existing or new people.",
|
||||
"facial_recognition_job_description": "Group detected faces into people. This step runs after Face Detection is complete. \"Reset\" (re-)clusters all faces. \"Missing\" queues faces that don't have a person assigned.",
|
||||
"failed_job_command": "Command {command} failed for job: {job}",
|
||||
"force_delete_user_warning": "WARNING: This will immediately remove the user and all assets. This cannot be undone and the files cannot be recovered.",
|
||||
"forcing_refresh_library_files": "Forcing refresh of all library files",
|
||||
"image_format": "Format",
|
||||
"image_format_description": "WebP produces smaller files than JPEG, but is slower to encode.",
|
||||
"image_fullsize_description": "Full-size image with stripped metadata, used when zoomed in",
|
||||
@@ -174,7 +170,7 @@
|
||||
"note_apply_storage_label_previous_assets": "Note: To apply the Storage Label to previously uploaded assets, run the",
|
||||
"note_cannot_be_changed_later": "NOTE: This cannot be changed later!",
|
||||
"notification_email_from_address": "From address",
|
||||
"notification_email_from_address_description": "Sender email address, for example: \"Immich Photo Server <noreply@example.com>\"",
|
||||
"notification_email_from_address_description": "Sender email address, for example: \"Immich Photo Server <noreply@example.com>\". Make sure to use an address you're allowed to send emails from.",
|
||||
"notification_email_host_description": "Host of the email server (e.g. smtp.immich.app)",
|
||||
"notification_email_ignore_certificate_errors": "Ignore certificate errors",
|
||||
"notification_email_ignore_certificate_errors_description": "Ignore TLS certificate validation errors (not recommended)",
|
||||
@@ -210,8 +206,6 @@
|
||||
"oauth_storage_quota_default_description": "Quota in GiB to be used when no claim is provided (Enter 0 for unlimited quota).",
|
||||
"oauth_timeout": "Request Timeout",
|
||||
"oauth_timeout_description": "Timeout for requests in milliseconds",
|
||||
"offline_paths": "Offline Paths",
|
||||
"offline_paths_description": "These results may be due to manual deletion of files that are not part of an external library.",
|
||||
"password_enable_description": "Login with email and password",
|
||||
"password_settings": "Password Login",
|
||||
"password_settings_description": "Manage password login settings",
|
||||
@@ -221,9 +215,6 @@
|
||||
"refreshing_all_libraries": "Refreshing all libraries",
|
||||
"registration": "Admin Registration",
|
||||
"registration_description": "Since you are the first user on the system, you will be assigned as the Admin and are responsible for administrative tasks, and additional users will be created by you.",
|
||||
"repair_all": "Repair All",
|
||||
"repair_matched_items": "Matched {count, plural, one {# item} other {# items}}",
|
||||
"repaired_items": "Repaired {count, plural, one {# item} other {# items}}",
|
||||
"require_password_change_on_login": "Require user to change password on first login",
|
||||
"reset_settings_to_default": "Reset settings to default",
|
||||
"reset_settings_to_recent_saved": "Reset settings to the recent saved settings",
|
||||
@@ -264,7 +255,6 @@
|
||||
"template_email_invite_album": "Invite Album Template",
|
||||
"template_email_preview": "Preview",
|
||||
"template_email_settings": "Email Templates",
|
||||
"template_email_settings_description": "Manage custom email notification templates",
|
||||
"template_email_update_album": "Update Album Template",
|
||||
"template_email_welcome": "Welcome email template",
|
||||
"template_settings": "Notification Templates",
|
||||
@@ -273,7 +263,6 @@
|
||||
"theme_custom_css_settings_description": "Cascading Style Sheets allow the design of Immich to be customized.",
|
||||
"theme_settings": "Theme Settings",
|
||||
"theme_settings_description": "Manage customization of the Immich web interface",
|
||||
"these_files_matched_by_checksum": "These files are matched by their checksums",
|
||||
"thumbnail_generation_job": "Generate Thumbnails",
|
||||
"thumbnail_generation_job_description": "Generate large, small and blurred thumbnails for each asset, as well as thumbnails for each person",
|
||||
"transcoding_acceleration_api": "Acceleration API",
|
||||
@@ -341,8 +330,6 @@
|
||||
"trash_number_of_days_description": "Number of days to keep the assets in trash before permanently removing them",
|
||||
"trash_settings": "Trash Settings",
|
||||
"trash_settings_description": "Manage trash settings",
|
||||
"untracked_files": "Untracked Files",
|
||||
"untracked_files_description": "These files are not tracked by the application. They can be the results of failed moves, interrupted uploads, or left behind due to a bug",
|
||||
"user_cleanup_job": "User cleanup",
|
||||
"user_delete_delay": "<b>{user}</b>'s account and assets will be scheduled for permanent deletion in {delay, plural, one {# day} other {# days}}.",
|
||||
"user_delete_delay_settings": "Delete delay",
|
||||
@@ -401,10 +388,6 @@
|
||||
"album_remove_user": "Remove user?",
|
||||
"album_remove_user_confirmation": "Are you sure you want to remove {user}?",
|
||||
"album_share_no_users": "Looks like you have shared this album with all users or you don't have any user to share with.",
|
||||
"album_thumbnail_card_item": "1 item",
|
||||
"album_thumbnail_card_items": "{count} items",
|
||||
"album_thumbnail_card_shared": " · Shared",
|
||||
"album_thumbnail_shared_by": "Shared by {user}",
|
||||
"album_updated": "Album updated",
|
||||
"album_updated_setting_description": "Receive an email notification when a shared album has new assets",
|
||||
"album_user_left": "Left {album}",
|
||||
@@ -420,6 +403,9 @@
|
||||
"album_with_link_access": "Let anyone with the link see photos and people in this album.",
|
||||
"albums": "Albums",
|
||||
"albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Albums}}",
|
||||
"albums_default_sort_order": "Default album sort order",
|
||||
"albums_default_sort_order_description": "Initial asset sort order when creating new albums.",
|
||||
"albums_feature_description": "Collections of assets that can be shared with other users.",
|
||||
"all": "All",
|
||||
"all_albums": "All albums",
|
||||
"all_people": "All people",
|
||||
@@ -478,9 +464,12 @@
|
||||
"assets_added_count": "Added {count, plural, one {# asset} other {# assets}}",
|
||||
"assets_added_to_album_count": "Added {count, plural, one {# asset} other {# assets}} to the album",
|
||||
"assets_added_to_name_count": "Added {count, plural, one {# asset} other {# assets}} to {hasName, select, true {<b>{name}</b>} other {new album}}",
|
||||
"assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} cannot be added to the album",
|
||||
"assets_count": "{count, plural, one {# asset} other {# assets}}",
|
||||
"assets_deleted_permanently": "{count} asset(s) deleted permanently",
|
||||
"assets_deleted_permanently_from_server": "{count} asset(s) deleted permanently from the Immich server",
|
||||
"assets_downloaded_failed": "{count, plural, one {Downloaded # file - {error} file failed} other {Downloaded # files - {error} files failed}}",
|
||||
"assets_downloaded_successfully": "{count, plural, one {Downloaded # file successfully} other {Downloaded # files successfully}}",
|
||||
"assets_moved_to_trash_count": "Moved {count, plural, one {# asset} other {# assets}} to trash",
|
||||
"assets_permanently_deleted_count": "Permanently deleted {count, plural, one {# asset} other {# assets}}",
|
||||
"assets_removed_count": "Removed {count, plural, one {# asset} other {# assets}}",
|
||||
@@ -495,6 +484,7 @@
|
||||
"authorized_devices": "Authorized Devices",
|
||||
"automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere",
|
||||
"automatic_endpoint_switching_title": "Automatic URL switching",
|
||||
"autoplay_slideshow": "Autoplay slideshow",
|
||||
"back": "Back",
|
||||
"back_close_deselect": "Back, close, or deselect",
|
||||
"background_location_permission": "Background location permission",
|
||||
@@ -576,21 +566,17 @@
|
||||
"bulk_keep_duplicates_confirmation": "Are you sure you want to keep {count, plural, one {# duplicate asset} other {# duplicate assets}}? This will resolve all duplicate groups without deleting anything.",
|
||||
"bulk_trash_duplicates_confirmation": "Are you sure you want to bulk trash {count, plural, one {# duplicate asset} other {# duplicate assets}}? This will keep the largest asset of each group and trash all other duplicates.",
|
||||
"buy": "Purchase Immich",
|
||||
"cache_settings_album_thumbnails": "Library page thumbnails ({count} assets)",
|
||||
"cache_settings_clear_cache_button": "Clear cache",
|
||||
"cache_settings_clear_cache_button_title": "Clears the app's cache. This will significantly impact the app's performance until the cache has rebuilt.",
|
||||
"cache_settings_duplicated_assets_clear_button": "CLEAR",
|
||||
"cache_settings_duplicated_assets_subtitle": "Photos and videos that are black listed by the app",
|
||||
"cache_settings_duplicated_assets_title": "Duplicated Assets ({count})",
|
||||
"cache_settings_image_cache_size": "Image cache size ({count} assets)",
|
||||
"cache_settings_statistics_album": "Library thumbnails",
|
||||
"cache_settings_statistics_assets": "{count} assets ({size})",
|
||||
"cache_settings_statistics_full": "Full images",
|
||||
"cache_settings_statistics_shared": "Shared album thumbnails",
|
||||
"cache_settings_statistics_thumbnail": "Thumbnails",
|
||||
"cache_settings_statistics_title": "Cache usage",
|
||||
"cache_settings_subtitle": "Control the caching behaviour of the Immich mobile application",
|
||||
"cache_settings_thumbnail_size": "Thumbnail cache size ({count} assets)",
|
||||
"cache_settings_tile_subtitle": "Control the local storage behaviour",
|
||||
"cache_settings_tile_title": "Local Storage",
|
||||
"cache_settings_title": "Caching Settings",
|
||||
@@ -604,6 +590,7 @@
|
||||
"cannot_undo_this_action": "You cannot undo this action!",
|
||||
"cannot_update_the_description": "Cannot update the description",
|
||||
"cast": "Cast",
|
||||
"cast_description": "Configure available cast destinations",
|
||||
"change_date": "Change date",
|
||||
"change_description": "Change description",
|
||||
"change_display_order": "Change display order",
|
||||
@@ -621,7 +608,6 @@
|
||||
"change_pin_code": "Change PIN code",
|
||||
"change_your_password": "Change your password",
|
||||
"changed_visibility_successfully": "Changed visibility successfully",
|
||||
"check_all": "Check All",
|
||||
"check_corrupt_asset_backup": "Check for corrupt asset backups",
|
||||
"check_corrupt_asset_backup_button": "Perform check",
|
||||
"check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.",
|
||||
@@ -663,11 +649,11 @@
|
||||
"confirm_password": "Confirm password",
|
||||
"confirm_tag_face": "Do you want to tag this face as {name}?",
|
||||
"confirm_tag_face_unnamed": "Do you want to tag this face?",
|
||||
"connected_device": "Connected device",
|
||||
"connected_to": "Connected to",
|
||||
"contain": "Contain",
|
||||
"context": "Context",
|
||||
"continue": "Continue",
|
||||
"control_bottom_app_bar_album_info_shared": "{count} items · Shared",
|
||||
"control_bottom_app_bar_create_new_album": "Create new album",
|
||||
"control_bottom_app_bar_delete_from_immich": "Delete from Immich",
|
||||
"control_bottom_app_bar_delete_from_local": "Delete from device",
|
||||
@@ -716,6 +702,7 @@
|
||||
"daily_title_text_date": "E, MMM dd",
|
||||
"daily_title_text_date_year": "E, MMM dd, yyyy",
|
||||
"dark": "Dark",
|
||||
"darkTheme": "Toggle dark theme",
|
||||
"date_after": "Date after",
|
||||
"date_and_time": "Date and Time",
|
||||
"date_before": "Date before",
|
||||
@@ -763,6 +750,7 @@
|
||||
"disallow_edits": "Disallow edits",
|
||||
"discord": "Discord",
|
||||
"discover": "Discover",
|
||||
"discovered_devices": "Discovered devices",
|
||||
"dismiss_all_errors": "Dismiss all errors",
|
||||
"dismiss_error": "Dismiss error",
|
||||
"display_options": "Display options",
|
||||
@@ -778,7 +766,6 @@
|
||||
"download_enqueue": "Download enqueued",
|
||||
"download_error": "Download Error",
|
||||
"download_failed": "Download failed",
|
||||
"download_filename": "file: {filename}",
|
||||
"download_finished": "Download finished",
|
||||
"download_include_embedded_motion_videos": "Embedded videos",
|
||||
"download_include_embedded_motion_videos_description": "Include videos embedded in motion photos as a separate file",
|
||||
@@ -854,7 +841,6 @@
|
||||
"cant_get_number_of_comments": "Can't get number of comments",
|
||||
"cant_search_people": "Can't search people",
|
||||
"cant_search_places": "Can't search places",
|
||||
"cleared_jobs": "Cleared jobs for: {job}",
|
||||
"error_adding_assets_to_album": "Error adding assets to album",
|
||||
"error_adding_users_to_album": "Error adding users to album",
|
||||
"error_deleting_shared_user": "Error deleting shared user",
|
||||
@@ -863,7 +849,6 @@
|
||||
"error_removing_assets_from_album": "Error removing assets from album, check console for more details",
|
||||
"error_selecting_all_assets": "Error selecting all assets",
|
||||
"exclusion_pattern_already_exists": "This exclusion pattern already exists.",
|
||||
"failed_job_command": "Command {command} failed for job: {job}",
|
||||
"failed_to_create_album": "Failed to create album",
|
||||
"failed_to_create_shared_link": "Failed to create shared link",
|
||||
"failed_to_edit_shared_link": "Failed to edit shared link",
|
||||
@@ -882,7 +867,6 @@
|
||||
"paths_validation_failed": "{paths, plural, one {# path} other {# paths}} failed validation",
|
||||
"profile_picture_transparent_pixels": "Profile pictures cannot have transparent pixels. Please zoom in and/or move the image.",
|
||||
"quota_higher_than_disk_size": "You set a quota higher than the disk size",
|
||||
"repair_unable_to_check_items": "Unable to check {count, select, one {item} other {items}}",
|
||||
"unable_to_add_album_users": "Unable to add users to album",
|
||||
"unable_to_add_assets_to_shared_link": "Unable to add assets to shared link",
|
||||
"unable_to_add_comment": "Unable to add comment",
|
||||
@@ -901,7 +885,6 @@
|
||||
"unable_to_change_visibility": "Unable to change the visibility for {count, plural, one {# person} other {# people}}",
|
||||
"unable_to_complete_oauth_login": "Unable to complete OAuth login",
|
||||
"unable_to_connect": "Unable to connect",
|
||||
"unable_to_connect_to_server": "Unable to connect to server",
|
||||
"unable_to_copy_to_clipboard": "Cannot copy to clipboard, make sure you are accessing the page through https",
|
||||
"unable_to_create_admin_account": "Unable to create admin account",
|
||||
"unable_to_create_api_key": "Unable to create a new API Key",
|
||||
@@ -925,14 +908,9 @@
|
||||
"unable_to_hide_person": "Unable to hide person",
|
||||
"unable_to_link_motion_video": "Unable to link motion video",
|
||||
"unable_to_link_oauth_account": "Unable to link OAuth account",
|
||||
"unable_to_load_album": "Unable to load album",
|
||||
"unable_to_load_asset_activity": "Unable to load asset activity",
|
||||
"unable_to_load_items": "Unable to load items",
|
||||
"unable_to_load_liked_status": "Unable to load liked status",
|
||||
"unable_to_log_out_all_devices": "Unable to log out all devices",
|
||||
"unable_to_log_out_device": "Unable to log out device",
|
||||
"unable_to_login_with_oauth": "Unable to login with OAuth",
|
||||
"unable_to_move_to_locked_folder": "Unable to move to locked folder",
|
||||
"unable_to_play_video": "Unable to play video",
|
||||
"unable_to_reassign_assets_existing_person": "Unable to reassign assets to {name, select, null {an existing person} other {{name}}}",
|
||||
"unable_to_reassign_assets_new_person": "Unable to reassign assets to a new person",
|
||||
@@ -940,11 +918,9 @@
|
||||
"unable_to_remove_album_users": "Unable to remove users from album",
|
||||
"unable_to_remove_api_key": "Unable to remove API Key",
|
||||
"unable_to_remove_assets_from_shared_link": "Unable to remove assets from shared link",
|
||||
"unable_to_remove_deleted_assets": "Unable to remove offline files",
|
||||
"unable_to_remove_library": "Unable to remove library",
|
||||
"unable_to_remove_partner": "Unable to remove partner",
|
||||
"unable_to_remove_reaction": "Unable to remove reaction",
|
||||
"unable_to_repair_items": "Unable to repair items",
|
||||
"unable_to_reset_password": "Unable to reset password",
|
||||
"unable_to_reset_pin_code": "Unable to reset PIN code",
|
||||
"unable_to_resolve_duplicate": "Unable to resolve duplicate",
|
||||
@@ -1027,6 +1003,8 @@
|
||||
"folders": "Folders",
|
||||
"folders_feature_description": "Browsing the folder view for the photos and videos on the file system",
|
||||
"forward": "Forward",
|
||||
"gcast_enabled": "Google Cast",
|
||||
"gcast_enabled_description": "This feature loads external resources from Google in order to work.",
|
||||
"general": "General",
|
||||
"get_help": "Get Help",
|
||||
"get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network",
|
||||
@@ -1115,6 +1093,12 @@
|
||||
"invalid_date_format": "Invalid date format",
|
||||
"invite_people": "Invite People",
|
||||
"invite_to_album": "Invite to album",
|
||||
"ios_debug_info_fetch_ran_at": "Fetch ran {dateTime}",
|
||||
"ios_debug_info_last_sync_at": "Last sync {dateTime}",
|
||||
"ios_debug_info_no_processes_queued": "No background processes queued",
|
||||
"ios_debug_info_no_sync_yet": "No background sync job has run yet",
|
||||
"ios_debug_info_processes_queued": "{count, plural, one {{count} background process queued} other {{count} background processes queued}}",
|
||||
"ios_debug_info_processing_ran_at": "Processing ran {dateTime}",
|
||||
"items_count": "{count, plural, one {# item} other {# items}}",
|
||||
"jobs": "Jobs",
|
||||
"keep": "Keep",
|
||||
@@ -1123,6 +1107,9 @@
|
||||
"kept_this_deleted_others": "Kept this asset and deleted {count, plural, one {# asset} other {# assets}}",
|
||||
"keyboard_shortcuts": "Keyboard shortcuts",
|
||||
"language": "Language",
|
||||
"language_no_results_subtitle": "Try adjusting your search term",
|
||||
"language_no_results_title": "No languages found",
|
||||
"language_search_hint": "Search languages...",
|
||||
"language_setting_description": "Select your preferred language",
|
||||
"last_seen": "Last seen",
|
||||
"latest_version": "Latest Version",
|
||||
@@ -1148,6 +1135,7 @@
|
||||
"list": "List",
|
||||
"loading": "Loading",
|
||||
"loading_search_results_failed": "Loading search results failed",
|
||||
"local_asset_cast_failed": "Unable to cast an asset that is not uploaded to the server",
|
||||
"local_network": "Local network",
|
||||
"local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network",
|
||||
"location_permission": "Location permission",
|
||||
@@ -1158,7 +1146,7 @@
|
||||
"location_picker_longitude_error": "Enter a valid longitude",
|
||||
"location_picker_longitude_hint": "Enter your longitude here",
|
||||
"lock": "Lock",
|
||||
"locked_folder": "Locked folder",
|
||||
"locked_folder": "Locked Folder",
|
||||
"log_out": "Log out",
|
||||
"log_out_all_devices": "Log Out All Devices",
|
||||
"logged_out_all_devices": "Logged out all devices",
|
||||
@@ -1192,7 +1180,7 @@
|
||||
"look": "Look",
|
||||
"loop_videos": "Loop videos",
|
||||
"loop_videos_description": "Enable to automatically loop a video in the detail viewer.",
|
||||
"main_branch_warning": "You’re using a development version; we strongly recommend using a release version!",
|
||||
"main_branch_warning": "You're using a development version; we strongly recommend using a release version!",
|
||||
"main_menu": "Main menu",
|
||||
"make": "Make",
|
||||
"manage_shared_links": "Manage shared links",
|
||||
@@ -1288,6 +1276,7 @@
|
||||
"no_archived_assets_message": "Archive photos and videos to hide them from your Photos view",
|
||||
"no_assets_message": "CLICK TO UPLOAD YOUR FIRST PHOTO",
|
||||
"no_assets_to_show": "No assets to show",
|
||||
"no_cast_devices_found": "No cast devices found",
|
||||
"no_duplicates_found": "No duplicates were found.",
|
||||
"no_exif_info_available": "No exif info available",
|
||||
"no_explore_results_message": "Upload more photos to explore your collection.",
|
||||
@@ -1316,15 +1305,15 @@
|
||||
"oauth": "OAuth",
|
||||
"official_immich_resources": "Official Immich Resources",
|
||||
"offline": "Offline",
|
||||
"offline_paths": "Offline paths",
|
||||
"offline_paths_description": "These results may be due to manual deletion of files that are not part of an external library.",
|
||||
"ok": "Ok",
|
||||
"oldest_first": "Oldest first",
|
||||
"on_this_device": "On this device",
|
||||
"onboarding": "Onboarding",
|
||||
"onboarding_privacy_description": "The following (optional) features rely on external services, and can be disabled at any time in the administration settings.",
|
||||
"onboarding_locale_description": "Select your preferred language. You can change this later in your settings.",
|
||||
"onboarding_privacy_description": "The following (optional) features rely on external services, and can be disabled at any time in settings.",
|
||||
"onboarding_server_welcome_description": "Let's get your instance set up with some common settings.",
|
||||
"onboarding_theme_description": "Choose a color theme for your instance. You can change this later in your settings.",
|
||||
"onboarding_welcome_description": "Let's get your instance set up with some common settings.",
|
||||
"onboarding_user_welcome_description": "Let's get you started!",
|
||||
"onboarding_welcome_user": "Welcome, {user}",
|
||||
"online": "Online",
|
||||
"only_favorites": "Only favorites",
|
||||
@@ -1381,6 +1370,8 @@
|
||||
"permanently_delete_assets_prompt": "Are you sure you want to permanently delete {count, plural, one {this asset?} other {these <b>#</b> assets?}} This will also remove {count, plural, one {it from its} other {them from their}} album(s).",
|
||||
"permanently_deleted_asset": "Permanently deleted asset",
|
||||
"permanently_deleted_assets_count": "Permanently deleted {count, plural, one {# asset} other {# assets}}",
|
||||
"permission": "Permission",
|
||||
"permission_empty": "Your permission shouldn't be empty",
|
||||
"permission_onboarding_back": "Back",
|
||||
"permission_onboarding_continue_anyway": "Continue anyway",
|
||||
"permission_onboarding_get_started": "Get started",
|
||||
@@ -1417,7 +1408,10 @@
|
||||
"preview": "Preview",
|
||||
"previous": "Previous",
|
||||
"previous_memory": "Previous memory",
|
||||
"previous_or_next_photo": "Previous or next photo",
|
||||
"previous_or_next_day": "Day forward/back",
|
||||
"previous_or_next_month": "Month forward/back",
|
||||
"previous_or_next_photo": "Photo forward/back",
|
||||
"previous_or_next_year": "Year forward/back",
|
||||
"primary": "Primary",
|
||||
"privacy": "Privacy",
|
||||
"profile": "Profile",
|
||||
@@ -1452,7 +1446,7 @@
|
||||
"purchase_lifetime_description": "Lifetime purchase",
|
||||
"purchase_option_title": "PURCHASE OPTIONS",
|
||||
"purchase_panel_info_1": "Building Immich takes a lot of time and effort, and we have full-time engineers working on it to make it as good as we possibly can. Our mission is for open-source software and ethical business practices to become a sustainable income source for developers and to create a privacy-respecting ecosystem with real alternatives to exploitative cloud services.",
|
||||
"purchase_panel_info_2": "As we’re committed not to add paywalls, this purchase will not grant you any additional features in Immich. We rely on users like you to support Immich’s ongoing development.",
|
||||
"purchase_panel_info_2": "As we're committed not to add paywalls, this purchase will not grant you any additional features in Immich. We rely on users like you to support Immich's ongoing development.",
|
||||
"purchase_panel_title": "Support the project",
|
||||
"purchase_per_server": "Per server",
|
||||
"purchase_per_user": "Per user",
|
||||
@@ -1505,6 +1499,7 @@
|
||||
"remove_from_shared_link": "Remove from shared link",
|
||||
"remove_memory": "Remove memory",
|
||||
"remove_photo_from_memory": "Remove photo from this memory",
|
||||
"remove_tag": "Remove tag",
|
||||
"remove_url": "Remove URL",
|
||||
"remove_user": "Remove user",
|
||||
"removed_api_key": "Removed API Key: {name}",
|
||||
@@ -1631,6 +1626,7 @@
|
||||
"server_info_box_server_url": "Server URL",
|
||||
"server_offline": "Server Offline",
|
||||
"server_online": "Server Online",
|
||||
"server_privacy": "Server Privacy",
|
||||
"server_stats": "Server Stats",
|
||||
"server_version": "Server Version",
|
||||
"set": "Set",
|
||||
@@ -1640,6 +1636,7 @@
|
||||
"set_date_of_birth": "Set date of birth",
|
||||
"set_profile_picture": "Set profile picture",
|
||||
"set_slideshow_to_fullscreen": "Set Slideshow to fullscreen",
|
||||
"set_stack_primary_asset": "Set as primary asset",
|
||||
"setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).",
|
||||
"setting_image_viewer_original_subtitle": "Enable to load the original full-resolution image (large!). Disable to reduce data usage (both network and on device cache).",
|
||||
"setting_image_viewer_original_title": "Load original image",
|
||||
@@ -1648,7 +1645,6 @@
|
||||
"setting_image_viewer_title": "Images",
|
||||
"setting_languages_apply": "Apply",
|
||||
"setting_languages_subtitle": "Change the app's language",
|
||||
"setting_languages_title": "Languages",
|
||||
"setting_notifications_notify_failures_grace_period": "Notify background backup failures: {duration}",
|
||||
"setting_notifications_notify_hours": "{count} hours",
|
||||
"setting_notifications_notify_immediately": "immediately",
|
||||
@@ -1778,6 +1774,7 @@
|
||||
"start_date": "Start date",
|
||||
"state": "State",
|
||||
"status": "Status",
|
||||
"stop_casting": "Stop casting",
|
||||
"stop_motion_photo": "Stop Motion Photo",
|
||||
"stop_photo_sharing": "Stop sharing your photos?",
|
||||
"stop_photo_sharing_description": "{partner} will no longer be able to access your photos.",
|
||||
@@ -1835,7 +1832,6 @@
|
||||
"to_parent": "Go to parent",
|
||||
"to_trash": "Trash",
|
||||
"toggle_settings": "Toggle settings",
|
||||
"toggle_theme": "Toggle dark theme",
|
||||
"total": "Total",
|
||||
"total_usage": "Total usage",
|
||||
"trash": "Trash",
|
||||
@@ -1857,6 +1853,7 @@
|
||||
"unable_to_setup_pin_code": "Unable to setup PIN code",
|
||||
"unarchive": "Unarchive",
|
||||
"unarchived_count": "{count, plural, other {Unarchived #}}",
|
||||
"undo": "Undo",
|
||||
"unfavorite": "Unfavorite",
|
||||
"unhide_person": "Unhide person",
|
||||
"unknown": "Unknown",
|
||||
@@ -1875,8 +1872,6 @@
|
||||
"unselect_all_duplicates": "Unselect all duplicates",
|
||||
"unstack": "Un-stack",
|
||||
"unstacked_assets_count": "Un-stacked {count, plural, one {# asset} other {# assets}}",
|
||||
"untracked_files": "Untracked files",
|
||||
"untracked_files_decription": "These files are not tracked by the application. They can be the results of failed moves, interrupted uploads, or left behind due to a bug",
|
||||
"up_next": "Up next",
|
||||
"updated_at": "Updated",
|
||||
"updated_password": "Updated password",
|
||||
@@ -1904,6 +1899,7 @@
|
||||
"user_liked": "{user} liked {type, select, photo {this photo} video {this video} asset {this asset} other {it}}",
|
||||
"user_pin_code_settings": "PIN Code",
|
||||
"user_pin_code_settings_description": "Manage your PIN code",
|
||||
"user_privacy": "User Privacy",
|
||||
"user_purchase_settings": "Purchase",
|
||||
"user_purchase_settings_description": "Manage your purchase",
|
||||
"user_role_set": "Set {user} as {role}",
|
||||
@@ -1919,11 +1915,6 @@
|
||||
"version": "Version",
|
||||
"version_announcement_closing": "Your friend, Alex",
|
||||
"version_announcement_message": "Hi there! A new version of Immich is available. Please take some time to read the <link>release notes</link> to ensure your setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your Immich instance automatically.",
|
||||
"version_announcement_overlay_release_notes": "release notes",
|
||||
"version_announcement_overlay_text_1": "Hi friend, there is a new release of",
|
||||
"version_announcement_overlay_text_2": "please take your time to visit the ",
|
||||
"version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.",
|
||||
"version_announcement_overlay_title": "New Server Version Available 🎉",
|
||||
"version_history": "Version History",
|
||||
"version_history_item": "Installed {version} on {date}",
|
||||
"video": "Video",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
ARG DEVICE=cpu
|
||||
|
||||
FROM python:3.11-bookworm@sha256:ab60e444e04215a62671149f24c59cc2893b49cb5dad26f9d139077a86be760e AS builder-cpu
|
||||
FROM python:3.11-bookworm@sha256:d2621a9f74d31a8a60af19f97b09cc3ac54382c8680b6544018713a12ef6c048 AS builder-cpu
|
||||
|
||||
FROM builder-cpu AS builder-openvino
|
||||
|
||||
@@ -59,7 +59,7 @@ ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends g++
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest@sha256:4a6c9444b126bd325fba904bff796bf91fb777bf6148d60109c4cb1de2ffc497 /uv /uvx /bin/
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest@sha256:4faec156e35a5f345d57804d8858c6ba1cf6352ce5f4bffc11b7fdebdef46a38 /uv /uvx /bin/
|
||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||
--mount=type=bind,source=uv.lock,target=uv.lock \
|
||||
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
||||
@@ -68,11 +68,11 @@ RUN if [ "$DEVICE" = "rocm" ]; then \
|
||||
uv pip install /opt/onnxruntime_rocm-*.whl; \
|
||||
fi
|
||||
|
||||
FROM python:3.11-slim-bookworm@sha256:97ef3198ec8c78690587167bb6a4905d00ffe053900687bdae93ad667e507cbb AS prod-cpu
|
||||
FROM python:3.11-slim-bookworm@sha256:7a3ed1226224bcc1fe5443262363d42f48cf832a540c1836ba8ccbeaadf8637c AS prod-cpu
|
||||
|
||||
ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2
|
||||
|
||||
FROM python:3.11-slim-bookworm@sha256:97ef3198ec8c78690587167bb6a4905d00ffe053900687bdae93ad667e507cbb AS prod-openvino
|
||||
FROM python:3.11-slim-bookworm@sha256:7a3ed1226224bcc1fe5443262363d42f48cf832a540c1836ba8ccbeaadf8637c AS prod-openvino
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install --no-install-recommends -yqq ocl-icd-libopencl1 wget && \
|
||||
|
||||
200
machine-learning/uv.lock
generated
200
machine-learning/uv.lock
generated
@@ -346,11 +346,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "configargparse"
|
||||
version = "1.7"
|
||||
version = "1.7.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/70/8a/73f1008adfad01cb923255b924b1528727b8270e67cb4ef41eabdc7d783e/ConfigArgParse-1.7.tar.gz", hash = "sha256:e7067471884de5478c58a511e529f0f9bd1c66bfef1dea90935438d6c23306d1", size = 43817, upload-time = "2023-07-23T16:20:04.95Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/85/4d/6c9ef746dfcc2a32e26f3860bb4a011c008c392b83eabdfb598d1a8bbe5d/configargparse-1.7.1.tar.gz", hash = "sha256:79c2ddae836a1e5914b71d58e4b9adbd9f7779d4e6351a637b7d2d9b6c46d3d9", size = 43958, upload-time = "2025-05-23T14:26:17.369Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/b3/b4ac838711fd74a2b4e6f746703cf9dd2cf5462d17dac07e349234e21b97/ConfigArgParse-1.7-py3-none-any.whl", hash = "sha256:d249da6591465c6c26df64a9f73d2536e743be2f244eb3ebe61114af2f94f86b", size = 25489, upload-time = "2023-07-23T16:20:03.27Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/28/d28211d29bcc3620b1fece85a65ce5bb22f18670a03cd28ea4b75ede270c/configargparse-1.7.1-py3-none-any.whl", hash = "sha256:8b586a31f9d873abd1ca527ffbe58863c99f36d896e2829779803125e83be4b6", size = 25607, upload-time = "2025-05-23T14:26:15.923Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -821,17 +821,17 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "hf-xet"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3a/09/e2fc5ccd6f9828efbd9135d5aab70895fa6891752ce84c57026c48f3f33d/hf_xet-1.1.1.tar.gz", hash = "sha256:3e75d6e04c38c80115b640c025d68c3dc14d62f8b244011dfe547363674a1e87", size = 277864, upload-time = "2025-05-12T21:34:25.002Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/95/be/58f20728a5b445f8b064e74f0618897b3439f5ef90934da1916b9dfac76f/hf_xet-1.1.2.tar.gz", hash = "sha256:3712d6d4819d3976a1c18e36db9f503e296283f9363af818f50703506ed63da3", size = 467009, upload-time = "2025-05-16T20:44:34.944Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/97/f5/81194ea8e4a585d7d4d0f2ad1ca16e05a4b0c5a385bb2610a8e6da1d2c3d/hf_xet-1.1.1-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e39a8513f0854656116c837d387d9a41e9d78430b1a181442f04c223cbc4e8f8", size = 5274857, upload-time = "2025-05-12T21:34:18.32Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/3c/36342b3fa247f2580821a4b183d38f36fb20e911a8307df625790e734359/hf_xet-1.1.1-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:c60cd67be384cb9e592fa6dfd29a10fddffa1feb2f3b31f53e980630d1ca0fd6", size = 5079657, upload-time = "2025-05-12T21:34:16.912Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/c1/4f770cc7be79287905e13765d4a7e1949dce3483f90867f532ff56e7126b/hf_xet-1.1.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5efc6cf15930d9b0cef25c0444e00c2f55d9e09f856f26ed8c809fd5cd1aa044", size = 25506200, upload-time = "2025-05-12T21:34:14.725Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/69/1ec612f8e9e2ca27563adfca926cfb41bbe988e30d4cd6fc1e0c019e5570/hf_xet-1.1.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:504bbc8341edc2aa4b3c20c1fdda818554ab34e4175730f42e2a90436cbbe706", size = 24469080, upload-time = "2025-05-12T21:34:11.974Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/96/9201773a0ebb982aa5936f19bdd04d404bc5d74e23f30bce6e857757998b/hf_xet-1.1.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:87d030157a21016c2cddf757a5fd6f1f364d86afef6e190e63a37dd4dc6f6c98", size = 25641374, upload-time = "2025-05-12T21:34:20.131Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/14/10a4cf526070e774bdc7ce68202dc27a15751ddc22c6b47a5ecb6d8ea4ad/hf_xet-1.1.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6e9b640f0f002b3bea36739b30cf3133b3175c27a342b39315be9a9bdb0cec5b", size = 25425434, upload-time = "2025-05-12T21:34:22.97Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/25/7015a82b3b165747ba85b0383e5d5278d268f3a30460f6d55849903cf272/hf_xet-1.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:215a4e95009a0b9795ca3cf33db4e8d1248139593d7e1185661cd19b062d2b82", size = 4391897, upload-time = "2025-05-12T21:34:26.469Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/ae/f1a63f75d9886f18a80220ba31a1c7b9c4752f03aae452f358f538c6a991/hf_xet-1.1.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:dfd1873fd648488c70735cb60f7728512bca0e459e61fcd107069143cd798469", size = 2642559, upload-time = "2025-05-16T20:44:30.217Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/ab/d2c83ae18f1015d926defd5bfbe94c62d15e93f900e6a192e318ee947105/hf_xet-1.1.2-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:29b584983b2d977c44157d9241dcf0fd50acde0b7bff8897fe4386912330090d", size = 2541360, upload-time = "2025-05-16T20:44:29.056Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/a7/693dc9f34f979e30a378125e2150a0b2d8d166e6d83ce3950eeb81e560aa/hf_xet-1.1.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b29ac84298147fe9164cc55ad994ba47399f90b5d045b0b803b99cf5f06d8ec", size = 5183081, upload-time = "2025-05-16T20:44:27.505Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/23/c48607883f692a36c0a7735f47f98bad32dbe459a32d1568c0f21576985d/hf_xet-1.1.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d921ba32615676e436a0d15e162331abc9ed43d440916b1d836dc27ce1546173", size = 5356100, upload-time = "2025-05-16T20:44:25.681Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/5b/b2316c7f1076da0582b52ea228f68bea95e243c388440d1dc80297c9d813/hf_xet-1.1.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d9b03c34e13c44893ab6e8fea18ee8d2a6878c15328dd3aabedbdd83ee9f2ed3", size = 5647688, upload-time = "2025-05-16T20:44:31.867Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/98/e6995f0fa579929da7795c961f403f4ee84af36c625963f52741d56f242c/hf_xet-1.1.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:01b18608955b3d826307d37da8bd38b28a46cd2d9908b3a3655d1363274f941a", size = 5322627, upload-time = "2025-05-16T20:44:33.677Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/40/8f1d5a44a64d8bf9e3c19576e789f716af54875b46daae65426714e75db1/hf_xet-1.1.2-cp37-abi3-win_amd64.whl", hash = "sha256:3562902c81299b09f3582ddfb324400c6a901a2f3bc854f83556495755f4954c", size = 2739542, upload-time = "2025-05-16T20:44:36.287Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -900,7 +900,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "huggingface-hub"
|
||||
version = "0.31.1"
|
||||
version = "0.32.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "filelock" },
|
||||
@@ -912,9 +912,9 @@ dependencies = [
|
||||
{ name = "tqdm" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/25/eb/9268c1205d19388659d5dc664f012177b752c0eef194a9159acc7227780f/huggingface_hub-0.31.1.tar.gz", hash = "sha256:492bb5f545337aa9e2f59b75ef4c5f535a371e8958a6ce90af056387e67f1180", size = 403036, upload-time = "2025-05-07T15:25:19.695Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/60/c8/4f7d270285c46324fd66f62159eb16739aa5696f422dba57678a8c6b78e9/huggingface_hub-0.32.4.tar.gz", hash = "sha256:f61d45cd338736f59fb0e97550b74c24ee771bcc92c05ae0766b9116abe720be", size = 424494, upload-time = "2025-06-03T09:59:46.105Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/bf/6002da17ec1c7a47bedeb216812929665927c70b6e7500b3c7bf36f01bdd/huggingface_hub-0.31.1-py3-none-any.whl", hash = "sha256:43f73124819b48b42d140cbc0d7a2e6bd15b2853b1b9d728d4d55ad1750cac5b", size = 484265, upload-time = "2025-05-07T15:25:17.921Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/8b/222140f3cfb6f17b0dd8c4b9a0b36bd4ebefe9fb0098ba35d6960abcda0f/huggingface_hub-0.32.4-py3-none-any.whl", hash = "sha256:37abf8826b38d971f60d3625229221c36e53fe58060286db9baf619cfbf39767", size = 512101, upload-time = "2025-06-03T09:59:44.099Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1225,7 +1225,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "locust"
|
||||
version = "2.37.1"
|
||||
version = "2.37.9"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "configargparse" },
|
||||
@@ -1245,25 +1245,26 @@ dependencies = [
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
||||
{ name = "werkzeug" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6a/8f/e358f3e3850a4057c05f635d94e27a2fe739301fae5f2ece230a6a8ea282/locust-2.37.1.tar.gz", hash = "sha256:97951b319cb08c8853ef76d4732359f04617d27be41c1bf91469b9a528b652e0", size = 2251378, upload-time = "2025-05-07T18:36:49.932Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/90/05/2bfdf19756c6a12f6f9513f75340ecf0595d83cab4d9fc91162225908e3d/locust-2.37.9.tar.gz", hash = "sha256:e43673b594ec5ecde4f9ba6e0d5c66c00d7c0ae93591951abe83e8d186c67175", size = 2252507, upload-time = "2025-06-05T09:26:58.186Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/12/10/bbfab1fd9f5fd5c7d377b9d595f5db663b2a720283949efc0135b8022758/locust-2.37.1-py3-none-any.whl", hash = "sha256:9a19a942feb0e58bf638f563b72f019dc19ddf622bee4d28c2c46a2baa8499c3", size = 2268091, upload-time = "2025-05-07T18:36:47.428Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/1c/0ece4176231c578e819d970ec08d124492833e50aafd171c582bcc414446/locust-2.37.9-py3-none-any.whl", hash = "sha256:e17da439f3a252d1fb6d4c34daf00d7e8b87e99d833a32e8a79f4f8ebb07767d", size = 2269084, upload-time = "2025-06-05T09:26:56.257Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "locust-cloud"
|
||||
version = "1.21.3"
|
||||
version = "1.23.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "configargparse" },
|
||||
{ name = "gevent" },
|
||||
{ name = "platformdirs" },
|
||||
{ name = "python-engineio" },
|
||||
{ name = "python-socketio", extra = ["client"] },
|
||||
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0f/07/94de2ed7cd7d2686f0348970808d03a070fd9264acacc4ed4c71711e2164/locust_cloud-1.21.3.tar.gz", hash = "sha256:7155fd0b64037d3031d002f56a1d3c83663dd825c0ff7af6709b5c3381c78507", size = 449927, upload-time = "2025-05-08T08:08:26.118Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bd/7c/d9cbbd051490aeedfbd6ddda8ad48f77dd848ee490f6ebd166d20db5911e/locust_cloud-1.23.1.tar.gz", hash = "sha256:a09161752b8c9a9205e97cef5223ee3ad967bc2d91c52d61952aaa3da6802a55", size = 450937, upload-time = "2025-06-05T06:07:53.773Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/27/a8/d02decd8cf38d949793c8da21d4d3806281668147d0b2cedd558c51f48db/locust_cloud-1.21.3-py3-none-any.whl", hash = "sha256:fda78be76230b32927b9893667240d49d05d74b7db99bf916e1017e1a2a31c30", size = 407164, upload-time = "2025-05-08T08:08:24.232Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/01/5af43edee540e38ba0ee0a2e3beb72c50073e0f646bb543a8b34650315e3/locust_cloud-1.23.1-py3-none-any.whl", hash = "sha256:11677895c6ed6d0beef1b425a6f04f10ea2cfcaeaefbf00a97fb3c9134296e54", size = 408323, upload-time = "2025-06-05T06:07:51.947Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1414,40 +1415,41 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "1.15.0"
|
||||
version = "1.16.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "mypy-extensions" },
|
||||
{ name = "pathspec" },
|
||||
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717, upload-time = "2025-02-05T03:50:34.655Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d4/38/13c2f1abae94d5ea0354e146b95a1be9b2137a0d506728e0da037c4276f6/mypy-1.16.0.tar.gz", hash = "sha256:84b94283f817e2aa6350a14b4a8fb2a35a53c286f97c9d30f53b63620e7af8ab", size = 3323139, upload-time = "2025-05-29T13:46:12.532Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/68/f8/65a7ce8d0e09b6329ad0c8d40330d100ea343bd4dd04c4f8ae26462d0a17/mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13", size = 10738433, upload-time = "2025-02-05T03:49:29.145Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/95/9c0ecb8eacfe048583706249439ff52105b3f552ea9c4024166c03224270/mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559", size = 9861472, upload-time = "2025-02-05T03:49:16.986Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/09/9ec95e982e282e20c0d5407bc65031dfd0f0f8ecc66b69538296e06fcbee/mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b", size = 11611424, upload-time = "2025-02-05T03:49:46.908Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/13/f7d14e55865036a1e6a0a69580c240f43bc1f37407fe9235c0d4ef25ffb0/mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3", size = 12365450, upload-time = "2025-02-05T03:50:05.89Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/48/e1/301a73852d40c241e915ac6d7bcd7fedd47d519246db2d7b86b9d7e7a0cb/mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b", size = 12551765, upload-time = "2025-02-05T03:49:33.56Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/ba/c37bc323ae5fe7f3f15a28e06ab012cd0b7552886118943e90b15af31195/mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828", size = 9274701, upload-time = "2025-02-05T03:49:38.981Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/03/bc/f6339726c627bd7ca1ce0fa56c9ae2d0144604a319e0e339bdadafbbb599/mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f", size = 10662338, upload-time = "2025-02-05T03:50:17.287Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/90/8dcf506ca1a09b0d17555cc00cd69aee402c203911410136cd716559efe7/mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5", size = 9787540, upload-time = "2025-02-05T03:49:51.21Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/05/a10f9479681e5da09ef2f9426f650d7b550d4bafbef683b69aad1ba87457/mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e", size = 11538051, upload-time = "2025-02-05T03:50:20.885Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/9a/1f7d18b30edd57441a6411fcbc0c6869448d1a4bacbaee60656ac0fc29c8/mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c", size = 12286751, upload-time = "2025-02-05T03:49:42.408Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/af/19ff499b6f1dafcaf56f9881f7a965ac2f474f69f6f618b5175b044299f5/mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f", size = 12421783, upload-time = "2025-02-05T03:49:07.707Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/39/11b57431a1f686c1aed54bf794870efe0f6aeca11aca281a0bd87a5ad42c/mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f", size = 9265618, upload-time = "2025-02-05T03:49:54.581Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/3a/03c74331c5eb8bd025734e04c9840532226775c47a2c39b56a0c8d4f128d/mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", size = 10793981, upload-time = "2025-02-05T03:50:28.25Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/1a/41759b18f2cfd568848a37c89030aeb03534411eef981df621d8fad08a1d/mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", size = 9749175, upload-time = "2025-02-05T03:50:13.411Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/7e/873481abf1ef112c582db832740f4c11b2bfa510e829d6da29b0ab8c3f9c/mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", size = 11455675, upload-time = "2025-02-05T03:50:31.421Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/d0/92ae4cde706923a2d3f2d6c39629134063ff64b9dedca9c1388363da072d/mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", size = 12410020, upload-time = "2025-02-05T03:48:48.705Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/8b/df49974b337cce35f828ba6fda228152d6db45fed4c86ba56ffe442434fd/mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", size = 12498582, upload-time = "2025-02-05T03:49:03.628Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/50/da5203fcf6c53044a0b699939f31075c45ae8a4cadf538a9069b165c1050/mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", size = 9366614, upload-time = "2025-02-05T03:50:00.313Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/9b/fd2e05d6ffff24d912f150b87db9e364fa8282045c875654ce7e32fffa66/mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445", size = 10788592, upload-time = "2025-02-05T03:48:55.789Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/37/b246d711c28a03ead1fd906bbc7106659aed7c089d55fe40dd58db812628/mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d", size = 9753611, upload-time = "2025-02-05T03:48:44.581Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/ac/395808a92e10cfdac8003c3de9a2ab6dc7cde6c0d2a4df3df1b815ffd067/mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5", size = 11438443, upload-time = "2025-02-05T03:49:25.514Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/8b/801aa06445d2de3895f59e476f38f3f8d610ef5d6908245f07d002676cbf/mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036", size = 12402541, upload-time = "2025-02-05T03:49:57.623Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/67/5a4268782eb77344cc613a4cf23540928e41f018a9a1ec4c6882baf20ab8/mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357", size = 12494348, upload-time = "2025-02-05T03:48:52.361Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/3e/57bb447f7bbbfaabf1712d96f9df142624a386d98fb026a761532526057e/mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf", size = 9373648, upload-time = "2025-02-05T03:49:11.395Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/4e/a7d65c7322c510de2c409ff3828b03354a7c43f5a8ed458a7a131b41c7b9/mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", size = 2221777, upload-time = "2025-02-05T03:50:08.348Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/5e/a0485f0608a3d67029d3d73cec209278b025e3493a3acfda3ef3a88540fd/mypy-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7909541fef256527e5ee9c0a7e2aeed78b6cda72ba44298d1334fe7881b05c5c", size = 10967416, upload-time = "2025-05-29T13:34:17.783Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/53/5837c221f74c0d53a4bfc3003296f8179c3a2a7f336d7de7bbafbe96b688/mypy-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e71d6f0090c2256c713ed3d52711d01859c82608b5d68d4fa01a3fe30df95571", size = 10087654, upload-time = "2025-05-29T13:32:37.878Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/59/5fd2400352c3093bed4c09017fe671d26bc5bb7e6ef2d4bf85f2a2488104/mypy-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:936ccfdd749af4766be824268bfe22d1db9eb2f34a3ea1d00ffbe5b5265f5491", size = 11875192, upload-time = "2025-05-29T13:34:54.281Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/3e/4bfec74663a64c2012f3e278dbc29ffe82b121bc551758590d1b6449ec0c/mypy-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4086883a73166631307fdd330c4a9080ce24913d4f4c5ec596c601b3a4bdd777", size = 12612939, upload-time = "2025-05-29T13:33:14.766Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/1f/fecbe3dcba4bf2ca34c26ca016383a9676711907f8db4da8354925cbb08f/mypy-1.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:feec38097f71797da0231997e0de3a58108c51845399669ebc532c815f93866b", size = 12874719, upload-time = "2025-05-29T13:21:52.09Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/51/c2d280601cd816c43dfa512a759270d5a5ef638d7ac9bea9134c8305a12f/mypy-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:09a8da6a0ee9a9770b8ff61b39c0bb07971cda90e7297f4213741b48a0cc8d93", size = 9487053, upload-time = "2025-05-29T13:33:29.797Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/c4/ff2f79db7075c274fe85b5fff8797d29c6b61b8854c39e3b7feb556aa377/mypy-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9f826aaa7ff8443bac6a494cf743f591488ea940dd360e7dd330e30dd772a5ab", size = 10884498, upload-time = "2025-05-29T13:18:54.066Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/07/12198e83006235f10f6a7808917376b5d6240a2fd5dce740fe5d2ebf3247/mypy-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:82d056e6faa508501af333a6af192c700b33e15865bda49611e3d7d8358ebea2", size = 10011755, upload-time = "2025-05-29T13:34:00.851Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/9b/5fd5801a72b5d6fb6ec0105ea1d0e01ab2d4971893076e558d4b6d6b5f80/mypy-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:089bedc02307c2548eb51f426e085546db1fa7dd87fbb7c9fa561575cf6eb1ff", size = 11800138, upload-time = "2025-05-29T13:32:55.082Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2e/81/a117441ea5dfc3746431e51d78a4aca569c677aa225bca2cc05a7c239b61/mypy-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a2322896003ba66bbd1318c10d3afdfe24e78ef12ea10e2acd985e9d684a666", size = 12533156, upload-time = "2025-05-29T13:19:12.963Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/38/88ec57c6c86014d3f06251e00f397b5a7daa6888884d0abf187e4f5f587f/mypy-1.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:021a68568082c5b36e977d54e8f1de978baf401a33884ffcea09bd8e88a98f4c", size = 12742426, upload-time = "2025-05-29T13:20:22.72Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/53/7e9d528433d56e6f6f77ccf24af6ce570986c2d98a5839e4c2009ef47283/mypy-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:54066fed302d83bf5128632d05b4ec68412e1f03ef2c300434057d66866cea4b", size = 9478319, upload-time = "2025-05-29T13:21:17.582Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/cf/158e5055e60ca2be23aec54a3010f89dcffd788732634b344fc9cb1e85a0/mypy-1.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5436d11e89a3ad16ce8afe752f0f373ae9620841c50883dc96f8b8805620b13", size = 11062927, upload-time = "2025-05-29T13:35:52.328Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/34/cfff7a56be1609f5d10ef386342ce3494158e4d506516890142007e6472c/mypy-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f2622af30bf01d8fc36466231bdd203d120d7a599a6d88fb22bdcb9dbff84090", size = 10083082, upload-time = "2025-05-29T13:35:33.378Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/7f/7242062ec6288c33d8ad89574df87c3903d394870e5e6ba1699317a65075/mypy-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d045d33c284e10a038f5e29faca055b90eee87da3fc63b8889085744ebabb5a1", size = 11828306, upload-time = "2025-05-29T13:21:02.164Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/5f/b392f7b4f659f5b619ce5994c5c43caab3d80df2296ae54fa888b3d17f5a/mypy-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b4968f14f44c62e2ec4a038c8797a87315be8df7740dc3ee8d3bfe1c6bf5dba8", size = 12702764, upload-time = "2025-05-29T13:20:42.826Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/c0/7646ef3a00fa39ac9bc0938626d9ff29d19d733011be929cfea59d82d136/mypy-1.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb14a4a871bb8efb1e4a50360d4e3c8d6c601e7a31028a2c79f9bb659b63d730", size = 12896233, upload-time = "2025-05-29T13:18:37.446Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/38/52f4b808b3fef7f0ef840ee8ff6ce5b5d77381e65425758d515cdd4f5bb5/mypy-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:bd4e1ebe126152a7bbaa4daedd781c90c8f9643c79b9748caa270ad542f12bec", size = 9565547, upload-time = "2025-05-29T13:20:02.836Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/9c/ca03bdbefbaa03b264b9318a98950a9c683e06472226b55472f96ebbc53d/mypy-1.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a9e056237c89f1587a3be1a3a70a06a698d25e2479b9a2f57325ddaaffc3567b", size = 11059753, upload-time = "2025-05-29T13:18:18.167Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/92/79a969b8302cfe316027c88f7dc6fee70129490a370b3f6eb11d777749d0/mypy-1.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b07e107affb9ee6ce1f342c07f51552d126c32cd62955f59a7db94a51ad12c0", size = 10073338, upload-time = "2025-05-29T13:19:48.079Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/9b/a943f09319167da0552d5cd722104096a9c99270719b1afeea60d11610aa/mypy-1.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6fb60cbd85dc65d4d63d37cb5c86f4e3a301ec605f606ae3a9173e5cf34997b", size = 11827764, upload-time = "2025-05-29T13:46:04.47Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/64/ff75e71c65a0cb6ee737287c7913ea155845a556c64144c65b811afdb9c7/mypy-1.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7e32297a437cc915599e0578fa6bc68ae6a8dc059c9e009c628e1c47f91495d", size = 12701356, upload-time = "2025-05-29T13:35:13.553Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/ad/0e93c18987a1182c350f7a5fab70550852f9fabe30ecb63bfbe51b602074/mypy-1.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:afe420c9380ccec31e744e8baff0d406c846683681025db3531b32db56962d52", size = 12900745, upload-time = "2025-05-29T13:17:24.409Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/28/5d/036c278d7a013e97e33f08c047fe5583ab4f1fc47c9a49f985f1cdd2a2d7/mypy-1.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:55f9076c6ce55dd3f8cd0c6fff26a008ca8e5131b89d5ba6d86bd3f47e736eeb", size = 9572200, upload-time = "2025-05-29T13:33:44.92Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/a3/6ed10530dec8e0fdc890d81361260c9ef1f5e5c217ad8c9b21ecb2b8366b/mypy-1.16.0-py3-none-any.whl", hash = "sha256:29e1499864a3888bca5c1542f2d7232c6e586295183320caa95758fc84034031", size = 2265773, upload-time = "2025-05-29T13:35:18.762Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1832,7 +1834,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.11.4"
|
||||
version = "2.11.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "annotated-types" },
|
||||
@@ -1840,9 +1842,9 @@ dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
{ name = "typing-inspection" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/77/ab/5250d56ad03884ab5efd07f734203943c8a8ab40d551e208af81d0257bf2/pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d", size = 786540, upload-time = "2025-04-29T20:38:55.02Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f0/86/8ce9040065e8f924d642c58e4a344e33163a07f6b57f836d0d734e0ad3fb/pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a", size = 787102, upload-time = "2025-05-22T21:18:08.761Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/12/46b65f3534d099349e38ef6ec98b1a5a81f42536d17e0ba382c28c67ba67/pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb", size = 443900, upload-time = "2025-04-29T20:38:52.724Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/69/831ed22b38ff9b4b64b66569f0e5b7b97cf3638346eb95a2147fdb49ad5f/pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7", size = 444229, upload-time = "2025-05-22T21:18:06.329Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1975,7 +1977,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "8.3.5"
|
||||
version = "8.4.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
@@ -1983,23 +1985,24 @@ dependencies = [
|
||||
{ name = "iniconfig" },
|
||||
{ name = "packaging" },
|
||||
{ name = "pluggy" },
|
||||
{ name = "pygments" },
|
||||
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891, upload-time = "2025-03-02T12:54:54.503Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fb/aa/405082ce2749be5398045152251ac69c0f3578c7077efc53431303af97ce/pytest-8.4.0.tar.gz", hash = "sha256:14d920b48472ea0dbf68e45b96cd1ffda4705f33307dcc86c676c1b5104838a6", size = 1515232, upload-time = "2025-06-02T17:36:30.03Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634, upload-time = "2025-03-02T12:54:52.069Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/de/afa024cbe022b1b318a3d224125aa24939e99b4ff6f22e0ba639a2eaee47/pytest-8.4.0-py3-none-any.whl", hash = "sha256:f40f825768ad76c0977cbacdf1fd37c6f7a468e460ea6a0636078f8972d4517e", size = 363797, upload-time = "2025-06-02T17:36:27.859Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-asyncio"
|
||||
version = "0.26.0"
|
||||
version = "1.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pytest" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8e/c4/453c52c659521066969523e87d85d54139bbd17b78f09532fb8eb8cdb58e/pytest_asyncio-0.26.0.tar.gz", hash = "sha256:c4df2a697648241ff39e7f0e4a73050b03f123f760673956cf0d72a4990e312f", size = 54156, upload-time = "2025-03-25T06:22:28.883Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d0/d4/14f53324cb1a6381bef29d698987625d80052bb33932d8e7cbf9b337b17c/pytest_asyncio-1.0.0.tar.gz", hash = "sha256:d15463d13f4456e1ead2594520216b225a16f781e144f8fdf6c5bb4667c48b3f", size = 46960, upload-time = "2025-05-26T04:54:40.484Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/20/7f/338843f449ace853647ace35870874f69a764d251872ed1b4de9f234822c/pytest_asyncio-0.26.0-py3-none-any.whl", hash = "sha256:7b51ed894f4fbea1340262bdae5135797ebbe21d8638978e35d31c6d19f72fb0", size = 19694, upload-time = "2025-03-25T06:22:27.807Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/05/ce271016e351fddc8399e546f6e23761967ee09c8c568bbfbecb0c150171/pytest_asyncio-1.0.0-py3-none-any.whl", hash = "sha256:4f024da9f1ef945e680dc68610b52550e36590a67fd31bb3b4943979a1f90ef3", size = 15976, upload-time = "2025-05-26T04:54:39.035Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2017,14 +2020,14 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pytest-mock"
|
||||
version = "3.14.0"
|
||||
version = "3.14.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pytest" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c6/90/a955c3ab35ccd41ad4de556596fa86685bf4fc5ffcc62d22d856cfd4e29a/pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0", size = 32814, upload-time = "2024-03-21T22:14:04.964Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/71/28/67172c96ba684058a4d24ffe144d64783d2a270d0af0d9e792737bddc75c/pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e", size = 33241, upload-time = "2025-05-26T13:58:45.167Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863, upload-time = "2024-03-21T22:14:02.694Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/05/77b60e520511c53d1c1ca75f1930c7dd8e971d0c4379b7f4b3f9644685ba/pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0", size = 9923, upload-time = "2025-05-26T13:58:43.487Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2050,14 +2053,14 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "python-engineio"
|
||||
version = "4.12.0"
|
||||
version = "4.12.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "simple-websocket" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f7/e1/eee1129544b7f78fa2afa9fa0fce153cdcb21015b9b331d1b8adf90f45cb/python_engineio-4.12.0.tar.gz", hash = "sha256:f42a36a868d7063aa10ddccf6bd6117a169b6bd00d7ca53999772093b62014f9", size = 91503, upload-time = "2025-04-12T15:30:23.905Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ba/0b/67295279b66835f9fa7a491650efcd78b20321c127036eef62c11a31e028/python_engineio-4.12.2.tar.gz", hash = "sha256:e7e712ffe1be1f6a05ee5f951e72d434854a32fcfc7f6e4d9d3cae24ec70defa", size = 91677, upload-time = "2025-06-04T19:22:18.789Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/f7/0aeea75424c47633c1d98557a2323be23bed31fa950f00161b34a5150d06/python_engineio-4.12.0-py3-none-any.whl", hash = "sha256:a0c47c129c39777e8ebc6d18011efd50db2144e4e8f08983acae8a3614626535", size = 59319, upload-time = "2025-04-12T15:30:22.325Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/fa/df59acedf7bbb937f69174d00f921a7b93aa5a5f5c17d05296c814fff6fc/python_engineio-4.12.2-py3-none-any.whl", hash = "sha256:8218ab66950e179dfec4b4bbb30aecf3f5d86f5e58e6fc1aa7fde2c698b2804f", size = 59536, upload-time = "2025-06-04T19:22:16.916Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2215,16 +2218,16 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "rich"
|
||||
version = "13.9.4"
|
||||
version = "14.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "markdown-it-py" },
|
||||
{ name = "pygments" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149, upload-time = "2024-11-01T16:43:57.873Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078, upload-time = "2025-03-30T14:15:14.23Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424, upload-time = "2024-11-01T16:43:55.817Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229, upload-time = "2025-03-30T14:15:12.283Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2300,27 +2303,27 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.11.8"
|
||||
version = "0.11.13"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/52/f6/adcf73711f31c9f5393862b4281c875a462d9f639f4ccdf69dc368311c20/ruff-0.11.8.tar.gz", hash = "sha256:6d742d10626f9004b781f4558154bb226620a7242080e11caeffab1a40e99df8", size = 4086399, upload-time = "2025-05-01T14:53:24.459Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ed/da/9c6f995903b4d9474b39da91d2d626659af3ff1eeb43e9ae7c119349dba6/ruff-0.11.13.tar.gz", hash = "sha256:26fa247dc68d1d4e72c179e08889a25ac0c7ba4d78aecfc835d49cbfd60bf514", size = 4282054, upload-time = "2025-06-05T21:00:15.721Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/60/c6aa9062fa518a9f86cb0b85248245cddcd892a125ca00441df77d79ef88/ruff-0.11.8-py3-none-linux_armv6l.whl", hash = "sha256:896a37516c594805e34020c4a7546c8f8a234b679a7716a3f08197f38913e1a3", size = 10272473, upload-time = "2025-05-01T14:52:37.252Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/e4/0325e50d106dc87c00695f7bcd5044c6d252ed5120ebf423773e00270f50/ruff-0.11.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ab86d22d3d721a40dd3ecbb5e86ab03b2e053bc93c700dc68d1c3346b36ce835", size = 11040862, upload-time = "2025-05-01T14:52:41.022Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/27/b87ea1a7be37fef0adbc7fd987abbf90b6607d96aa3fc67e2c5b858e1e53/ruff-0.11.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:258f3585057508d317610e8a412788cf726efeefa2fec4dba4001d9e6f90d46c", size = 10385273, upload-time = "2025-05-01T14:52:43.551Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/f7/3346161570d789045ed47a86110183f6ac3af0e94e7fd682772d89f7f1a1/ruff-0.11.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:727d01702f7c30baed3fc3a34901a640001a2828c793525043c29f7614994a8c", size = 10578330, upload-time = "2025-05-01T14:52:45.48Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/c3/327fb950b4763c7b3784f91d3038ef10c13b2d42322d4ade5ce13a2f9edb/ruff-0.11.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3dca977cc4fc8f66e89900fa415ffe4dbc2e969da9d7a54bfca81a128c5ac219", size = 10122223, upload-time = "2025-05-01T14:52:47.675Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/c7/ba686bce9adfeb6c61cb1bbadc17d58110fe1d602f199d79d4c880170f19/ruff-0.11.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c657fa987d60b104d2be8b052d66da0a2a88f9bd1d66b2254333e84ea2720c7f", size = 11697353, upload-time = "2025-05-01T14:52:50.264Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/8e/a4fb4a1ddde3c59e73996bb3ac51844ff93384d533629434b1def7a336b0/ruff-0.11.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f2e74b021d0de5eceb8bd32919f6ff8a9b40ee62ed97becd44993ae5b9949474", size = 12375936, upload-time = "2025-05-01T14:52:52.394Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/a1/9529cb1e2936e2479a51aeb011307e7229225df9ac64ae064d91ead54571/ruff-0.11.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9b5ef39820abc0f2c62111f7045009e46b275f5b99d5e59dda113c39b7f4f38", size = 11850083, upload-time = "2025-05-01T14:52:55.424Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/94/8f7eac4c612673ae15a4ad2bc0ee62e03c68a2d4f458daae3de0e47c67ba/ruff-0.11.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1dba3135ca503727aa4648152c0fa67c3b1385d3dc81c75cd8a229c4b2a1458", size = 14005834, upload-time = "2025-05-01T14:52:58.056Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/7c/6f63b46b2be870cbf3f54c9c4154d13fac4b8827f22fa05ac835c10835b2/ruff-0.11.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f024d32e62faad0f76b2d6afd141b8c171515e4fb91ce9fd6464335c81244e5", size = 11503713, upload-time = "2025-05-01T14:53:01.244Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/91/57de411b544b5fe072779678986a021d87c3ee5b89551f2ca41200c5d643/ruff-0.11.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d365618d3ad747432e1ae50d61775b78c055fee5936d77fb4d92c6f559741948", size = 10457182, upload-time = "2025-05-01T14:53:03.726Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/01/49/cfe73e0ce5ecdd3e6f1137bf1f1be03dcc819d1bfe5cff33deb40c5926db/ruff-0.11.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4d9aaa91035bdf612c8ee7266153bcf16005c7c7e2f5878406911c92a31633cb", size = 10101027, upload-time = "2025-05-01T14:53:06.555Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/21/a5cfe47c62b3531675795f38a0ef1c52ff8de62eaddf370d46634391a3fb/ruff-0.11.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0eba551324733efc76116d9f3a0d52946bc2751f0cd30661564117d6fd60897c", size = 11111298, upload-time = "2025-05-01T14:53:08.825Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/98/f76225f87e88f7cb669ae92c062b11c0a1e91f32705f829bd426f8e48b7b/ruff-0.11.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:161eb4cff5cfefdb6c9b8b3671d09f7def2f960cee33481dd898caf2bcd02304", size = 11566884, upload-time = "2025-05-01T14:53:11.626Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/7e/fff70b02e57852fda17bd43f99dda37b9bcf3e1af3d97c5834ff48d04715/ruff-0.11.8-py3-none-win32.whl", hash = "sha256:5b18caa297a786465cc511d7f8be19226acf9c0a1127e06e736cd4e1878c3ea2", size = 10451102, upload-time = "2025-05-01T14:53:14.303Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/a9/eaa571eb70648c9bde3120a1d5892597de57766e376b831b06e7c1e43945/ruff-0.11.8-py3-none-win_amd64.whl", hash = "sha256:6e70d11043bef637c5617297bdedec9632af15d53ac1e1ba29c448da9341b0c4", size = 11597410, upload-time = "2025-05-01T14:53:16.571Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/be/f6b790d6ae98f1f32c645f8540d5c96248b72343b0a56fab3a07f2941897/ruff-0.11.8-py3-none-win_arm64.whl", hash = "sha256:304432e4c4a792e3da85b7699feb3426a0908ab98bf29df22a31b0cdd098fac2", size = 10713129, upload-time = "2025-05-01T14:53:22.27Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/ce/a11d381192966e0b4290842cc8d4fac7dc9214ddf627c11c1afff87da29b/ruff-0.11.13-py3-none-linux_armv6l.whl", hash = "sha256:4bdfbf1240533f40042ec00c9e09a3aade6f8c10b6414cf11b519488d2635d46", size = 10292516, upload-time = "2025-06-05T20:59:32.944Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/db/87c3b59b0d4e753e40b6a3b4a2642dfd1dcaefbff121ddc64d6c8b47ba00/ruff-0.11.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aef9c9ed1b5ca28bb15c7eac83b8670cf3b20b478195bd49c8d756ba0a36cf48", size = 11106083, upload-time = "2025-06-05T20:59:37.03Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/79/d8cec175856ff810a19825d09ce700265f905c643c69f45d2b737e4a470a/ruff-0.11.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:53b15a9dfdce029c842e9a5aebc3855e9ab7771395979ff85b7c1dedb53ddc2b", size = 10436024, upload-time = "2025-06-05T20:59:39.741Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/5b/f6d94f2980fa1ee854b41568368a2e1252681b9238ab2895e133d303538f/ruff-0.11.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab153241400789138d13f362c43f7edecc0edfffce2afa6a68434000ecd8f69a", size = 10646324, upload-time = "2025-06-05T20:59:42.185Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/9c/b4c2acf24ea4426016d511dfdc787f4ce1ceb835f3c5fbdbcb32b1c63bda/ruff-0.11.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c51f93029d54a910d3d24f7dd0bb909e31b6cd989a5e4ac513f4eb41629f0dc", size = 10174416, upload-time = "2025-06-05T20:59:44.319Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/10/e2e62f77c65ede8cd032c2ca39c41f48feabedb6e282bfd6073d81bb671d/ruff-0.11.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1808b3ed53e1a777c2ef733aca9051dc9bf7c99b26ece15cb59a0320fbdbd629", size = 11724197, upload-time = "2025-06-05T20:59:46.935Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/f0/466fe8469b85c561e081d798c45f8a1d21e0b4a5ef795a1d7f1a9a9ec182/ruff-0.11.13-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d28ce58b5ecf0f43c1b71edffabe6ed7f245d5336b17805803312ec9bc665933", size = 12511615, upload-time = "2025-06-05T20:59:49.534Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/0e/cefe778b46dbd0cbcb03a839946c8f80a06f7968eb298aa4d1a4293f3448/ruff-0.11.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55e4bc3a77842da33c16d55b32c6cac1ec5fb0fbec9c8c513bdce76c4f922165", size = 12117080, upload-time = "2025-06-05T20:59:51.654Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/2c/caaeda564cbe103bed145ea557cb86795b18651b0f6b3ff6a10e84e5a33f/ruff-0.11.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:633bf2c6f35678c56ec73189ba6fa19ff1c5e4807a78bf60ef487b9dd272cc71", size = 11326315, upload-time = "2025-06-05T20:59:54.469Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/f0/782e7d681d660eda8c536962920c41309e6dd4ebcea9a2714ed5127d44bd/ruff-0.11.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ffbc82d70424b275b089166310448051afdc6e914fdab90e08df66c43bb5ca9", size = 11555640, upload-time = "2025-06-05T20:59:56.986Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/d4/3d580c616316c7f07fb3c99dbecfe01fbaea7b6fd9a82b801e72e5de742a/ruff-0.11.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a9ddd3ec62a9a89578c85842b836e4ac832d4a2e0bfaad3b02243f930ceafcc", size = 10507364, upload-time = "2025-06-05T20:59:59.154Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/dc/195e6f17d7b3ea6b12dc4f3e9de575db7983db187c378d44606e5d503319/ruff-0.11.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d237a496e0778d719efb05058c64d28b757c77824e04ffe8796c7436e26712b7", size = 10141462, upload-time = "2025-06-05T21:00:01.481Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/8e/39a094af6967faa57ecdeacb91bedfb232474ff8c3d20f16a5514e6b3534/ruff-0.11.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:26816a218ca6ef02142343fd24c70f7cd8c5aa6c203bca284407adf675984432", size = 11121028, upload-time = "2025-06-05T21:00:04.06Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/c0/b0b508193b0e8a1654ec683ebab18d309861f8bd64e3a2f9648b80d392cb/ruff-0.11.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:51c3f95abd9331dc5b87c47ac7f376db5616041173826dfd556cfe3d4977f492", size = 11602992, upload-time = "2025-06-05T21:00:06.249Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/91/263e33ab93ab09ca06ce4f8f8547a858cc198072f873ebc9be7466790bae/ruff-0.11.13-py3-none-win32.whl", hash = "sha256:96c27935418e4e8e77a26bb05962817f28b8ef3843a6c6cc49d8783b5507f250", size = 10474944, upload-time = "2025-06-05T21:00:08.459Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/f4/7c27734ac2073aae8efb0119cae6931b6fb48017adf048fdf85c19337afc/ruff-0.11.13-py3-none-win_amd64.whl", hash = "sha256:29c3189895a8a6a657b7af4e97d330c8a3afd2c9c8f46c81e2fc5a31866517e3", size = 11548669, upload-time = "2025-06-05T21:00:11.147Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/bf/b273dd11673fed8a6bd46032c0ea2a04b2ac9bfa9c628756a5856ba113b0/ruff-0.11.13-py3-none-win_arm64.whl", hash = "sha256:b4385285e9179d608ff1d2fb9922062663c658605819a6876d8beef0c30b7f3b", size = 10683928, upload-time = "2025-06-05T21:00:13.758Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2546,35 +2549,32 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "types-pyyaml"
|
||||
version = "6.0.12.20250402"
|
||||
version = "6.0.12.20250516"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/2d/68/609eed7402f87c9874af39d35942744e39646d1ea9011765ec87b01b2a3c/types_pyyaml-6.0.12.20250402.tar.gz", hash = "sha256:d7c13c3e6d335b6af4b0122a01ff1d270aba84ab96d1a1a1063ecba3e13ec075", size = 17282, upload-time = "2025-04-02T02:56:00.235Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4e/22/59e2aeb48ceeee1f7cd4537db9568df80d62bdb44a7f9e743502ea8aab9c/types_pyyaml-6.0.12.20250516.tar.gz", hash = "sha256:9f21a70216fc0fa1b216a8176db5f9e0af6eb35d2f2932acb87689d03a5bf6ba", size = 17378, upload-time = "2025-05-16T03:08:04.897Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/56/1fe61db05685fbb512c07ea9323f06ea727125951f1eb4dff110b3311da3/types_pyyaml-6.0.12.20250402-py3-none-any.whl", hash = "sha256:652348fa9e7a203d4b0d21066dfb00760d3cbd5a15ebb7cf8d33c88a49546681", size = 20329, upload-time = "2025-04-02T02:55:59.382Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/5f/e0af6f7f6a260d9af67e1db4f54d732abad514252a7a378a6c4d17dd1036/types_pyyaml-6.0.12.20250516-py3-none-any.whl", hash = "sha256:8478208feaeb53a34cb5d970c56a7cd76b72659442e733e268a94dc72b2d0530", size = 20312, upload-time = "2025-05-16T03:08:04.019Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-requests"
|
||||
version = "2.32.0.20250328"
|
||||
version = "2.32.0.20250602"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/00/7d/eb174f74e3f5634eaacb38031bbe467dfe2e545bc255e5c90096ec46bc46/types_requests-2.32.0.20250328.tar.gz", hash = "sha256:c9e67228ea103bd811c96984fac36ed2ae8da87a36a633964a21f199d60baf32", size = 22995, upload-time = "2025-03-28T02:55:13.271Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/48/b0/5321e6eeba5d59e4347fcf9bf06a5052f085c3aa0f4876230566d6a4dc97/types_requests-2.32.0.20250602.tar.gz", hash = "sha256:ee603aeefec42051195ae62ca7667cd909a2f8128fdf8aad9e8a5219ecfab3bf", size = 23042, upload-time = "2025-06-02T03:15:02.958Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/15/3700282a9d4ea3b37044264d3e4d1b1f0095a4ebf860a99914fd544e3be3/types_requests-2.32.0.20250328-py3-none-any.whl", hash = "sha256:72ff80f84b15eb3aa7a8e2625fffb6a93f2ad5a0c20215fc1dcfa61117bcb2a2", size = 20663, upload-time = "2025-03-28T02:55:11.946Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/18/9b782980e575c6581d5c0c1c99f4c6f89a1d7173dad072ee96b2756c02e6/types_requests-2.32.0.20250602-py3-none-any.whl", hash = "sha256:f4f335f87779b47ce10b8b8597b409130299f6971ead27fead4fe7ba6ea3e726", size = 20638, upload-time = "2025-06-02T03:15:01.959Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-setuptools"
|
||||
version = "76.0.0.20250313"
|
||||
version = "80.9.0.20250529"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "setuptools" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b8/0f/2d1d000c2be3919bcdea15e5da48456bf1e55c18d02c5509ea59dade1408/types_setuptools-76.0.0.20250313.tar.gz", hash = "sha256:b2be66f550f95f3cad2a7d46177b273c7e9c80df7d257fa57addbbcfc8126a9e", size = 43627, upload-time = "2025-03-13T02:51:28.3Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/79/66/1b276526aad4696a9519919e637801f2c103419d2c248a6feb2729e034d1/types_setuptools-80.9.0.20250529.tar.gz", hash = "sha256:79e088ba0cba2186c8d6499cbd3e143abb142d28a44b042c28d3148b1e353c91", size = 41337, upload-time = "2025-05-29T03:07:34.487Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/89/ea9669a0a76b160ffb312d0b02b15bad053c1bc81d2a54e42e3a402ca754/types_setuptools-76.0.0.20250313-py3-none-any.whl", hash = "sha256:bf454b2a49b8cfd7ebcf5844d4dd5fe4c8666782df1e3663c5866fd51a47460e", size = 65845, upload-time = "2025-03-13T02:51:27.055Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1b/d8/83790d67ec771bf029a45ff1bd1aedbb738d8aa58c09dd0cc3033eea0e69/types_setuptools-80.9.0.20250529-py3-none-any.whl", hash = "sha256:00dfcedd73e333a430e10db096e4d46af93faf9314f832f13b6bbe3d6757e95f", size = 63263, upload-time = "2025-05-29T03:07:33.064Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2627,16 +2627,16 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "uvicorn"
|
||||
version = "0.34.2"
|
||||
version = "0.34.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "click" },
|
||||
{ name = "h11" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a6/ae/9bbb19b9e1c450cf9ecaef06463e40234d98d95bf572fab11b4f19ae5ded/uvicorn-0.34.2.tar.gz", hash = "sha256:0e929828f6186353a80b58ea719861d2629d766293b6d19baf086ba31d4f3328", size = 76815, upload-time = "2025-04-19T06:02:50.101Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/de/ad/713be230bcda622eaa35c28f0d328c3675c371238470abdea52417f17a8e/uvicorn-0.34.3.tar.gz", hash = "sha256:35919a9a979d7a59334b6b10e05d77c1d0d574c50e0fc98b8b1a0f165708b55a", size = 76631, upload-time = "2025-06-01T07:48:17.531Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/4b/4cef6ce21a2aaca9d852a6e84ef4f135d99fcd74fa75105e2fc0c8308acd/uvicorn-0.34.2-py3-none-any.whl", hash = "sha256:deb49af569084536d269fe0a6d67e3754f104cf03aba7c11c40f01aadf33c403", size = 62483, upload-time = "2025-04-19T06:02:48.42Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/0d/8adfeaa62945f90d19ddc461c55f4a50c258af7662d34b6a3d5d1f8646f6/uvicorn-0.34.3-py3-none-any.whl", hash = "sha256:16246631db62bdfbf069b0645177d6e8a77ba950cfedbfd093acef9444e4d885", size = 62431, upload-time = "2025-06-01T07:48:15.664Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
|
||||
2
mobile/.gitignore
vendored
2
mobile/.gitignore
vendored
@@ -59,4 +59,4 @@ libisar.so
|
||||
.fvm/
|
||||
|
||||
# Translation file
|
||||
lib/generated/codegen_loader.g.dart
|
||||
lib/generated/
|
||||
@@ -17,6 +17,27 @@ To add a new translation text, enter the key-value pair in the `i18n/en.json` in
|
||||
make translation
|
||||
```
|
||||
|
||||
## Static Analysis
|
||||
|
||||
The following checks of static analysis must pass for a contribution to the mobile app to be valid:
|
||||
|
||||
```bash
|
||||
dart format lib
|
||||
dart analyze
|
||||
dart run custom_lint
|
||||
dcm analyze lib
|
||||
```
|
||||
|
||||
[DCM](https://dcm.dev/) is a vendor tool that needs to be downloaded manually to run locally.
|
||||
Immich was provided an open source license.
|
||||
To use it, it is important that you do not have an active free tier license (can be verified with `dcm license`).
|
||||
If you have write-access to the Immich repository directly, running dcm in your clone should just work.
|
||||
If you are working on a clone of a fork, you need to connect to the main Immich repository as remote first:
|
||||
|
||||
```bash
|
||||
git remote add immich git@github.com:immich-app/immich.git
|
||||
```
|
||||
|
||||
## Immich-Flutter Directory Structure
|
||||
|
||||
Below are the directory inside the `lib` directory:
|
||||
|
||||
@@ -55,6 +55,8 @@ custom_lint:
|
||||
restrict: package:photo_manager
|
||||
allowed:
|
||||
# required / wanted
|
||||
- 'lib/infrastructure/repositories/album_media.repository.dart'
|
||||
- 'lib/infrastructure/repositories/storage.repository.dart'
|
||||
- 'lib/repositories/{album,asset,file}_media.repository.dart'
|
||||
# acceptable exceptions for the time being
|
||||
- lib/entities/asset.entity.dart # to provide local AssetEntity for now
|
||||
@@ -126,82 +128,169 @@ custom_lint:
|
||||
- test/**.dart
|
||||
|
||||
dart_code_metrics:
|
||||
extends:
|
||||
- recommended
|
||||
rules:
|
||||
# Common
|
||||
- arguments-ordering:
|
||||
last:
|
||||
- child
|
||||
- children
|
||||
- avoid-accessing-other-classes-private-members
|
||||
- avoid-assigning-to-static-field
|
||||
- avoid-assignments-as-conditions
|
||||
- avoid-async-call-in-sync-function
|
||||
- avoid-collapsible-if
|
||||
- avoid-collection-equality-checks
|
||||
- avoid-complex-loop-conditions
|
||||
- avoid-declaring-call-method
|
||||
- avoid-extensions-on-records
|
||||
- avoid-function-type-in-records
|
||||
- avoid-future-ignore
|
||||
- avoid-global-state
|
||||
- avoid-inverted-boolean-checks
|
||||
- avoid-late-final-reassignment
|
||||
- avoid-local-functions:
|
||||
exclude:
|
||||
- test/**.dart
|
||||
- avoid-negated-conditions
|
||||
- avoid-nested-streams-and-futures
|
||||
- avoid-referencing-subclasses
|
||||
- avoid-unnecessary-continue
|
||||
- avoid-unnecessary-nullable-return-type: false
|
||||
- binary-expression-operand-order
|
||||
- pattern-fields-ordering
|
||||
- prefer-abstract-final-static-class
|
||||
- prefer-commenting-future-delayed
|
||||
- prefer-early-return
|
||||
- prefer-first
|
||||
- prefer-immediate-return
|
||||
- prefer-last
|
||||
- prefer-simpler-boolean-expressions
|
||||
- prefer-switch-expression
|
||||
- prefer-type-over-var
|
||||
- use-existing-destructuring
|
||||
- use-existing-variable
|
||||
# Flutter
|
||||
- avoid-border-all
|
||||
- avoid-complex-arithmetic-expressions
|
||||
- avoid-expanded-as-spacer
|
||||
- avoid-if-with-many-branches
|
||||
- avoid-inherited-widget-in-initstate
|
||||
- avoid-late-context
|
||||
- avoid-returning-widgets
|
||||
- avoid-shrink-wrap-in-lists
|
||||
- avoid-single-child-column-or-row
|
||||
- avoid-stateless-widget-initialized-fields
|
||||
- avoid-wrapping-in-padding
|
||||
- prefer-align-over-container
|
||||
- prefer-const-border-radius
|
||||
- prefer-correct-callback-field-name: false
|
||||
- prefer-correct-edge-insets-constructor
|
||||
- prefer-define-hero-tag
|
||||
- prefer-extracting-callbacks
|
||||
- prefer-for-loop-in-children
|
||||
- prefer-match-file-name: false
|
||||
- prefer-sliver-prefix
|
||||
- prefer-spacing
|
||||
- prefer-text-rich
|
||||
- prefer-transform-over-container
|
||||
- prefer-using-list-view
|
||||
- prefer-widget-private-members:
|
||||
ignore-static: true
|
||||
- use-closest-build-context
|
||||
# riverpod
|
||||
- avoid-calling-notifier-members-inside-build
|
||||
- avoid-notifier-constructors
|
||||
- avoid-ref-read-inside-build
|
||||
- avoid-ref-watch-outside-build
|
||||
- avoid-unnecessary-consumer-widgets
|
||||
- dispose-provided-instances
|
||||
- use-ref-read-synchronously
|
||||
# All rules from "recommended" preset
|
||||
# Show potential errors
|
||||
# - avoid-cascade-after-if-null
|
||||
# - avoid-collection-methods-with-unrelated-types
|
||||
# - avoid-duplicate-exports
|
||||
# - avoid-dynamic
|
||||
# - avoid-missing-enum-constant-in-map
|
||||
# - avoid-passing-async-when-sync-expected
|
||||
# - avoid-throw-in-catch-block
|
||||
- avoid-unused-parameters
|
||||
# - avoid-unnecessary-type-assertions
|
||||
# - avoid-unnecessary-type-casts
|
||||
# - avoid-unrelated-type-assertions
|
||||
# - avoid-unrelated-type-casts
|
||||
# - no-empty-block
|
||||
# - no-equal-then-else
|
||||
# - prefer-correct-test-file-name
|
||||
# - prefer-match-file-name
|
||||
# - prefer-return-await
|
||||
# - avoid-self-assignment
|
||||
# - avoid-self-compare
|
||||
# - avoid-shadowing
|
||||
# - prefer-iterable-of
|
||||
# - no-equal-switch-case
|
||||
# - no-equal-conditions
|
||||
# - avoid-equal-expressions
|
||||
# - avoid-missed-calls
|
||||
# - avoid-unnecessary-negations
|
||||
# - avoid-unused-generics
|
||||
# - function-always-returns-null
|
||||
# - avoid-throw-objects-without-tostring
|
||||
# - avoid-unsafe-collection-methods
|
||||
# - prefer-wildcard-pattern
|
||||
# - no-equal-switch-expression-cases
|
||||
# - avoid-future-tostring
|
||||
# - avoid-unassigned-late-fields
|
||||
# - avoid-nested-futures
|
||||
# - avoid-generics-shadowing
|
||||
# - prefer-parentheses-with-if-null
|
||||
# - no-equal-nested-conditions
|
||||
# - avoid-shadowed-extension-methods
|
||||
# - avoid-unnecessary-conditionals
|
||||
# - avoid-double-slash-imports
|
||||
# - avoid-map-keys-contains
|
||||
# - prefer-correct-json-casts
|
||||
# - avoid-duplicate-mixins
|
||||
# - avoid-nullable-interpolation
|
||||
# - avoid-unused-instances
|
||||
# - prefer-correct-for-loop-increment
|
||||
# - prefer-public-exception-classes
|
||||
# - avoid-uncaught-future-errors
|
||||
# - always-remove-listener
|
||||
# - avoid-unnecessary-setstate
|
||||
# - check-for-equals-in-render-object-setters
|
||||
# - consistent-update-render-object
|
||||
# - use-setstate-synchronously
|
||||
# - avoid-incomplete-copy-with
|
||||
# - proper-super-calls
|
||||
# - dispose-fields
|
||||
# - avoid-empty-setstate
|
||||
# - avoid-state-constructors
|
||||
# - avoid-recursive-widget-calls
|
||||
# - avoid-missing-image-alt
|
||||
# - avoid-passing-self-as-argument
|
||||
# - avoid-unnecessary-if
|
||||
# - avoid-unconditional-break
|
||||
# - avoid-referencing-discarded-variables
|
||||
# - avoid-unnecessary-local-late
|
||||
# - avoid-wildcard-cases-with-enums
|
||||
# - match-getter-setter-field-names
|
||||
# - avoid-accessing-collections-by-constant-index
|
||||
# - prefer-unique-test-names
|
||||
# - avoid-duplicate-cascades
|
||||
# - prefer-specific-cases-first
|
||||
# - avoid-duplicate-switch-case-conditions
|
||||
# - prefer-explicit-function-type
|
||||
# - avoid-misused-test-matchers
|
||||
# - avoid-duplicate-test-assertions
|
||||
# - prefer-switch-with-enums
|
||||
# - prefer-any-or-every
|
||||
# - avoid-duplicate-map-keys
|
||||
# - avoid-nullable-tostring
|
||||
# - avoid-undisposed-instances
|
||||
# - avoid-duplicate-initializers
|
||||
# - avoid-unassigned-stream-subscriptions
|
||||
# - avoid-empty-test-groups
|
||||
# - avoid-not-encodable-in-to-json
|
||||
# - avoid-contradictory-expressions
|
||||
# - avoid-excessive-expressions
|
||||
# - prefer-private-extension-type-field
|
||||
# - avoid-renaming-representation-getters
|
||||
# - avoid-empty-spread
|
||||
# - avoid-unnecessary-gesture-detector
|
||||
# - avoid-missing-completer-stack-trace
|
||||
# - avoid-casting-to-extension-type
|
||||
# - prefer-overriding-parent-equality
|
||||
# - avoid-missing-controller
|
||||
# - avoid-unknown-pragma
|
||||
# - avoid-conditions-with-boolean-literals
|
||||
# - avoid-multi-assignment
|
||||
# - avoid-collection-equality-checks
|
||||
# - avoid-only-rethrow
|
||||
# - avoid-incorrect-image-opacity
|
||||
# - avoid-misused-set-literals
|
||||
# - dispose-class-fields
|
||||
# - avoid-suspicious-super-overrides
|
||||
# - avoid-assignments-as-conditions
|
||||
# - avoid-unused-assignment
|
||||
# - avoid-unnecessary-overrides
|
||||
# - avoid-implicitly-nullable-extension-types
|
||||
# Enable with the next release
|
||||
# - avoid-late-final-reassignment
|
||||
# - avoid-duplicate-constant-values
|
||||
# - function-always-returns-same-value
|
||||
# - avoid-flexible-outside-flex
|
||||
# - avoid-unnecessary-patterns
|
||||
# - use-closest-build-context
|
||||
# - avoid-commented-out-code
|
||||
# - avoid-recursive-tostring
|
||||
# - avoid-enum-values-by-index
|
||||
# - avoid-constant-assert-conditions
|
||||
# - avoid-inconsistent-digit-separators
|
||||
# - pass-existing-future-to-future-builder
|
||||
# - pass-existing-stream-to-stream-builder
|
||||
|
||||
# Code simplification
|
||||
# - avoid-redundant-async
|
||||
# - avoid-redundant-else
|
||||
# - avoid-unnecessary-nullable-return-type
|
||||
# - avoid-redundant-pragma-inline
|
||||
# - avoid-nested-records
|
||||
# - avoid-redundant-positional-field-name
|
||||
# - avoid-explicit-pattern-field-name
|
||||
# - prefer-simpler-patterns-null-check
|
||||
# - avoid-unnecessary-return
|
||||
# - avoid-duplicate-patterns
|
||||
# - avoid-keywords-in-wildcard-pattern
|
||||
# - avoid-unnecessary-futures
|
||||
# - avoid-unnecessary-reassignment
|
||||
# - avoid-unnecessary-call
|
||||
# - avoid-unnecessary-stateful-widgets
|
||||
# - prefer-dedicated-media-query-methods
|
||||
# - avoid-unnecessary-overrides-in-state
|
||||
# - move-variable-closer-to-its-usage
|
||||
# - avoid-nullable-parameters-with-default-values
|
||||
# - prefer-null-aware-spread
|
||||
# - avoid-inferrable-type-arguments
|
||||
# - avoid-unnecessary-super
|
||||
# - avoid-unnecessary-collections
|
||||
# - avoid-unnecessary-extends
|
||||
# - avoid-unnecessary-enum-arguments
|
||||
# - prefer-contains
|
||||
# Enable with the next release
|
||||
# - prefer-simpler-boolean-expressions
|
||||
# - prefer-spacing
|
||||
# - avoid-unnecessary-continue
|
||||
# - avoid-unnecessary-compare-to
|
||||
|
||||
# Style
|
||||
# - prefer-trailing-comma
|
||||
# - unnecessary-trailing-comma
|
||||
# - prefer-declaring-const-constructor
|
||||
# - prefer-single-widget-per-file
|
||||
# - prefer-prefixed-global-constants
|
||||
# - prefer-correct-callback-field-name
|
||||
|
||||
@@ -1,103 +1,107 @@
|
||||
plugins {
|
||||
id "com.android.application"
|
||||
id "kotlin-android"
|
||||
id "dev.flutter.flutter-gradle-plugin"
|
||||
id 'com.google.devtools.ksp'
|
||||
id "com.android.application"
|
||||
id "kotlin-android"
|
||||
id "dev.flutter.flutter-gradle-plugin"
|
||||
id 'com.google.devtools.ksp'
|
||||
}
|
||||
|
||||
def localProperties = new Properties()
|
||||
def localPropertiesFile = rootProject.file('local.properties')
|
||||
if (localPropertiesFile.exists()) {
|
||||
localPropertiesFile.withInputStream { localProperties.load(it) }
|
||||
localPropertiesFile.withInputStream { localProperties.load(it) }
|
||||
}
|
||||
|
||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = '1'
|
||||
flutterVersionCode = '1'
|
||||
}
|
||||
|
||||
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||
if (flutterVersionName == null) {
|
||||
flutterVersionName = '1.0'
|
||||
flutterVersionName = '1.0'
|
||||
}
|
||||
|
||||
def keystoreProperties = new Properties()
|
||||
def keystorePropertiesFile = rootProject.file('key.properties')
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
keystorePropertiesFile.withInputStream { keystoreProperties.load(it) }
|
||||
keystorePropertiesFile.withInputStream { keystoreProperties.load(it) }
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 35
|
||||
compileSdkVersion 35
|
||||
ndkVersion = "28.1.13356709"
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
coreLibraryDesugaringEnabled true
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
coreLibraryDesugaringEnabled true
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '17'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "app.alextran.immich"
|
||||
minSdkVersion 26
|
||||
targetSdkVersion 35
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
def keyAliasVal = System.getenv("ALIAS")
|
||||
def keyPasswordVal = System.getenv("ANDROID_KEY_PASSWORD")
|
||||
def storePasswordVal = System.getenv("ANDROID_STORE_PASSWORD")
|
||||
|
||||
keyAlias keyAliasVal ? keyAliasVal : keystoreProperties['keyAlias']
|
||||
keyPassword keyPasswordVal ? keyPasswordVal : keystoreProperties['keyPassword']
|
||||
storeFile file("../key.jks") ? file("../key.jks") : file(keystoreProperties['storeFile'])
|
||||
storePassword storePasswordVal ? storePasswordVal : keystoreProperties['storePassword']
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
applicationIdSuffix '.debug'
|
||||
versionNameSuffix '-DEBUG'
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '17'
|
||||
release {
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "app.alextran.immich"
|
||||
minSdkVersion 26
|
||||
targetSdkVersion 35
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
def keyAliasVal = System.getenv("ALIAS")
|
||||
def keyPasswordVal = System.getenv("ANDROID_KEY_PASSWORD")
|
||||
def storePasswordVal = System.getenv("ANDROID_STORE_PASSWORD")
|
||||
|
||||
keyAlias keyAliasVal ? keyAliasVal : keystoreProperties['keyAlias']
|
||||
keyPassword keyPasswordVal ? keyPasswordVal : keystoreProperties['keyPassword']
|
||||
storeFile file("../key.jks") ? file("../key.jks") : file(keystoreProperties['storeFile'])
|
||||
storePassword storePasswordVal ? storePasswordVal : keystoreProperties['storePassword']
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
applicationIdSuffix '.debug'
|
||||
versionNameSuffix '-DEBUG'
|
||||
}
|
||||
|
||||
release {
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
namespace 'app.alextran.immich'
|
||||
}
|
||||
namespace 'app.alextran.immich'
|
||||
}
|
||||
|
||||
flutter {
|
||||
source '../..'
|
||||
source '../..'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
def kotlin_version = '2.0.20'
|
||||
def kotlin_coroutines_version = '1.9.0'
|
||||
def work_version = '2.9.1'
|
||||
def concurrent_version = '1.2.0'
|
||||
def guava_version = '33.3.1-android'
|
||||
def glide_version = '4.16.0'
|
||||
def kotlin_version = '2.0.20'
|
||||
def kotlin_coroutines_version = '1.9.0'
|
||||
def work_version = '2.9.1'
|
||||
def concurrent_version = '1.2.0'
|
||||
def guava_version = '33.3.1-android'
|
||||
def glide_version = '4.16.0'
|
||||
def serialization_version = '1.8.1'
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
|
||||
implementation "androidx.work:work-runtime-ktx:$work_version"
|
||||
implementation "androidx.concurrent:concurrent-futures:$concurrent_version"
|
||||
implementation "com.google.guava:guava:$guava_version"
|
||||
implementation "com.github.bumptech.glide:glide:$glide_version"
|
||||
ksp "com.github.bumptech.glide:ksp:$glide_version"
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.2'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
|
||||
implementation "androidx.work:work-runtime-ktx:$work_version"
|
||||
implementation "androidx.concurrent:concurrent-futures:$concurrent_version"
|
||||
implementation "com.google.guava:guava:$guava_version"
|
||||
implementation "com.github.bumptech.glide:glide:$glide_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$serialization_version"
|
||||
|
||||
ksp "com.github.bumptech.glide:ksp:$glide_version"
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.2'
|
||||
}
|
||||
|
||||
// This is uncommented in F-Droid build script
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
package app.alextran.immich
|
||||
|
||||
import android.os.Build
|
||||
import android.os.ext.SdkExtensions
|
||||
import androidx.annotation.NonNull
|
||||
import app.alextran.immich.sync.NativeSyncApi
|
||||
import app.alextran.immich.sync.NativeSyncApiImpl26
|
||||
import app.alextran.immich.sync.NativeSyncApiImpl30
|
||||
import io.flutter.embedding.android.FlutterFragmentActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
|
||||
@@ -10,5 +15,13 @@ class MainActivity : FlutterFragmentActivity() {
|
||||
flutterEngine.plugins.add(BackgroundServicePlugin())
|
||||
flutterEngine.plugins.add(HttpSSLOptionsPlugin())
|
||||
// No need to set up method channel here as it's now handled in the plugin
|
||||
|
||||
val nativeSyncApiImpl =
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R || SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R) < 1) {
|
||||
NativeSyncApiImpl26(this)
|
||||
} else {
|
||||
NativeSyncApiImpl30(this)
|
||||
}
|
||||
NativeSyncApi.setUp(flutterEngine.dartExecutor.binaryMessenger, nativeSyncApiImpl)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,417 @@
|
||||
// Autogenerated from Pigeon (v25.3.2), do not edit directly.
|
||||
// See also: https://pub.dev/packages/pigeon
|
||||
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
|
||||
|
||||
package app.alextran.immich.sync
|
||||
|
||||
import android.util.Log
|
||||
import io.flutter.plugin.common.BasicMessageChannel
|
||||
import io.flutter.plugin.common.BinaryMessenger
|
||||
import io.flutter.plugin.common.EventChannel
|
||||
import io.flutter.plugin.common.MessageCodec
|
||||
import io.flutter.plugin.common.StandardMethodCodec
|
||||
import io.flutter.plugin.common.StandardMessageCodec
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.nio.ByteBuffer
|
||||
private object MessagesPigeonUtils {
|
||||
|
||||
fun wrapResult(result: Any?): List<Any?> {
|
||||
return listOf(result)
|
||||
}
|
||||
|
||||
fun wrapError(exception: Throwable): List<Any?> {
|
||||
return if (exception is FlutterError) {
|
||||
listOf(
|
||||
exception.code,
|
||||
exception.message,
|
||||
exception.details
|
||||
)
|
||||
} else {
|
||||
listOf(
|
||||
exception.javaClass.simpleName,
|
||||
exception.toString(),
|
||||
"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)
|
||||
)
|
||||
}
|
||||
}
|
||||
fun deepEquals(a: Any?, b: Any?): Boolean {
|
||||
if (a is ByteArray && b is ByteArray) {
|
||||
return a.contentEquals(b)
|
||||
}
|
||||
if (a is IntArray && b is IntArray) {
|
||||
return a.contentEquals(b)
|
||||
}
|
||||
if (a is LongArray && b is LongArray) {
|
||||
return a.contentEquals(b)
|
||||
}
|
||||
if (a is DoubleArray && b is DoubleArray) {
|
||||
return a.contentEquals(b)
|
||||
}
|
||||
if (a is Array<*> && b is Array<*>) {
|
||||
return a.size == b.size &&
|
||||
a.indices.all{ deepEquals(a[it], b[it]) }
|
||||
}
|
||||
if (a is List<*> && b is List<*>) {
|
||||
return a.size == b.size &&
|
||||
a.indices.all{ deepEquals(a[it], b[it]) }
|
||||
}
|
||||
if (a is Map<*, *> && b is Map<*, *>) {
|
||||
return a.size == b.size && a.all {
|
||||
(b as Map<Any?, Any?>).containsKey(it.key) &&
|
||||
deepEquals(it.value, b[it.key])
|
||||
}
|
||||
}
|
||||
return a == b
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Error class for passing custom error details to Flutter via a thrown PlatformException.
|
||||
* @property code The error code.
|
||||
* @property message The error message.
|
||||
* @property details The error details. Must be a datatype supported by the api codec.
|
||||
*/
|
||||
class FlutterError (
|
||||
val code: String,
|
||||
override val message: String? = null,
|
||||
val details: Any? = null
|
||||
) : Throwable()
|
||||
|
||||
/** Generated class from Pigeon that represents data sent in messages. */
|
||||
data class PlatformAsset (
|
||||
val id: String,
|
||||
val name: String,
|
||||
val type: Long,
|
||||
val createdAt: Long? = null,
|
||||
val updatedAt: Long? = null,
|
||||
val width: Long? = null,
|
||||
val height: Long? = null,
|
||||
val durationInSeconds: Long
|
||||
)
|
||||
{
|
||||
companion object {
|
||||
fun fromList(pigeonVar_list: List<Any?>): PlatformAsset {
|
||||
val id = pigeonVar_list[0] as String
|
||||
val name = pigeonVar_list[1] as String
|
||||
val type = pigeonVar_list[2] as Long
|
||||
val createdAt = pigeonVar_list[3] as Long?
|
||||
val updatedAt = pigeonVar_list[4] as Long?
|
||||
val width = pigeonVar_list[5] as Long?
|
||||
val height = pigeonVar_list[6] as Long?
|
||||
val durationInSeconds = pigeonVar_list[7] as Long
|
||||
return PlatformAsset(id, name, type, createdAt, updatedAt, width, height, durationInSeconds)
|
||||
}
|
||||
}
|
||||
fun toList(): List<Any?> {
|
||||
return listOf(
|
||||
id,
|
||||
name,
|
||||
type,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
width,
|
||||
height,
|
||||
durationInSeconds,
|
||||
)
|
||||
}
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is PlatformAsset) {
|
||||
return false
|
||||
}
|
||||
if (this === other) {
|
||||
return true
|
||||
}
|
||||
return MessagesPigeonUtils.deepEquals(toList(), other.toList()) }
|
||||
|
||||
override fun hashCode(): Int = toList().hashCode()
|
||||
}
|
||||
|
||||
/** Generated class from Pigeon that represents data sent in messages. */
|
||||
data class PlatformAlbum (
|
||||
val id: String,
|
||||
val name: String,
|
||||
val updatedAt: Long? = null,
|
||||
val isCloud: Boolean,
|
||||
val assetCount: Long
|
||||
)
|
||||
{
|
||||
companion object {
|
||||
fun fromList(pigeonVar_list: List<Any?>): PlatformAlbum {
|
||||
val id = pigeonVar_list[0] as String
|
||||
val name = pigeonVar_list[1] as String
|
||||
val updatedAt = pigeonVar_list[2] as Long?
|
||||
val isCloud = pigeonVar_list[3] as Boolean
|
||||
val assetCount = pigeonVar_list[4] as Long
|
||||
return PlatformAlbum(id, name, updatedAt, isCloud, assetCount)
|
||||
}
|
||||
}
|
||||
fun toList(): List<Any?> {
|
||||
return listOf(
|
||||
id,
|
||||
name,
|
||||
updatedAt,
|
||||
isCloud,
|
||||
assetCount,
|
||||
)
|
||||
}
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is PlatformAlbum) {
|
||||
return false
|
||||
}
|
||||
if (this === other) {
|
||||
return true
|
||||
}
|
||||
return MessagesPigeonUtils.deepEquals(toList(), other.toList()) }
|
||||
|
||||
override fun hashCode(): Int = toList().hashCode()
|
||||
}
|
||||
|
||||
/** Generated class from Pigeon that represents data sent in messages. */
|
||||
data class SyncDelta (
|
||||
val hasChanges: Boolean,
|
||||
val updates: List<PlatformAsset>,
|
||||
val deletes: List<String>,
|
||||
val assetAlbums: Map<String, List<String>>
|
||||
)
|
||||
{
|
||||
companion object {
|
||||
fun fromList(pigeonVar_list: List<Any?>): SyncDelta {
|
||||
val hasChanges = pigeonVar_list[0] as Boolean
|
||||
val updates = pigeonVar_list[1] as List<PlatformAsset>
|
||||
val deletes = pigeonVar_list[2] as List<String>
|
||||
val assetAlbums = pigeonVar_list[3] as Map<String, List<String>>
|
||||
return SyncDelta(hasChanges, updates, deletes, assetAlbums)
|
||||
}
|
||||
}
|
||||
fun toList(): List<Any?> {
|
||||
return listOf(
|
||||
hasChanges,
|
||||
updates,
|
||||
deletes,
|
||||
assetAlbums,
|
||||
)
|
||||
}
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is SyncDelta) {
|
||||
return false
|
||||
}
|
||||
if (this === other) {
|
||||
return true
|
||||
}
|
||||
return MessagesPigeonUtils.deepEquals(toList(), other.toList()) }
|
||||
|
||||
override fun hashCode(): Int = toList().hashCode()
|
||||
}
|
||||
private open class MessagesPigeonCodec : StandardMessageCodec() {
|
||||
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
|
||||
return when (type) {
|
||||
129.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
PlatformAsset.fromList(it)
|
||||
}
|
||||
}
|
||||
130.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
PlatformAlbum.fromList(it)
|
||||
}
|
||||
}
|
||||
131.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
SyncDelta.fromList(it)
|
||||
}
|
||||
}
|
||||
else -> super.readValueOfType(type, buffer)
|
||||
}
|
||||
}
|
||||
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
|
||||
when (value) {
|
||||
is PlatformAsset -> {
|
||||
stream.write(129)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
is PlatformAlbum -> {
|
||||
stream.write(130)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
is SyncDelta -> {
|
||||
stream.write(131)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
else -> super.writeValue(stream, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||
interface NativeSyncApi {
|
||||
fun shouldFullSync(): Boolean
|
||||
fun getMediaChanges(): SyncDelta
|
||||
fun checkpointSync()
|
||||
fun clearSyncCheckpoint()
|
||||
fun getAssetIdsForAlbum(albumId: String): List<String>
|
||||
fun getAlbums(): List<PlatformAlbum>
|
||||
fun getAssetsCountSince(albumId: String, timestamp: Long): Long
|
||||
fun getAssetsForAlbum(albumId: String, updatedTimeCond: Long?): List<PlatformAsset>
|
||||
fun hashPaths(paths: List<String>): List<ByteArray?>
|
||||
|
||||
companion object {
|
||||
/** The codec used by NativeSyncApi. */
|
||||
val codec: MessageCodec<Any?> by lazy {
|
||||
MessagesPigeonCodec()
|
||||
}
|
||||
/** Sets up an instance of `NativeSyncApi` to handle messages through the `binaryMessenger`. */
|
||||
@JvmOverloads
|
||||
fun setUp(binaryMessenger: BinaryMessenger, api: NativeSyncApi?, messageChannelSuffix: String = "") {
|
||||
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
|
||||
val taskQueue = binaryMessenger.makeBackgroundTaskQueue()
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.shouldFullSync$separatedMessageChannelSuffix", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { _, reply ->
|
||||
val wrapped: List<Any?> = try {
|
||||
listOf(api.shouldFullSync())
|
||||
} catch (exception: Throwable) {
|
||||
MessagesPigeonUtils.wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getMediaChanges$separatedMessageChannelSuffix", codec, taskQueue)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { _, reply ->
|
||||
val wrapped: List<Any?> = try {
|
||||
listOf(api.getMediaChanges())
|
||||
} catch (exception: Throwable) {
|
||||
MessagesPigeonUtils.wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.checkpointSync$separatedMessageChannelSuffix", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { _, reply ->
|
||||
val wrapped: List<Any?> = try {
|
||||
api.checkpointSync()
|
||||
listOf(null)
|
||||
} catch (exception: Throwable) {
|
||||
MessagesPigeonUtils.wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.clearSyncCheckpoint$separatedMessageChannelSuffix", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { _, reply ->
|
||||
val wrapped: List<Any?> = try {
|
||||
api.clearSyncCheckpoint()
|
||||
listOf(null)
|
||||
} catch (exception: Throwable) {
|
||||
MessagesPigeonUtils.wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetIdsForAlbum$separatedMessageChannelSuffix", codec, taskQueue)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val albumIdArg = args[0] as String
|
||||
val wrapped: List<Any?> = try {
|
||||
listOf(api.getAssetIdsForAlbum(albumIdArg))
|
||||
} catch (exception: Throwable) {
|
||||
MessagesPigeonUtils.wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAlbums$separatedMessageChannelSuffix", codec, taskQueue)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { _, reply ->
|
||||
val wrapped: List<Any?> = try {
|
||||
listOf(api.getAlbums())
|
||||
} catch (exception: Throwable) {
|
||||
MessagesPigeonUtils.wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetsCountSince$separatedMessageChannelSuffix", codec, taskQueue)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val albumIdArg = args[0] as String
|
||||
val timestampArg = args[1] as Long
|
||||
val wrapped: List<Any?> = try {
|
||||
listOf(api.getAssetsCountSince(albumIdArg, timestampArg))
|
||||
} catch (exception: Throwable) {
|
||||
MessagesPigeonUtils.wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetsForAlbum$separatedMessageChannelSuffix", codec, taskQueue)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val albumIdArg = args[0] as String
|
||||
val updatedTimeCondArg = args[1] as Long?
|
||||
val wrapped: List<Any?> = try {
|
||||
listOf(api.getAssetsForAlbum(albumIdArg, updatedTimeCondArg))
|
||||
} catch (exception: Throwable) {
|
||||
MessagesPigeonUtils.wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.hashPaths$separatedMessageChannelSuffix", codec, taskQueue)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val pathsArg = args[0] as List<String>
|
||||
val wrapped: List<Any?> = try {
|
||||
listOf(api.hashPaths(pathsArg))
|
||||
} catch (exception: Throwable) {
|
||||
MessagesPigeonUtils.wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package app.alextran.immich.sync
|
||||
|
||||
import android.content.Context
|
||||
|
||||
|
||||
class NativeSyncApiImpl26(context: Context) : NativeSyncApiImplBase(context), NativeSyncApi {
|
||||
override fun shouldFullSync(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
// No-op for Android 10 and below
|
||||
override fun checkpointSync() {
|
||||
// Cannot throw exception as this is called from the Dart side
|
||||
// during the full sync process as well
|
||||
}
|
||||
|
||||
override fun clearSyncCheckpoint() {
|
||||
// No-op for Android 10 and below
|
||||
}
|
||||
|
||||
override fun getMediaChanges(): SyncDelta {
|
||||
throw IllegalStateException("Method not supported on this Android version.")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package app.alextran.immich.sync
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.provider.MediaStore
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.annotation.RequiresExtension
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.Q)
|
||||
@RequiresExtension(extension = Build.VERSION_CODES.R, version = 1)
|
||||
class NativeSyncApiImpl30(context: Context) : NativeSyncApiImplBase(context), NativeSyncApi {
|
||||
private val ctx: Context = context.applicationContext
|
||||
private val prefs = ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
|
||||
|
||||
companion object {
|
||||
const val SHARED_PREF_NAME = "Immich::MediaManager"
|
||||
const val SHARED_PREF_MEDIA_STORE_VERSION_KEY = "MediaStore::getVersion"
|
||||
const val SHARED_PREF_MEDIA_STORE_GEN_KEY = "MediaStore::getGeneration"
|
||||
}
|
||||
|
||||
private fun getSavedGenerationMap(): Map<String, Long> {
|
||||
return prefs.getString(SHARED_PREF_MEDIA_STORE_GEN_KEY, null)?.let {
|
||||
Json.decodeFromString<Map<String, Long>>(it)
|
||||
} ?: emptyMap()
|
||||
}
|
||||
|
||||
override fun clearSyncCheckpoint() {
|
||||
prefs.edit().apply {
|
||||
remove(SHARED_PREF_MEDIA_STORE_VERSION_KEY)
|
||||
remove(SHARED_PREF_MEDIA_STORE_GEN_KEY)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
override fun shouldFullSync(): Boolean =
|
||||
MediaStore.getVersion(ctx) != prefs.getString(SHARED_PREF_MEDIA_STORE_VERSION_KEY, null)
|
||||
|
||||
override fun checkpointSync() {
|
||||
val genMap = MediaStore.getExternalVolumeNames(ctx)
|
||||
.associateWith { MediaStore.getGeneration(ctx, it) }
|
||||
|
||||
prefs.edit().apply {
|
||||
putString(SHARED_PREF_MEDIA_STORE_VERSION_KEY, MediaStore.getVersion(ctx))
|
||||
putString(SHARED_PREF_MEDIA_STORE_GEN_KEY, Json.encodeToString(genMap))
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMediaChanges(): SyncDelta {
|
||||
val genMap = getSavedGenerationMap()
|
||||
val currentVolumes = MediaStore.getExternalVolumeNames(ctx)
|
||||
val changed = mutableListOf<PlatformAsset>()
|
||||
val deleted = mutableListOf<String>()
|
||||
val assetAlbums = mutableMapOf<String, List<String>>()
|
||||
var hasChanges = genMap.keys != currentVolumes
|
||||
|
||||
for (volume in currentVolumes) {
|
||||
val currentGen = MediaStore.getGeneration(ctx, volume)
|
||||
val storedGen = genMap[volume] ?: 0
|
||||
if (currentGen <= storedGen) {
|
||||
continue
|
||||
}
|
||||
|
||||
hasChanges = true
|
||||
|
||||
val selection =
|
||||
"$MEDIA_SELECTION AND (${MediaStore.MediaColumns.GENERATION_MODIFIED} > ? OR ${MediaStore.MediaColumns.GENERATION_ADDED} > ?)"
|
||||
val selectionArgs = arrayOf(
|
||||
*MEDIA_SELECTION_ARGS,
|
||||
storedGen.toString(),
|
||||
storedGen.toString()
|
||||
)
|
||||
|
||||
getAssets(getCursor(volume, selection, selectionArgs)).forEach {
|
||||
when (it) {
|
||||
is AssetResult.ValidAsset -> {
|
||||
changed.add(it.asset)
|
||||
assetAlbums[it.asset.id] = listOf(it.albumId)
|
||||
}
|
||||
|
||||
is AssetResult.InvalidAsset -> deleted.add(it.assetId)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Unmounted volumes are handled in dart when the album is removed
|
||||
return SyncDelta(hasChanges, changed, deleted, assetAlbums)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
package app.alextran.immich.sync
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.provider.MediaStore
|
||||
import android.util.Log
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.security.MessageDigest
|
||||
|
||||
sealed class AssetResult {
|
||||
data class ValidAsset(val asset: PlatformAsset, val albumId: String) : AssetResult()
|
||||
data class InvalidAsset(val assetId: String) : AssetResult()
|
||||
}
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
open class NativeSyncApiImplBase(context: Context) {
|
||||
private val ctx: Context = context.applicationContext
|
||||
|
||||
companion object {
|
||||
private const val TAG = "NativeSyncApiImplBase"
|
||||
|
||||
const val MEDIA_SELECTION =
|
||||
"(${MediaStore.Files.FileColumns.MEDIA_TYPE} = ? OR ${MediaStore.Files.FileColumns.MEDIA_TYPE} = ?)"
|
||||
val MEDIA_SELECTION_ARGS = arrayOf(
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE.toString(),
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO.toString()
|
||||
)
|
||||
const val BUCKET_SELECTION = "(${MediaStore.Files.FileColumns.BUCKET_ID} = ?)"
|
||||
val ASSET_PROJECTION = arrayOf(
|
||||
MediaStore.MediaColumns._ID,
|
||||
MediaStore.MediaColumns.DATA,
|
||||
MediaStore.MediaColumns.DISPLAY_NAME,
|
||||
MediaStore.MediaColumns.DATE_TAKEN,
|
||||
MediaStore.MediaColumns.DATE_ADDED,
|
||||
MediaStore.MediaColumns.DATE_MODIFIED,
|
||||
MediaStore.Files.FileColumns.MEDIA_TYPE,
|
||||
MediaStore.MediaColumns.BUCKET_ID,
|
||||
MediaStore.MediaColumns.WIDTH,
|
||||
MediaStore.MediaColumns.HEIGHT,
|
||||
MediaStore.MediaColumns.DURATION
|
||||
)
|
||||
|
||||
const val HASH_BUFFER_SIZE = 2 * 1024 * 1024
|
||||
}
|
||||
|
||||
protected fun getCursor(
|
||||
volume: String,
|
||||
selection: String,
|
||||
selectionArgs: Array<String>,
|
||||
projection: Array<String> = ASSET_PROJECTION,
|
||||
sortOrder: String? = null
|
||||
): Cursor? = ctx.contentResolver.query(
|
||||
MediaStore.Files.getContentUri(volume),
|
||||
projection,
|
||||
selection,
|
||||
selectionArgs,
|
||||
sortOrder,
|
||||
)
|
||||
|
||||
protected fun getAssets(cursor: Cursor?): Sequence<AssetResult> {
|
||||
return sequence {
|
||||
cursor?.use { c ->
|
||||
val idColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns._ID)
|
||||
val dataColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA)
|
||||
val nameColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME)
|
||||
val dateTakenColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_TAKEN)
|
||||
val dateAddedColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_ADDED)
|
||||
val dateModifiedColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_MODIFIED)
|
||||
val mediaTypeColumn = c.getColumnIndexOrThrow(MediaStore.Files.FileColumns.MEDIA_TYPE)
|
||||
val bucketIdColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.BUCKET_ID)
|
||||
val widthColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.WIDTH)
|
||||
val heightColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.HEIGHT)
|
||||
val durationColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.DURATION)
|
||||
|
||||
while (c.moveToNext()) {
|
||||
val id = c.getLong(idColumn).toString()
|
||||
|
||||
val path = c.getString(dataColumn)
|
||||
if (path.isNullOrBlank() || !File(path).exists()) {
|
||||
yield(AssetResult.InvalidAsset(id))
|
||||
continue
|
||||
}
|
||||
|
||||
val mediaType = c.getInt(mediaTypeColumn)
|
||||
val name = c.getString(nameColumn)
|
||||
// Date taken is milliseconds since epoch, Date added is seconds since epoch
|
||||
val createdAt = (c.getLong(dateTakenColumn).takeIf { it > 0 }?.div(1000))
|
||||
?: c.getLong(dateAddedColumn)
|
||||
// Date modified is seconds since epoch
|
||||
val modifiedAt = c.getLong(dateModifiedColumn)
|
||||
val width = c.getInt(widthColumn).toLong()
|
||||
val height = c.getInt(heightColumn).toLong()
|
||||
// Duration is milliseconds
|
||||
val duration = if (mediaType == MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE) 0
|
||||
else c.getLong(durationColumn) / 1000
|
||||
val bucketId = c.getString(bucketIdColumn)
|
||||
|
||||
val asset = PlatformAsset(
|
||||
id,
|
||||
name,
|
||||
mediaType.toLong(),
|
||||
createdAt,
|
||||
modifiedAt,
|
||||
width,
|
||||
height,
|
||||
duration
|
||||
)
|
||||
yield(AssetResult.ValidAsset(asset, bucketId))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getAlbums(): List<PlatformAlbum> {
|
||||
val albums = mutableListOf<PlatformAlbum>()
|
||||
val albumsCount = mutableMapOf<String, Int>()
|
||||
|
||||
val projection = arrayOf(
|
||||
MediaStore.Files.FileColumns.BUCKET_ID,
|
||||
MediaStore.Files.FileColumns.BUCKET_DISPLAY_NAME,
|
||||
MediaStore.Files.FileColumns.DATE_MODIFIED,
|
||||
)
|
||||
val selection =
|
||||
"(${MediaStore.Files.FileColumns.BUCKET_ID} IS NOT NULL) AND $MEDIA_SELECTION"
|
||||
|
||||
getCursor(
|
||||
MediaStore.VOLUME_EXTERNAL,
|
||||
selection,
|
||||
MEDIA_SELECTION_ARGS,
|
||||
projection,
|
||||
"${MediaStore.Files.FileColumns.DATE_MODIFIED} DESC"
|
||||
)?.use { cursor ->
|
||||
val bucketIdColumn =
|
||||
cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.BUCKET_ID)
|
||||
val bucketNameColumn =
|
||||
cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.BUCKET_DISPLAY_NAME)
|
||||
val dateModified =
|
||||
cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATE_MODIFIED)
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
val id = cursor.getString(bucketIdColumn)
|
||||
|
||||
val count = albumsCount.getOrDefault(id, 0)
|
||||
if (count != 0) {
|
||||
albumsCount[id] = count + 1
|
||||
continue
|
||||
}
|
||||
|
||||
val name = cursor.getString(bucketNameColumn)
|
||||
val updatedAt = cursor.getLong(dateModified)
|
||||
albums.add(PlatformAlbum(id, name, updatedAt, false, 0))
|
||||
albumsCount[id] = 1
|
||||
}
|
||||
}
|
||||
|
||||
return albums.map { it.copy(assetCount = albumsCount[it.id]?.toLong() ?: 0) }
|
||||
.sortedBy { it.id }
|
||||
}
|
||||
|
||||
fun getAssetIdsForAlbum(albumId: String): List<String> {
|
||||
val projection = arrayOf(MediaStore.MediaColumns._ID)
|
||||
|
||||
return getCursor(
|
||||
MediaStore.VOLUME_EXTERNAL,
|
||||
"$BUCKET_SELECTION AND $MEDIA_SELECTION",
|
||||
arrayOf(albumId, *MEDIA_SELECTION_ARGS),
|
||||
projection
|
||||
)?.use { cursor ->
|
||||
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID)
|
||||
generateSequence {
|
||||
if (cursor.moveToNext()) cursor.getLong(idColumn).toString() else null
|
||||
}.toList()
|
||||
} ?: emptyList()
|
||||
}
|
||||
|
||||
fun getAssetsCountSince(albumId: String, timestamp: Long): Long =
|
||||
getCursor(
|
||||
MediaStore.VOLUME_EXTERNAL,
|
||||
"$BUCKET_SELECTION AND ${MediaStore.Files.FileColumns.DATE_ADDED} > ? AND $MEDIA_SELECTION",
|
||||
arrayOf(albumId, timestamp.toString(), *MEDIA_SELECTION_ARGS),
|
||||
)?.use { cursor -> cursor.count.toLong() } ?: 0L
|
||||
|
||||
|
||||
fun getAssetsForAlbum(albumId: String, updatedTimeCond: Long?): List<PlatformAsset> {
|
||||
var selection = "$BUCKET_SELECTION AND $MEDIA_SELECTION"
|
||||
val selectionArgs = mutableListOf(albumId, *MEDIA_SELECTION_ARGS)
|
||||
|
||||
if (updatedTimeCond != null) {
|
||||
selection += " AND (${MediaStore.Files.FileColumns.DATE_MODIFIED} > ? OR ${MediaStore.Files.FileColumns.DATE_ADDED} > ?)"
|
||||
selectionArgs.addAll(listOf(updatedTimeCond.toString(), updatedTimeCond.toString()))
|
||||
}
|
||||
|
||||
return getAssets(getCursor(MediaStore.VOLUME_EXTERNAL, selection, selectionArgs.toTypedArray()))
|
||||
.mapNotNull { result -> (result as? AssetResult.ValidAsset)?.asset }
|
||||
.toList()
|
||||
}
|
||||
|
||||
fun hashPaths(paths: List<String>): List<ByteArray?> {
|
||||
val buffer = ByteArray(HASH_BUFFER_SIZE)
|
||||
val digest = MessageDigest.getInstance("SHA-1")
|
||||
|
||||
return paths.map { path ->
|
||||
try {
|
||||
FileInputStream(path).use { file ->
|
||||
var bytesRead: Int
|
||||
while (file.read(buffer).also { bytesRead = it } > 0) {
|
||||
digest.update(buffer, 0, bytesRead)
|
||||
}
|
||||
}
|
||||
digest.digest()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to hash file $path: $e")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,27 @@
|
||||
pluginManagement {
|
||||
def flutterSdkPath = {
|
||||
def properties = new Properties()
|
||||
file("local.properties").withInputStream { properties.load(it) }
|
||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||
return flutterSdkPath
|
||||
}()
|
||||
def flutterSdkPath = {
|
||||
def properties = new Properties()
|
||||
file("local.properties").withInputStream { properties.load(it) }
|
||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||
return flutterSdkPath
|
||||
}()
|
||||
|
||||
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version '8.7.2' apply false
|
||||
id "org.jetbrains.kotlin.android" version "2.0.20" apply false
|
||||
id 'com.google.devtools.ksp' version '2.0.20-1.0.24' apply false
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version '8.7.2' apply false
|
||||
id "org.jetbrains.kotlin.android" version "2.0.20" apply false
|
||||
id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.22' apply false
|
||||
id 'com.google.devtools.ksp' version '2.0.20-1.0.24' apply false
|
||||
}
|
||||
|
||||
include ":app"
|
||||
|
||||
63
mobile/bin/generate_keys.dart
Normal file
63
mobile/bin/generate_keys.dart
Normal file
@@ -0,0 +1,63 @@
|
||||
// ignore_for_file: avoid_print
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
const _kReservedWords = ['continue'];
|
||||
|
||||
void main() async {
|
||||
final sourceFile = File('../i18n/en.json');
|
||||
if (!await sourceFile.exists()) {
|
||||
stderr.writeln('Source file does not exist');
|
||||
return;
|
||||
}
|
||||
|
||||
final outputDir = Directory('lib/generated');
|
||||
await outputDir.create(recursive: true);
|
||||
|
||||
final outputFile = File('lib/generated/intl_keys.g.dart');
|
||||
await _generate(sourceFile, outputFile);
|
||||
print('Generated ${outputFile.path}');
|
||||
}
|
||||
|
||||
Future<void> _generate(File source, File output) async {
|
||||
final content = await source.readAsString();
|
||||
final translations = json.decode(content) as Map<String, dynamic>;
|
||||
|
||||
final buffer = StringBuffer('''
|
||||
// DO NOT EDIT. This is code generated via generate_keys.dart
|
||||
|
||||
abstract class IntlKeys {
|
||||
''');
|
||||
|
||||
_writeKeys(buffer, translations);
|
||||
buffer.writeln('}');
|
||||
|
||||
await output.writeAsString(buffer.toString());
|
||||
}
|
||||
|
||||
void _writeKeys(
|
||||
StringBuffer buffer,
|
||||
Map<String, dynamic> map, [
|
||||
String prefix = '',
|
||||
]) {
|
||||
for (final entry in map.entries) {
|
||||
final key = entry.key;
|
||||
final value = entry.value;
|
||||
|
||||
if (value is Map<String, dynamic>) {
|
||||
_writeKeys(buffer, value, prefix.isEmpty ? key : '${prefix}_$key');
|
||||
} else {
|
||||
final name = _cleanName(prefix.isEmpty ? key : '${prefix}_$key');
|
||||
final path = prefix.isEmpty ? key : '$prefix.$key'.replaceAll('_', '.');
|
||||
buffer.writeln(' static const $name = \'$path\';');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String _cleanName(String name) {
|
||||
name = name.replaceAll(RegExp(r'[^a-zA-Z0-9_]'), '_');
|
||||
if (RegExp(r'^[0-9]').hasMatch(name)) name = 'k_$name';
|
||||
if (_kReservedWords.contains(name)) name = '${name}_';
|
||||
return name;
|
||||
}
|
||||
1
mobile/dcm_global.yaml
Normal file
1
mobile/dcm_global.yaml
Normal file
@@ -0,0 +1 @@
|
||||
version: '>=1.29.0 <1.30.0'
|
||||
2
mobile/drift_schemas/main/drift_schema_v1.json
generated
2
mobile/drift_schemas/main/drift_schema_v1.json
generated
File diff suppressed because one or more lines are too long
@@ -5,31 +5,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
|
||||
sha256: dc27559385e905ad30838356c5f5d574014ba39872d732111cd07ac0beff4c57
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "76.0.0"
|
||||
_macros:
|
||||
dependency: transitive
|
||||
description: dart
|
||||
source: sdk
|
||||
version: "0.3.3"
|
||||
version: "80.0.0"
|
||||
analyzer:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
|
||||
sha256: "192d1c5b944e7e53b24b5586db760db934b177d4147c42fbca8c8c5f1eb8d11e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.11.0"
|
||||
version: "7.3.0"
|
||||
analyzer_plugin:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: analyzer_plugin
|
||||
sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161"
|
||||
sha256: b3075265c5ab222f8b3188342dcb50b476286394a40323e85d1fa725035d40a4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.3"
|
||||
version: "0.13.0"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -106,34 +101,42 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: custom_lint
|
||||
sha256: "4500e88854e7581ee43586abeaf4443cb22375d6d289241a87b1aadf678d5545"
|
||||
sha256: "409c485fd14f544af1da965d5a0d160ee57cd58b63eeaa7280a4f28cf5bda7f1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.10"
|
||||
version: "0.7.5"
|
||||
custom_lint_builder:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: custom_lint_builder
|
||||
sha256: "5a95eff100da256fbf086b329c17c8b49058c261cdf56d3a4157d3c31c511d78"
|
||||
sha256: "107e0a43606138015777590ee8ce32f26ba7415c25b722ff0908a6f5d7a4c228"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.10"
|
||||
version: "0.7.5"
|
||||
custom_lint_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: custom_lint_core
|
||||
sha256: "76a4046cc71d976222a078a8fd4a65e198b70545a8d690a75196dd14f08510f6"
|
||||
sha256: "31110af3dde9d29fb10828ca33f1dce24d2798477b167675543ce3d208dee8be"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.10"
|
||||
version: "0.7.5"
|
||||
custom_lint_visitor:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: custom_lint_visitor
|
||||
sha256: "36282d85714af494ee2d7da8c8913630aa6694da99f104fb2ed4afcf8fc857d8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0+7.3.0"
|
||||
dart_style:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_style
|
||||
sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
|
||||
sha256: "5b236382b47ee411741447c1f1e111459c941ea1b3f2b540dde54c210a3662af"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.8"
|
||||
version: "3.1.0"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -154,10 +157,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: freezed_annotation
|
||||
sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2
|
||||
sha256: c87ff004c8aa6af2d531668b46a4ea379f7191dc6dfa066acd53d506da6e044b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.4"
|
||||
version: "3.0.0"
|
||||
glob:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -186,10 +189,10 @@ packages:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: lints
|
||||
sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
|
||||
sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.1"
|
||||
version: "6.0.0"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -198,14 +201,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
macros:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: macros
|
||||
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.3-main.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -367,4 +362,4 @@ packages:
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
sdks:
|
||||
dart: ">=3.6.0 <4.0.0"
|
||||
dart: ">=3.8.0-0 <4.0.0"
|
||||
|
||||
@@ -5,10 +5,10 @@ environment:
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
|
||||
dependencies:
|
||||
analyzer: ^6.0.0
|
||||
analyzer_plugin: ^0.11.3
|
||||
custom_lint_builder: ^0.6.4
|
||||
analyzer: ^7.0.0
|
||||
analyzer_plugin: ^0.13.0
|
||||
custom_lint_builder: ^0.7.5
|
||||
glob: ^2.1.2
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^5.0.0
|
||||
lints: ^6.0.0
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
PODS:
|
||||
- background_downloader (0.0.1):
|
||||
- Flutter
|
||||
- bonsoir_darwin (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- connectivity_plus (0.0.1):
|
||||
- Flutter
|
||||
- device_info_plus (0.0.1):
|
||||
@@ -64,10 +67,10 @@ PODS:
|
||||
- local_auth_darwin (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- MapLibre (6.5.0)
|
||||
- MapLibre (6.14.0)
|
||||
- maplibre_gl (0.0.1):
|
||||
- Flutter
|
||||
- MapLibre (= 6.5.0)
|
||||
- MapLibre (= 6.14.0)
|
||||
- native_video_player (1.0.0):
|
||||
- Flutter
|
||||
- network_info_plus (0.0.1):
|
||||
@@ -129,6 +132,7 @@ PODS:
|
||||
|
||||
DEPENDENCIES:
|
||||
- background_downloader (from `.symlinks/plugins/background_downloader/ios`)
|
||||
- bonsoir_darwin (from `.symlinks/plugins/bonsoir_darwin/darwin`)
|
||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||
@@ -173,6 +177,8 @@ SPEC REPOS:
|
||||
EXTERNAL SOURCES:
|
||||
background_downloader:
|
||||
:path: ".symlinks/plugins/background_downloader/ios"
|
||||
bonsoir_darwin:
|
||||
:path: ".symlinks/plugins/bonsoir_darwin/darwin"
|
||||
connectivity_plus:
|
||||
:path: ".symlinks/plugins/connectivity_plus/ios"
|
||||
device_info_plus:
|
||||
@@ -236,6 +242,7 @@ EXTERNAL SOURCES:
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
background_downloader: 50e91d979067b82081aba359d7d916b3ba5fadad
|
||||
bonsoir_darwin: 29c7ccf356646118844721f36e1de4b61f6cbd0e
|
||||
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
|
||||
device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
|
||||
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
||||
@@ -253,8 +260,8 @@ SPEC CHECKSUMS:
|
||||
integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
|
||||
isar_flutter_libs: bc909e72c3d756c2759f14c8776c13b5b0556e26
|
||||
local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391
|
||||
MapLibre: 0ebfa9329d313cec8bf0a5ba5a336a1dc903785e
|
||||
maplibre_gl: eab61cca6e1cfa9187249bacd3f08b51e8cd8ae9
|
||||
MapLibre: 69e572367f4ef6287e18246cfafc39c80cdcabcd
|
||||
maplibre_gl: 3c924e44725147b03dda33430ad216005b40555f
|
||||
native_video_player: b65c58951ede2f93d103a25366bdebca95081265
|
||||
network_info_plus: cf61925ab5205dce05a4f0895989afdb6aade5fc
|
||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||
|
||||
@@ -89,6 +89,16 @@
|
||||
FAC7416727DB9F5500C668D8 /* RunnerProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerProfile.entitlements; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
B2CF7F8C2DDE4EBB00744BF6 /* Sync */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
);
|
||||
path = Sync;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
@@ -175,6 +185,7 @@
|
||||
97C146F01CF9000F007C117D /* Runner */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B2CF7F8C2DDE4EBB00744BF6 /* Sync */,
|
||||
FA9973382CF6DF4B000EF859 /* Runner.entitlements */,
|
||||
65DD438629917FAD0047FFA8 /* BackgroundSync */,
|
||||
FAC7416727DB9F5500C668D8 /* RunnerProfile.entitlements */,
|
||||
@@ -224,6 +235,9 @@
|
||||
dependencies = (
|
||||
FAC6F8992D287C890078CB2F /* PBXTargetDependency */,
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
B2CF7F8C2DDE4EBB00744BF6 /* Sync */,
|
||||
);
|
||||
name = Runner;
|
||||
productName = Runner;
|
||||
productReference = 97C146EE1CF9000F007C117D /* Immich-Debug.app */;
|
||||
@@ -270,7 +284,6 @@
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
@@ -278,6 +291,7 @@
|
||||
Base,
|
||||
);
|
||||
mainGroup = 97C146E51CF9000F007C117D;
|
||||
preferredProjectObjectVersion = 77;
|
||||
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
@@ -543,7 +557,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 207;
|
||||
CURRENT_PROJECT_VERSION = 208;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -687,7 +701,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 207;
|
||||
CURRENT_PROJECT_VERSION = 208;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -717,7 +731,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 207;
|
||||
CURRENT_PROJECT_VERSION = 208;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -750,7 +764,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 207;
|
||||
CURRENT_PROJECT_VERSION = 208;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
@@ -794,7 +808,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 207;
|
||||
CURRENT_PROJECT_VERSION = 208;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
@@ -835,7 +849,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 207;
|
||||
CURRENT_PROJECT_VERSION = 208;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
|
||||
@@ -22,6 +22,9 @@ import UIKit
|
||||
BackgroundServicePlugin.registerBackgroundProcessing()
|
||||
|
||||
BackgroundServicePlugin.register(with: self.registrar(forPlugin: "BackgroundServicePlugin")!)
|
||||
|
||||
let controller: FlutterViewController = window?.rootViewController as! FlutterViewController
|
||||
NativeSyncApiSetup.setUp(binaryMessenger: controller.binaryMessenger, api: NativeSyncApiImpl())
|
||||
|
||||
BackgroundServicePlugin.setPluginRegistrantCallback { registry in
|
||||
if !registry.hasPlugin("org.cocoapods.path-provider-foundation") {
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.133.1</string>
|
||||
<string>1.134.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
@@ -93,7 +93,7 @@
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>207</string>
|
||||
<string>208</string>
|
||||
<key>FLTEnableImpeller</key>
|
||||
<true />
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
@@ -113,6 +113,11 @@
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true />
|
||||
</dict>
|
||||
<key>NSBonjourServices</key>
|
||||
<array>
|
||||
<string>_googlecast._tcp</string>
|
||||
<string>_CC1AD845._googlecast._tcp</string>
|
||||
</array>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>We need to access the camera to let you take beautiful video using this app</string>
|
||||
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
||||
@@ -164,4 +169,4 @@
|
||||
<key>NSFaceIDUsageDescription</key>
|
||||
<string>We need to use FaceID to allow access to your locked folder</string>
|
||||
</dict>
|
||||
</plist>
|
||||
</plist>
|
||||
|
||||
472
mobile/ios/Runner/Sync/Messages.g.swift
Normal file
472
mobile/ios/Runner/Sync/Messages.g.swift
Normal file
@@ -0,0 +1,472 @@
|
||||
// Autogenerated from Pigeon (v25.3.2), do not edit directly.
|
||||
// See also: https://pub.dev/packages/pigeon
|
||||
|
||||
import Foundation
|
||||
|
||||
#if os(iOS)
|
||||
import Flutter
|
||||
#elseif os(macOS)
|
||||
import FlutterMacOS
|
||||
#else
|
||||
#error("Unsupported platform.")
|
||||
#endif
|
||||
|
||||
/// Error class for passing custom error details to Dart side.
|
||||
final class PigeonError: Error {
|
||||
let code: String
|
||||
let message: String?
|
||||
let details: Sendable?
|
||||
|
||||
init(code: String, message: String?, details: Sendable?) {
|
||||
self.code = code
|
||||
self.message = message
|
||||
self.details = details
|
||||
}
|
||||
|
||||
var localizedDescription: String {
|
||||
return
|
||||
"PigeonError(code: \(code), message: \(message ?? "<nil>"), details: \(details ?? "<nil>")"
|
||||
}
|
||||
}
|
||||
|
||||
private func wrapResult(_ result: Any?) -> [Any?] {
|
||||
return [result]
|
||||
}
|
||||
|
||||
private func wrapError(_ error: Any) -> [Any?] {
|
||||
if let pigeonError = error as? PigeonError {
|
||||
return [
|
||||
pigeonError.code,
|
||||
pigeonError.message,
|
||||
pigeonError.details,
|
||||
]
|
||||
}
|
||||
if let flutterError = error as? FlutterError {
|
||||
return [
|
||||
flutterError.code,
|
||||
flutterError.message,
|
||||
flutterError.details,
|
||||
]
|
||||
}
|
||||
return [
|
||||
"\(error)",
|
||||
"\(type(of: error))",
|
||||
"Stacktrace: \(Thread.callStackSymbols)",
|
||||
]
|
||||
}
|
||||
|
||||
private func isNullish(_ value: Any?) -> Bool {
|
||||
return value is NSNull || value == nil
|
||||
}
|
||||
|
||||
private func nilOrValue<T>(_ value: Any?) -> T? {
|
||||
if value is NSNull { return nil }
|
||||
return value as! T?
|
||||
}
|
||||
|
||||
func deepEqualsMessages(_ lhs: Any?, _ rhs: Any?) -> Bool {
|
||||
let cleanLhs = nilOrValue(lhs) as Any?
|
||||
let cleanRhs = nilOrValue(rhs) as Any?
|
||||
switch (cleanLhs, cleanRhs) {
|
||||
case (nil, nil):
|
||||
return true
|
||||
|
||||
case (nil, _), (_, nil):
|
||||
return false
|
||||
|
||||
case is (Void, Void):
|
||||
return true
|
||||
|
||||
case let (cleanLhsHashable, cleanRhsHashable) as (AnyHashable, AnyHashable):
|
||||
return cleanLhsHashable == cleanRhsHashable
|
||||
|
||||
case let (cleanLhsArray, cleanRhsArray) as ([Any?], [Any?]):
|
||||
guard cleanLhsArray.count == cleanRhsArray.count else { return false }
|
||||
for (index, element) in cleanLhsArray.enumerated() {
|
||||
if !deepEqualsMessages(element, cleanRhsArray[index]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
case let (cleanLhsDictionary, cleanRhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]):
|
||||
guard cleanLhsDictionary.count == cleanRhsDictionary.count else { return false }
|
||||
for (key, cleanLhsValue) in cleanLhsDictionary {
|
||||
guard cleanRhsDictionary.index(forKey: key) != nil else { return false }
|
||||
if !deepEqualsMessages(cleanLhsValue, cleanRhsDictionary[key]!) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
default:
|
||||
// Any other type shouldn't be able to be used with pigeon. File an issue if you find this to be untrue.
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func deepHashMessages(value: Any?, hasher: inout Hasher) {
|
||||
if let valueList = value as? [AnyHashable] {
|
||||
for item in valueList { deepHashMessages(value: item, hasher: &hasher) }
|
||||
return
|
||||
}
|
||||
|
||||
if let valueDict = value as? [AnyHashable: AnyHashable] {
|
||||
for key in valueDict.keys {
|
||||
hasher.combine(key)
|
||||
deepHashMessages(value: valueDict[key]!, hasher: &hasher)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if let hashableValue = value as? AnyHashable {
|
||||
hasher.combine(hashableValue.hashValue)
|
||||
}
|
||||
|
||||
return hasher.combine(String(describing: value))
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Generated class from Pigeon that represents data sent in messages.
|
||||
struct PlatformAsset: Hashable {
|
||||
var id: String
|
||||
var name: String
|
||||
var type: Int64
|
||||
var createdAt: Int64? = nil
|
||||
var updatedAt: Int64? = nil
|
||||
var width: Int64? = nil
|
||||
var height: Int64? = nil
|
||||
var durationInSeconds: Int64
|
||||
|
||||
|
||||
// swift-format-ignore: AlwaysUseLowerCamelCase
|
||||
static func fromList(_ pigeonVar_list: [Any?]) -> PlatformAsset? {
|
||||
let id = pigeonVar_list[0] as! String
|
||||
let name = pigeonVar_list[1] as! String
|
||||
let type = pigeonVar_list[2] as! Int64
|
||||
let createdAt: Int64? = nilOrValue(pigeonVar_list[3])
|
||||
let updatedAt: Int64? = nilOrValue(pigeonVar_list[4])
|
||||
let width: Int64? = nilOrValue(pigeonVar_list[5])
|
||||
let height: Int64? = nilOrValue(pigeonVar_list[6])
|
||||
let durationInSeconds = pigeonVar_list[7] as! Int64
|
||||
|
||||
return PlatformAsset(
|
||||
id: id,
|
||||
name: name,
|
||||
type: type,
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
width: width,
|
||||
height: height,
|
||||
durationInSeconds: durationInSeconds
|
||||
)
|
||||
}
|
||||
func toList() -> [Any?] {
|
||||
return [
|
||||
id,
|
||||
name,
|
||||
type,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
width,
|
||||
height,
|
||||
durationInSeconds,
|
||||
]
|
||||
}
|
||||
static func == (lhs: PlatformAsset, rhs: PlatformAsset) -> Bool {
|
||||
return deepEqualsMessages(lhs.toList(), rhs.toList()) }
|
||||
func hash(into hasher: inout Hasher) {
|
||||
deepHashMessages(value: toList(), hasher: &hasher)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generated class from Pigeon that represents data sent in messages.
|
||||
struct PlatformAlbum: Hashable {
|
||||
var id: String
|
||||
var name: String
|
||||
var updatedAt: Int64? = nil
|
||||
var isCloud: Bool
|
||||
var assetCount: Int64
|
||||
|
||||
|
||||
// swift-format-ignore: AlwaysUseLowerCamelCase
|
||||
static func fromList(_ pigeonVar_list: [Any?]) -> PlatformAlbum? {
|
||||
let id = pigeonVar_list[0] as! String
|
||||
let name = pigeonVar_list[1] as! String
|
||||
let updatedAt: Int64? = nilOrValue(pigeonVar_list[2])
|
||||
let isCloud = pigeonVar_list[3] as! Bool
|
||||
let assetCount = pigeonVar_list[4] as! Int64
|
||||
|
||||
return PlatformAlbum(
|
||||
id: id,
|
||||
name: name,
|
||||
updatedAt: updatedAt,
|
||||
isCloud: isCloud,
|
||||
assetCount: assetCount
|
||||
)
|
||||
}
|
||||
func toList() -> [Any?] {
|
||||
return [
|
||||
id,
|
||||
name,
|
||||
updatedAt,
|
||||
isCloud,
|
||||
assetCount,
|
||||
]
|
||||
}
|
||||
static func == (lhs: PlatformAlbum, rhs: PlatformAlbum) -> Bool {
|
||||
return deepEqualsMessages(lhs.toList(), rhs.toList()) }
|
||||
func hash(into hasher: inout Hasher) {
|
||||
deepHashMessages(value: toList(), hasher: &hasher)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generated class from Pigeon that represents data sent in messages.
|
||||
struct SyncDelta: Hashable {
|
||||
var hasChanges: Bool
|
||||
var updates: [PlatformAsset]
|
||||
var deletes: [String]
|
||||
var assetAlbums: [String: [String]]
|
||||
|
||||
|
||||
// swift-format-ignore: AlwaysUseLowerCamelCase
|
||||
static func fromList(_ pigeonVar_list: [Any?]) -> SyncDelta? {
|
||||
let hasChanges = pigeonVar_list[0] as! Bool
|
||||
let updates = pigeonVar_list[1] as! [PlatformAsset]
|
||||
let deletes = pigeonVar_list[2] as! [String]
|
||||
let assetAlbums = pigeonVar_list[3] as! [String: [String]]
|
||||
|
||||
return SyncDelta(
|
||||
hasChanges: hasChanges,
|
||||
updates: updates,
|
||||
deletes: deletes,
|
||||
assetAlbums: assetAlbums
|
||||
)
|
||||
}
|
||||
func toList() -> [Any?] {
|
||||
return [
|
||||
hasChanges,
|
||||
updates,
|
||||
deletes,
|
||||
assetAlbums,
|
||||
]
|
||||
}
|
||||
static func == (lhs: SyncDelta, rhs: SyncDelta) -> Bool {
|
||||
return deepEqualsMessages(lhs.toList(), rhs.toList()) }
|
||||
func hash(into hasher: inout Hasher) {
|
||||
deepHashMessages(value: toList(), hasher: &hasher)
|
||||
}
|
||||
}
|
||||
|
||||
private class MessagesPigeonCodecReader: FlutterStandardReader {
|
||||
override func readValue(ofType type: UInt8) -> Any? {
|
||||
switch type {
|
||||
case 129:
|
||||
return PlatformAsset.fromList(self.readValue() as! [Any?])
|
||||
case 130:
|
||||
return PlatformAlbum.fromList(self.readValue() as! [Any?])
|
||||
case 131:
|
||||
return SyncDelta.fromList(self.readValue() as! [Any?])
|
||||
default:
|
||||
return super.readValue(ofType: type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class MessagesPigeonCodecWriter: FlutterStandardWriter {
|
||||
override func writeValue(_ value: Any) {
|
||||
if let value = value as? PlatformAsset {
|
||||
super.writeByte(129)
|
||||
super.writeValue(value.toList())
|
||||
} else if let value = value as? PlatformAlbum {
|
||||
super.writeByte(130)
|
||||
super.writeValue(value.toList())
|
||||
} else if let value = value as? SyncDelta {
|
||||
super.writeByte(131)
|
||||
super.writeValue(value.toList())
|
||||
} else {
|
||||
super.writeValue(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class MessagesPigeonCodecReaderWriter: FlutterStandardReaderWriter {
|
||||
override func reader(with data: Data) -> FlutterStandardReader {
|
||||
return MessagesPigeonCodecReader(data: data)
|
||||
}
|
||||
|
||||
override func writer(with data: NSMutableData) -> FlutterStandardWriter {
|
||||
return MessagesPigeonCodecWriter(data: data)
|
||||
}
|
||||
}
|
||||
|
||||
class MessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable {
|
||||
static let shared = MessagesPigeonCodec(readerWriter: MessagesPigeonCodecReaderWriter())
|
||||
}
|
||||
|
||||
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
|
||||
protocol NativeSyncApi {
|
||||
func shouldFullSync() throws -> Bool
|
||||
func getMediaChanges() throws -> SyncDelta
|
||||
func checkpointSync() throws
|
||||
func clearSyncCheckpoint() throws
|
||||
func getAssetIdsForAlbum(albumId: String) throws -> [String]
|
||||
func getAlbums() throws -> [PlatformAlbum]
|
||||
func getAssetsCountSince(albumId: String, timestamp: Int64) throws -> Int64
|
||||
func getAssetsForAlbum(albumId: String, updatedTimeCond: Int64?) throws -> [PlatformAsset]
|
||||
func hashPaths(paths: [String]) throws -> [FlutterStandardTypedData?]
|
||||
}
|
||||
|
||||
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
|
||||
class NativeSyncApiSetup {
|
||||
static var codec: FlutterStandardMessageCodec { MessagesPigeonCodec.shared }
|
||||
/// Sets up an instance of `NativeSyncApi` to handle messages through the `binaryMessenger`.
|
||||
static func setUp(binaryMessenger: FlutterBinaryMessenger, api: NativeSyncApi?, messageChannelSuffix: String = "") {
|
||||
let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : ""
|
||||
#if os(iOS)
|
||||
let taskQueue = binaryMessenger.makeBackgroundTaskQueue?()
|
||||
#else
|
||||
let taskQueue: FlutterTaskQueue? = nil
|
||||
#endif
|
||||
let shouldFullSyncChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.shouldFullSync\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
shouldFullSyncChannel.setMessageHandler { _, reply in
|
||||
do {
|
||||
let result = try api.shouldFullSync()
|
||||
reply(wrapResult(result))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
shouldFullSyncChannel.setMessageHandler(nil)
|
||||
}
|
||||
let getMediaChangesChannel = taskQueue == nil
|
||||
? FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getMediaChanges\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
: FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getMediaChanges\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue)
|
||||
if let api = api {
|
||||
getMediaChangesChannel.setMessageHandler { _, reply in
|
||||
do {
|
||||
let result = try api.getMediaChanges()
|
||||
reply(wrapResult(result))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
getMediaChangesChannel.setMessageHandler(nil)
|
||||
}
|
||||
let checkpointSyncChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.checkpointSync\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
checkpointSyncChannel.setMessageHandler { _, reply in
|
||||
do {
|
||||
try api.checkpointSync()
|
||||
reply(wrapResult(nil))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
checkpointSyncChannel.setMessageHandler(nil)
|
||||
}
|
||||
let clearSyncCheckpointChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.clearSyncCheckpoint\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
clearSyncCheckpointChannel.setMessageHandler { _, reply in
|
||||
do {
|
||||
try api.clearSyncCheckpoint()
|
||||
reply(wrapResult(nil))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
clearSyncCheckpointChannel.setMessageHandler(nil)
|
||||
}
|
||||
let getAssetIdsForAlbumChannel = taskQueue == nil
|
||||
? FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetIdsForAlbum\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
: FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetIdsForAlbum\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue)
|
||||
if let api = api {
|
||||
getAssetIdsForAlbumChannel.setMessageHandler { message, reply in
|
||||
let args = message as! [Any?]
|
||||
let albumIdArg = args[0] as! String
|
||||
do {
|
||||
let result = try api.getAssetIdsForAlbum(albumId: albumIdArg)
|
||||
reply(wrapResult(result))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
getAssetIdsForAlbumChannel.setMessageHandler(nil)
|
||||
}
|
||||
let getAlbumsChannel = taskQueue == nil
|
||||
? FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAlbums\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
: FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAlbums\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue)
|
||||
if let api = api {
|
||||
getAlbumsChannel.setMessageHandler { _, reply in
|
||||
do {
|
||||
let result = try api.getAlbums()
|
||||
reply(wrapResult(result))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
getAlbumsChannel.setMessageHandler(nil)
|
||||
}
|
||||
let getAssetsCountSinceChannel = taskQueue == nil
|
||||
? FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetsCountSince\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
: FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetsCountSince\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue)
|
||||
if let api = api {
|
||||
getAssetsCountSinceChannel.setMessageHandler { message, reply in
|
||||
let args = message as! [Any?]
|
||||
let albumIdArg = args[0] as! String
|
||||
let timestampArg = args[1] as! Int64
|
||||
do {
|
||||
let result = try api.getAssetsCountSince(albumId: albumIdArg, timestamp: timestampArg)
|
||||
reply(wrapResult(result))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
getAssetsCountSinceChannel.setMessageHandler(nil)
|
||||
}
|
||||
let getAssetsForAlbumChannel = taskQueue == nil
|
||||
? FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetsForAlbum\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
: FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetsForAlbum\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue)
|
||||
if let api = api {
|
||||
getAssetsForAlbumChannel.setMessageHandler { message, reply in
|
||||
let args = message as! [Any?]
|
||||
let albumIdArg = args[0] as! String
|
||||
let updatedTimeCondArg: Int64? = nilOrValue(args[1])
|
||||
do {
|
||||
let result = try api.getAssetsForAlbum(albumId: albumIdArg, updatedTimeCond: updatedTimeCondArg)
|
||||
reply(wrapResult(result))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
getAssetsForAlbumChannel.setMessageHandler(nil)
|
||||
}
|
||||
let hashPathsChannel = taskQueue == nil
|
||||
? FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.hashPaths\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
: FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.hashPaths\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue)
|
||||
if let api = api {
|
||||
hashPathsChannel.setMessageHandler { message, reply in
|
||||
let args = message as! [Any?]
|
||||
let pathsArg = args[0] as! [String]
|
||||
do {
|
||||
let result = try api.hashPaths(paths: pathsArg)
|
||||
reply(wrapResult(result))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hashPathsChannel.setMessageHandler(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
269
mobile/ios/Runner/Sync/MessagesImpl.swift
Normal file
269
mobile/ios/Runner/Sync/MessagesImpl.swift
Normal file
@@ -0,0 +1,269 @@
|
||||
import Photos
|
||||
import CryptoKit
|
||||
|
||||
struct AssetWrapper: Hashable, Equatable {
|
||||
let asset: PlatformAsset
|
||||
|
||||
init(with asset: PlatformAsset) {
|
||||
self.asset = asset
|
||||
}
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(self.asset.id)
|
||||
}
|
||||
|
||||
static func == (lhs: AssetWrapper, rhs: AssetWrapper) -> Bool {
|
||||
return lhs.asset.id == rhs.asset.id
|
||||
}
|
||||
}
|
||||
|
||||
extension PHAsset {
|
||||
func toPlatformAsset() -> PlatformAsset {
|
||||
return PlatformAsset(
|
||||
id: localIdentifier,
|
||||
name: title(),
|
||||
type: Int64(mediaType.rawValue),
|
||||
createdAt: creationDate.map { Int64($0.timeIntervalSince1970) },
|
||||
updatedAt: modificationDate.map { Int64($0.timeIntervalSince1970) },
|
||||
width: Int64(pixelWidth),
|
||||
height: Int64(pixelHeight),
|
||||
durationInSeconds: Int64(duration)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class NativeSyncApiImpl: NativeSyncApi {
|
||||
private let defaults: UserDefaults
|
||||
private let changeTokenKey = "immich:changeToken"
|
||||
private let albumTypes: [PHAssetCollectionType] = [.album, .smartAlbum]
|
||||
|
||||
private let hashBufferSize = 2 * 1024 * 1024
|
||||
|
||||
init(with defaults: UserDefaults = .standard) {
|
||||
self.defaults = defaults
|
||||
}
|
||||
|
||||
@available(iOS 16, *)
|
||||
private func getChangeToken() -> PHPersistentChangeToken? {
|
||||
guard let data = defaults.data(forKey: changeTokenKey) else {
|
||||
return nil
|
||||
}
|
||||
return try? NSKeyedUnarchiver.unarchivedObject(ofClass: PHPersistentChangeToken.self, from: data)
|
||||
}
|
||||
|
||||
@available(iOS 16, *)
|
||||
private func saveChangeToken(token: PHPersistentChangeToken) -> Void {
|
||||
guard let data = try? NSKeyedArchiver.archivedData(withRootObject: token, requiringSecureCoding: true) else {
|
||||
return
|
||||
}
|
||||
defaults.set(data, forKey: changeTokenKey)
|
||||
}
|
||||
|
||||
func clearSyncCheckpoint() -> Void {
|
||||
defaults.removeObject(forKey: changeTokenKey)
|
||||
}
|
||||
|
||||
func checkpointSync() {
|
||||
guard #available(iOS 16, *) else {
|
||||
return
|
||||
}
|
||||
saveChangeToken(token: PHPhotoLibrary.shared().currentChangeToken)
|
||||
}
|
||||
|
||||
func shouldFullSync() -> Bool {
|
||||
guard #available(iOS 16, *),
|
||||
PHPhotoLibrary.authorizationStatus(for: .readWrite) == .authorized,
|
||||
let storedToken = getChangeToken() else {
|
||||
// When we do not have access to photo library, older iOS version or No token available, fallback to full sync
|
||||
return true
|
||||
}
|
||||
|
||||
guard let _ = try? PHPhotoLibrary.shared().fetchPersistentChanges(since: storedToken) else {
|
||||
// Cannot fetch persistent changes
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func getAlbums() throws -> [PlatformAlbum] {
|
||||
var albums: [PlatformAlbum] = []
|
||||
|
||||
albumTypes.forEach { type in
|
||||
let collections = PHAssetCollection.fetchAssetCollections(with: type, subtype: .any, options: nil)
|
||||
collections.enumerateObjects { (album, _, _) in
|
||||
let options = PHFetchOptions()
|
||||
options.sortDescriptors = [NSSortDescriptor(key: "modificationDate", ascending: false)]
|
||||
let assets = PHAsset.fetchAssets(in: album, options: options)
|
||||
let isCloud = album.assetCollectionSubtype == .albumCloudShared || album.assetCollectionSubtype == .albumMyPhotoStream
|
||||
|
||||
var domainAlbum = PlatformAlbum(
|
||||
id: album.localIdentifier,
|
||||
name: album.localizedTitle!,
|
||||
updatedAt: nil,
|
||||
isCloud: isCloud,
|
||||
assetCount: Int64(assets.count)
|
||||
)
|
||||
|
||||
if let firstAsset = assets.firstObject {
|
||||
domainAlbum.updatedAt = firstAsset.modificationDate.map { Int64($0.timeIntervalSince1970) }
|
||||
}
|
||||
|
||||
albums.append(domainAlbum)
|
||||
}
|
||||
}
|
||||
return albums.sorted { $0.id < $1.id }
|
||||
}
|
||||
|
||||
func getMediaChanges() throws -> SyncDelta {
|
||||
guard #available(iOS 16, *) else {
|
||||
throw PigeonError(code: "UNSUPPORTED_OS", message: "This feature requires iOS 16 or later.", details: nil)
|
||||
}
|
||||
|
||||
guard PHPhotoLibrary.authorizationStatus(for: .readWrite) == .authorized else {
|
||||
throw PigeonError(code: "NO_AUTH", message: "No photo library access", details: nil)
|
||||
}
|
||||
|
||||
guard let storedToken = getChangeToken() else {
|
||||
// No token exists, definitely need a full sync
|
||||
print("MediaManager::getMediaChanges: No token found")
|
||||
throw PigeonError(code: "NO_TOKEN", message: "No stored change token", details: nil)
|
||||
}
|
||||
|
||||
let currentToken = PHPhotoLibrary.shared().currentChangeToken
|
||||
if storedToken == currentToken {
|
||||
return SyncDelta(hasChanges: false, updates: [], deletes: [], assetAlbums: [:])
|
||||
}
|
||||
|
||||
do {
|
||||
let changes = try PHPhotoLibrary.shared().fetchPersistentChanges(since: storedToken)
|
||||
|
||||
var updatedAssets: Set<AssetWrapper> = []
|
||||
var deletedAssets: Set<String> = []
|
||||
|
||||
for change in changes {
|
||||
guard let details = try? change.changeDetails(for: PHObjectType.asset) else { continue }
|
||||
|
||||
let updated = details.updatedLocalIdentifiers.union(details.insertedLocalIdentifiers)
|
||||
deletedAssets.formUnion(details.deletedLocalIdentifiers)
|
||||
|
||||
if (updated.isEmpty) { continue }
|
||||
|
||||
let result = PHAsset.fetchAssets(withLocalIdentifiers: Array(updated), options: nil)
|
||||
for i in 0..<result.count {
|
||||
let asset = result.object(at: i)
|
||||
|
||||
// Asset wrapper only uses the id for comparison. Multiple change can contain the same asset, skip duplicate changes
|
||||
let predicate = PlatformAsset(
|
||||
id: asset.localIdentifier,
|
||||
name: "",
|
||||
type: 0,
|
||||
durationInSeconds: 0
|
||||
)
|
||||
if (updatedAssets.contains(AssetWrapper(with: predicate))) {
|
||||
continue
|
||||
}
|
||||
|
||||
let domainAsset = AssetWrapper(with: asset.toPlatformAsset())
|
||||
updatedAssets.insert(domainAsset)
|
||||
}
|
||||
}
|
||||
|
||||
let updates = Array(updatedAssets.map { $0.asset })
|
||||
return SyncDelta(hasChanges: true, updates: updates, deletes: Array(deletedAssets), assetAlbums: buildAssetAlbumsMap(assets: updates))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func buildAssetAlbumsMap(assets: Array<PlatformAsset>) -> [String: [String]] {
|
||||
guard !assets.isEmpty else {
|
||||
return [:]
|
||||
}
|
||||
|
||||
var albumAssets: [String: [String]] = [:]
|
||||
|
||||
for type in albumTypes {
|
||||
let collections = PHAssetCollection.fetchAssetCollections(with: type, subtype: .any, options: nil)
|
||||
collections.enumerateObjects { (album, _, _) in
|
||||
let options = PHFetchOptions()
|
||||
options.predicate = NSPredicate(format: "localIdentifier IN %@", assets.map(\.id))
|
||||
let result = PHAsset.fetchAssets(in: album, options: options)
|
||||
result.enumerateObjects { (asset, _, _) in
|
||||
albumAssets[asset.localIdentifier, default: []].append(album.localIdentifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
return albumAssets
|
||||
}
|
||||
|
||||
func getAssetIdsForAlbum(albumId: String) throws -> [String] {
|
||||
let collections = PHAssetCollection.fetchAssetCollections(withLocalIdentifiers: [albumId], options: nil)
|
||||
guard let album = collections.firstObject else {
|
||||
return []
|
||||
}
|
||||
|
||||
var ids: [String] = []
|
||||
let assets = PHAsset.fetchAssets(in: album, options: nil)
|
||||
assets.enumerateObjects { (asset, _, _) in
|
||||
ids.append(asset.localIdentifier)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func getAssetsCountSince(albumId: String, timestamp: Int64) throws -> Int64 {
|
||||
let collections = PHAssetCollection.fetchAssetCollections(withLocalIdentifiers: [albumId], options: nil)
|
||||
guard let album = collections.firstObject else {
|
||||
return 0
|
||||
}
|
||||
|
||||
let date = NSDate(timeIntervalSince1970: TimeInterval(timestamp))
|
||||
let options = PHFetchOptions()
|
||||
options.predicate = NSPredicate(format: "creationDate > %@ OR modificationDate > %@", date, date)
|
||||
let assets = PHAsset.fetchAssets(in: album, options: options)
|
||||
return Int64(assets.count)
|
||||
}
|
||||
|
||||
func getAssetsForAlbum(albumId: String, updatedTimeCond: Int64?) throws -> [PlatformAsset] {
|
||||
let collections = PHAssetCollection.fetchAssetCollections(withLocalIdentifiers: [albumId], options: nil)
|
||||
guard let album = collections.firstObject else {
|
||||
return []
|
||||
}
|
||||
|
||||
let options = PHFetchOptions()
|
||||
if(updatedTimeCond != nil) {
|
||||
let date = NSDate(timeIntervalSince1970: TimeInterval(updatedTimeCond!))
|
||||
options.predicate = NSPredicate(format: "creationDate > %@ OR modificationDate > %@", date, date)
|
||||
}
|
||||
|
||||
let result = PHAsset.fetchAssets(in: album, options: options)
|
||||
if(result.count == 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
var assets: [PlatformAsset] = []
|
||||
result.enumerateObjects { (asset, _, _) in
|
||||
assets.append(asset.toPlatformAsset())
|
||||
}
|
||||
return assets
|
||||
}
|
||||
|
||||
func hashPaths(paths: [String]) throws -> [FlutterStandardTypedData?] {
|
||||
return paths.map { path in
|
||||
guard let file = FileHandle(forReadingAtPath: path) else {
|
||||
print("Cannot open file: \(path)")
|
||||
return nil
|
||||
}
|
||||
|
||||
var hasher = Insecure.SHA1()
|
||||
while autoreleasepool(invoking: {
|
||||
let chunk = file.readData(ofLength: hashBufferSize)
|
||||
guard !chunk.isEmpty else { return false }
|
||||
hasher.update(data: chunk)
|
||||
return true
|
||||
}) { }
|
||||
|
||||
let digest = hasher.finalize()
|
||||
return FlutterStandardTypedData(bytes: Data(digest))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ const int kLogTruncateLimit = 250;
|
||||
|
||||
// Sync
|
||||
const int kSyncEventBatchSize = 5000;
|
||||
const int kFetchLocalAssetsBatchSize = 40000;
|
||||
|
||||
// Hash batch limits
|
||||
const int kBatchHashFileLimit = 128;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
List<ColorFilter> filters = [
|
||||
const List<ColorFilter> filters = [
|
||||
//Original
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
@@ -25,7 +25,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Vintage
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
0.8,
|
||||
0.1,
|
||||
0.1,
|
||||
@@ -48,7 +48,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Mood
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
1.2,
|
||||
0.1,
|
||||
0.1,
|
||||
@@ -71,7 +71,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Crisp
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
1.2,
|
||||
0,
|
||||
0,
|
||||
@@ -94,7 +94,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Cool
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
0.9,
|
||||
0,
|
||||
0.2,
|
||||
@@ -117,7 +117,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Blush
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
1.1,
|
||||
0.1,
|
||||
0.1,
|
||||
@@ -140,7 +140,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Sunkissed
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
1.3,
|
||||
0,
|
||||
0.1,
|
||||
@@ -163,7 +163,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Fresh
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
1.2,
|
||||
0,
|
||||
0,
|
||||
@@ -186,7 +186,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Classic
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
1.1,
|
||||
0,
|
||||
-0.1,
|
||||
@@ -209,7 +209,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Lomo-ish
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
1.5,
|
||||
0,
|
||||
0.1,
|
||||
@@ -232,7 +232,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Nashville
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
1.2,
|
||||
0.15,
|
||||
-0.15,
|
||||
@@ -255,7 +255,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Valencia
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
1.15,
|
||||
0.1,
|
||||
0.1,
|
||||
@@ -278,7 +278,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Clarendon
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
1.2,
|
||||
0,
|
||||
0,
|
||||
@@ -301,7 +301,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Moon
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
0.33,
|
||||
0.33,
|
||||
0.33,
|
||||
@@ -324,7 +324,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Willow
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
0.5,
|
||||
0.5,
|
||||
0.5,
|
||||
@@ -347,7 +347,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Kodak
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
1.3,
|
||||
0.1,
|
||||
-0.1,
|
||||
@@ -370,7 +370,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Frost
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
0.8,
|
||||
0.2,
|
||||
0.1,
|
||||
@@ -393,7 +393,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Night Vision
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
0.1,
|
||||
0.95,
|
||||
0.2,
|
||||
@@ -416,7 +416,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Sunset
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
1.5,
|
||||
0.2,
|
||||
0,
|
||||
@@ -439,7 +439,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Noir
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
1.3,
|
||||
-0.3,
|
||||
0.1,
|
||||
@@ -462,7 +462,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Dreamy
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
1.1,
|
||||
0.1,
|
||||
0.1,
|
||||
@@ -485,7 +485,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Sepia
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
0.393,
|
||||
0.769,
|
||||
0.189,
|
||||
@@ -508,7 +508,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Radium
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
1.438,
|
||||
-0.062,
|
||||
-0.062,
|
||||
@@ -531,7 +531,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Aqua
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
0.2126,
|
||||
0.7152,
|
||||
0.0722,
|
||||
@@ -554,7 +554,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Purple Haze
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
1.3,
|
||||
0,
|
||||
1.2,
|
||||
@@ -577,7 +577,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Lemonade
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
1.2,
|
||||
0.1,
|
||||
0,
|
||||
@@ -600,7 +600,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Caramel
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
1.6,
|
||||
0.2,
|
||||
0,
|
||||
@@ -623,7 +623,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Peachy
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
1.3,
|
||||
0.5,
|
||||
0,
|
||||
@@ -646,7 +646,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Neon
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
@@ -669,7 +669,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Cold Morning
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
0.9,
|
||||
0.1,
|
||||
0.2,
|
||||
@@ -692,7 +692,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Lush
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
0.9,
|
||||
0.2,
|
||||
0,
|
||||
@@ -715,7 +715,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Urban Neon
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
1.1,
|
||||
0,
|
||||
0.3,
|
||||
@@ -738,7 +738,7 @@ List<ColorFilter> filters = [
|
||||
0,
|
||||
]),
|
||||
//Monochrome
|
||||
const ColorFilter.matrix([
|
||||
ColorFilter.matrix([
|
||||
0.6,
|
||||
0.2,
|
||||
0.2,
|
||||
|
||||
@@ -5,6 +5,7 @@ const Map<String, Locale> locales = {
|
||||
'English (en)': Locale('en'),
|
||||
// Additional locales
|
||||
'Arabic (ar)': Locale('ar'),
|
||||
'Bulgarian (bg)': Locale('bg'),
|
||||
'Catalan (ca)': Locale('ca'),
|
||||
'Chinese Simplified (zh_CN)':
|
||||
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'SIMPLIFIED'),
|
||||
@@ -31,6 +32,7 @@ const Map<String, Locale> locales = {
|
||||
'Mongolian (mn)': Locale('mn'),
|
||||
'Norwegian Bokmål (nb_NO)': Locale('nb', 'NO'),
|
||||
'Polish (pl)': Locale('pl'),
|
||||
'Brazilian Portuguese (pt_BR)': Locale('pt', 'BR'),
|
||||
'Portuguese (pt)': Locale('pt'),
|
||||
'Romanian (ro)': Locale('ro'),
|
||||
'Russian (ru)': Locale('ru'),
|
||||
@@ -42,6 +44,8 @@ const Map<String, Locale> locales = {
|
||||
'Slovenian (sl)': Locale('sl'),
|
||||
'Spanish (es)': Locale('es'),
|
||||
'Swedish (sv)': Locale('sv'),
|
||||
'Tamil (ta)': Locale('ta'),
|
||||
'Telugu (te)': Locale('te'),
|
||||
'Thai (th)': Locale('th'),
|
||||
'Turkish (tr)': Locale('tr'),
|
||||
'Ukrainian (uk)': Locale('uk'),
|
||||
|
||||
33
mobile/lib/domain/interfaces/local_album.interface.dart
Normal file
33
mobile/lib/domain/interfaces/local_album.interface.dart
Normal file
@@ -0,0 +1,33 @@
|
||||
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/local_album.model.dart';
|
||||
|
||||
abstract interface class ILocalAlbumRepository implements IDatabaseRepository {
|
||||
Future<List<LocalAlbum>> getAll({Set<SortLocalAlbumsBy> sortBy = const {}});
|
||||
|
||||
Future<List<LocalAsset>> getAssets(String albumId);
|
||||
|
||||
Future<List<String>> getAssetIds(String albumId);
|
||||
|
||||
Future<void> upsert(
|
||||
LocalAlbum album, {
|
||||
Iterable<LocalAsset> toUpsert = const [],
|
||||
Iterable<String> toDelete = const [],
|
||||
});
|
||||
|
||||
Future<void> updateAll(Iterable<LocalAlbum> albums);
|
||||
|
||||
Future<void> delete(String albumId);
|
||||
|
||||
Future<void> processDelta({
|
||||
required List<LocalAsset> updates,
|
||||
required List<String> deletes,
|
||||
required Map<String, List<String>> assetAlbums,
|
||||
});
|
||||
|
||||
Future<void> syncDeletes(String albumId, Iterable<String> assetIdsToKeep);
|
||||
|
||||
Future<List<LocalAsset>> getAssetsToHash(String albumId);
|
||||
}
|
||||
|
||||
enum SortLocalAlbumsBy { id, backupSelection, isIosSharedAlbum }
|
||||
6
mobile/lib/domain/interfaces/local_asset.interface.dart
Normal file
6
mobile/lib/domain/interfaces/local_asset.interface.dart
Normal file
@@ -0,0 +1,6 @@
|
||||
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
|
||||
abstract interface class ILocalAssetRepository implements IDatabaseRepository {
|
||||
Future<void> updateHashes(Iterable<LocalAsset> hashes);
|
||||
}
|
||||
7
mobile/lib/domain/interfaces/storage.interface.dart
Normal file
7
mobile/lib/domain/interfaces/storage.interface.dart
Normal file
@@ -0,0 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
|
||||
abstract interface class IStorageRepository {
|
||||
Future<File?> getFileForAsset(LocalAsset asset);
|
||||
}
|
||||
@@ -6,9 +6,11 @@ abstract interface class IStoreRepository implements IDatabaseRepository {
|
||||
|
||||
Future<T?> tryGet<T>(StoreKey<T> key);
|
||||
|
||||
Future<List<StoreDto<Object>>> getAll();
|
||||
|
||||
Stream<T?> watch<T>(StoreKey<T> key);
|
||||
|
||||
Stream<StoreUpdateEvent> watchAll();
|
||||
Stream<StoreDto<Object>> watchAll();
|
||||
|
||||
Future<bool> update<T>(StoreKey<T> key, T value);
|
||||
|
||||
|
||||
@@ -15,4 +15,13 @@ abstract interface class ISyncStreamRepository implements IDatabaseRepository {
|
||||
Future<void> updatePartnerAssetsV1(Iterable<SyncAssetV1> data);
|
||||
Future<void> deletePartnerAssetsV1(Iterable<SyncAssetDeleteV1> data);
|
||||
Future<void> updatePartnerAssetsExifV1(Iterable<SyncAssetExifV1> data);
|
||||
|
||||
Future<void> updateAlbumsV1(Iterable<SyncAlbumV1> data);
|
||||
Future<void> deleteAlbumsV1(Iterable<SyncAlbumDeleteV1> data);
|
||||
|
||||
// Future<void> updateAlbumAssetsV1(Iterable<SyncAlbumAssetV1> data);
|
||||
// Future<void> deleteAlbumAssetsV1(Iterable<SyncAlbumAssetV1> data);
|
||||
|
||||
Future<void> updateAlbumUsersV1(Iterable<SyncAlbumUserV1> data);
|
||||
Future<void> deleteAlbumUsersV1(Iterable<SyncAlbumUserDeleteV1> data);
|
||||
}
|
||||
|
||||
68
mobile/lib/domain/models/album/album.model.dart
Normal file
68
mobile/lib/domain/models/album/album.model.dart
Normal file
@@ -0,0 +1,68 @@
|
||||
enum AssetOrder {
|
||||
// do not change this order!
|
||||
asc,
|
||||
desc,
|
||||
}
|
||||
|
||||
// Model for an album stored in the server
|
||||
class Album {
|
||||
final String id;
|
||||
final String name;
|
||||
final String description;
|
||||
final DateTime createdAt;
|
||||
final DateTime updatedAt;
|
||||
final String? thumbnailAssetId;
|
||||
final bool isActivityEnabled;
|
||||
final AssetOrder order;
|
||||
|
||||
const Album({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.description,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
this.thumbnailAssetId,
|
||||
required this.isActivityEnabled,
|
||||
required this.order,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '''Album {
|
||||
id: $id,
|
||||
name: $name,
|
||||
description: $description,
|
||||
createdAt: $createdAt,
|
||||
updatedAt: $updatedAt,
|
||||
isActivityEnabled: $isActivityEnabled,
|
||||
order: $order,
|
||||
thumbnailAssetId: ${thumbnailAssetId ?? "<NA>"}
|
||||
}''';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (other is! Album) return false;
|
||||
if (identical(this, other)) return true;
|
||||
return id == other.id &&
|
||||
name == other.name &&
|
||||
description == other.description &&
|
||||
createdAt == other.createdAt &&
|
||||
updatedAt == other.updatedAt &&
|
||||
thumbnailAssetId == other.thumbnailAssetId &&
|
||||
isActivityEnabled == other.isActivityEnabled &&
|
||||
order == other.order;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return id.hashCode ^
|
||||
name.hashCode ^
|
||||
description.hashCode ^
|
||||
createdAt.hashCode ^
|
||||
updatedAt.hashCode ^
|
||||
thumbnailAssetId.hashCode ^
|
||||
isActivityEnabled.hashCode ^
|
||||
order.hashCode;
|
||||
}
|
||||
}
|
||||
5
mobile/lib/domain/models/album_user.model.dart
Normal file
5
mobile/lib/domain/models/album_user.model.dart
Normal file
@@ -0,0 +1,5 @@
|
||||
enum AlbumUserRole {
|
||||
// do not change this order!
|
||||
editor,
|
||||
viewer,
|
||||
}
|
||||
61
mobile/lib/domain/models/asset/asset.model.dart
Normal file
61
mobile/lib/domain/models/asset/asset.model.dart
Normal file
@@ -0,0 +1,61 @@
|
||||
part of 'base_asset.model.dart';
|
||||
|
||||
enum AssetVisibility {
|
||||
timeline,
|
||||
hidden,
|
||||
archive,
|
||||
locked,
|
||||
}
|
||||
|
||||
// Model for an asset stored in the server
|
||||
class Asset extends BaseAsset {
|
||||
final String id;
|
||||
final String? localId;
|
||||
final AssetVisibility visibility;
|
||||
|
||||
const Asset({
|
||||
required this.id,
|
||||
this.localId,
|
||||
required super.name,
|
||||
required super.checksum,
|
||||
required super.type,
|
||||
required super.createdAt,
|
||||
required super.updatedAt,
|
||||
super.width,
|
||||
super.height,
|
||||
super.durationInSeconds,
|
||||
super.isFavorite = false,
|
||||
this.visibility = AssetVisibility.timeline,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '''Asset {
|
||||
id: $id,
|
||||
name: $name,
|
||||
type: $type,
|
||||
createdAt: $createdAt,
|
||||
updatedAt: $updatedAt,
|
||||
width: ${width ?? "<NA>"},
|
||||
height: ${height ?? "<NA>"},
|
||||
durationInSeconds: ${durationInSeconds ?? "<NA>"},
|
||||
localId: ${localId ?? "<NA>"},
|
||||
isFavorite: $isFavorite,
|
||||
visibility: $visibility,
|
||||
}''';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (other is! Asset) return false;
|
||||
if (identical(this, other)) return true;
|
||||
return super == other &&
|
||||
id == other.id &&
|
||||
localId == other.localId &&
|
||||
visibility == other.visibility;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
super.hashCode ^ id.hashCode ^ localId.hashCode ^ visibility.hashCode;
|
||||
}
|
||||
76
mobile/lib/domain/models/asset/base_asset.model.dart
Normal file
76
mobile/lib/domain/models/asset/base_asset.model.dart
Normal file
@@ -0,0 +1,76 @@
|
||||
part 'asset.model.dart';
|
||||
part 'local_asset.model.dart';
|
||||
|
||||
enum AssetType {
|
||||
// do not change this order!
|
||||
other,
|
||||
image,
|
||||
video,
|
||||
audio,
|
||||
}
|
||||
|
||||
sealed class BaseAsset {
|
||||
final String name;
|
||||
final String? checksum;
|
||||
final AssetType type;
|
||||
final DateTime createdAt;
|
||||
final DateTime updatedAt;
|
||||
final int? width;
|
||||
final int? height;
|
||||
final int? durationInSeconds;
|
||||
final bool isFavorite;
|
||||
|
||||
const BaseAsset({
|
||||
required this.name,
|
||||
required this.checksum,
|
||||
required this.type,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
this.width,
|
||||
this.height,
|
||||
this.durationInSeconds,
|
||||
this.isFavorite = false,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '''BaseAsset {
|
||||
name: $name,
|
||||
type: $type,
|
||||
createdAt: $createdAt,
|
||||
updatedAt: $updatedAt,
|
||||
width: ${width ?? "<NA>"},
|
||||
height: ${height ?? "<NA>"},
|
||||
durationInSeconds: ${durationInSeconds ?? "<NA>"},
|
||||
isFavorite: $isFavorite,
|
||||
}''';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is BaseAsset) {
|
||||
return name == other.name &&
|
||||
type == other.type &&
|
||||
createdAt == other.createdAt &&
|
||||
updatedAt == other.updatedAt &&
|
||||
width == other.width &&
|
||||
height == other.height &&
|
||||
durationInSeconds == other.durationInSeconds &&
|
||||
isFavorite == other.isFavorite;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return name.hashCode ^
|
||||
type.hashCode ^
|
||||
createdAt.hashCode ^
|
||||
updatedAt.hashCode ^
|
||||
width.hashCode ^
|
||||
height.hashCode ^
|
||||
durationInSeconds.hashCode ^
|
||||
isFavorite.hashCode;
|
||||
}
|
||||
}
|
||||
74
mobile/lib/domain/models/asset/local_asset.model.dart
Normal file
74
mobile/lib/domain/models/asset/local_asset.model.dart
Normal file
@@ -0,0 +1,74 @@
|
||||
part of 'base_asset.model.dart';
|
||||
|
||||
class LocalAsset extends BaseAsset {
|
||||
final String id;
|
||||
final String? remoteId;
|
||||
|
||||
const LocalAsset({
|
||||
required this.id,
|
||||
this.remoteId,
|
||||
required super.name,
|
||||
super.checksum,
|
||||
required super.type,
|
||||
required super.createdAt,
|
||||
required super.updatedAt,
|
||||
super.width,
|
||||
super.height,
|
||||
super.durationInSeconds,
|
||||
super.isFavorite = false,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '''LocalAsset {
|
||||
id: $id,
|
||||
name: $name,
|
||||
type: $type,
|
||||
createdAt: $createdAt,
|
||||
updatedAt: $updatedAt,
|
||||
width: ${width ?? "<NA>"},
|
||||
height: ${height ?? "<NA>"},
|
||||
durationInSeconds: ${durationInSeconds ?? "<NA>"},
|
||||
remoteId: ${remoteId ?? "<NA>"}
|
||||
isFavorite: $isFavorite,
|
||||
}''';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (other is! LocalAsset) return false;
|
||||
if (identical(this, other)) return true;
|
||||
return super == other && id == other.id && remoteId == other.remoteId;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => super.hashCode ^ id.hashCode ^ remoteId.hashCode;
|
||||
|
||||
LocalAsset copyWith({
|
||||
String? id,
|
||||
String? remoteId,
|
||||
String? name,
|
||||
String? checksum,
|
||||
AssetType? type,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
int? width,
|
||||
int? height,
|
||||
int? durationInSeconds,
|
||||
bool? isFavorite,
|
||||
}) {
|
||||
return LocalAsset(
|
||||
id: id ?? this.id,
|
||||
remoteId: remoteId ?? this.remoteId,
|
||||
name: name ?? this.name,
|
||||
checksum: checksum ?? this.checksum,
|
||||
type: type ?? this.type,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
width: width ?? this.width,
|
||||
height: height ?? this.height,
|
||||
durationInSeconds: durationInSeconds ?? this.durationInSeconds,
|
||||
isFavorite: isFavorite ?? this.isFavorite,
|
||||
);
|
||||
}
|
||||
}
|
||||
80
mobile/lib/domain/models/local_album.model.dart
Normal file
80
mobile/lib/domain/models/local_album.model.dart
Normal file
@@ -0,0 +1,80 @@
|
||||
enum BackupSelection {
|
||||
// Used to sort albums based on the backupSelection
|
||||
// selected -> none -> excluded
|
||||
// Do not change the order of these values
|
||||
selected,
|
||||
none,
|
||||
excluded,
|
||||
}
|
||||
|
||||
class LocalAlbum {
|
||||
final String id;
|
||||
final String name;
|
||||
final DateTime updatedAt;
|
||||
final bool isIosSharedAlbum;
|
||||
|
||||
final int assetCount;
|
||||
final BackupSelection backupSelection;
|
||||
|
||||
const LocalAlbum({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.updatedAt,
|
||||
this.assetCount = 0,
|
||||
this.backupSelection = BackupSelection.none,
|
||||
this.isIosSharedAlbum = false,
|
||||
});
|
||||
|
||||
LocalAlbum copyWith({
|
||||
String? id,
|
||||
String? name,
|
||||
DateTime? updatedAt,
|
||||
int? assetCount,
|
||||
BackupSelection? backupSelection,
|
||||
bool? isIosSharedAlbum,
|
||||
}) {
|
||||
return LocalAlbum(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
assetCount: assetCount ?? this.assetCount,
|
||||
backupSelection: backupSelection ?? this.backupSelection,
|
||||
isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (other is! LocalAlbum) return false;
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other.id == id &&
|
||||
other.name == name &&
|
||||
other.updatedAt == updatedAt &&
|
||||
other.assetCount == assetCount &&
|
||||
other.backupSelection == backupSelection &&
|
||||
other.isIosSharedAlbum == isIosSharedAlbum;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return id.hashCode ^
|
||||
name.hashCode ^
|
||||
updatedAt.hashCode ^
|
||||
assetCount.hashCode ^
|
||||
backupSelection.hashCode ^
|
||||
isIosSharedAlbum.hashCode;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '''LocalAlbum: {
|
||||
id: $id,
|
||||
name: $name,
|
||||
updatedAt: $updatedAt,
|
||||
assetCount: $assetCount,
|
||||
backupSelection: $backupSelection,
|
||||
isIosSharedAlbum: $isIosSharedAlbum
|
||||
}''';
|
||||
}
|
||||
}
|
||||
@@ -75,23 +75,23 @@ enum StoreKey<T> {
|
||||
Type get type => T;
|
||||
}
|
||||
|
||||
class StoreUpdateEvent<T> {
|
||||
class StoreDto<T> {
|
||||
final StoreKey<T> key;
|
||||
final T? value;
|
||||
|
||||
const StoreUpdateEvent(this.key, this.value);
|
||||
const StoreDto(this.key, this.value);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '''
|
||||
StoreUpdateEvent: {
|
||||
StoreDto: {
|
||||
key: $key,
|
||||
value: ${value ?? '<NA>'},
|
||||
}''';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(covariant StoreUpdateEvent<T> other) {
|
||||
bool operator ==(covariant StoreDto<T> other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other.key == key && other.value == value;
|
||||
|
||||
@@ -2,8 +2,7 @@ import 'package:openapi/api.dart';
|
||||
|
||||
class SyncEvent {
|
||||
final SyncEntityType type;
|
||||
// ignore: avoid-dynamic
|
||||
final dynamic data;
|
||||
final Object data;
|
||||
final String ack;
|
||||
|
||||
const SyncEvent({required this.type, required this.data, required this.ack});
|
||||
|
||||
120
mobile/lib/domain/services/hash.service.dart
Normal file
120
mobile/lib/domain/services/hash.service.dart
Normal file
@@ -0,0 +1,120 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:immich_mobile/constants/constants.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/local_album.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/local_asset.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/storage.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
||||
import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
class HashService {
|
||||
final int batchSizeLimit;
|
||||
final int batchFileLimit;
|
||||
final ILocalAlbumRepository _localAlbumRepository;
|
||||
final ILocalAssetRepository _localAssetRepository;
|
||||
final IStorageRepository _storageRepository;
|
||||
final NativeSyncApi _nativeSyncApi;
|
||||
final _log = Logger('HashService');
|
||||
|
||||
HashService({
|
||||
required ILocalAlbumRepository localAlbumRepository,
|
||||
required ILocalAssetRepository localAssetRepository,
|
||||
required IStorageRepository storageRepository,
|
||||
required NativeSyncApi nativeSyncApi,
|
||||
this.batchSizeLimit = kBatchHashSizeLimit,
|
||||
this.batchFileLimit = kBatchHashFileLimit,
|
||||
}) : _localAlbumRepository = localAlbumRepository,
|
||||
_localAssetRepository = localAssetRepository,
|
||||
_storageRepository = storageRepository,
|
||||
_nativeSyncApi = nativeSyncApi;
|
||||
|
||||
Future<void> hashAssets() async {
|
||||
final Stopwatch stopwatch = Stopwatch()..start();
|
||||
// Sorted by backupSelection followed by isCloud
|
||||
final localAlbums = await _localAlbumRepository.getAll(
|
||||
sortBy: {
|
||||
SortLocalAlbumsBy.backupSelection,
|
||||
SortLocalAlbumsBy.isIosSharedAlbum,
|
||||
},
|
||||
);
|
||||
|
||||
for (final album in localAlbums) {
|
||||
final assetsToHash =
|
||||
await _localAlbumRepository.getAssetsToHash(album.id);
|
||||
if (assetsToHash.isNotEmpty) {
|
||||
await _hashAssets(assetsToHash);
|
||||
}
|
||||
}
|
||||
|
||||
stopwatch.stop();
|
||||
_log.info("Hashing took - ${stopwatch.elapsedMilliseconds}ms");
|
||||
DLog.log("Hashing took - ${stopwatch.elapsedMilliseconds}ms");
|
||||
}
|
||||
|
||||
/// Processes a list of [LocalAsset]s, storing their hash and updating the assets in the DB
|
||||
/// with hash for those that were successfully hashed. Hashes are looked up in a table
|
||||
/// [LocalAssetHashEntity] by local id. Only missing entries are newly hashed and added to the DB.
|
||||
Future<void> _hashAssets(List<LocalAsset> assetsToHash) async {
|
||||
int bytesProcessed = 0;
|
||||
final toHash = <_AssetToPath>[];
|
||||
|
||||
for (final asset in assetsToHash) {
|
||||
final file = await _storageRepository.getFileForAsset(asset);
|
||||
if (file == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bytesProcessed += await file.length();
|
||||
toHash.add(_AssetToPath(asset: asset, path: file.path));
|
||||
|
||||
if (toHash.length >= batchFileLimit || bytesProcessed >= batchSizeLimit) {
|
||||
await _processBatch(toHash);
|
||||
toHash.clear();
|
||||
bytesProcessed = 0;
|
||||
}
|
||||
}
|
||||
|
||||
await _processBatch(toHash);
|
||||
}
|
||||
|
||||
/// Processes a batch of assets.
|
||||
Future<void> _processBatch(List<_AssetToPath> toHash) async {
|
||||
if (toHash.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
_log.fine("Hashing ${toHash.length} files");
|
||||
|
||||
final hashed = <LocalAsset>[];
|
||||
final hashes =
|
||||
await _nativeSyncApi.hashPaths(toHash.map((e) => e.path).toList());
|
||||
assert(
|
||||
hashes.length == toHash.length,
|
||||
"Hashes length does not match toHash length: ${hashes.length} != ${toHash.length}",
|
||||
);
|
||||
|
||||
for (int i = 0; i < hashes.length; i++) {
|
||||
final hash = hashes[i];
|
||||
final asset = toHash[i].asset;
|
||||
if (hash?.length == 20) {
|
||||
hashed.add(asset.copyWith(checksum: base64.encode(hash!)));
|
||||
} else {
|
||||
_log.warning("Failed to hash file for ${asset.id}");
|
||||
}
|
||||
}
|
||||
|
||||
_log.fine("Hashed ${hashed.length}/${toHash.length} assets");
|
||||
DLog.log("Hashed ${hashed.length}/${toHash.length} assets");
|
||||
|
||||
await _localAssetRepository.updateHashes(hashed);
|
||||
}
|
||||
}
|
||||
|
||||
class _AssetToPath {
|
||||
final LocalAsset asset;
|
||||
final String path;
|
||||
|
||||
const _AssetToPath({required this.asset, required this.path});
|
||||
}
|
||||
382
mobile/lib/domain/services/local_sync.service.dart
Normal file
382
mobile/lib/domain/services/local_sync.service.dart
Normal file
@@ -0,0 +1,382 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/local_album.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/local_album.model.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
||||
import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart';
|
||||
import 'package:immich_mobile/utils/diff.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:platform/platform.dart';
|
||||
|
||||
class LocalSyncService {
|
||||
final ILocalAlbumRepository _localAlbumRepository;
|
||||
final NativeSyncApi _nativeSyncApi;
|
||||
final Platform _platform;
|
||||
final StoreService _storeService;
|
||||
final Logger _log = Logger("DeviceSyncService");
|
||||
|
||||
LocalSyncService({
|
||||
required ILocalAlbumRepository localAlbumRepository,
|
||||
required NativeSyncApi nativeSyncApi,
|
||||
required StoreService storeService,
|
||||
Platform? platform,
|
||||
}) : _localAlbumRepository = localAlbumRepository,
|
||||
_nativeSyncApi = nativeSyncApi,
|
||||
_storeService = storeService,
|
||||
_platform = platform ?? const LocalPlatform();
|
||||
|
||||
bool get _ignoreIcloudAssets =>
|
||||
_storeService.get(StoreKey.ignoreIcloudAssets, false) == true;
|
||||
|
||||
Future<void> sync({bool full = false}) async {
|
||||
final Stopwatch stopwatch = Stopwatch()..start();
|
||||
try {
|
||||
if (full || await _nativeSyncApi.shouldFullSync()) {
|
||||
_log.fine("Full sync request from ${full ? "user" : "native"}");
|
||||
DLog.log("Full sync request from ${full ? "user" : "native"}");
|
||||
return await fullSync();
|
||||
}
|
||||
|
||||
final delta = await _nativeSyncApi.getMediaChanges();
|
||||
if (!delta.hasChanges) {
|
||||
_log.fine("No media changes detected. Skipping sync");
|
||||
DLog.log("No media changes detected. Skipping sync");
|
||||
return;
|
||||
}
|
||||
|
||||
DLog.log("Delta updated: ${delta.updates.length}");
|
||||
DLog.log("Delta deleted: ${delta.deletes.length}");
|
||||
|
||||
final deviceAlbums = await _nativeSyncApi.getAlbums();
|
||||
await _localAlbumRepository.updateAll(deviceAlbums.toLocalAlbums());
|
||||
await _localAlbumRepository.processDelta(
|
||||
updates: delta.updates.toLocalAssets(),
|
||||
deletes: delta.deletes,
|
||||
assetAlbums: delta.assetAlbums,
|
||||
);
|
||||
|
||||
final dbAlbums = await _localAlbumRepository.getAll();
|
||||
// On Android, we need to sync all albums since it is not possible to
|
||||
// detect album deletions from the native side
|
||||
if (_platform.isAndroid) {
|
||||
for (final album in dbAlbums) {
|
||||
final deviceIds = await _nativeSyncApi.getAssetIdsForAlbum(album.id);
|
||||
await _localAlbumRepository.syncDeletes(album.id, deviceIds);
|
||||
}
|
||||
}
|
||||
|
||||
if (_platform.isIOS) {
|
||||
// On iOS, we need to full sync albums that are marked as cloud as the delta sync
|
||||
// does not include changes for cloud albums. If ignoreIcloudAssets is enabled,
|
||||
// remove the albums from the local database from the previous sync
|
||||
final cloudAlbums =
|
||||
deviceAlbums.where((a) => a.isCloud).toLocalAlbums();
|
||||
for (final album in cloudAlbums) {
|
||||
final dbAlbum = dbAlbums.firstWhereOrNull((a) => a.id == album.id);
|
||||
if (dbAlbum == null) {
|
||||
_log.warning(
|
||||
"Cloud album ${album.name} not found in local database. Skipping sync.",
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if (_ignoreIcloudAssets) {
|
||||
await removeAlbum(dbAlbum);
|
||||
} else {
|
||||
await updateAlbum(dbAlbum, album);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await _nativeSyncApi.checkpointSync();
|
||||
} catch (e, s) {
|
||||
_log.severe("Error performing device sync", e, s);
|
||||
} finally {
|
||||
stopwatch.stop();
|
||||
_log.info("Device sync took - ${stopwatch.elapsedMilliseconds}ms");
|
||||
DLog.log("Device sync took - ${stopwatch.elapsedMilliseconds}ms");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> fullSync() async {
|
||||
try {
|
||||
final Stopwatch stopwatch = Stopwatch()..start();
|
||||
|
||||
List<PlatformAlbum> deviceAlbums =
|
||||
List.of(await _nativeSyncApi.getAlbums());
|
||||
if (_platform.isIOS && _ignoreIcloudAssets) {
|
||||
deviceAlbums.removeWhere((album) => album.isCloud);
|
||||
}
|
||||
|
||||
final dbAlbums =
|
||||
await _localAlbumRepository.getAll(sortBy: {SortLocalAlbumsBy.id});
|
||||
|
||||
await diffSortedLists(
|
||||
dbAlbums,
|
||||
deviceAlbums.toLocalAlbums(),
|
||||
compare: (a, b) => a.id.compareTo(b.id),
|
||||
both: updateAlbum,
|
||||
onlyFirst: removeAlbum,
|
||||
onlySecond: addAlbum,
|
||||
);
|
||||
|
||||
await _nativeSyncApi.checkpointSync();
|
||||
stopwatch.stop();
|
||||
_log.info("Full device sync took - ${stopwatch.elapsedMilliseconds}ms");
|
||||
DLog.log("Full device sync took - ${stopwatch.elapsedMilliseconds}ms");
|
||||
} catch (e, s) {
|
||||
_log.severe("Error performing full device sync", e, s);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> addAlbum(LocalAlbum album) async {
|
||||
try {
|
||||
_log.fine("Adding device album ${album.name}");
|
||||
|
||||
final assets = album.assetCount > 0
|
||||
? await _nativeSyncApi.getAssetsForAlbum(album.id)
|
||||
: <PlatformAsset>[];
|
||||
|
||||
await _localAlbumRepository.upsert(
|
||||
album,
|
||||
toUpsert: assets.toLocalAssets(),
|
||||
);
|
||||
_log.fine("Successfully added device album ${album.name}");
|
||||
} catch (e, s) {
|
||||
_log.warning("Error while adding device album", e, s);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> removeAlbum(LocalAlbum a) async {
|
||||
_log.fine("Removing device album ${a.name}");
|
||||
try {
|
||||
// Asset deletion is handled in the repository
|
||||
await _localAlbumRepository.delete(a.id);
|
||||
} catch (e, s) {
|
||||
_log.warning("Error while removing device album", e, s);
|
||||
}
|
||||
}
|
||||
|
||||
// The deviceAlbum is ignored since we are going to refresh it anyways
|
||||
FutureOr<bool> updateAlbum(LocalAlbum dbAlbum, LocalAlbum deviceAlbum) async {
|
||||
try {
|
||||
_log.fine("Syncing device album ${dbAlbum.name}");
|
||||
|
||||
if (_albumsEqual(deviceAlbum, dbAlbum)) {
|
||||
_log.fine(
|
||||
"Device album ${dbAlbum.name} has not changed. Skipping sync.",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
_log.fine("Device album ${dbAlbum.name} has changed. Syncing...");
|
||||
|
||||
// Faster path - only new assets added
|
||||
if (await checkAddition(dbAlbum, deviceAlbum)) {
|
||||
_log.fine("Fast synced device album ${dbAlbum.name}");
|
||||
DLog.log("Fast synced device album ${dbAlbum.name}");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Slower path - full sync
|
||||
return await fullDiff(dbAlbum, deviceAlbum);
|
||||
} catch (e, s) {
|
||||
_log.warning("Error while diff device album", e, s);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
// The [deviceAlbum] is expected to be refreshed before calling this method
|
||||
// with modified time and asset count
|
||||
Future<bool> checkAddition(
|
||||
LocalAlbum dbAlbum,
|
||||
LocalAlbum deviceAlbum,
|
||||
) async {
|
||||
try {
|
||||
_log.fine("Fast syncing device album ${dbAlbum.name}");
|
||||
// Assets has been modified
|
||||
if (deviceAlbum.assetCount <= dbAlbum.assetCount) {
|
||||
_log.fine("Local album has modifications. Proceeding to full sync");
|
||||
return false;
|
||||
}
|
||||
|
||||
final updatedTime =
|
||||
(dbAlbum.updatedAt.millisecondsSinceEpoch ~/ 1000) + 1;
|
||||
final newAssetsCount =
|
||||
await _nativeSyncApi.getAssetsCountSince(deviceAlbum.id, updatedTime);
|
||||
|
||||
// Early return if no new assets were found
|
||||
if (newAssetsCount == 0) {
|
||||
_log.fine(
|
||||
"No new assets found despite album having changes. Proceeding to full sync for ${dbAlbum.name}",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check whether there is only addition or if there has been deletions
|
||||
if (deviceAlbum.assetCount != dbAlbum.assetCount + newAssetsCount) {
|
||||
_log.fine("Local album has modifications. Proceeding to full sync");
|
||||
return false;
|
||||
}
|
||||
|
||||
final newAssets = await _nativeSyncApi.getAssetsForAlbum(
|
||||
deviceAlbum.id,
|
||||
updatedTimeCond: updatedTime,
|
||||
);
|
||||
|
||||
await _localAlbumRepository.upsert(
|
||||
deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection),
|
||||
toUpsert: newAssets.toLocalAssets(),
|
||||
);
|
||||
|
||||
return true;
|
||||
} catch (e, s) {
|
||||
_log.warning("Error on fast syncing local album: ${dbAlbum.name}", e, s);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
// The [deviceAlbum] is expected to be refreshed before calling this method
|
||||
// with modified time and asset count
|
||||
Future<bool> fullDiff(LocalAlbum dbAlbum, LocalAlbum deviceAlbum) async {
|
||||
try {
|
||||
final assetsInDevice = deviceAlbum.assetCount > 0
|
||||
? await _nativeSyncApi
|
||||
.getAssetsForAlbum(deviceAlbum.id)
|
||||
.then((a) => a.toLocalAssets())
|
||||
: <LocalAsset>[];
|
||||
final assetsInDb = dbAlbum.assetCount > 0
|
||||
? await _localAlbumRepository.getAssets(dbAlbum.id)
|
||||
: <LocalAsset>[];
|
||||
|
||||
if (deviceAlbum.assetCount == 0) {
|
||||
_log.fine(
|
||||
"Device album ${deviceAlbum.name} is empty. Removing assets from DB.",
|
||||
);
|
||||
await _localAlbumRepository.upsert(
|
||||
deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection),
|
||||
toDelete: assetsInDb.map((a) => a.id),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
final updatedDeviceAlbum = deviceAlbum.copyWith(
|
||||
backupSelection: dbAlbum.backupSelection,
|
||||
);
|
||||
|
||||
if (dbAlbum.assetCount == 0) {
|
||||
_log.fine(
|
||||
"Device album ${deviceAlbum.name} is empty. Adding assets to DB.",
|
||||
);
|
||||
await _localAlbumRepository.upsert(
|
||||
updatedDeviceAlbum,
|
||||
toUpsert: assetsInDevice,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
assert(assetsInDb.isSortedBy((a) => a.id));
|
||||
assetsInDevice.sort((a, b) => a.id.compareTo(b.id));
|
||||
|
||||
final assetsToUpsert = <LocalAsset>[];
|
||||
final assetsToDelete = <String>[];
|
||||
|
||||
diffSortedListsSync(
|
||||
assetsInDb,
|
||||
assetsInDevice,
|
||||
compare: (a, b) => a.id.compareTo(b.id),
|
||||
both: (dbAsset, deviceAsset) {
|
||||
// Custom comparison to check if the asset has been modified without
|
||||
// comparing the checksum
|
||||
if (!_assetsEqual(dbAsset, deviceAsset)) {
|
||||
assetsToUpsert.add(deviceAsset);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
onlyFirst: (dbAsset) => assetsToDelete.add(dbAsset.id),
|
||||
onlySecond: (deviceAsset) => assetsToUpsert.add(deviceAsset),
|
||||
);
|
||||
|
||||
_log.fine(
|
||||
"Syncing ${deviceAlbum.name}. ${assetsToUpsert.length} assets to add/update and ${assetsToDelete.length} assets to delete",
|
||||
);
|
||||
|
||||
if (assetsToUpsert.isEmpty && assetsToDelete.isEmpty) {
|
||||
_log.fine(
|
||||
"No asset changes detected in album ${deviceAlbum.name}. Updating metadata.",
|
||||
);
|
||||
_localAlbumRepository.upsert(updatedDeviceAlbum);
|
||||
return true;
|
||||
}
|
||||
|
||||
await _localAlbumRepository.upsert(
|
||||
updatedDeviceAlbum,
|
||||
toUpsert: assetsToUpsert,
|
||||
toDelete: assetsToDelete,
|
||||
);
|
||||
|
||||
return true;
|
||||
} catch (e, s) {
|
||||
_log.warning("Error on full syncing local album: ${dbAlbum.name}", e, s);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _assetsEqual(LocalAsset a, LocalAsset b) {
|
||||
return a.updatedAt.isAtSameMomentAs(b.updatedAt) &&
|
||||
a.createdAt.isAtSameMomentAs(b.createdAt) &&
|
||||
a.width == b.width &&
|
||||
a.height == b.height &&
|
||||
a.durationInSeconds == b.durationInSeconds;
|
||||
}
|
||||
|
||||
bool _albumsEqual(LocalAlbum a, LocalAlbum b) {
|
||||
return a.name == b.name &&
|
||||
a.assetCount == b.assetCount &&
|
||||
a.updatedAt.isAtSameMomentAs(b.updatedAt);
|
||||
}
|
||||
}
|
||||
|
||||
extension on Iterable<PlatformAlbum> {
|
||||
List<LocalAlbum> toLocalAlbums() {
|
||||
return map(
|
||||
(e) => LocalAlbum(
|
||||
id: e.id,
|
||||
name: e.name,
|
||||
updatedAt: e.updatedAt == null
|
||||
? DateTime.now()
|
||||
: DateTime.fromMillisecondsSinceEpoch(e.updatedAt! * 1000),
|
||||
assetCount: e.assetCount,
|
||||
),
|
||||
).toList();
|
||||
}
|
||||
}
|
||||
|
||||
extension on Iterable<PlatformAsset> {
|
||||
List<LocalAsset> toLocalAssets() {
|
||||
return map(
|
||||
(e) => LocalAsset(
|
||||
id: e.id,
|
||||
name: e.name,
|
||||
checksum: null,
|
||||
type: AssetType.values.elementAtOrNull(e.type) ?? AssetType.other,
|
||||
createdAt: e.createdAt == null
|
||||
? DateTime.now()
|
||||
: DateTime.fromMillisecondsSinceEpoch(e.createdAt! * 1000),
|
||||
updatedAt: e.updatedAt == null
|
||||
? DateTime.now()
|
||||
: DateTime.fromMillisecondsSinceEpoch(e.updatedAt! * 1000),
|
||||
width: e.width,
|
||||
height: e.height,
|
||||
durationInSeconds: e.durationInSeconds,
|
||||
),
|
||||
).toList();
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,11 @@ import 'package:immich_mobile/domain/models/log.model.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
/// Service responsible for handling application logging.
|
||||
///
|
||||
/// It listens to Dart's [Logger.root], buffers logs in memory (optionally),
|
||||
/// writes them to a persistent [ILogRepository], and manages log levels
|
||||
/// via [IStoreRepository]
|
||||
class LogService {
|
||||
final ILogRepository _logRepository;
|
||||
final IStoreRepository _storeRepository;
|
||||
@@ -18,19 +23,11 @@ class LogService {
|
||||
/// This is useful when logging in quick succession, as it increases performance
|
||||
/// and reduces NAND wear. However, it may cause the logs to be lost in case of a crash / in isolates.
|
||||
final bool _shouldBuffer;
|
||||
|
||||
Timer? _flushTimer;
|
||||
|
||||
late final StreamSubscription<LogRecord> _logSubscription;
|
||||
|
||||
LogService._(
|
||||
this._logRepository,
|
||||
this._storeRepository,
|
||||
this._shouldBuffer,
|
||||
) {
|
||||
// Listen to log messages and write them to the database
|
||||
_logSubscription = Logger.root.onRecord.listen(_writeLogToDatabase);
|
||||
}
|
||||
|
||||
static LogService? _instance;
|
||||
static LogService get I {
|
||||
if (_instance == null) {
|
||||
@@ -44,10 +41,7 @@ class LogService {
|
||||
required IStoreRepository storeRepository,
|
||||
bool shouldBuffer = true,
|
||||
}) async {
|
||||
if (_instance != null) {
|
||||
return _instance!;
|
||||
}
|
||||
_instance = await create(
|
||||
_instance ??= await create(
|
||||
logRepository: logRepository,
|
||||
storeRepository: storeRepository,
|
||||
shouldBuffer: shouldBuffer,
|
||||
@@ -61,55 +55,28 @@ class LogService {
|
||||
bool shouldBuffer = true,
|
||||
}) async {
|
||||
final instance = LogService._(logRepository, storeRepository, shouldBuffer);
|
||||
// Truncate logs to 250
|
||||
await logRepository.truncate(limit: kLogTruncateLimit);
|
||||
// Get log level from store
|
||||
final level = await instance._storeRepository.tryGet(StoreKey.logLevel);
|
||||
if (level != null) {
|
||||
Logger.root.level = Level.LEVELS.elementAtOrNull(level) ?? Level.INFO;
|
||||
}
|
||||
final level = await instance._storeRepository.tryGet(StoreKey.logLevel) ??
|
||||
LogLevel.info.index;
|
||||
Logger.root.level = Level.LEVELS.elementAtOrNull(level) ?? Level.INFO;
|
||||
return instance;
|
||||
}
|
||||
|
||||
Future<void> setlogLevel(LogLevel level) async {
|
||||
await _storeRepository.insert(StoreKey.logLevel, level.index);
|
||||
Logger.root.level = level.toLevel();
|
||||
LogService._(
|
||||
this._logRepository,
|
||||
this._storeRepository,
|
||||
this._shouldBuffer,
|
||||
) {
|
||||
_logSubscription = Logger.root.onRecord.listen(_handleLogRecord);
|
||||
}
|
||||
|
||||
Future<List<LogMessage>> getMessages() async {
|
||||
final logsFromDb = await _logRepository.getAll();
|
||||
if (_msgBuffer.isNotEmpty) {
|
||||
return [..._msgBuffer.reversed, ...logsFromDb];
|
||||
}
|
||||
return logsFromDb;
|
||||
}
|
||||
|
||||
Future<void> clearLogs() async {
|
||||
_flushTimer?.cancel();
|
||||
_flushTimer = null;
|
||||
_msgBuffer.clear();
|
||||
await _logRepository.deleteAll();
|
||||
}
|
||||
|
||||
/// Flush pending log messages to persistent storage
|
||||
void flush() {
|
||||
if (_flushTimer == null) {
|
||||
return;
|
||||
}
|
||||
_flushTimer!.cancel();
|
||||
// TODO: Rename enable this after moving to sqlite - #16504
|
||||
// await _flushBufferToDatabase();
|
||||
}
|
||||
|
||||
Future<void> dispose() {
|
||||
_flushTimer?.cancel();
|
||||
_logSubscription.cancel();
|
||||
return _flushBufferToDatabase();
|
||||
}
|
||||
|
||||
void _writeLogToDatabase(LogRecord r) {
|
||||
void _handleLogRecord(LogRecord r) {
|
||||
if (kDebugMode) {
|
||||
debugPrint('[${r.level.name}] [${r.time}] ${r.message}');
|
||||
debugPrint(
|
||||
'[${r.level.name}] [${r.time}] [${r.loggerName}] ${r.message}'
|
||||
'${r.error == null ? '' : '\nError: ${r.error}'}'
|
||||
'${r.stackTrace == null ? '' : '\nStack: ${r.stackTrace}'}',
|
||||
);
|
||||
}
|
||||
|
||||
final record = LogMessage(
|
||||
@@ -125,14 +92,44 @@ class LogService {
|
||||
_msgBuffer.add(record);
|
||||
_flushTimer ??= Timer(
|
||||
const Duration(seconds: 5),
|
||||
() => unawaited(_flushBufferToDatabase()),
|
||||
() => unawaited(flushBuffer()),
|
||||
);
|
||||
} else {
|
||||
unawaited(_logRepository.insert(record));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _flushBufferToDatabase() async {
|
||||
Future<void> setLogLevel(LogLevel level) async {
|
||||
await _storeRepository.insert(StoreKey.logLevel, level.index);
|
||||
Logger.root.level = level.toLevel();
|
||||
}
|
||||
|
||||
Future<List<LogMessage>> getMessages() async {
|
||||
final logsFromDb = await _logRepository.getAll();
|
||||
return [..._msgBuffer.reversed, ...logsFromDb];
|
||||
}
|
||||
|
||||
Future<void> clearLogs() async {
|
||||
_flushTimer?.cancel();
|
||||
_flushTimer = null;
|
||||
_msgBuffer.clear();
|
||||
await _logRepository.deleteAll();
|
||||
}
|
||||
|
||||
void flush() {
|
||||
_flushTimer?.cancel();
|
||||
// TODO: Rename enable this after moving to sqlite - #16504
|
||||
// await _flushBufferToDatabase();
|
||||
}
|
||||
|
||||
Future<void> dispose() {
|
||||
_flushTimer?.cancel();
|
||||
_logSubscription.cancel();
|
||||
return flushBuffer();
|
||||
}
|
||||
|
||||
// TOOD: Move this to private once Isar is removed
|
||||
Future<void> flushBuffer() async {
|
||||
_flushTimer = null;
|
||||
final buffer = [..._msgBuffer];
|
||||
_msgBuffer.clear();
|
||||
|
||||
@@ -3,15 +3,17 @@ import 'dart:async';
|
||||
import 'package:immich_mobile/domain/interfaces/store.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
|
||||
/// Provides access to a persistent key-value store with an in-memory cache.
|
||||
/// Listens for repository changes to keep the cache updated.
|
||||
class StoreService {
|
||||
final IStoreRepository _storeRepository;
|
||||
|
||||
final Map<int, dynamic> _cache = {};
|
||||
late final StreamSubscription<StoreUpdateEvent> _storeUpdateSubscription;
|
||||
/// In-memory cache. Keys are [StoreKey.id]
|
||||
final Map<int, Object?> _cache = {};
|
||||
late final StreamSubscription<StoreDto> _storeUpdateSubscription;
|
||||
|
||||
StoreService._({
|
||||
required IStoreRepository storeRepository,
|
||||
}) : _storeRepository = storeRepository;
|
||||
StoreService._({required IStoreRepository storeRepository})
|
||||
: _storeRepository = storeRepository;
|
||||
|
||||
// TODO: Temporary typedef to make minimal changes. Remove this and make the presentation layer access store through a provider
|
||||
static StoreService? _instance;
|
||||
@@ -23,7 +25,6 @@ class StoreService {
|
||||
}
|
||||
|
||||
// TODO: Replace the implementation with the one from create after removing the typedef
|
||||
/// Initializes the store with the given [storeRepository]
|
||||
static Future<StoreService> init({
|
||||
required IStoreRepository storeRepository,
|
||||
}) async {
|
||||
@@ -31,7 +32,6 @@ class StoreService {
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
/// Initializes the store with the given [storeRepository]
|
||||
static Future<StoreService> create({
|
||||
required IStoreRepository storeRepository,
|
||||
}) async {
|
||||
@@ -41,16 +41,14 @@ class StoreService {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// Fills the cache with the values from the DB
|
||||
Future<void> _populateCache() async {
|
||||
for (StoreKey key in StoreKey.values) {
|
||||
final storeValue = await _storeRepository.tryGet(key);
|
||||
_cache[key.id] = storeValue;
|
||||
final storeValues = await _storeRepository.getAll();
|
||||
for (StoreDto storeValue in storeValues) {
|
||||
_cache[storeValue.key.id] = storeValue.value;
|
||||
}
|
||||
}
|
||||
|
||||
/// Listens for changes in the DB and updates the cache
|
||||
StreamSubscription<StoreUpdateEvent> _listenForChange() =>
|
||||
StreamSubscription<StoreDto> _listenForChange() =>
|
||||
_storeRepository.watchAll().listen((event) {
|
||||
_cache[event.key.id] = event.value;
|
||||
});
|
||||
@@ -61,11 +59,11 @@ class StoreService {
|
||||
_cache.clear();
|
||||
}
|
||||
|
||||
/// Returns the stored value for the given key (possibly null)
|
||||
T? tryGet<T>(StoreKey<T> key) => _cache[key.id];
|
||||
/// Returns the cached value for [key], or `null`
|
||||
T? tryGet<T>(StoreKey<T> key) => _cache[key.id] as T?;
|
||||
|
||||
/// Returns the stored value for the given key or if null the [defaultValue]
|
||||
/// Throws a [StoreKeyNotFoundException] if both are null
|
||||
/// Returns the stored value for [key] or [defaultValue].
|
||||
/// Throws [StoreKeyNotFoundException] if value and [defaultValue] are null.
|
||||
T get<T>(StoreKey<T> key, [T? defaultValue]) {
|
||||
final value = tryGet(key) ?? defaultValue;
|
||||
if (value == null) {
|
||||
@@ -74,23 +72,23 @@ class StoreService {
|
||||
return value;
|
||||
}
|
||||
|
||||
/// Asynchronously stores the value in the Store
|
||||
/// Stores the [value] for the [key]. Skips write if value hasn't changed.
|
||||
Future<void> put<U extends StoreKey<T>, T>(U key, T value) async {
|
||||
if (_cache[key.id] == value) return;
|
||||
await _storeRepository.insert(key, value);
|
||||
_cache[key.id] = value;
|
||||
}
|
||||
|
||||
/// Watches a specific key for changes
|
||||
/// Returns a stream that emits the value for [key] on change.
|
||||
Stream<T?> watch<T>(StoreKey<T> key) => _storeRepository.watch(key);
|
||||
|
||||
/// Removes the value asynchronously from the Store
|
||||
/// Removes the value for [key]
|
||||
Future<void> delete<T>(StoreKey<T> key) async {
|
||||
await _storeRepository.delete(key);
|
||||
_cache.remove(key.id);
|
||||
}
|
||||
|
||||
/// Clears all values from this store (cache and DB)
|
||||
/// Clears all values from thw store (cache and DB)
|
||||
Future<void> clear() async {
|
||||
await _storeRepository.deleteAll();
|
||||
_cache.clear();
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// ignore_for_file: avoid-passing-async-when-sync-expected
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:immich_mobile/domain/interfaces/sync_api.interface.dart';
|
||||
@@ -59,11 +57,9 @@ class SyncStreamService {
|
||||
|
||||
Future<void> _handleSyncData(
|
||||
SyncEntityType type,
|
||||
// ignore: avoid-dynamic
|
||||
Iterable<dynamic> data,
|
||||
Iterable<Object> data,
|
||||
) async {
|
||||
_logger.fine("Processing sync data for $type of length ${data.length}");
|
||||
// ignore: prefer-switch-expression
|
||||
switch (type) {
|
||||
case SyncEntityType.userV1:
|
||||
return _syncStreamRepository.updateUsersV1(data.cast());
|
||||
@@ -85,6 +81,18 @@ class SyncStreamService {
|
||||
return _syncStreamRepository.deletePartnerAssetsV1(data.cast());
|
||||
case SyncEntityType.partnerAssetExifV1:
|
||||
return _syncStreamRepository.updatePartnerAssetsExifV1(data.cast());
|
||||
case SyncEntityType.albumV1:
|
||||
return _syncStreamRepository.updateAlbumsV1(data.cast());
|
||||
case SyncEntityType.albumDeleteV1:
|
||||
return _syncStreamRepository.deleteAlbumsV1(data.cast());
|
||||
// case SyncEntityType.albumAssetV1:
|
||||
// return _syncStreamRepository.updateAlbumAssetsV1(data.cast());
|
||||
// case SyncEntityType.albumAssetDeleteV1:
|
||||
// return _syncStreamRepository.deleteAlbumAssetsV1(data.cast());
|
||||
case SyncEntityType.albumUserV1:
|
||||
return _syncStreamRepository.updateAlbumUsersV1(data.cast());
|
||||
case SyncEntityType.albumUserDeleteV1:
|
||||
return _syncStreamRepository.deleteAlbumUsersV1(data.cast());
|
||||
default:
|
||||
_logger.warning("Unknown sync data type: $type");
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// ignore_for_file: avoid-passing-async-when-sync-expected
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:immich_mobile/providers/infrastructure/sync_stream.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/sync.provider.dart';
|
||||
import 'package:immich_mobile/utils/isolate.dart';
|
||||
import 'package:worker_manager/worker_manager.dart';
|
||||
|
||||
class BackgroundSyncManager {
|
||||
Cancelable<void>? _syncTask;
|
||||
Cancelable<void>? _deviceAlbumSyncTask;
|
||||
Cancelable<void>? _hashTask;
|
||||
|
||||
BackgroundSyncManager();
|
||||
|
||||
@@ -23,7 +23,44 @@ class BackgroundSyncManager {
|
||||
return Future.wait(futures);
|
||||
}
|
||||
|
||||
Future<void> sync() {
|
||||
// No need to cancel the task, as it can also be run when the user logs out
|
||||
Future<void> syncLocal({bool full = false}) {
|
||||
if (_deviceAlbumSyncTask != null) {
|
||||
return _deviceAlbumSyncTask!.future;
|
||||
}
|
||||
|
||||
// We use a ternary operator to avoid [_deviceAlbumSyncTask] from being
|
||||
// captured by the closure passed to [runInIsolateGentle].
|
||||
_deviceAlbumSyncTask = full
|
||||
? runInIsolateGentle(
|
||||
computation: (ref) =>
|
||||
ref.read(localSyncServiceProvider).sync(full: true),
|
||||
)
|
||||
: runInIsolateGentle(
|
||||
computation: (ref) =>
|
||||
ref.read(localSyncServiceProvider).sync(full: false),
|
||||
);
|
||||
|
||||
return _deviceAlbumSyncTask!.whenComplete(() {
|
||||
_deviceAlbumSyncTask = null;
|
||||
});
|
||||
}
|
||||
|
||||
// No need to cancel the task, as it can also be run when the user logs out
|
||||
Future<void> hashAssets() {
|
||||
if (_hashTask != null) {
|
||||
return _hashTask!.future;
|
||||
}
|
||||
|
||||
_hashTask = runInIsolateGentle(
|
||||
computation: (ref) => ref.read(hashServiceProvider).hashAssets(),
|
||||
);
|
||||
return _hashTask!.whenComplete(() {
|
||||
_hashTask = null;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> syncRemote() {
|
||||
if (_syncTask != null) {
|
||||
return _syncTask!.future;
|
||||
}
|
||||
@@ -31,9 +68,8 @@ class BackgroundSyncManager {
|
||||
_syncTask = runInIsolateGentle(
|
||||
computation: (ref) => ref.read(syncStreamServiceProvider).sync(),
|
||||
);
|
||||
_syncTask!.whenComplete(() {
|
||||
return _syncTask!.whenComplete(() {
|
||||
_syncTask = null;
|
||||
});
|
||||
return _syncTask!.future;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ class Album {
|
||||
required this.name,
|
||||
required this.createdAt,
|
||||
required this.modifiedAt,
|
||||
this.description,
|
||||
this.startDate,
|
||||
this.endDate,
|
||||
this.lastModifiedAssetTimestamp,
|
||||
@@ -34,6 +35,7 @@ class Album {
|
||||
@Index(unique: false, replace: false, type: IndexType.hash)
|
||||
String? localId;
|
||||
String name;
|
||||
String? description;
|
||||
DateTime createdAt;
|
||||
DateTime modifiedAt;
|
||||
DateTime? startDate;
|
||||
@@ -108,6 +110,7 @@ class Album {
|
||||
remoteId == other.remoteId &&
|
||||
localId == other.localId &&
|
||||
name == other.name &&
|
||||
description == other.description &&
|
||||
createdAt.isAtSameMomentAs(other.createdAt) &&
|
||||
modifiedAt.isAtSameMomentAs(other.modifiedAt) &&
|
||||
isAtSameMomentAs(startDate, other.startDate) &&
|
||||
@@ -135,6 +138,7 @@ class Album {
|
||||
modifiedAt.hashCode ^
|
||||
startDate.hashCode ^
|
||||
endDate.hashCode ^
|
||||
description.hashCode ^
|
||||
lastModifiedAssetTimestamp.hashCode ^
|
||||
shared.hashCode ^
|
||||
activityEnabled.hashCode ^
|
||||
@@ -150,6 +154,7 @@ class Album {
|
||||
name: dto.albumName,
|
||||
createdAt: dto.createdAt,
|
||||
modifiedAt: dto.updatedAt,
|
||||
description: dto.description,
|
||||
lastModifiedAssetTimestamp: dto.lastModifiedAssetTimestamp,
|
||||
shared: dto.shared,
|
||||
startDate: dto.startDate,
|
||||
@@ -184,7 +189,8 @@ class Album {
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => name;
|
||||
String toString() =>
|
||||
'remoteId: $remoteId name: $name description: $description';
|
||||
}
|
||||
|
||||
extension AssetsHelper on IsarCollection<Album> {
|
||||
|
||||
271
mobile/lib/entities/album.entity.g.dart
generated
271
mobile/lib/entities/album.entity.g.dart
generated
@@ -27,49 +27,54 @@ const AlbumSchema = CollectionSchema(
|
||||
name: r'createdAt',
|
||||
type: IsarType.dateTime,
|
||||
),
|
||||
r'endDate': PropertySchema(
|
||||
r'description': PropertySchema(
|
||||
id: 2,
|
||||
name: r'description',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'endDate': PropertySchema(
|
||||
id: 3,
|
||||
name: r'endDate',
|
||||
type: IsarType.dateTime,
|
||||
),
|
||||
r'lastModifiedAssetTimestamp': PropertySchema(
|
||||
id: 3,
|
||||
id: 4,
|
||||
name: r'lastModifiedAssetTimestamp',
|
||||
type: IsarType.dateTime,
|
||||
),
|
||||
r'localId': PropertySchema(
|
||||
id: 4,
|
||||
id: 5,
|
||||
name: r'localId',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'modifiedAt': PropertySchema(
|
||||
id: 5,
|
||||
id: 6,
|
||||
name: r'modifiedAt',
|
||||
type: IsarType.dateTime,
|
||||
),
|
||||
r'name': PropertySchema(
|
||||
id: 6,
|
||||
id: 7,
|
||||
name: r'name',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'remoteId': PropertySchema(
|
||||
id: 7,
|
||||
id: 8,
|
||||
name: r'remoteId',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'shared': PropertySchema(
|
||||
id: 8,
|
||||
id: 9,
|
||||
name: r'shared',
|
||||
type: IsarType.bool,
|
||||
),
|
||||
r'sortOrder': PropertySchema(
|
||||
id: 9,
|
||||
id: 10,
|
||||
name: r'sortOrder',
|
||||
type: IsarType.byte,
|
||||
enumMap: _AlbumsortOrderEnumValueMap,
|
||||
),
|
||||
r'startDate': PropertySchema(
|
||||
id: 10,
|
||||
id: 11,
|
||||
name: r'startDate',
|
||||
type: IsarType.dateTime,
|
||||
)
|
||||
@@ -146,6 +151,12 @@ int _albumEstimateSize(
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
var bytesCount = offsets.last;
|
||||
{
|
||||
final value = object.description;
|
||||
if (value != null) {
|
||||
bytesCount += 3 + value.length * 3;
|
||||
}
|
||||
}
|
||||
{
|
||||
final value = object.localId;
|
||||
if (value != null) {
|
||||
@@ -170,15 +181,16 @@ void _albumSerialize(
|
||||
) {
|
||||
writer.writeBool(offsets[0], object.activityEnabled);
|
||||
writer.writeDateTime(offsets[1], object.createdAt);
|
||||
writer.writeDateTime(offsets[2], object.endDate);
|
||||
writer.writeDateTime(offsets[3], object.lastModifiedAssetTimestamp);
|
||||
writer.writeString(offsets[4], object.localId);
|
||||
writer.writeDateTime(offsets[5], object.modifiedAt);
|
||||
writer.writeString(offsets[6], object.name);
|
||||
writer.writeString(offsets[7], object.remoteId);
|
||||
writer.writeBool(offsets[8], object.shared);
|
||||
writer.writeByte(offsets[9], object.sortOrder.index);
|
||||
writer.writeDateTime(offsets[10], object.startDate);
|
||||
writer.writeString(offsets[2], object.description);
|
||||
writer.writeDateTime(offsets[3], object.endDate);
|
||||
writer.writeDateTime(offsets[4], object.lastModifiedAssetTimestamp);
|
||||
writer.writeString(offsets[5], object.localId);
|
||||
writer.writeDateTime(offsets[6], object.modifiedAt);
|
||||
writer.writeString(offsets[7], object.name);
|
||||
writer.writeString(offsets[8], object.remoteId);
|
||||
writer.writeBool(offsets[9], object.shared);
|
||||
writer.writeByte(offsets[10], object.sortOrder.index);
|
||||
writer.writeDateTime(offsets[11], object.startDate);
|
||||
}
|
||||
|
||||
Album _albumDeserialize(
|
||||
@@ -190,16 +202,18 @@ Album _albumDeserialize(
|
||||
final object = Album(
|
||||
activityEnabled: reader.readBool(offsets[0]),
|
||||
createdAt: reader.readDateTime(offsets[1]),
|
||||
endDate: reader.readDateTimeOrNull(offsets[2]),
|
||||
lastModifiedAssetTimestamp: reader.readDateTimeOrNull(offsets[3]),
|
||||
localId: reader.readStringOrNull(offsets[4]),
|
||||
modifiedAt: reader.readDateTime(offsets[5]),
|
||||
name: reader.readString(offsets[6]),
|
||||
remoteId: reader.readStringOrNull(offsets[7]),
|
||||
shared: reader.readBool(offsets[8]),
|
||||
sortOrder: _AlbumsortOrderValueEnumMap[reader.readByteOrNull(offsets[9])] ??
|
||||
SortOrder.desc,
|
||||
startDate: reader.readDateTimeOrNull(offsets[10]),
|
||||
description: reader.readStringOrNull(offsets[2]),
|
||||
endDate: reader.readDateTimeOrNull(offsets[3]),
|
||||
lastModifiedAssetTimestamp: reader.readDateTimeOrNull(offsets[4]),
|
||||
localId: reader.readStringOrNull(offsets[5]),
|
||||
modifiedAt: reader.readDateTime(offsets[6]),
|
||||
name: reader.readString(offsets[7]),
|
||||
remoteId: reader.readStringOrNull(offsets[8]),
|
||||
shared: reader.readBool(offsets[9]),
|
||||
sortOrder:
|
||||
_AlbumsortOrderValueEnumMap[reader.readByteOrNull(offsets[10])] ??
|
||||
SortOrder.desc,
|
||||
startDate: reader.readDateTimeOrNull(offsets[11]),
|
||||
);
|
||||
object.id = id;
|
||||
return object;
|
||||
@@ -217,23 +231,25 @@ P _albumDeserializeProp<P>(
|
||||
case 1:
|
||||
return (reader.readDateTime(offset)) as P;
|
||||
case 2:
|
||||
return (reader.readDateTimeOrNull(offset)) as P;
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 3:
|
||||
return (reader.readDateTimeOrNull(offset)) as P;
|
||||
case 4:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
return (reader.readDateTimeOrNull(offset)) as P;
|
||||
case 5:
|
||||
return (reader.readDateTime(offset)) as P;
|
||||
case 6:
|
||||
return (reader.readString(offset)) as P;
|
||||
case 7:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 6:
|
||||
return (reader.readDateTime(offset)) as P;
|
||||
case 7:
|
||||
return (reader.readString(offset)) as P;
|
||||
case 8:
|
||||
return (reader.readBool(offset)) as P;
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 9:
|
||||
return (reader.readBool(offset)) as P;
|
||||
case 10:
|
||||
return (_AlbumsortOrderValueEnumMap[reader.readByteOrNull(offset)] ??
|
||||
SortOrder.desc) as P;
|
||||
case 10:
|
||||
case 11:
|
||||
return (reader.readDateTimeOrNull(offset)) as P;
|
||||
default:
|
||||
throw IsarError('Unknown property with id $propertyId');
|
||||
@@ -535,6 +551,152 @@ extension AlbumQueryFilter on QueryBuilder<Album, Album, QFilterCondition> {
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNull(
|
||||
property: r'description',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNotNull(
|
||||
property: r'description',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionEqualTo(
|
||||
String? value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'description',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionGreaterThan(
|
||||
String? value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'description',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionLessThan(
|
||||
String? value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'description',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionBetween(
|
||||
String? lower,
|
||||
String? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.between(
|
||||
property: r'description',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionStartsWith(
|
||||
String value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.startsWith(
|
||||
property: r'description',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionEndsWith(
|
||||
String value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.endsWith(
|
||||
property: r'description',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionContains(
|
||||
String value,
|
||||
{bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.contains(
|
||||
property: r'description',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionMatches(
|
||||
String pattern,
|
||||
{bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.matches(
|
||||
property: r'description',
|
||||
wildcard: pattern,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionIsEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'description',
|
||||
value: '',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> descriptionIsNotEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
property: r'description',
|
||||
value: '',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterFilterCondition> endDateIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNull(
|
||||
@@ -1502,6 +1664,18 @@ extension AlbumQuerySortBy on QueryBuilder<Album, Album, QSortBy> {
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterSortBy> sortByDescription() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'description', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterSortBy> sortByDescriptionDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'description', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterSortBy> sortByEndDate() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'endDate', Sort.asc);
|
||||
@@ -1637,6 +1811,18 @@ extension AlbumQuerySortThenBy on QueryBuilder<Album, Album, QSortThenBy> {
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterSortBy> thenByDescription() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'description', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterSortBy> thenByDescriptionDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'description', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QAfterSortBy> thenByEndDate() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'endDate', Sort.asc);
|
||||
@@ -1772,6 +1958,13 @@ extension AlbumQueryWhereDistinct on QueryBuilder<Album, Album, QDistinct> {
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QDistinct> distinctByDescription(
|
||||
{bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'description', caseSensitive: caseSensitive);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, Album, QDistinct> distinctByEndDate() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'endDate');
|
||||
@@ -1849,6 +2042,12 @@ extension AlbumQueryProperty on QueryBuilder<Album, Album, QQueryProperty> {
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, String?, QQueryOperations> descriptionProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'description');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<Album, DateTime?, QQueryOperations> endDateProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'endDate');
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:uuid/parsing.dart';
|
||||
|
||||
extension StringExtension on String {
|
||||
String capitalize() {
|
||||
return split(" ")
|
||||
@@ -33,8 +29,3 @@ extension DurationExtension on String {
|
||||
return int.parse(this);
|
||||
}
|
||||
}
|
||||
|
||||
extension UUIDExtension on String {
|
||||
Uint8List toUuidByte({bool shouldValidate = false}) =>
|
||||
UuidParsing.parseAsByteList(this, validate: shouldValidate);
|
||||
}
|
||||
|
||||
20
mobile/lib/infrastructure/entities/album_user.entity.dart
Normal file
20
mobile/lib/infrastructure/entities/album_user.entity.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:immich_mobile/domain/models/album_user.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
||||
|
||||
class AlbumUserEntity extends Table with DriftDefaultsMixin {
|
||||
const AlbumUserEntity();
|
||||
|
||||
TextColumn get albumId =>
|
||||
text().references(RemoteAlbumEntity, #id, onDelete: KeyAction.cascade)();
|
||||
|
||||
TextColumn get userId =>
|
||||
text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
|
||||
|
||||
IntColumn get role => intEnum<AlbumUserRole>()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {albumId, userId};
|
||||
}
|
||||
602
mobile/lib/infrastructure/entities/album_user.entity.drift.dart
generated
Normal file
602
mobile/lib/infrastructure/entities/album_user.entity.drift.dart
generated
Normal file
@@ -0,0 +1,602 @@
|
||||
// dart format width=80
|
||||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:immich_mobile/infrastructure/entities/album_user.entity.drift.dart'
|
||||
as i1;
|
||||
import 'package:immich_mobile/domain/models/album_user.model.dart' as i2;
|
||||
import 'package:immich_mobile/infrastructure/entities/album_user.entity.dart'
|
||||
as i3;
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
|
||||
as i4;
|
||||
import 'package:drift/internal/modular.dart' as i5;
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
|
||||
as i6;
|
||||
|
||||
typedef $$AlbumUserEntityTableCreateCompanionBuilder
|
||||
= i1.AlbumUserEntityCompanion Function({
|
||||
required String albumId,
|
||||
required String userId,
|
||||
required i2.AlbumUserRole role,
|
||||
});
|
||||
typedef $$AlbumUserEntityTableUpdateCompanionBuilder
|
||||
= i1.AlbumUserEntityCompanion Function({
|
||||
i0.Value<String> albumId,
|
||||
i0.Value<String> userId,
|
||||
i0.Value<i2.AlbumUserRole> role,
|
||||
});
|
||||
|
||||
final class $$AlbumUserEntityTableReferences extends i0.BaseReferences<
|
||||
i0.GeneratedDatabase, i1.$AlbumUserEntityTable, i1.AlbumUserEntityData> {
|
||||
$$AlbumUserEntityTableReferences(
|
||||
super.$_db, super.$_table, super.$_typedResult);
|
||||
|
||||
static i4.$RemoteAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) =>
|
||||
i5.ReadDatabaseContainer(db)
|
||||
.resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity')
|
||||
.createAlias(i0.$_aliasNameGenerator(
|
||||
i5.ReadDatabaseContainer(db)
|
||||
.resultSet<i1.$AlbumUserEntityTable>('album_user_entity')
|
||||
.albumId,
|
||||
i5.ReadDatabaseContainer(db)
|
||||
.resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity')
|
||||
.id));
|
||||
|
||||
i4.$$RemoteAlbumEntityTableProcessedTableManager get albumId {
|
||||
final $_column = $_itemColumn<String>('album_id')!;
|
||||
|
||||
final manager = i4
|
||||
.$$RemoteAlbumEntityTableTableManager(
|
||||
$_db,
|
||||
i5.ReadDatabaseContainer($_db)
|
||||
.resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'))
|
||||
.filter((f) => f.id.sqlEquals($_column));
|
||||
final item = $_typedResult.readTableOrNull(_albumIdTable($_db));
|
||||
if (item == null) return manager;
|
||||
return i0.ProcessedTableManager(
|
||||
manager.$state.copyWith(prefetchedData: [item]));
|
||||
}
|
||||
|
||||
static i6.$UserEntityTable _userIdTable(i0.GeneratedDatabase db) =>
|
||||
i5.ReadDatabaseContainer(db)
|
||||
.resultSet<i6.$UserEntityTable>('user_entity')
|
||||
.createAlias(i0.$_aliasNameGenerator(
|
||||
i5.ReadDatabaseContainer(db)
|
||||
.resultSet<i1.$AlbumUserEntityTable>('album_user_entity')
|
||||
.userId,
|
||||
i5.ReadDatabaseContainer(db)
|
||||
.resultSet<i6.$UserEntityTable>('user_entity')
|
||||
.id));
|
||||
|
||||
i6.$$UserEntityTableProcessedTableManager get userId {
|
||||
final $_column = $_itemColumn<String>('user_id')!;
|
||||
|
||||
final manager = i6
|
||||
.$$UserEntityTableTableManager(
|
||||
$_db,
|
||||
i5.ReadDatabaseContainer($_db)
|
||||
.resultSet<i6.$UserEntityTable>('user_entity'))
|
||||
.filter((f) => f.id.sqlEquals($_column));
|
||||
final item = $_typedResult.readTableOrNull(_userIdTable($_db));
|
||||
if (item == null) return manager;
|
||||
return i0.ProcessedTableManager(
|
||||
manager.$state.copyWith(prefetchedData: [item]));
|
||||
}
|
||||
}
|
||||
|
||||
class $$AlbumUserEntityTableFilterComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.$AlbumUserEntityTable> {
|
||||
$$AlbumUserEntityTableFilterComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.ColumnWithTypeConverterFilters<i2.AlbumUserRole, i2.AlbumUserRole, int>
|
||||
get role => $composableBuilder(
|
||||
column: $table.role,
|
||||
builder: (column) => i0.ColumnWithTypeConverterFilters(column));
|
||||
|
||||
i4.$$RemoteAlbumEntityTableFilterComposer get albumId {
|
||||
final i4.$$RemoteAlbumEntityTableFilterComposer composer = $composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.albumId,
|
||||
referencedTable: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i4.$$RemoteAlbumEntityTableFilterComposer(
|
||||
$db: $db,
|
||||
$table: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
|
||||
i6.$$UserEntityTableFilterComposer get userId {
|
||||
final i6.$$UserEntityTableFilterComposer composer = $composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.userId,
|
||||
referencedTable: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i6.$UserEntityTable>('user_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i6.$$UserEntityTableFilterComposer(
|
||||
$db: $db,
|
||||
$table: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i6.$UserEntityTable>('user_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
}
|
||||
|
||||
class $$AlbumUserEntityTableOrderingComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.$AlbumUserEntityTable> {
|
||||
$$AlbumUserEntityTableOrderingComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.ColumnOrderings<int> get role => $composableBuilder(
|
||||
column: $table.role, builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i4.$$RemoteAlbumEntityTableOrderingComposer get albumId {
|
||||
final i4.$$RemoteAlbumEntityTableOrderingComposer composer =
|
||||
$composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.albumId,
|
||||
referencedTable: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i4.$$RemoteAlbumEntityTableOrderingComposer(
|
||||
$db: $db,
|
||||
$table: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i4.$RemoteAlbumEntityTable>(
|
||||
'remote_album_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
|
||||
i6.$$UserEntityTableOrderingComposer get userId {
|
||||
final i6.$$UserEntityTableOrderingComposer composer = $composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.userId,
|
||||
referencedTable: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i6.$UserEntityTable>('user_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i6.$$UserEntityTableOrderingComposer(
|
||||
$db: $db,
|
||||
$table: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i6.$UserEntityTable>('user_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
}
|
||||
|
||||
class $$AlbumUserEntityTableAnnotationComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.$AlbumUserEntityTable> {
|
||||
$$AlbumUserEntityTableAnnotationComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.GeneratedColumnWithTypeConverter<i2.AlbumUserRole, int> get role =>
|
||||
$composableBuilder(column: $table.role, builder: (column) => column);
|
||||
|
||||
i4.$$RemoteAlbumEntityTableAnnotationComposer get albumId {
|
||||
final i4.$$RemoteAlbumEntityTableAnnotationComposer composer =
|
||||
$composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.albumId,
|
||||
referencedTable: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i4.$$RemoteAlbumEntityTableAnnotationComposer(
|
||||
$db: $db,
|
||||
$table: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i4.$RemoteAlbumEntityTable>(
|
||||
'remote_album_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
|
||||
i6.$$UserEntityTableAnnotationComposer get userId {
|
||||
final i6.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.userId,
|
||||
referencedTable: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i6.$UserEntityTable>('user_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i6.$$UserEntityTableAnnotationComposer(
|
||||
$db: $db,
|
||||
$table: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i6.$UserEntityTable>('user_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
}
|
||||
|
||||
class $$AlbumUserEntityTableTableManager extends i0.RootTableManager<
|
||||
i0.GeneratedDatabase,
|
||||
i1.$AlbumUserEntityTable,
|
||||
i1.AlbumUserEntityData,
|
||||
i1.$$AlbumUserEntityTableFilterComposer,
|
||||
i1.$$AlbumUserEntityTableOrderingComposer,
|
||||
i1.$$AlbumUserEntityTableAnnotationComposer,
|
||||
$$AlbumUserEntityTableCreateCompanionBuilder,
|
||||
$$AlbumUserEntityTableUpdateCompanionBuilder,
|
||||
(i1.AlbumUserEntityData, i1.$$AlbumUserEntityTableReferences),
|
||||
i1.AlbumUserEntityData,
|
||||
i0.PrefetchHooks Function({bool albumId, bool userId})> {
|
||||
$$AlbumUserEntityTableTableManager(
|
||||
i0.GeneratedDatabase db, i1.$AlbumUserEntityTable table)
|
||||
: super(i0.TableManagerState(
|
||||
db: db,
|
||||
table: table,
|
||||
createFilteringComposer: () =>
|
||||
i1.$$AlbumUserEntityTableFilterComposer($db: db, $table: table),
|
||||
createOrderingComposer: () =>
|
||||
i1.$$AlbumUserEntityTableOrderingComposer($db: db, $table: table),
|
||||
createComputedFieldComposer: () => i1
|
||||
.$$AlbumUserEntityTableAnnotationComposer($db: db, $table: table),
|
||||
updateCompanionCallback: ({
|
||||
i0.Value<String> albumId = const i0.Value.absent(),
|
||||
i0.Value<String> userId = const i0.Value.absent(),
|
||||
i0.Value<i2.AlbumUserRole> role = const i0.Value.absent(),
|
||||
}) =>
|
||||
i1.AlbumUserEntityCompanion(
|
||||
albumId: albumId,
|
||||
userId: userId,
|
||||
role: role,
|
||||
),
|
||||
createCompanionCallback: ({
|
||||
required String albumId,
|
||||
required String userId,
|
||||
required i2.AlbumUserRole role,
|
||||
}) =>
|
||||
i1.AlbumUserEntityCompanion.insert(
|
||||
albumId: albumId,
|
||||
userId: userId,
|
||||
role: role,
|
||||
),
|
||||
withReferenceMapper: (p0) => p0
|
||||
.map((e) => (
|
||||
e.readTable(table),
|
||||
i1.$$AlbumUserEntityTableReferences(db, table, e)
|
||||
))
|
||||
.toList(),
|
||||
prefetchHooksCallback: ({albumId = false, userId = false}) {
|
||||
return i0.PrefetchHooks(
|
||||
db: db,
|
||||
explicitlyWatchedTables: [],
|
||||
addJoins: <
|
||||
T extends i0.TableManagerState<
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic>>(state) {
|
||||
if (albumId) {
|
||||
state = state.withJoin(
|
||||
currentTable: table,
|
||||
currentColumn: table.albumId,
|
||||
referencedTable:
|
||||
i1.$$AlbumUserEntityTableReferences._albumIdTable(db),
|
||||
referencedColumn: i1.$$AlbumUserEntityTableReferences
|
||||
._albumIdTable(db)
|
||||
.id,
|
||||
) as T;
|
||||
}
|
||||
if (userId) {
|
||||
state = state.withJoin(
|
||||
currentTable: table,
|
||||
currentColumn: table.userId,
|
||||
referencedTable:
|
||||
i1.$$AlbumUserEntityTableReferences._userIdTable(db),
|
||||
referencedColumn:
|
||||
i1.$$AlbumUserEntityTableReferences._userIdTable(db).id,
|
||||
) as T;
|
||||
}
|
||||
|
||||
return state;
|
||||
},
|
||||
getPrefetchedDataCallback: (items) async {
|
||||
return [];
|
||||
},
|
||||
);
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
typedef $$AlbumUserEntityTableProcessedTableManager = i0.ProcessedTableManager<
|
||||
i0.GeneratedDatabase,
|
||||
i1.$AlbumUserEntityTable,
|
||||
i1.AlbumUserEntityData,
|
||||
i1.$$AlbumUserEntityTableFilterComposer,
|
||||
i1.$$AlbumUserEntityTableOrderingComposer,
|
||||
i1.$$AlbumUserEntityTableAnnotationComposer,
|
||||
$$AlbumUserEntityTableCreateCompanionBuilder,
|
||||
$$AlbumUserEntityTableUpdateCompanionBuilder,
|
||||
(i1.AlbumUserEntityData, i1.$$AlbumUserEntityTableReferences),
|
||||
i1.AlbumUserEntityData,
|
||||
i0.PrefetchHooks Function({bool albumId, bool userId})>;
|
||||
|
||||
class $AlbumUserEntityTable extends i3.AlbumUserEntity
|
||||
with i0.TableInfo<$AlbumUserEntityTable, i1.AlbumUserEntityData> {
|
||||
@override
|
||||
final i0.GeneratedDatabase attachedDatabase;
|
||||
final String? _alias;
|
||||
$AlbumUserEntityTable(this.attachedDatabase, [this._alias]);
|
||||
static const i0.VerificationMeta _albumIdMeta =
|
||||
const i0.VerificationMeta('albumId');
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> albumId = i0.GeneratedColumn<String>(
|
||||
'album_id', aliasedName, false,
|
||||
type: i0.DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||
'REFERENCES remote_album_entity (id) ON DELETE CASCADE'));
|
||||
static const i0.VerificationMeta _userIdMeta =
|
||||
const i0.VerificationMeta('userId');
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> userId = i0.GeneratedColumn<String>(
|
||||
'user_id', aliasedName, false,
|
||||
type: i0.DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||
'REFERENCES user_entity (id) ON DELETE CASCADE'));
|
||||
@override
|
||||
late final i0.GeneratedColumnWithTypeConverter<i2.AlbumUserRole, int> role =
|
||||
i0.GeneratedColumn<int>('role', aliasedName, false,
|
||||
type: i0.DriftSqlType.int, requiredDuringInsert: true)
|
||||
.withConverter<i2.AlbumUserRole>(
|
||||
i1.$AlbumUserEntityTable.$converterrole);
|
||||
@override
|
||||
List<i0.GeneratedColumn> get $columns => [albumId, userId, role];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@override
|
||||
String get actualTableName => $name;
|
||||
static const String $name = 'album_user_entity';
|
||||
@override
|
||||
i0.VerificationContext validateIntegrity(
|
||||
i0.Insertable<i1.AlbumUserEntityData> instance,
|
||||
{bool isInserting = false}) {
|
||||
final context = i0.VerificationContext();
|
||||
final data = instance.toColumns(true);
|
||||
if (data.containsKey('album_id')) {
|
||||
context.handle(_albumIdMeta,
|
||||
albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_albumIdMeta);
|
||||
}
|
||||
if (data.containsKey('user_id')) {
|
||||
context.handle(_userIdMeta,
|
||||
userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_userIdMeta);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@override
|
||||
Set<i0.GeneratedColumn> get $primaryKey => {albumId, userId};
|
||||
@override
|
||||
i1.AlbumUserEntityData map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return i1.AlbumUserEntityData(
|
||||
albumId: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}album_id'])!,
|
||||
userId: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}user_id'])!,
|
||||
role: i1.$AlbumUserEntityTable.$converterrole.fromSql(attachedDatabase
|
||||
.typeMapping
|
||||
.read(i0.DriftSqlType.int, data['${effectivePrefix}role'])!),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
$AlbumUserEntityTable createAlias(String alias) {
|
||||
return $AlbumUserEntityTable(attachedDatabase, alias);
|
||||
}
|
||||
|
||||
static i0.JsonTypeConverter2<i2.AlbumUserRole, int, int> $converterrole =
|
||||
const i0.EnumIndexConverter<i2.AlbumUserRole>(i2.AlbumUserRole.values);
|
||||
@override
|
||||
bool get withoutRowId => true;
|
||||
@override
|
||||
bool get isStrict => true;
|
||||
}
|
||||
|
||||
class AlbumUserEntityData extends i0.DataClass
|
||||
implements i0.Insertable<i1.AlbumUserEntityData> {
|
||||
final String albumId;
|
||||
final String userId;
|
||||
final i2.AlbumUserRole role;
|
||||
const AlbumUserEntityData(
|
||||
{required this.albumId, required this.userId, required this.role});
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
map['album_id'] = i0.Variable<String>(albumId);
|
||||
map['user_id'] = i0.Variable<String>(userId);
|
||||
{
|
||||
map['role'] =
|
||||
i0.Variable<int>(i1.$AlbumUserEntityTable.$converterrole.toSql(role));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
factory AlbumUserEntityData.fromJson(Map<String, dynamic> json,
|
||||
{i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return AlbumUserEntityData(
|
||||
albumId: serializer.fromJson<String>(json['albumId']),
|
||||
userId: serializer.fromJson<String>(json['userId']),
|
||||
role: i1.$AlbumUserEntityTable.$converterrole
|
||||
.fromJson(serializer.fromJson<int>(json['role'])),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'albumId': serializer.toJson<String>(albumId),
|
||||
'userId': serializer.toJson<String>(userId),
|
||||
'role': serializer
|
||||
.toJson<int>(i1.$AlbumUserEntityTable.$converterrole.toJson(role)),
|
||||
};
|
||||
}
|
||||
|
||||
i1.AlbumUserEntityData copyWith(
|
||||
{String? albumId, String? userId, i2.AlbumUserRole? role}) =>
|
||||
i1.AlbumUserEntityData(
|
||||
albumId: albumId ?? this.albumId,
|
||||
userId: userId ?? this.userId,
|
||||
role: role ?? this.role,
|
||||
);
|
||||
AlbumUserEntityData copyWithCompanion(i1.AlbumUserEntityCompanion data) {
|
||||
return AlbumUserEntityData(
|
||||
albumId: data.albumId.present ? data.albumId.value : this.albumId,
|
||||
userId: data.userId.present ? data.userId.value : this.userId,
|
||||
role: data.role.present ? data.role.value : this.role,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('AlbumUserEntityData(')
|
||||
..write('albumId: $albumId, ')
|
||||
..write('userId: $userId, ')
|
||||
..write('role: $role')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(albumId, userId, role);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is i1.AlbumUserEntityData &&
|
||||
other.albumId == this.albumId &&
|
||||
other.userId == this.userId &&
|
||||
other.role == this.role);
|
||||
}
|
||||
|
||||
class AlbumUserEntityCompanion
|
||||
extends i0.UpdateCompanion<i1.AlbumUserEntityData> {
|
||||
final i0.Value<String> albumId;
|
||||
final i0.Value<String> userId;
|
||||
final i0.Value<i2.AlbumUserRole> role;
|
||||
const AlbumUserEntityCompanion({
|
||||
this.albumId = const i0.Value.absent(),
|
||||
this.userId = const i0.Value.absent(),
|
||||
this.role = const i0.Value.absent(),
|
||||
});
|
||||
AlbumUserEntityCompanion.insert({
|
||||
required String albumId,
|
||||
required String userId,
|
||||
required i2.AlbumUserRole role,
|
||||
}) : albumId = i0.Value(albumId),
|
||||
userId = i0.Value(userId),
|
||||
role = i0.Value(role);
|
||||
static i0.Insertable<i1.AlbumUserEntityData> custom({
|
||||
i0.Expression<String>? albumId,
|
||||
i0.Expression<String>? userId,
|
||||
i0.Expression<int>? role,
|
||||
}) {
|
||||
return i0.RawValuesInsertable({
|
||||
if (albumId != null) 'album_id': albumId,
|
||||
if (userId != null) 'user_id': userId,
|
||||
if (role != null) 'role': role,
|
||||
});
|
||||
}
|
||||
|
||||
i1.AlbumUserEntityCompanion copyWith(
|
||||
{i0.Value<String>? albumId,
|
||||
i0.Value<String>? userId,
|
||||
i0.Value<i2.AlbumUserRole>? role}) {
|
||||
return i1.AlbumUserEntityCompanion(
|
||||
albumId: albumId ?? this.albumId,
|
||||
userId: userId ?? this.userId,
|
||||
role: role ?? this.role,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
if (albumId.present) {
|
||||
map['album_id'] = i0.Variable<String>(albumId.value);
|
||||
}
|
||||
if (userId.present) {
|
||||
map['user_id'] = i0.Variable<String>(userId.value);
|
||||
}
|
||||
if (role.present) {
|
||||
map['role'] = i0.Variable<int>(
|
||||
i1.$AlbumUserEntityTable.$converterrole.toSql(role.value));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('AlbumUserEntityCompanion(')
|
||||
..write('albumId: $albumId, ')
|
||||
..write('userId: $userId, ')
|
||||
..write('role: $role')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
import 'package:drift/drift.dart' hide Query;
|
||||
import 'package:immich_mobile/domain/models/exif.model.dart' as domain;
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/exif.converter.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
@@ -90,3 +93,53 @@ class ExifInfo {
|
||||
exposureSeconds: exposureSeconds,
|
||||
);
|
||||
}
|
||||
|
||||
class RemoteExifEntity extends Table with DriftDefaultsMixin {
|
||||
const RemoteExifEntity();
|
||||
|
||||
TextColumn get assetId =>
|
||||
text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)();
|
||||
|
||||
TextColumn get city => text().nullable()();
|
||||
|
||||
TextColumn get state => text().nullable()();
|
||||
|
||||
TextColumn get country => text().nullable()();
|
||||
|
||||
DateTimeColumn get dateTimeOriginal => dateTime().nullable()();
|
||||
|
||||
TextColumn get description => text().nullable()();
|
||||
|
||||
IntColumn get height => integer().nullable()();
|
||||
|
||||
IntColumn get width => integer().nullable()();
|
||||
|
||||
TextColumn get exposureTime => text().nullable()();
|
||||
|
||||
IntColumn get fNumber => integer().nullable()();
|
||||
|
||||
IntColumn get fileSize => integer().nullable()();
|
||||
|
||||
IntColumn get focalLength => integer().nullable()();
|
||||
|
||||
IntColumn get latitude => integer().nullable()();
|
||||
|
||||
IntColumn get longitude => integer().nullable()();
|
||||
|
||||
IntColumn get iso => integer().nullable()();
|
||||
|
||||
TextColumn get make => text().nullable()();
|
||||
|
||||
TextColumn get model => text().nullable()();
|
||||
|
||||
TextColumn get orientation => text().nullable()();
|
||||
|
||||
TextColumn get timeZone => text().nullable()();
|
||||
|
||||
IntColumn get rating => integer().nullable()();
|
||||
|
||||
TextColumn get projectionType => text().nullable()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {assetId};
|
||||
}
|
||||
|
||||
1484
mobile/lib/infrastructure/entities/exif.entity.drift.dart
generated
Normal file
1484
mobile/lib/infrastructure/entities/exif.entity.drift.dart
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
mobile/lib/infrastructure/entities/local_album.entity.dart
Normal file
20
mobile/lib/infrastructure/entities/local_album.entity.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:immich_mobile/domain/models/local_album.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
||||
|
||||
class LocalAlbumEntity extends Table with DriftDefaultsMixin {
|
||||
const LocalAlbumEntity();
|
||||
|
||||
TextColumn get id => text()();
|
||||
TextColumn get name => text()();
|
||||
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
|
||||
IntColumn get backupSelection => intEnum<BackupSelection>()();
|
||||
BoolColumn get isIosSharedAlbum =>
|
||||
boolean().withDefault(const Constant(false))();
|
||||
|
||||
// Used for mark & sweep
|
||||
BoolColumn get marker_ => boolean().nullable()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {id};
|
||||
}
|
||||
555
mobile/lib/infrastructure/entities/local_album.entity.drift.dart
generated
Normal file
555
mobile/lib/infrastructure/entities/local_album.entity.drift.dart
generated
Normal file
@@ -0,0 +1,555 @@
|
||||
// dart format width=80
|
||||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'
|
||||
as i1;
|
||||
import 'package:immich_mobile/domain/models/local_album.model.dart' as i2;
|
||||
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'
|
||||
as i3;
|
||||
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4;
|
||||
|
||||
typedef $$LocalAlbumEntityTableCreateCompanionBuilder
|
||||
= i1.LocalAlbumEntityCompanion Function({
|
||||
required String id,
|
||||
required String name,
|
||||
i0.Value<DateTime> updatedAt,
|
||||
required i2.BackupSelection backupSelection,
|
||||
i0.Value<bool> isIosSharedAlbum,
|
||||
i0.Value<bool?> marker_,
|
||||
});
|
||||
typedef $$LocalAlbumEntityTableUpdateCompanionBuilder
|
||||
= i1.LocalAlbumEntityCompanion Function({
|
||||
i0.Value<String> id,
|
||||
i0.Value<String> name,
|
||||
i0.Value<DateTime> updatedAt,
|
||||
i0.Value<i2.BackupSelection> backupSelection,
|
||||
i0.Value<bool> isIosSharedAlbum,
|
||||
i0.Value<bool?> marker_,
|
||||
});
|
||||
|
||||
class $$LocalAlbumEntityTableFilterComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAlbumEntityTable> {
|
||||
$$LocalAlbumEntityTableFilterComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.ColumnFilters<String> get id => $composableBuilder(
|
||||
column: $table.id, builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnFilters<String> get name => $composableBuilder(
|
||||
column: $table.name, builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnFilters<DateTime> get updatedAt => $composableBuilder(
|
||||
column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnWithTypeConverterFilters<i2.BackupSelection, i2.BackupSelection, int>
|
||||
get backupSelection => $composableBuilder(
|
||||
column: $table.backupSelection,
|
||||
builder: (column) => i0.ColumnWithTypeConverterFilters(column));
|
||||
|
||||
i0.ColumnFilters<bool> get isIosSharedAlbum => $composableBuilder(
|
||||
column: $table.isIosSharedAlbum,
|
||||
builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnFilters<bool> get marker_ => $composableBuilder(
|
||||
column: $table.marker_, builder: (column) => i0.ColumnFilters(column));
|
||||
}
|
||||
|
||||
class $$LocalAlbumEntityTableOrderingComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAlbumEntityTable> {
|
||||
$$LocalAlbumEntityTableOrderingComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.ColumnOrderings<String> get id => $composableBuilder(
|
||||
column: $table.id, builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<String> get name => $composableBuilder(
|
||||
column: $table.name, builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<DateTime> get updatedAt => $composableBuilder(
|
||||
column: $table.updatedAt,
|
||||
builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<int> get backupSelection => $composableBuilder(
|
||||
column: $table.backupSelection,
|
||||
builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<bool> get isIosSharedAlbum => $composableBuilder(
|
||||
column: $table.isIosSharedAlbum,
|
||||
builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<bool> get marker_ => $composableBuilder(
|
||||
column: $table.marker_, builder: (column) => i0.ColumnOrderings(column));
|
||||
}
|
||||
|
||||
class $$LocalAlbumEntityTableAnnotationComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAlbumEntityTable> {
|
||||
$$LocalAlbumEntityTableAnnotationComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.GeneratedColumn<String> get id =>
|
||||
$composableBuilder(column: $table.id, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<String> get name =>
|
||||
$composableBuilder(column: $table.name, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<DateTime> get updatedAt =>
|
||||
$composableBuilder(column: $table.updatedAt, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumnWithTypeConverter<i2.BackupSelection, int>
|
||||
get backupSelection => $composableBuilder(
|
||||
column: $table.backupSelection, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<bool> get isIosSharedAlbum => $composableBuilder(
|
||||
column: $table.isIosSharedAlbum, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<bool> get marker_ =>
|
||||
$composableBuilder(column: $table.marker_, builder: (column) => column);
|
||||
}
|
||||
|
||||
class $$LocalAlbumEntityTableTableManager extends i0.RootTableManager<
|
||||
i0.GeneratedDatabase,
|
||||
i1.$LocalAlbumEntityTable,
|
||||
i1.LocalAlbumEntityData,
|
||||
i1.$$LocalAlbumEntityTableFilterComposer,
|
||||
i1.$$LocalAlbumEntityTableOrderingComposer,
|
||||
i1.$$LocalAlbumEntityTableAnnotationComposer,
|
||||
$$LocalAlbumEntityTableCreateCompanionBuilder,
|
||||
$$LocalAlbumEntityTableUpdateCompanionBuilder,
|
||||
(
|
||||
i1.LocalAlbumEntityData,
|
||||
i0.BaseReferences<i0.GeneratedDatabase, i1.$LocalAlbumEntityTable,
|
||||
i1.LocalAlbumEntityData>
|
||||
),
|
||||
i1.LocalAlbumEntityData,
|
||||
i0.PrefetchHooks Function()> {
|
||||
$$LocalAlbumEntityTableTableManager(
|
||||
i0.GeneratedDatabase db, i1.$LocalAlbumEntityTable table)
|
||||
: super(i0.TableManagerState(
|
||||
db: db,
|
||||
table: table,
|
||||
createFilteringComposer: () =>
|
||||
i1.$$LocalAlbumEntityTableFilterComposer($db: db, $table: table),
|
||||
createOrderingComposer: () => i1
|
||||
.$$LocalAlbumEntityTableOrderingComposer($db: db, $table: table),
|
||||
createComputedFieldComposer: () =>
|
||||
i1.$$LocalAlbumEntityTableAnnotationComposer(
|
||||
$db: db, $table: table),
|
||||
updateCompanionCallback: ({
|
||||
i0.Value<String> id = const i0.Value.absent(),
|
||||
i0.Value<String> name = const i0.Value.absent(),
|
||||
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
||||
i0.Value<i2.BackupSelection> backupSelection =
|
||||
const i0.Value.absent(),
|
||||
i0.Value<bool> isIosSharedAlbum = const i0.Value.absent(),
|
||||
i0.Value<bool?> marker_ = const i0.Value.absent(),
|
||||
}) =>
|
||||
i1.LocalAlbumEntityCompanion(
|
||||
id: id,
|
||||
name: name,
|
||||
updatedAt: updatedAt,
|
||||
backupSelection: backupSelection,
|
||||
isIosSharedAlbum: isIosSharedAlbum,
|
||||
marker_: marker_,
|
||||
),
|
||||
createCompanionCallback: ({
|
||||
required String id,
|
||||
required String name,
|
||||
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
||||
required i2.BackupSelection backupSelection,
|
||||
i0.Value<bool> isIosSharedAlbum = const i0.Value.absent(),
|
||||
i0.Value<bool?> marker_ = const i0.Value.absent(),
|
||||
}) =>
|
||||
i1.LocalAlbumEntityCompanion.insert(
|
||||
id: id,
|
||||
name: name,
|
||||
updatedAt: updatedAt,
|
||||
backupSelection: backupSelection,
|
||||
isIosSharedAlbum: isIosSharedAlbum,
|
||||
marker_: marker_,
|
||||
),
|
||||
withReferenceMapper: (p0) => p0
|
||||
.map((e) => (e.readTable(table), i0.BaseReferences(db, table, e)))
|
||||
.toList(),
|
||||
prefetchHooksCallback: null,
|
||||
));
|
||||
}
|
||||
|
||||
typedef $$LocalAlbumEntityTableProcessedTableManager = i0.ProcessedTableManager<
|
||||
i0.GeneratedDatabase,
|
||||
i1.$LocalAlbumEntityTable,
|
||||
i1.LocalAlbumEntityData,
|
||||
i1.$$LocalAlbumEntityTableFilterComposer,
|
||||
i1.$$LocalAlbumEntityTableOrderingComposer,
|
||||
i1.$$LocalAlbumEntityTableAnnotationComposer,
|
||||
$$LocalAlbumEntityTableCreateCompanionBuilder,
|
||||
$$LocalAlbumEntityTableUpdateCompanionBuilder,
|
||||
(
|
||||
i1.LocalAlbumEntityData,
|
||||
i0.BaseReferences<i0.GeneratedDatabase, i1.$LocalAlbumEntityTable,
|
||||
i1.LocalAlbumEntityData>
|
||||
),
|
||||
i1.LocalAlbumEntityData,
|
||||
i0.PrefetchHooks Function()>;
|
||||
|
||||
class $LocalAlbumEntityTable extends i3.LocalAlbumEntity
|
||||
with i0.TableInfo<$LocalAlbumEntityTable, i1.LocalAlbumEntityData> {
|
||||
@override
|
||||
final i0.GeneratedDatabase attachedDatabase;
|
||||
final String? _alias;
|
||||
$LocalAlbumEntityTable(this.attachedDatabase, [this._alias]);
|
||||
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
|
||||
'id', aliasedName, false,
|
||||
type: i0.DriftSqlType.string, requiredDuringInsert: true);
|
||||
static const i0.VerificationMeta _nameMeta =
|
||||
const i0.VerificationMeta('name');
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> name = i0.GeneratedColumn<String>(
|
||||
'name', aliasedName, false,
|
||||
type: i0.DriftSqlType.string, requiredDuringInsert: true);
|
||||
static const i0.VerificationMeta _updatedAtMeta =
|
||||
const i0.VerificationMeta('updatedAt');
|
||||
@override
|
||||
late final i0.GeneratedColumn<DateTime> updatedAt =
|
||||
i0.GeneratedColumn<DateTime>('updated_at', aliasedName, false,
|
||||
type: i0.DriftSqlType.dateTime,
|
||||
requiredDuringInsert: false,
|
||||
defaultValue: i4.currentDateAndTime);
|
||||
@override
|
||||
late final i0.GeneratedColumnWithTypeConverter<i2.BackupSelection, int>
|
||||
backupSelection = i0.GeneratedColumn<int>(
|
||||
'backup_selection', aliasedName, false,
|
||||
type: i0.DriftSqlType.int, requiredDuringInsert: true)
|
||||
.withConverter<i2.BackupSelection>(
|
||||
i1.$LocalAlbumEntityTable.$converterbackupSelection);
|
||||
static const i0.VerificationMeta _isIosSharedAlbumMeta =
|
||||
const i0.VerificationMeta('isIosSharedAlbum');
|
||||
@override
|
||||
late final i0.GeneratedColumn<bool> isIosSharedAlbum =
|
||||
i0.GeneratedColumn<bool>('is_ios_shared_album', aliasedName, false,
|
||||
type: i0.DriftSqlType.bool,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||
'CHECK ("is_ios_shared_album" IN (0, 1))'),
|
||||
defaultValue: const i4.Constant(false));
|
||||
static const i0.VerificationMeta _marker_Meta =
|
||||
const i0.VerificationMeta('marker_');
|
||||
@override
|
||||
late final i0.GeneratedColumn<bool> marker_ = i0.GeneratedColumn<bool>(
|
||||
'marker', aliasedName, true,
|
||||
type: i0.DriftSqlType.bool,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints:
|
||||
i0.GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))'));
|
||||
@override
|
||||
List<i0.GeneratedColumn> get $columns =>
|
||||
[id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@override
|
||||
String get actualTableName => $name;
|
||||
static const String $name = 'local_album_entity';
|
||||
@override
|
||||
i0.VerificationContext validateIntegrity(
|
||||
i0.Insertable<i1.LocalAlbumEntityData> instance,
|
||||
{bool isInserting = false}) {
|
||||
final context = i0.VerificationContext();
|
||||
final data = instance.toColumns(true);
|
||||
if (data.containsKey('id')) {
|
||||
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_idMeta);
|
||||
}
|
||||
if (data.containsKey('name')) {
|
||||
context.handle(
|
||||
_nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_nameMeta);
|
||||
}
|
||||
if (data.containsKey('updated_at')) {
|
||||
context.handle(_updatedAtMeta,
|
||||
updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta));
|
||||
}
|
||||
if (data.containsKey('is_ios_shared_album')) {
|
||||
context.handle(
|
||||
_isIosSharedAlbumMeta,
|
||||
isIosSharedAlbum.isAcceptableOrUnknown(
|
||||
data['is_ios_shared_album']!, _isIosSharedAlbumMeta));
|
||||
}
|
||||
if (data.containsKey('marker')) {
|
||||
context.handle(_marker_Meta,
|
||||
marker_.isAcceptableOrUnknown(data['marker']!, _marker_Meta));
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@override
|
||||
Set<i0.GeneratedColumn> get $primaryKey => {id};
|
||||
@override
|
||||
i1.LocalAlbumEntityData map(Map<String, dynamic> data,
|
||||
{String? tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return i1.LocalAlbumEntityData(
|
||||
id: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!,
|
||||
name: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!,
|
||||
updatedAt: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!,
|
||||
backupSelection: i1.$LocalAlbumEntityTable.$converterbackupSelection
|
||||
.fromSql(attachedDatabase.typeMapping.read(i0.DriftSqlType.int,
|
||||
data['${effectivePrefix}backup_selection'])!),
|
||||
isIosSharedAlbum: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.bool, data['${effectivePrefix}is_ios_shared_album'])!,
|
||||
marker_: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.bool, data['${effectivePrefix}marker']),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
$LocalAlbumEntityTable createAlias(String alias) {
|
||||
return $LocalAlbumEntityTable(attachedDatabase, alias);
|
||||
}
|
||||
|
||||
static i0.JsonTypeConverter2<i2.BackupSelection, int, int>
|
||||
$converterbackupSelection =
|
||||
const i0.EnumIndexConverter<i2.BackupSelection>(
|
||||
i2.BackupSelection.values);
|
||||
@override
|
||||
bool get withoutRowId => true;
|
||||
@override
|
||||
bool get isStrict => true;
|
||||
}
|
||||
|
||||
class LocalAlbumEntityData extends i0.DataClass
|
||||
implements i0.Insertable<i1.LocalAlbumEntityData> {
|
||||
final String id;
|
||||
final String name;
|
||||
final DateTime updatedAt;
|
||||
final i2.BackupSelection backupSelection;
|
||||
final bool isIosSharedAlbum;
|
||||
final bool? marker_;
|
||||
const LocalAlbumEntityData(
|
||||
{required this.id,
|
||||
required this.name,
|
||||
required this.updatedAt,
|
||||
required this.backupSelection,
|
||||
required this.isIosSharedAlbum,
|
||||
this.marker_});
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
map['id'] = i0.Variable<String>(id);
|
||||
map['name'] = i0.Variable<String>(name);
|
||||
map['updated_at'] = i0.Variable<DateTime>(updatedAt);
|
||||
{
|
||||
map['backup_selection'] = i0.Variable<int>(i1
|
||||
.$LocalAlbumEntityTable.$converterbackupSelection
|
||||
.toSql(backupSelection));
|
||||
}
|
||||
map['is_ios_shared_album'] = i0.Variable<bool>(isIosSharedAlbum);
|
||||
if (!nullToAbsent || marker_ != null) {
|
||||
map['marker'] = i0.Variable<bool>(marker_);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
factory LocalAlbumEntityData.fromJson(Map<String, dynamic> json,
|
||||
{i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return LocalAlbumEntityData(
|
||||
id: serializer.fromJson<String>(json['id']),
|
||||
name: serializer.fromJson<String>(json['name']),
|
||||
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
|
||||
backupSelection: i1.$LocalAlbumEntityTable.$converterbackupSelection
|
||||
.fromJson(serializer.fromJson<int>(json['backupSelection'])),
|
||||
isIosSharedAlbum: serializer.fromJson<bool>(json['isIosSharedAlbum']),
|
||||
marker_: serializer.fromJson<bool?>(json['marker_']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'id': serializer.toJson<String>(id),
|
||||
'name': serializer.toJson<String>(name),
|
||||
'updatedAt': serializer.toJson<DateTime>(updatedAt),
|
||||
'backupSelection': serializer.toJson<int>(i1
|
||||
.$LocalAlbumEntityTable.$converterbackupSelection
|
||||
.toJson(backupSelection)),
|
||||
'isIosSharedAlbum': serializer.toJson<bool>(isIosSharedAlbum),
|
||||
'marker_': serializer.toJson<bool?>(marker_),
|
||||
};
|
||||
}
|
||||
|
||||
i1.LocalAlbumEntityData copyWith(
|
||||
{String? id,
|
||||
String? name,
|
||||
DateTime? updatedAt,
|
||||
i2.BackupSelection? backupSelection,
|
||||
bool? isIosSharedAlbum,
|
||||
i0.Value<bool?> marker_ = const i0.Value.absent()}) =>
|
||||
i1.LocalAlbumEntityData(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
backupSelection: backupSelection ?? this.backupSelection,
|
||||
isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum,
|
||||
marker_: marker_.present ? marker_.value : this.marker_,
|
||||
);
|
||||
LocalAlbumEntityData copyWithCompanion(i1.LocalAlbumEntityCompanion data) {
|
||||
return LocalAlbumEntityData(
|
||||
id: data.id.present ? data.id.value : this.id,
|
||||
name: data.name.present ? data.name.value : this.name,
|
||||
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
|
||||
backupSelection: data.backupSelection.present
|
||||
? data.backupSelection.value
|
||||
: this.backupSelection,
|
||||
isIosSharedAlbum: data.isIosSharedAlbum.present
|
||||
? data.isIosSharedAlbum.value
|
||||
: this.isIosSharedAlbum,
|
||||
marker_: data.marker_.present ? data.marker_.value : this.marker_,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('LocalAlbumEntityData(')
|
||||
..write('id: $id, ')
|
||||
..write('name: $name, ')
|
||||
..write('updatedAt: $updatedAt, ')
|
||||
..write('backupSelection: $backupSelection, ')
|
||||
..write('isIosSharedAlbum: $isIosSharedAlbum, ')
|
||||
..write('marker_: $marker_')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is i1.LocalAlbumEntityData &&
|
||||
other.id == this.id &&
|
||||
other.name == this.name &&
|
||||
other.updatedAt == this.updatedAt &&
|
||||
other.backupSelection == this.backupSelection &&
|
||||
other.isIosSharedAlbum == this.isIosSharedAlbum &&
|
||||
other.marker_ == this.marker_);
|
||||
}
|
||||
|
||||
class LocalAlbumEntityCompanion
|
||||
extends i0.UpdateCompanion<i1.LocalAlbumEntityData> {
|
||||
final i0.Value<String> id;
|
||||
final i0.Value<String> name;
|
||||
final i0.Value<DateTime> updatedAt;
|
||||
final i0.Value<i2.BackupSelection> backupSelection;
|
||||
final i0.Value<bool> isIosSharedAlbum;
|
||||
final i0.Value<bool?> marker_;
|
||||
const LocalAlbumEntityCompanion({
|
||||
this.id = const i0.Value.absent(),
|
||||
this.name = const i0.Value.absent(),
|
||||
this.updatedAt = const i0.Value.absent(),
|
||||
this.backupSelection = const i0.Value.absent(),
|
||||
this.isIosSharedAlbum = const i0.Value.absent(),
|
||||
this.marker_ = const i0.Value.absent(),
|
||||
});
|
||||
LocalAlbumEntityCompanion.insert({
|
||||
required String id,
|
||||
required String name,
|
||||
this.updatedAt = const i0.Value.absent(),
|
||||
required i2.BackupSelection backupSelection,
|
||||
this.isIosSharedAlbum = const i0.Value.absent(),
|
||||
this.marker_ = const i0.Value.absent(),
|
||||
}) : id = i0.Value(id),
|
||||
name = i0.Value(name),
|
||||
backupSelection = i0.Value(backupSelection);
|
||||
static i0.Insertable<i1.LocalAlbumEntityData> custom({
|
||||
i0.Expression<String>? id,
|
||||
i0.Expression<String>? name,
|
||||
i0.Expression<DateTime>? updatedAt,
|
||||
i0.Expression<int>? backupSelection,
|
||||
i0.Expression<bool>? isIosSharedAlbum,
|
||||
i0.Expression<bool>? marker_,
|
||||
}) {
|
||||
return i0.RawValuesInsertable({
|
||||
if (id != null) 'id': id,
|
||||
if (name != null) 'name': name,
|
||||
if (updatedAt != null) 'updated_at': updatedAt,
|
||||
if (backupSelection != null) 'backup_selection': backupSelection,
|
||||
if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum,
|
||||
if (marker_ != null) 'marker': marker_,
|
||||
});
|
||||
}
|
||||
|
||||
i1.LocalAlbumEntityCompanion copyWith(
|
||||
{i0.Value<String>? id,
|
||||
i0.Value<String>? name,
|
||||
i0.Value<DateTime>? updatedAt,
|
||||
i0.Value<i2.BackupSelection>? backupSelection,
|
||||
i0.Value<bool>? isIosSharedAlbum,
|
||||
i0.Value<bool?>? marker_}) {
|
||||
return i1.LocalAlbumEntityCompanion(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
backupSelection: backupSelection ?? this.backupSelection,
|
||||
isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum,
|
||||
marker_: marker_ ?? this.marker_,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
if (id.present) {
|
||||
map['id'] = i0.Variable<String>(id.value);
|
||||
}
|
||||
if (name.present) {
|
||||
map['name'] = i0.Variable<String>(name.value);
|
||||
}
|
||||
if (updatedAt.present) {
|
||||
map['updated_at'] = i0.Variable<DateTime>(updatedAt.value);
|
||||
}
|
||||
if (backupSelection.present) {
|
||||
map['backup_selection'] = i0.Variable<int>(i1
|
||||
.$LocalAlbumEntityTable.$converterbackupSelection
|
||||
.toSql(backupSelection.value));
|
||||
}
|
||||
if (isIosSharedAlbum.present) {
|
||||
map['is_ios_shared_album'] = i0.Variable<bool>(isIosSharedAlbum.value);
|
||||
}
|
||||
if (marker_.present) {
|
||||
map['marker'] = i0.Variable<bool>(marker_.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('LocalAlbumEntityCompanion(')
|
||||
..write('id: $id, ')
|
||||
..write('name: $name, ')
|
||||
..write('updatedAt: $updatedAt, ')
|
||||
..write('backupSelection: $backupSelection, ')
|
||||
..write('isIosSharedAlbum: $isIosSharedAlbum, ')
|
||||
..write('marker_: $marker_')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
||||
|
||||
class LocalAlbumAssetEntity extends Table with DriftDefaultsMixin {
|
||||
const LocalAlbumAssetEntity();
|
||||
|
||||
TextColumn get assetId =>
|
||||
text().references(LocalAssetEntity, #id, onDelete: KeyAction.cascade)();
|
||||
|
||||
TextColumn get albumId =>
|
||||
text().references(LocalAlbumEntity, #id, onDelete: KeyAction.cascade)();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {assetId, albumId};
|
||||
}
|
||||
565
mobile/lib/infrastructure/entities/local_album_asset.entity.drift.dart
generated
Normal file
565
mobile/lib/infrastructure/entities/local_album_asset.entity.drift.dart
generated
Normal file
@@ -0,0 +1,565 @@
|
||||
// dart format width=80
|
||||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart'
|
||||
as i1;
|
||||
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart'
|
||||
as i2;
|
||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'
|
||||
as i3;
|
||||
import 'package:drift/internal/modular.dart' as i4;
|
||||
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'
|
||||
as i5;
|
||||
|
||||
typedef $$LocalAlbumAssetEntityTableCreateCompanionBuilder
|
||||
= i1.LocalAlbumAssetEntityCompanion Function({
|
||||
required String assetId,
|
||||
required String albumId,
|
||||
});
|
||||
typedef $$LocalAlbumAssetEntityTableUpdateCompanionBuilder
|
||||
= i1.LocalAlbumAssetEntityCompanion Function({
|
||||
i0.Value<String> assetId,
|
||||
i0.Value<String> albumId,
|
||||
});
|
||||
|
||||
final class $$LocalAlbumAssetEntityTableReferences extends i0.BaseReferences<
|
||||
i0.GeneratedDatabase,
|
||||
i1.$LocalAlbumAssetEntityTable,
|
||||
i1.LocalAlbumAssetEntityData> {
|
||||
$$LocalAlbumAssetEntityTableReferences(
|
||||
super.$_db, super.$_table, super.$_typedResult);
|
||||
|
||||
static i3.$LocalAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) =>
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i3.$LocalAssetEntityTable>('local_asset_entity')
|
||||
.createAlias(i0.$_aliasNameGenerator(
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i1.$LocalAlbumAssetEntityTable>(
|
||||
'local_album_asset_entity')
|
||||
.assetId,
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i3.$LocalAssetEntityTable>('local_asset_entity')
|
||||
.id));
|
||||
|
||||
i3.$$LocalAssetEntityTableProcessedTableManager get assetId {
|
||||
final $_column = $_itemColumn<String>('asset_id')!;
|
||||
|
||||
final manager = i3
|
||||
.$$LocalAssetEntityTableTableManager(
|
||||
$_db,
|
||||
i4.ReadDatabaseContainer($_db)
|
||||
.resultSet<i3.$LocalAssetEntityTable>('local_asset_entity'))
|
||||
.filter((f) => f.id.sqlEquals($_column));
|
||||
final item = $_typedResult.readTableOrNull(_assetIdTable($_db));
|
||||
if (item == null) return manager;
|
||||
return i0.ProcessedTableManager(
|
||||
manager.$state.copyWith(prefetchedData: [item]));
|
||||
}
|
||||
|
||||
static i5.$LocalAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) =>
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i5.$LocalAlbumEntityTable>('local_album_entity')
|
||||
.createAlias(i0.$_aliasNameGenerator(
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i1.$LocalAlbumAssetEntityTable>(
|
||||
'local_album_asset_entity')
|
||||
.albumId,
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i5.$LocalAlbumEntityTable>('local_album_entity')
|
||||
.id));
|
||||
|
||||
i5.$$LocalAlbumEntityTableProcessedTableManager get albumId {
|
||||
final $_column = $_itemColumn<String>('album_id')!;
|
||||
|
||||
final manager = i5
|
||||
.$$LocalAlbumEntityTableTableManager(
|
||||
$_db,
|
||||
i4.ReadDatabaseContainer($_db)
|
||||
.resultSet<i5.$LocalAlbumEntityTable>('local_album_entity'))
|
||||
.filter((f) => f.id.sqlEquals($_column));
|
||||
final item = $_typedResult.readTableOrNull(_albumIdTable($_db));
|
||||
if (item == null) return manager;
|
||||
return i0.ProcessedTableManager(
|
||||
manager.$state.copyWith(prefetchedData: [item]));
|
||||
}
|
||||
}
|
||||
|
||||
class $$LocalAlbumAssetEntityTableFilterComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAlbumAssetEntityTable> {
|
||||
$$LocalAlbumAssetEntityTableFilterComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i3.$$LocalAssetEntityTableFilterComposer get assetId {
|
||||
final i3.$$LocalAssetEntityTableFilterComposer composer = $composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.assetId,
|
||||
referencedTable: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i3.$LocalAssetEntityTable>('local_asset_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i3.$$LocalAssetEntityTableFilterComposer(
|
||||
$db: $db,
|
||||
$table: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i3.$LocalAssetEntityTable>('local_asset_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
|
||||
i5.$$LocalAlbumEntityTableFilterComposer get albumId {
|
||||
final i5.$$LocalAlbumEntityTableFilterComposer composer = $composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.albumId,
|
||||
referencedTable: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$LocalAlbumEntityTable>('local_album_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i5.$$LocalAlbumEntityTableFilterComposer(
|
||||
$db: $db,
|
||||
$table: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$LocalAlbumEntityTable>('local_album_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
}
|
||||
|
||||
class $$LocalAlbumAssetEntityTableOrderingComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAlbumAssetEntityTable> {
|
||||
$$LocalAlbumAssetEntityTableOrderingComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i3.$$LocalAssetEntityTableOrderingComposer get assetId {
|
||||
final i3.$$LocalAssetEntityTableOrderingComposer composer =
|
||||
$composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.assetId,
|
||||
referencedTable: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i3.$LocalAssetEntityTable>('local_asset_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i3.$$LocalAssetEntityTableOrderingComposer(
|
||||
$db: $db,
|
||||
$table: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i3.$LocalAssetEntityTable>(
|
||||
'local_asset_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
|
||||
i5.$$LocalAlbumEntityTableOrderingComposer get albumId {
|
||||
final i5.$$LocalAlbumEntityTableOrderingComposer composer =
|
||||
$composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.albumId,
|
||||
referencedTable: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$LocalAlbumEntityTable>('local_album_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i5.$$LocalAlbumEntityTableOrderingComposer(
|
||||
$db: $db,
|
||||
$table: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$LocalAlbumEntityTable>(
|
||||
'local_album_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
}
|
||||
|
||||
class $$LocalAlbumAssetEntityTableAnnotationComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAlbumAssetEntityTable> {
|
||||
$$LocalAlbumAssetEntityTableAnnotationComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i3.$$LocalAssetEntityTableAnnotationComposer get assetId {
|
||||
final i3.$$LocalAssetEntityTableAnnotationComposer composer =
|
||||
$composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.assetId,
|
||||
referencedTable: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i3.$LocalAssetEntityTable>('local_asset_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i3.$$LocalAssetEntityTableAnnotationComposer(
|
||||
$db: $db,
|
||||
$table: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i3.$LocalAssetEntityTable>(
|
||||
'local_asset_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
|
||||
i5.$$LocalAlbumEntityTableAnnotationComposer get albumId {
|
||||
final i5.$$LocalAlbumEntityTableAnnotationComposer composer =
|
||||
$composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.albumId,
|
||||
referencedTable: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$LocalAlbumEntityTable>('local_album_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i5.$$LocalAlbumEntityTableAnnotationComposer(
|
||||
$db: $db,
|
||||
$table: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$LocalAlbumEntityTable>(
|
||||
'local_album_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
}
|
||||
|
||||
class $$LocalAlbumAssetEntityTableTableManager extends i0.RootTableManager<
|
||||
i0.GeneratedDatabase,
|
||||
i1.$LocalAlbumAssetEntityTable,
|
||||
i1.LocalAlbumAssetEntityData,
|
||||
i1.$$LocalAlbumAssetEntityTableFilterComposer,
|
||||
i1.$$LocalAlbumAssetEntityTableOrderingComposer,
|
||||
i1.$$LocalAlbumAssetEntityTableAnnotationComposer,
|
||||
$$LocalAlbumAssetEntityTableCreateCompanionBuilder,
|
||||
$$LocalAlbumAssetEntityTableUpdateCompanionBuilder,
|
||||
(i1.LocalAlbumAssetEntityData, i1.$$LocalAlbumAssetEntityTableReferences),
|
||||
i1.LocalAlbumAssetEntityData,
|
||||
i0.PrefetchHooks Function({bool assetId, bool albumId})> {
|
||||
$$LocalAlbumAssetEntityTableTableManager(
|
||||
i0.GeneratedDatabase db, i1.$LocalAlbumAssetEntityTable table)
|
||||
: super(i0.TableManagerState(
|
||||
db: db,
|
||||
table: table,
|
||||
createFilteringComposer: () =>
|
||||
i1.$$LocalAlbumAssetEntityTableFilterComposer(
|
||||
$db: db, $table: table),
|
||||
createOrderingComposer: () =>
|
||||
i1.$$LocalAlbumAssetEntityTableOrderingComposer(
|
||||
$db: db, $table: table),
|
||||
createComputedFieldComposer: () =>
|
||||
i1.$$LocalAlbumAssetEntityTableAnnotationComposer(
|
||||
$db: db, $table: table),
|
||||
updateCompanionCallback: ({
|
||||
i0.Value<String> assetId = const i0.Value.absent(),
|
||||
i0.Value<String> albumId = const i0.Value.absent(),
|
||||
}) =>
|
||||
i1.LocalAlbumAssetEntityCompanion(
|
||||
assetId: assetId,
|
||||
albumId: albumId,
|
||||
),
|
||||
createCompanionCallback: ({
|
||||
required String assetId,
|
||||
required String albumId,
|
||||
}) =>
|
||||
i1.LocalAlbumAssetEntityCompanion.insert(
|
||||
assetId: assetId,
|
||||
albumId: albumId,
|
||||
),
|
||||
withReferenceMapper: (p0) => p0
|
||||
.map((e) => (
|
||||
e.readTable(table),
|
||||
i1.$$LocalAlbumAssetEntityTableReferences(db, table, e)
|
||||
))
|
||||
.toList(),
|
||||
prefetchHooksCallback: ({assetId = false, albumId = false}) {
|
||||
return i0.PrefetchHooks(
|
||||
db: db,
|
||||
explicitlyWatchedTables: [],
|
||||
addJoins: <
|
||||
T extends i0.TableManagerState<
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic>>(state) {
|
||||
if (assetId) {
|
||||
state = state.withJoin(
|
||||
currentTable: table,
|
||||
currentColumn: table.assetId,
|
||||
referencedTable: i1.$$LocalAlbumAssetEntityTableReferences
|
||||
._assetIdTable(db),
|
||||
referencedColumn: i1.$$LocalAlbumAssetEntityTableReferences
|
||||
._assetIdTable(db)
|
||||
.id,
|
||||
) as T;
|
||||
}
|
||||
if (albumId) {
|
||||
state = state.withJoin(
|
||||
currentTable: table,
|
||||
currentColumn: table.albumId,
|
||||
referencedTable: i1.$$LocalAlbumAssetEntityTableReferences
|
||||
._albumIdTable(db),
|
||||
referencedColumn: i1.$$LocalAlbumAssetEntityTableReferences
|
||||
._albumIdTable(db)
|
||||
.id,
|
||||
) as T;
|
||||
}
|
||||
|
||||
return state;
|
||||
},
|
||||
getPrefetchedDataCallback: (items) async {
|
||||
return [];
|
||||
},
|
||||
);
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
typedef $$LocalAlbumAssetEntityTableProcessedTableManager
|
||||
= i0.ProcessedTableManager<
|
||||
i0.GeneratedDatabase,
|
||||
i1.$LocalAlbumAssetEntityTable,
|
||||
i1.LocalAlbumAssetEntityData,
|
||||
i1.$$LocalAlbumAssetEntityTableFilterComposer,
|
||||
i1.$$LocalAlbumAssetEntityTableOrderingComposer,
|
||||
i1.$$LocalAlbumAssetEntityTableAnnotationComposer,
|
||||
$$LocalAlbumAssetEntityTableCreateCompanionBuilder,
|
||||
$$LocalAlbumAssetEntityTableUpdateCompanionBuilder,
|
||||
(
|
||||
i1.LocalAlbumAssetEntityData,
|
||||
i1.$$LocalAlbumAssetEntityTableReferences
|
||||
),
|
||||
i1.LocalAlbumAssetEntityData,
|
||||
i0.PrefetchHooks Function({bool assetId, bool albumId})>;
|
||||
|
||||
class $LocalAlbumAssetEntityTable extends i2.LocalAlbumAssetEntity
|
||||
with
|
||||
i0
|
||||
.TableInfo<$LocalAlbumAssetEntityTable, i1.LocalAlbumAssetEntityData> {
|
||||
@override
|
||||
final i0.GeneratedDatabase attachedDatabase;
|
||||
final String? _alias;
|
||||
$LocalAlbumAssetEntityTable(this.attachedDatabase, [this._alias]);
|
||||
static const i0.VerificationMeta _assetIdMeta =
|
||||
const i0.VerificationMeta('assetId');
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> assetId = i0.GeneratedColumn<String>(
|
||||
'asset_id', aliasedName, false,
|
||||
type: i0.DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||
'REFERENCES local_asset_entity (id) ON DELETE CASCADE'));
|
||||
static const i0.VerificationMeta _albumIdMeta =
|
||||
const i0.VerificationMeta('albumId');
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> albumId = i0.GeneratedColumn<String>(
|
||||
'album_id', aliasedName, false,
|
||||
type: i0.DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||
'REFERENCES local_album_entity (id) ON DELETE CASCADE'));
|
||||
@override
|
||||
List<i0.GeneratedColumn> get $columns => [assetId, albumId];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@override
|
||||
String get actualTableName => $name;
|
||||
static const String $name = 'local_album_asset_entity';
|
||||
@override
|
||||
i0.VerificationContext validateIntegrity(
|
||||
i0.Insertable<i1.LocalAlbumAssetEntityData> instance,
|
||||
{bool isInserting = false}) {
|
||||
final context = i0.VerificationContext();
|
||||
final data = instance.toColumns(true);
|
||||
if (data.containsKey('asset_id')) {
|
||||
context.handle(_assetIdMeta,
|
||||
assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_assetIdMeta);
|
||||
}
|
||||
if (data.containsKey('album_id')) {
|
||||
context.handle(_albumIdMeta,
|
||||
albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_albumIdMeta);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@override
|
||||
Set<i0.GeneratedColumn> get $primaryKey => {assetId, albumId};
|
||||
@override
|
||||
i1.LocalAlbumAssetEntityData map(Map<String, dynamic> data,
|
||||
{String? tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return i1.LocalAlbumAssetEntityData(
|
||||
assetId: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!,
|
||||
albumId: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}album_id'])!,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
$LocalAlbumAssetEntityTable createAlias(String alias) {
|
||||
return $LocalAlbumAssetEntityTable(attachedDatabase, alias);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get withoutRowId => true;
|
||||
@override
|
||||
bool get isStrict => true;
|
||||
}
|
||||
|
||||
class LocalAlbumAssetEntityData extends i0.DataClass
|
||||
implements i0.Insertable<i1.LocalAlbumAssetEntityData> {
|
||||
final String assetId;
|
||||
final String albumId;
|
||||
const LocalAlbumAssetEntityData(
|
||||
{required this.assetId, required this.albumId});
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
map['asset_id'] = i0.Variable<String>(assetId);
|
||||
map['album_id'] = i0.Variable<String>(albumId);
|
||||
return map;
|
||||
}
|
||||
|
||||
factory LocalAlbumAssetEntityData.fromJson(Map<String, dynamic> json,
|
||||
{i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return LocalAlbumAssetEntityData(
|
||||
assetId: serializer.fromJson<String>(json['assetId']),
|
||||
albumId: serializer.fromJson<String>(json['albumId']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'assetId': serializer.toJson<String>(assetId),
|
||||
'albumId': serializer.toJson<String>(albumId),
|
||||
};
|
||||
}
|
||||
|
||||
i1.LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) =>
|
||||
i1.LocalAlbumAssetEntityData(
|
||||
assetId: assetId ?? this.assetId,
|
||||
albumId: albumId ?? this.albumId,
|
||||
);
|
||||
LocalAlbumAssetEntityData copyWithCompanion(
|
||||
i1.LocalAlbumAssetEntityCompanion data) {
|
||||
return LocalAlbumAssetEntityData(
|
||||
assetId: data.assetId.present ? data.assetId.value : this.assetId,
|
||||
albumId: data.albumId.present ? data.albumId.value : this.albumId,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('LocalAlbumAssetEntityData(')
|
||||
..write('assetId: $assetId, ')
|
||||
..write('albumId: $albumId')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(assetId, albumId);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is i1.LocalAlbumAssetEntityData &&
|
||||
other.assetId == this.assetId &&
|
||||
other.albumId == this.albumId);
|
||||
}
|
||||
|
||||
class LocalAlbumAssetEntityCompanion
|
||||
extends i0.UpdateCompanion<i1.LocalAlbumAssetEntityData> {
|
||||
final i0.Value<String> assetId;
|
||||
final i0.Value<String> albumId;
|
||||
const LocalAlbumAssetEntityCompanion({
|
||||
this.assetId = const i0.Value.absent(),
|
||||
this.albumId = const i0.Value.absent(),
|
||||
});
|
||||
LocalAlbumAssetEntityCompanion.insert({
|
||||
required String assetId,
|
||||
required String albumId,
|
||||
}) : assetId = i0.Value(assetId),
|
||||
albumId = i0.Value(albumId);
|
||||
static i0.Insertable<i1.LocalAlbumAssetEntityData> custom({
|
||||
i0.Expression<String>? assetId,
|
||||
i0.Expression<String>? albumId,
|
||||
}) {
|
||||
return i0.RawValuesInsertable({
|
||||
if (assetId != null) 'asset_id': assetId,
|
||||
if (albumId != null) 'album_id': albumId,
|
||||
});
|
||||
}
|
||||
|
||||
i1.LocalAlbumAssetEntityCompanion copyWith(
|
||||
{i0.Value<String>? assetId, i0.Value<String>? albumId}) {
|
||||
return i1.LocalAlbumAssetEntityCompanion(
|
||||
assetId: assetId ?? this.assetId,
|
||||
albumId: albumId ?? this.albumId,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
if (assetId.present) {
|
||||
map['asset_id'] = i0.Variable<String>(assetId.value);
|
||||
}
|
||||
if (albumId.present) {
|
||||
map['album_id'] = i0.Variable<String>(albumId.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('LocalAlbumAssetEntityCompanion(')
|
||||
..write('assetId: $assetId, ')
|
||||
..write('albumId: $albumId')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
17
mobile/lib/infrastructure/entities/local_asset.entity.dart
Normal file
17
mobile/lib/infrastructure/entities/local_asset.entity.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
||||
|
||||
@TableIndex(name: 'idx_local_asset_checksum', columns: {#checksum})
|
||||
class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
|
||||
const LocalAssetEntity();
|
||||
|
||||
TextColumn get id => text()();
|
||||
TextColumn get checksum => text().nullable()();
|
||||
|
||||
// Only used during backup to mirror the favorite status of the asset in the server
|
||||
BoolColumn get isFavorite => boolean().withDefault(const Constant(false))();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {id};
|
||||
}
|
||||
760
mobile/lib/infrastructure/entities/local_asset.entity.drift.dart
generated
Normal file
760
mobile/lib/infrastructure/entities/local_asset.entity.drift.dart
generated
Normal file
@@ -0,0 +1,760 @@
|
||||
// dart format width=80
|
||||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'
|
||||
as i1;
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart' as i2;
|
||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'
|
||||
as i3;
|
||||
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4;
|
||||
|
||||
typedef $$LocalAssetEntityTableCreateCompanionBuilder
|
||||
= i1.LocalAssetEntityCompanion Function({
|
||||
required String name,
|
||||
required i2.AssetType type,
|
||||
i0.Value<DateTime> createdAt,
|
||||
i0.Value<DateTime> updatedAt,
|
||||
i0.Value<int?> width,
|
||||
i0.Value<int?> height,
|
||||
i0.Value<int?> durationInSeconds,
|
||||
required String id,
|
||||
i0.Value<String?> checksum,
|
||||
i0.Value<bool> isFavorite,
|
||||
});
|
||||
typedef $$LocalAssetEntityTableUpdateCompanionBuilder
|
||||
= i1.LocalAssetEntityCompanion Function({
|
||||
i0.Value<String> name,
|
||||
i0.Value<i2.AssetType> type,
|
||||
i0.Value<DateTime> createdAt,
|
||||
i0.Value<DateTime> updatedAt,
|
||||
i0.Value<int?> width,
|
||||
i0.Value<int?> height,
|
||||
i0.Value<int?> durationInSeconds,
|
||||
i0.Value<String> id,
|
||||
i0.Value<String?> checksum,
|
||||
i0.Value<bool> isFavorite,
|
||||
});
|
||||
|
||||
class $$LocalAssetEntityTableFilterComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAssetEntityTable> {
|
||||
$$LocalAssetEntityTableFilterComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.ColumnFilters<String> get name => $composableBuilder(
|
||||
column: $table.name, builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnWithTypeConverterFilters<i2.AssetType, i2.AssetType, int> get type =>
|
||||
$composableBuilder(
|
||||
column: $table.type,
|
||||
builder: (column) => i0.ColumnWithTypeConverterFilters(column));
|
||||
|
||||
i0.ColumnFilters<DateTime> get createdAt => $composableBuilder(
|
||||
column: $table.createdAt, builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnFilters<DateTime> get updatedAt => $composableBuilder(
|
||||
column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnFilters<int> get width => $composableBuilder(
|
||||
column: $table.width, builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnFilters<int> get height => $composableBuilder(
|
||||
column: $table.height, builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnFilters<int> get durationInSeconds => $composableBuilder(
|
||||
column: $table.durationInSeconds,
|
||||
builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnFilters<String> get id => $composableBuilder(
|
||||
column: $table.id, builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnFilters<String> get checksum => $composableBuilder(
|
||||
column: $table.checksum, builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnFilters<bool> get isFavorite => $composableBuilder(
|
||||
column: $table.isFavorite, builder: (column) => i0.ColumnFilters(column));
|
||||
}
|
||||
|
||||
class $$LocalAssetEntityTableOrderingComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAssetEntityTable> {
|
||||
$$LocalAssetEntityTableOrderingComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.ColumnOrderings<String> get name => $composableBuilder(
|
||||
column: $table.name, builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<int> get type => $composableBuilder(
|
||||
column: $table.type, builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<DateTime> get createdAt => $composableBuilder(
|
||||
column: $table.createdAt,
|
||||
builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<DateTime> get updatedAt => $composableBuilder(
|
||||
column: $table.updatedAt,
|
||||
builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<int> get width => $composableBuilder(
|
||||
column: $table.width, builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<int> get height => $composableBuilder(
|
||||
column: $table.height, builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<int> get durationInSeconds => $composableBuilder(
|
||||
column: $table.durationInSeconds,
|
||||
builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<String> get id => $composableBuilder(
|
||||
column: $table.id, builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<String> get checksum => $composableBuilder(
|
||||
column: $table.checksum, builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<bool> get isFavorite => $composableBuilder(
|
||||
column: $table.isFavorite,
|
||||
builder: (column) => i0.ColumnOrderings(column));
|
||||
}
|
||||
|
||||
class $$LocalAssetEntityTableAnnotationComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAssetEntityTable> {
|
||||
$$LocalAssetEntityTableAnnotationComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.GeneratedColumn<String> get name =>
|
||||
$composableBuilder(column: $table.name, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumnWithTypeConverter<i2.AssetType, int> get type =>
|
||||
$composableBuilder(column: $table.type, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<DateTime> get createdAt =>
|
||||
$composableBuilder(column: $table.createdAt, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<DateTime> get updatedAt =>
|
||||
$composableBuilder(column: $table.updatedAt, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<int> get width =>
|
||||
$composableBuilder(column: $table.width, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<int> get height =>
|
||||
$composableBuilder(column: $table.height, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<int> get durationInSeconds => $composableBuilder(
|
||||
column: $table.durationInSeconds, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<String> get id =>
|
||||
$composableBuilder(column: $table.id, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<String> get checksum =>
|
||||
$composableBuilder(column: $table.checksum, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<bool> get isFavorite => $composableBuilder(
|
||||
column: $table.isFavorite, builder: (column) => column);
|
||||
}
|
||||
|
||||
class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
|
||||
i0.GeneratedDatabase,
|
||||
i1.$LocalAssetEntityTable,
|
||||
i1.LocalAssetEntityData,
|
||||
i1.$$LocalAssetEntityTableFilterComposer,
|
||||
i1.$$LocalAssetEntityTableOrderingComposer,
|
||||
i1.$$LocalAssetEntityTableAnnotationComposer,
|
||||
$$LocalAssetEntityTableCreateCompanionBuilder,
|
||||
$$LocalAssetEntityTableUpdateCompanionBuilder,
|
||||
(
|
||||
i1.LocalAssetEntityData,
|
||||
i0.BaseReferences<i0.GeneratedDatabase, i1.$LocalAssetEntityTable,
|
||||
i1.LocalAssetEntityData>
|
||||
),
|
||||
i1.LocalAssetEntityData,
|
||||
i0.PrefetchHooks Function()> {
|
||||
$$LocalAssetEntityTableTableManager(
|
||||
i0.GeneratedDatabase db, i1.$LocalAssetEntityTable table)
|
||||
: super(i0.TableManagerState(
|
||||
db: db,
|
||||
table: table,
|
||||
createFilteringComposer: () =>
|
||||
i1.$$LocalAssetEntityTableFilterComposer($db: db, $table: table),
|
||||
createOrderingComposer: () => i1
|
||||
.$$LocalAssetEntityTableOrderingComposer($db: db, $table: table),
|
||||
createComputedFieldComposer: () =>
|
||||
i1.$$LocalAssetEntityTableAnnotationComposer(
|
||||
$db: db, $table: table),
|
||||
updateCompanionCallback: ({
|
||||
i0.Value<String> name = const i0.Value.absent(),
|
||||
i0.Value<i2.AssetType> type = const i0.Value.absent(),
|
||||
i0.Value<DateTime> createdAt = const i0.Value.absent(),
|
||||
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
||||
i0.Value<int?> width = const i0.Value.absent(),
|
||||
i0.Value<int?> height = const i0.Value.absent(),
|
||||
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
|
||||
i0.Value<String> id = const i0.Value.absent(),
|
||||
i0.Value<String?> checksum = const i0.Value.absent(),
|
||||
i0.Value<bool> isFavorite = const i0.Value.absent(),
|
||||
}) =>
|
||||
i1.LocalAssetEntityCompanion(
|
||||
name: name,
|
||||
type: type,
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
width: width,
|
||||
height: height,
|
||||
durationInSeconds: durationInSeconds,
|
||||
id: id,
|
||||
checksum: checksum,
|
||||
isFavorite: isFavorite,
|
||||
),
|
||||
createCompanionCallback: ({
|
||||
required String name,
|
||||
required i2.AssetType type,
|
||||
i0.Value<DateTime> createdAt = const i0.Value.absent(),
|
||||
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
||||
i0.Value<int?> width = const i0.Value.absent(),
|
||||
i0.Value<int?> height = const i0.Value.absent(),
|
||||
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
|
||||
required String id,
|
||||
i0.Value<String?> checksum = const i0.Value.absent(),
|
||||
i0.Value<bool> isFavorite = const i0.Value.absent(),
|
||||
}) =>
|
||||
i1.LocalAssetEntityCompanion.insert(
|
||||
name: name,
|
||||
type: type,
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
width: width,
|
||||
height: height,
|
||||
durationInSeconds: durationInSeconds,
|
||||
id: id,
|
||||
checksum: checksum,
|
||||
isFavorite: isFavorite,
|
||||
),
|
||||
withReferenceMapper: (p0) => p0
|
||||
.map((e) => (e.readTable(table), i0.BaseReferences(db, table, e)))
|
||||
.toList(),
|
||||
prefetchHooksCallback: null,
|
||||
));
|
||||
}
|
||||
|
||||
typedef $$LocalAssetEntityTableProcessedTableManager = i0.ProcessedTableManager<
|
||||
i0.GeneratedDatabase,
|
||||
i1.$LocalAssetEntityTable,
|
||||
i1.LocalAssetEntityData,
|
||||
i1.$$LocalAssetEntityTableFilterComposer,
|
||||
i1.$$LocalAssetEntityTableOrderingComposer,
|
||||
i1.$$LocalAssetEntityTableAnnotationComposer,
|
||||
$$LocalAssetEntityTableCreateCompanionBuilder,
|
||||
$$LocalAssetEntityTableUpdateCompanionBuilder,
|
||||
(
|
||||
i1.LocalAssetEntityData,
|
||||
i0.BaseReferences<i0.GeneratedDatabase, i1.$LocalAssetEntityTable,
|
||||
i1.LocalAssetEntityData>
|
||||
),
|
||||
i1.LocalAssetEntityData,
|
||||
i0.PrefetchHooks Function()>;
|
||||
i0.Index get idxLocalAssetChecksum => i0.Index('idx_local_asset_checksum',
|
||||
'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)');
|
||||
|
||||
class $LocalAssetEntityTable extends i3.LocalAssetEntity
|
||||
with i0.TableInfo<$LocalAssetEntityTable, i1.LocalAssetEntityData> {
|
||||
@override
|
||||
final i0.GeneratedDatabase attachedDatabase;
|
||||
final String? _alias;
|
||||
$LocalAssetEntityTable(this.attachedDatabase, [this._alias]);
|
||||
static const i0.VerificationMeta _nameMeta =
|
||||
const i0.VerificationMeta('name');
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> name = i0.GeneratedColumn<String>(
|
||||
'name', aliasedName, false,
|
||||
type: i0.DriftSqlType.string, requiredDuringInsert: true);
|
||||
@override
|
||||
late final i0.GeneratedColumnWithTypeConverter<i2.AssetType, int> type =
|
||||
i0.GeneratedColumn<int>('type', aliasedName, false,
|
||||
type: i0.DriftSqlType.int, requiredDuringInsert: true)
|
||||
.withConverter<i2.AssetType>(
|
||||
i1.$LocalAssetEntityTable.$convertertype);
|
||||
static const i0.VerificationMeta _createdAtMeta =
|
||||
const i0.VerificationMeta('createdAt');
|
||||
@override
|
||||
late final i0.GeneratedColumn<DateTime> createdAt =
|
||||
i0.GeneratedColumn<DateTime>('created_at', aliasedName, false,
|
||||
type: i0.DriftSqlType.dateTime,
|
||||
requiredDuringInsert: false,
|
||||
defaultValue: i4.currentDateAndTime);
|
||||
static const i0.VerificationMeta _updatedAtMeta =
|
||||
const i0.VerificationMeta('updatedAt');
|
||||
@override
|
||||
late final i0.GeneratedColumn<DateTime> updatedAt =
|
||||
i0.GeneratedColumn<DateTime>('updated_at', aliasedName, false,
|
||||
type: i0.DriftSqlType.dateTime,
|
||||
requiredDuringInsert: false,
|
||||
defaultValue: i4.currentDateAndTime);
|
||||
static const i0.VerificationMeta _widthMeta =
|
||||
const i0.VerificationMeta('width');
|
||||
@override
|
||||
late final i0.GeneratedColumn<int> width = i0.GeneratedColumn<int>(
|
||||
'width', aliasedName, true,
|
||||
type: i0.DriftSqlType.int, requiredDuringInsert: false);
|
||||
static const i0.VerificationMeta _heightMeta =
|
||||
const i0.VerificationMeta('height');
|
||||
@override
|
||||
late final i0.GeneratedColumn<int> height = i0.GeneratedColumn<int>(
|
||||
'height', aliasedName, true,
|
||||
type: i0.DriftSqlType.int, requiredDuringInsert: false);
|
||||
static const i0.VerificationMeta _durationInSecondsMeta =
|
||||
const i0.VerificationMeta('durationInSeconds');
|
||||
@override
|
||||
late final i0.GeneratedColumn<int> durationInSeconds =
|
||||
i0.GeneratedColumn<int>('duration_in_seconds', aliasedName, true,
|
||||
type: i0.DriftSqlType.int, requiredDuringInsert: false);
|
||||
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
|
||||
'id', aliasedName, false,
|
||||
type: i0.DriftSqlType.string, requiredDuringInsert: true);
|
||||
static const i0.VerificationMeta _checksumMeta =
|
||||
const i0.VerificationMeta('checksum');
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> checksum = i0.GeneratedColumn<String>(
|
||||
'checksum', aliasedName, true,
|
||||
type: i0.DriftSqlType.string, requiredDuringInsert: false);
|
||||
static const i0.VerificationMeta _isFavoriteMeta =
|
||||
const i0.VerificationMeta('isFavorite');
|
||||
@override
|
||||
late final i0.GeneratedColumn<bool> isFavorite = i0.GeneratedColumn<bool>(
|
||||
'is_favorite', aliasedName, false,
|
||||
type: i0.DriftSqlType.bool,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||
'CHECK ("is_favorite" IN (0, 1))'),
|
||||
defaultValue: const i4.Constant(false));
|
||||
@override
|
||||
List<i0.GeneratedColumn> get $columns => [
|
||||
name,
|
||||
type,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
width,
|
||||
height,
|
||||
durationInSeconds,
|
||||
id,
|
||||
checksum,
|
||||
isFavorite
|
||||
];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@override
|
||||
String get actualTableName => $name;
|
||||
static const String $name = 'local_asset_entity';
|
||||
@override
|
||||
i0.VerificationContext validateIntegrity(
|
||||
i0.Insertable<i1.LocalAssetEntityData> instance,
|
||||
{bool isInserting = false}) {
|
||||
final context = i0.VerificationContext();
|
||||
final data = instance.toColumns(true);
|
||||
if (data.containsKey('name')) {
|
||||
context.handle(
|
||||
_nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_nameMeta);
|
||||
}
|
||||
if (data.containsKey('created_at')) {
|
||||
context.handle(_createdAtMeta,
|
||||
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta));
|
||||
}
|
||||
if (data.containsKey('updated_at')) {
|
||||
context.handle(_updatedAtMeta,
|
||||
updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta));
|
||||
}
|
||||
if (data.containsKey('width')) {
|
||||
context.handle(
|
||||
_widthMeta, width.isAcceptableOrUnknown(data['width']!, _widthMeta));
|
||||
}
|
||||
if (data.containsKey('height')) {
|
||||
context.handle(_heightMeta,
|
||||
height.isAcceptableOrUnknown(data['height']!, _heightMeta));
|
||||
}
|
||||
if (data.containsKey('duration_in_seconds')) {
|
||||
context.handle(
|
||||
_durationInSecondsMeta,
|
||||
durationInSeconds.isAcceptableOrUnknown(
|
||||
data['duration_in_seconds']!, _durationInSecondsMeta));
|
||||
}
|
||||
if (data.containsKey('id')) {
|
||||
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_idMeta);
|
||||
}
|
||||
if (data.containsKey('checksum')) {
|
||||
context.handle(_checksumMeta,
|
||||
checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta));
|
||||
}
|
||||
if (data.containsKey('is_favorite')) {
|
||||
context.handle(
|
||||
_isFavoriteMeta,
|
||||
isFavorite.isAcceptableOrUnknown(
|
||||
data['is_favorite']!, _isFavoriteMeta));
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@override
|
||||
Set<i0.GeneratedColumn> get $primaryKey => {id};
|
||||
@override
|
||||
i1.LocalAssetEntityData map(Map<String, dynamic> data,
|
||||
{String? tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return i1.LocalAssetEntityData(
|
||||
name: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!,
|
||||
type: i1.$LocalAssetEntityTable.$convertertype.fromSql(attachedDatabase
|
||||
.typeMapping
|
||||
.read(i0.DriftSqlType.int, data['${effectivePrefix}type'])!),
|
||||
createdAt: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
|
||||
updatedAt: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!,
|
||||
width: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.int, data['${effectivePrefix}width']),
|
||||
height: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.int, data['${effectivePrefix}height']),
|
||||
durationInSeconds: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']),
|
||||
id: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!,
|
||||
checksum: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}checksum']),
|
||||
isFavorite: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
$LocalAssetEntityTable createAlias(String alias) {
|
||||
return $LocalAssetEntityTable(attachedDatabase, alias);
|
||||
}
|
||||
|
||||
static i0.JsonTypeConverter2<i2.AssetType, int, int> $convertertype =
|
||||
const i0.EnumIndexConverter<i2.AssetType>(i2.AssetType.values);
|
||||
@override
|
||||
bool get withoutRowId => true;
|
||||
@override
|
||||
bool get isStrict => true;
|
||||
}
|
||||
|
||||
class LocalAssetEntityData extends i0.DataClass
|
||||
implements i0.Insertable<i1.LocalAssetEntityData> {
|
||||
final String name;
|
||||
final i2.AssetType type;
|
||||
final DateTime createdAt;
|
||||
final DateTime updatedAt;
|
||||
final int? width;
|
||||
final int? height;
|
||||
final int? durationInSeconds;
|
||||
final String id;
|
||||
final String? checksum;
|
||||
final bool isFavorite;
|
||||
const LocalAssetEntityData(
|
||||
{required this.name,
|
||||
required this.type,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
this.width,
|
||||
this.height,
|
||||
this.durationInSeconds,
|
||||
required this.id,
|
||||
this.checksum,
|
||||
required this.isFavorite});
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
map['name'] = i0.Variable<String>(name);
|
||||
{
|
||||
map['type'] = i0.Variable<int>(
|
||||
i1.$LocalAssetEntityTable.$convertertype.toSql(type));
|
||||
}
|
||||
map['created_at'] = i0.Variable<DateTime>(createdAt);
|
||||
map['updated_at'] = i0.Variable<DateTime>(updatedAt);
|
||||
if (!nullToAbsent || width != null) {
|
||||
map['width'] = i0.Variable<int>(width);
|
||||
}
|
||||
if (!nullToAbsent || height != null) {
|
||||
map['height'] = i0.Variable<int>(height);
|
||||
}
|
||||
if (!nullToAbsent || durationInSeconds != null) {
|
||||
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds);
|
||||
}
|
||||
map['id'] = i0.Variable<String>(id);
|
||||
if (!nullToAbsent || checksum != null) {
|
||||
map['checksum'] = i0.Variable<String>(checksum);
|
||||
}
|
||||
map['is_favorite'] = i0.Variable<bool>(isFavorite);
|
||||
return map;
|
||||
}
|
||||
|
||||
factory LocalAssetEntityData.fromJson(Map<String, dynamic> json,
|
||||
{i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return LocalAssetEntityData(
|
||||
name: serializer.fromJson<String>(json['name']),
|
||||
type: i1.$LocalAssetEntityTable.$convertertype
|
||||
.fromJson(serializer.fromJson<int>(json['type'])),
|
||||
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
|
||||
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
|
||||
width: serializer.fromJson<int?>(json['width']),
|
||||
height: serializer.fromJson<int?>(json['height']),
|
||||
durationInSeconds: serializer.fromJson<int?>(json['durationInSeconds']),
|
||||
id: serializer.fromJson<String>(json['id']),
|
||||
checksum: serializer.fromJson<String?>(json['checksum']),
|
||||
isFavorite: serializer.fromJson<bool>(json['isFavorite']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'name': serializer.toJson<String>(name),
|
||||
'type': serializer
|
||||
.toJson<int>(i1.$LocalAssetEntityTable.$convertertype.toJson(type)),
|
||||
'createdAt': serializer.toJson<DateTime>(createdAt),
|
||||
'updatedAt': serializer.toJson<DateTime>(updatedAt),
|
||||
'width': serializer.toJson<int?>(width),
|
||||
'height': serializer.toJson<int?>(height),
|
||||
'durationInSeconds': serializer.toJson<int?>(durationInSeconds),
|
||||
'id': serializer.toJson<String>(id),
|
||||
'checksum': serializer.toJson<String?>(checksum),
|
||||
'isFavorite': serializer.toJson<bool>(isFavorite),
|
||||
};
|
||||
}
|
||||
|
||||
i1.LocalAssetEntityData copyWith(
|
||||
{String? name,
|
||||
i2.AssetType? type,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
i0.Value<int?> width = const i0.Value.absent(),
|
||||
i0.Value<int?> height = const i0.Value.absent(),
|
||||
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
|
||||
String? id,
|
||||
i0.Value<String?> checksum = const i0.Value.absent(),
|
||||
bool? isFavorite}) =>
|
||||
i1.LocalAssetEntityData(
|
||||
name: name ?? this.name,
|
||||
type: type ?? this.type,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
width: width.present ? width.value : this.width,
|
||||
height: height.present ? height.value : this.height,
|
||||
durationInSeconds: durationInSeconds.present
|
||||
? durationInSeconds.value
|
||||
: this.durationInSeconds,
|
||||
id: id ?? this.id,
|
||||
checksum: checksum.present ? checksum.value : this.checksum,
|
||||
isFavorite: isFavorite ?? this.isFavorite,
|
||||
);
|
||||
LocalAssetEntityData copyWithCompanion(i1.LocalAssetEntityCompanion data) {
|
||||
return LocalAssetEntityData(
|
||||
name: data.name.present ? data.name.value : this.name,
|
||||
type: data.type.present ? data.type.value : this.type,
|
||||
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
|
||||
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
|
||||
width: data.width.present ? data.width.value : this.width,
|
||||
height: data.height.present ? data.height.value : this.height,
|
||||
durationInSeconds: data.durationInSeconds.present
|
||||
? data.durationInSeconds.value
|
||||
: this.durationInSeconds,
|
||||
id: data.id.present ? data.id.value : this.id,
|
||||
checksum: data.checksum.present ? data.checksum.value : this.checksum,
|
||||
isFavorite:
|
||||
data.isFavorite.present ? data.isFavorite.value : this.isFavorite,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('LocalAssetEntityData(')
|
||||
..write('name: $name, ')
|
||||
..write('type: $type, ')
|
||||
..write('createdAt: $createdAt, ')
|
||||
..write('updatedAt: $updatedAt, ')
|
||||
..write('width: $width, ')
|
||||
..write('height: $height, ')
|
||||
..write('durationInSeconds: $durationInSeconds, ')
|
||||
..write('id: $id, ')
|
||||
..write('checksum: $checksum, ')
|
||||
..write('isFavorite: $isFavorite')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(name, type, createdAt, updatedAt, width,
|
||||
height, durationInSeconds, id, checksum, isFavorite);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is i1.LocalAssetEntityData &&
|
||||
other.name == this.name &&
|
||||
other.type == this.type &&
|
||||
other.createdAt == this.createdAt &&
|
||||
other.updatedAt == this.updatedAt &&
|
||||
other.width == this.width &&
|
||||
other.height == this.height &&
|
||||
other.durationInSeconds == this.durationInSeconds &&
|
||||
other.id == this.id &&
|
||||
other.checksum == this.checksum &&
|
||||
other.isFavorite == this.isFavorite);
|
||||
}
|
||||
|
||||
class LocalAssetEntityCompanion
|
||||
extends i0.UpdateCompanion<i1.LocalAssetEntityData> {
|
||||
final i0.Value<String> name;
|
||||
final i0.Value<i2.AssetType> type;
|
||||
final i0.Value<DateTime> createdAt;
|
||||
final i0.Value<DateTime> updatedAt;
|
||||
final i0.Value<int?> width;
|
||||
final i0.Value<int?> height;
|
||||
final i0.Value<int?> durationInSeconds;
|
||||
final i0.Value<String> id;
|
||||
final i0.Value<String?> checksum;
|
||||
final i0.Value<bool> isFavorite;
|
||||
const LocalAssetEntityCompanion({
|
||||
this.name = const i0.Value.absent(),
|
||||
this.type = const i0.Value.absent(),
|
||||
this.createdAt = const i0.Value.absent(),
|
||||
this.updatedAt = const i0.Value.absent(),
|
||||
this.width = const i0.Value.absent(),
|
||||
this.height = const i0.Value.absent(),
|
||||
this.durationInSeconds = const i0.Value.absent(),
|
||||
this.id = const i0.Value.absent(),
|
||||
this.checksum = const i0.Value.absent(),
|
||||
this.isFavorite = const i0.Value.absent(),
|
||||
});
|
||||
LocalAssetEntityCompanion.insert({
|
||||
required String name,
|
||||
required i2.AssetType type,
|
||||
this.createdAt = const i0.Value.absent(),
|
||||
this.updatedAt = const i0.Value.absent(),
|
||||
this.width = const i0.Value.absent(),
|
||||
this.height = const i0.Value.absent(),
|
||||
this.durationInSeconds = const i0.Value.absent(),
|
||||
required String id,
|
||||
this.checksum = const i0.Value.absent(),
|
||||
this.isFavorite = const i0.Value.absent(),
|
||||
}) : name = i0.Value(name),
|
||||
type = i0.Value(type),
|
||||
id = i0.Value(id);
|
||||
static i0.Insertable<i1.LocalAssetEntityData> custom({
|
||||
i0.Expression<String>? name,
|
||||
i0.Expression<int>? type,
|
||||
i0.Expression<DateTime>? createdAt,
|
||||
i0.Expression<DateTime>? updatedAt,
|
||||
i0.Expression<int>? width,
|
||||
i0.Expression<int>? height,
|
||||
i0.Expression<int>? durationInSeconds,
|
||||
i0.Expression<String>? id,
|
||||
i0.Expression<String>? checksum,
|
||||
i0.Expression<bool>? isFavorite,
|
||||
}) {
|
||||
return i0.RawValuesInsertable({
|
||||
if (name != null) 'name': name,
|
||||
if (type != null) 'type': type,
|
||||
if (createdAt != null) 'created_at': createdAt,
|
||||
if (updatedAt != null) 'updated_at': updatedAt,
|
||||
if (width != null) 'width': width,
|
||||
if (height != null) 'height': height,
|
||||
if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds,
|
||||
if (id != null) 'id': id,
|
||||
if (checksum != null) 'checksum': checksum,
|
||||
if (isFavorite != null) 'is_favorite': isFavorite,
|
||||
});
|
||||
}
|
||||
|
||||
i1.LocalAssetEntityCompanion copyWith(
|
||||
{i0.Value<String>? name,
|
||||
i0.Value<i2.AssetType>? type,
|
||||
i0.Value<DateTime>? createdAt,
|
||||
i0.Value<DateTime>? updatedAt,
|
||||
i0.Value<int?>? width,
|
||||
i0.Value<int?>? height,
|
||||
i0.Value<int?>? durationInSeconds,
|
||||
i0.Value<String>? id,
|
||||
i0.Value<String?>? checksum,
|
||||
i0.Value<bool>? isFavorite}) {
|
||||
return i1.LocalAssetEntityCompanion(
|
||||
name: name ?? this.name,
|
||||
type: type ?? this.type,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
width: width ?? this.width,
|
||||
height: height ?? this.height,
|
||||
durationInSeconds: durationInSeconds ?? this.durationInSeconds,
|
||||
id: id ?? this.id,
|
||||
checksum: checksum ?? this.checksum,
|
||||
isFavorite: isFavorite ?? this.isFavorite,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
if (name.present) {
|
||||
map['name'] = i0.Variable<String>(name.value);
|
||||
}
|
||||
if (type.present) {
|
||||
map['type'] = i0.Variable<int>(
|
||||
i1.$LocalAssetEntityTable.$convertertype.toSql(type.value));
|
||||
}
|
||||
if (createdAt.present) {
|
||||
map['created_at'] = i0.Variable<DateTime>(createdAt.value);
|
||||
}
|
||||
if (updatedAt.present) {
|
||||
map['updated_at'] = i0.Variable<DateTime>(updatedAt.value);
|
||||
}
|
||||
if (width.present) {
|
||||
map['width'] = i0.Variable<int>(width.value);
|
||||
}
|
||||
if (height.present) {
|
||||
map['height'] = i0.Variable<int>(height.value);
|
||||
}
|
||||
if (durationInSeconds.present) {
|
||||
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds.value);
|
||||
}
|
||||
if (id.present) {
|
||||
map['id'] = i0.Variable<String>(id.value);
|
||||
}
|
||||
if (checksum.present) {
|
||||
map['checksum'] = i0.Variable<String>(checksum.value);
|
||||
}
|
||||
if (isFavorite.present) {
|
||||
map['is_favorite'] = i0.Variable<bool>(isFavorite.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('LocalAssetEntityCompanion(')
|
||||
..write('name: $name, ')
|
||||
..write('type: $type, ')
|
||||
..write('createdAt: $createdAt, ')
|
||||
..write('updatedAt: $updatedAt, ')
|
||||
..write('width: $width, ')
|
||||
..write('height: $height, ')
|
||||
..write('durationInSeconds: $durationInSeconds, ')
|
||||
..write('id: $id, ')
|
||||
..write('checksum: $checksum, ')
|
||||
..write('isFavorite: $isFavorite')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,11 @@ import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
||||
class PartnerEntity extends Table with DriftDefaultsMixin {
|
||||
const PartnerEntity();
|
||||
|
||||
BlobColumn get sharedById =>
|
||||
blob().references(UserEntity, #id, onDelete: KeyAction.cascade)();
|
||||
TextColumn get sharedById =>
|
||||
text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
|
||||
|
||||
BlobColumn get sharedWithId =>
|
||||
blob().references(UserEntity, #id, onDelete: KeyAction.cascade)();
|
||||
TextColumn get sharedWithId =>
|
||||
text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
|
||||
|
||||
BoolColumn get inTimeline => boolean().withDefault(const Constant(false))();
|
||||
|
||||
|
||||
@@ -3,24 +3,23 @@
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart'
|
||||
as i1;
|
||||
import 'dart:typed_data' as i2;
|
||||
import 'package:immich_mobile/infrastructure/entities/partner.entity.dart'
|
||||
as i3;
|
||||
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4;
|
||||
as i2;
|
||||
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i3;
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
|
||||
as i5;
|
||||
import 'package:drift/internal/modular.dart' as i6;
|
||||
as i4;
|
||||
import 'package:drift/internal/modular.dart' as i5;
|
||||
|
||||
typedef $$PartnerEntityTableCreateCompanionBuilder = i1.PartnerEntityCompanion
|
||||
Function({
|
||||
required i2.Uint8List sharedById,
|
||||
required i2.Uint8List sharedWithId,
|
||||
required String sharedById,
|
||||
required String sharedWithId,
|
||||
i0.Value<bool> inTimeline,
|
||||
});
|
||||
typedef $$PartnerEntityTableUpdateCompanionBuilder = i1.PartnerEntityCompanion
|
||||
Function({
|
||||
i0.Value<i2.Uint8List> sharedById,
|
||||
i0.Value<i2.Uint8List> sharedWithId,
|
||||
i0.Value<String> sharedById,
|
||||
i0.Value<String> sharedWithId,
|
||||
i0.Value<bool> inTimeline,
|
||||
});
|
||||
|
||||
@@ -29,25 +28,25 @@ final class $$PartnerEntityTableReferences extends i0.BaseReferences<
|
||||
$$PartnerEntityTableReferences(
|
||||
super.$_db, super.$_table, super.$_typedResult);
|
||||
|
||||
static i5.$UserEntityTable _sharedByIdTable(i0.GeneratedDatabase db) =>
|
||||
i6.ReadDatabaseContainer(db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity')
|
||||
static i4.$UserEntityTable _sharedByIdTable(i0.GeneratedDatabase db) =>
|
||||
i5.ReadDatabaseContainer(db)
|
||||
.resultSet<i4.$UserEntityTable>('user_entity')
|
||||
.createAlias(i0.$_aliasNameGenerator(
|
||||
i6.ReadDatabaseContainer(db)
|
||||
i5.ReadDatabaseContainer(db)
|
||||
.resultSet<i1.$PartnerEntityTable>('partner_entity')
|
||||
.sharedById,
|
||||
i6.ReadDatabaseContainer(db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity')
|
||||
i5.ReadDatabaseContainer(db)
|
||||
.resultSet<i4.$UserEntityTable>('user_entity')
|
||||
.id));
|
||||
|
||||
i5.$$UserEntityTableProcessedTableManager get sharedById {
|
||||
final $_column = $_itemColumn<i2.Uint8List>('shared_by_id')!;
|
||||
i4.$$UserEntityTableProcessedTableManager get sharedById {
|
||||
final $_column = $_itemColumn<String>('shared_by_id')!;
|
||||
|
||||
final manager = i5
|
||||
final manager = i4
|
||||
.$$UserEntityTableTableManager(
|
||||
$_db,
|
||||
i6.ReadDatabaseContainer($_db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity'))
|
||||
i5.ReadDatabaseContainer($_db)
|
||||
.resultSet<i4.$UserEntityTable>('user_entity'))
|
||||
.filter((f) => f.id.sqlEquals($_column));
|
||||
final item = $_typedResult.readTableOrNull(_sharedByIdTable($_db));
|
||||
if (item == null) return manager;
|
||||
@@ -55,25 +54,25 @@ final class $$PartnerEntityTableReferences extends i0.BaseReferences<
|
||||
manager.$state.copyWith(prefetchedData: [item]));
|
||||
}
|
||||
|
||||
static i5.$UserEntityTable _sharedWithIdTable(i0.GeneratedDatabase db) =>
|
||||
i6.ReadDatabaseContainer(db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity')
|
||||
static i4.$UserEntityTable _sharedWithIdTable(i0.GeneratedDatabase db) =>
|
||||
i5.ReadDatabaseContainer(db)
|
||||
.resultSet<i4.$UserEntityTable>('user_entity')
|
||||
.createAlias(i0.$_aliasNameGenerator(
|
||||
i6.ReadDatabaseContainer(db)
|
||||
i5.ReadDatabaseContainer(db)
|
||||
.resultSet<i1.$PartnerEntityTable>('partner_entity')
|
||||
.sharedWithId,
|
||||
i6.ReadDatabaseContainer(db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity')
|
||||
i5.ReadDatabaseContainer(db)
|
||||
.resultSet<i4.$UserEntityTable>('user_entity')
|
||||
.id));
|
||||
|
||||
i5.$$UserEntityTableProcessedTableManager get sharedWithId {
|
||||
final $_column = $_itemColumn<i2.Uint8List>('shared_with_id')!;
|
||||
i4.$$UserEntityTableProcessedTableManager get sharedWithId {
|
||||
final $_column = $_itemColumn<String>('shared_with_id')!;
|
||||
|
||||
final manager = i5
|
||||
final manager = i4
|
||||
.$$UserEntityTableTableManager(
|
||||
$_db,
|
||||
i6.ReadDatabaseContainer($_db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity'))
|
||||
i5.ReadDatabaseContainer($_db)
|
||||
.resultSet<i4.$UserEntityTable>('user_entity'))
|
||||
.filter((f) => f.id.sqlEquals($_column));
|
||||
final item = $_typedResult.readTableOrNull(_sharedWithIdTable($_db));
|
||||
if (item == null) return manager;
|
||||
@@ -94,20 +93,20 @@ class $$PartnerEntityTableFilterComposer
|
||||
i0.ColumnFilters<bool> get inTimeline => $composableBuilder(
|
||||
column: $table.inTimeline, builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i5.$$UserEntityTableFilterComposer get sharedById {
|
||||
final i5.$$UserEntityTableFilterComposer composer = $composerBuilder(
|
||||
i4.$$UserEntityTableFilterComposer get sharedById {
|
||||
final i4.$$UserEntityTableFilterComposer composer = $composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.sharedById,
|
||||
referencedTable: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
referencedTable: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i5.$$UserEntityTableFilterComposer(
|
||||
i4.$$UserEntityTableFilterComposer(
|
||||
$db: $db,
|
||||
$table: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
$table: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
@@ -116,20 +115,20 @@ class $$PartnerEntityTableFilterComposer
|
||||
return composer;
|
||||
}
|
||||
|
||||
i5.$$UserEntityTableFilterComposer get sharedWithId {
|
||||
final i5.$$UserEntityTableFilterComposer composer = $composerBuilder(
|
||||
i4.$$UserEntityTableFilterComposer get sharedWithId {
|
||||
final i4.$$UserEntityTableFilterComposer composer = $composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.sharedWithId,
|
||||
referencedTable: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
referencedTable: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i5.$$UserEntityTableFilterComposer(
|
||||
i4.$$UserEntityTableFilterComposer(
|
||||
$db: $db,
|
||||
$table: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
$table: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
@@ -152,20 +151,20 @@ class $$PartnerEntityTableOrderingComposer
|
||||
column: $table.inTimeline,
|
||||
builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i5.$$UserEntityTableOrderingComposer get sharedById {
|
||||
final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder(
|
||||
i4.$$UserEntityTableOrderingComposer get sharedById {
|
||||
final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.sharedById,
|
||||
referencedTable: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
referencedTable: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i5.$$UserEntityTableOrderingComposer(
|
||||
i4.$$UserEntityTableOrderingComposer(
|
||||
$db: $db,
|
||||
$table: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
$table: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
@@ -174,20 +173,20 @@ class $$PartnerEntityTableOrderingComposer
|
||||
return composer;
|
||||
}
|
||||
|
||||
i5.$$UserEntityTableOrderingComposer get sharedWithId {
|
||||
final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder(
|
||||
i4.$$UserEntityTableOrderingComposer get sharedWithId {
|
||||
final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.sharedWithId,
|
||||
referencedTable: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
referencedTable: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i5.$$UserEntityTableOrderingComposer(
|
||||
i4.$$UserEntityTableOrderingComposer(
|
||||
$db: $db,
|
||||
$table: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
$table: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
@@ -209,20 +208,20 @@ class $$PartnerEntityTableAnnotationComposer
|
||||
i0.GeneratedColumn<bool> get inTimeline => $composableBuilder(
|
||||
column: $table.inTimeline, builder: (column) => column);
|
||||
|
||||
i5.$$UserEntityTableAnnotationComposer get sharedById {
|
||||
final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
|
||||
i4.$$UserEntityTableAnnotationComposer get sharedById {
|
||||
final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.sharedById,
|
||||
referencedTable: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
referencedTable: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i5.$$UserEntityTableAnnotationComposer(
|
||||
i4.$$UserEntityTableAnnotationComposer(
|
||||
$db: $db,
|
||||
$table: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
$table: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
@@ -231,20 +230,20 @@ class $$PartnerEntityTableAnnotationComposer
|
||||
return composer;
|
||||
}
|
||||
|
||||
i5.$$UserEntityTableAnnotationComposer get sharedWithId {
|
||||
final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
|
||||
i4.$$UserEntityTableAnnotationComposer get sharedWithId {
|
||||
final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.sharedWithId,
|
||||
referencedTable: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
referencedTable: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i5.$$UserEntityTableAnnotationComposer(
|
||||
i4.$$UserEntityTableAnnotationComposer(
|
||||
$db: $db,
|
||||
$table: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
$table: i5.ReadDatabaseContainer($db)
|
||||
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
@@ -278,8 +277,8 @@ class $$PartnerEntityTableTableManager extends i0.RootTableManager<
|
||||
createComputedFieldComposer: () =>
|
||||
i1.$$PartnerEntityTableAnnotationComposer($db: db, $table: table),
|
||||
updateCompanionCallback: ({
|
||||
i0.Value<i2.Uint8List> sharedById = const i0.Value.absent(),
|
||||
i0.Value<i2.Uint8List> sharedWithId = const i0.Value.absent(),
|
||||
i0.Value<String> sharedById = const i0.Value.absent(),
|
||||
i0.Value<String> sharedWithId = const i0.Value.absent(),
|
||||
i0.Value<bool> inTimeline = const i0.Value.absent(),
|
||||
}) =>
|
||||
i1.PartnerEntityCompanion(
|
||||
@@ -288,8 +287,8 @@ class $$PartnerEntityTableTableManager extends i0.RootTableManager<
|
||||
inTimeline: inTimeline,
|
||||
),
|
||||
createCompanionCallback: ({
|
||||
required i2.Uint8List sharedById,
|
||||
required i2.Uint8List sharedWithId,
|
||||
required String sharedById,
|
||||
required String sharedWithId,
|
||||
i0.Value<bool> inTimeline = const i0.Value.absent(),
|
||||
}) =>
|
||||
i1.PartnerEntityCompanion.insert(
|
||||
@@ -366,7 +365,7 @@ typedef $$PartnerEntityTableProcessedTableManager = i0.ProcessedTableManager<
|
||||
i1.PartnerEntityData,
|
||||
i0.PrefetchHooks Function({bool sharedById, bool sharedWithId})>;
|
||||
|
||||
class $PartnerEntityTable extends i3.PartnerEntity
|
||||
class $PartnerEntityTable extends i2.PartnerEntity
|
||||
with i0.TableInfo<$PartnerEntityTable, i1.PartnerEntityData> {
|
||||
@override
|
||||
final i0.GeneratedDatabase attachedDatabase;
|
||||
@@ -375,18 +374,18 @@ class $PartnerEntityTable extends i3.PartnerEntity
|
||||
static const i0.VerificationMeta _sharedByIdMeta =
|
||||
const i0.VerificationMeta('sharedById');
|
||||
@override
|
||||
late final i0.GeneratedColumn<i2.Uint8List> sharedById =
|
||||
i0.GeneratedColumn<i2.Uint8List>('shared_by_id', aliasedName, false,
|
||||
type: i0.DriftSqlType.blob,
|
||||
requiredDuringInsert: true,
|
||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||
'REFERENCES user_entity (id) ON DELETE CASCADE'));
|
||||
late final i0.GeneratedColumn<String> sharedById = i0.GeneratedColumn<String>(
|
||||
'shared_by_id', aliasedName, false,
|
||||
type: i0.DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||
'REFERENCES user_entity (id) ON DELETE CASCADE'));
|
||||
static const i0.VerificationMeta _sharedWithIdMeta =
|
||||
const i0.VerificationMeta('sharedWithId');
|
||||
@override
|
||||
late final i0.GeneratedColumn<i2.Uint8List> sharedWithId =
|
||||
i0.GeneratedColumn<i2.Uint8List>('shared_with_id', aliasedName, false,
|
||||
type: i0.DriftSqlType.blob,
|
||||
late final i0.GeneratedColumn<String> sharedWithId =
|
||||
i0.GeneratedColumn<String>('shared_with_id', aliasedName, false,
|
||||
type: i0.DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||
'REFERENCES user_entity (id) ON DELETE CASCADE'));
|
||||
@@ -399,7 +398,7 @@ class $PartnerEntityTable extends i3.PartnerEntity
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||
'CHECK ("in_timeline" IN (0, 1))'),
|
||||
defaultValue: const i4.Constant(false));
|
||||
defaultValue: const i3.Constant(false));
|
||||
@override
|
||||
List<i0.GeneratedColumn> get $columns =>
|
||||
[sharedById, sharedWithId, inTimeline];
|
||||
@@ -445,10 +444,10 @@ class $PartnerEntityTable extends i3.PartnerEntity
|
||||
i1.PartnerEntityData map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return i1.PartnerEntityData(
|
||||
sharedById: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.blob, data['${effectivePrefix}shared_by_id'])!,
|
||||
sharedById: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!,
|
||||
sharedWithId: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.blob, data['${effectivePrefix}shared_with_id'])!,
|
||||
i0.DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!,
|
||||
inTimeline: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!,
|
||||
);
|
||||
@@ -467,8 +466,8 @@ class $PartnerEntityTable extends i3.PartnerEntity
|
||||
|
||||
class PartnerEntityData extends i0.DataClass
|
||||
implements i0.Insertable<i1.PartnerEntityData> {
|
||||
final i2.Uint8List sharedById;
|
||||
final i2.Uint8List sharedWithId;
|
||||
final String sharedById;
|
||||
final String sharedWithId;
|
||||
final bool inTimeline;
|
||||
const PartnerEntityData(
|
||||
{required this.sharedById,
|
||||
@@ -477,8 +476,8 @@ class PartnerEntityData extends i0.DataClass
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
map['shared_by_id'] = i0.Variable<i2.Uint8List>(sharedById);
|
||||
map['shared_with_id'] = i0.Variable<i2.Uint8List>(sharedWithId);
|
||||
map['shared_by_id'] = i0.Variable<String>(sharedById);
|
||||
map['shared_with_id'] = i0.Variable<String>(sharedWithId);
|
||||
map['in_timeline'] = i0.Variable<bool>(inTimeline);
|
||||
return map;
|
||||
}
|
||||
@@ -487,8 +486,8 @@ class PartnerEntityData extends i0.DataClass
|
||||
{i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return PartnerEntityData(
|
||||
sharedById: serializer.fromJson<i2.Uint8List>(json['sharedById']),
|
||||
sharedWithId: serializer.fromJson<i2.Uint8List>(json['sharedWithId']),
|
||||
sharedById: serializer.fromJson<String>(json['sharedById']),
|
||||
sharedWithId: serializer.fromJson<String>(json['sharedWithId']),
|
||||
inTimeline: serializer.fromJson<bool>(json['inTimeline']),
|
||||
);
|
||||
}
|
||||
@@ -496,16 +495,14 @@ class PartnerEntityData extends i0.DataClass
|
||||
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'sharedById': serializer.toJson<i2.Uint8List>(sharedById),
|
||||
'sharedWithId': serializer.toJson<i2.Uint8List>(sharedWithId),
|
||||
'sharedById': serializer.toJson<String>(sharedById),
|
||||
'sharedWithId': serializer.toJson<String>(sharedWithId),
|
||||
'inTimeline': serializer.toJson<bool>(inTimeline),
|
||||
};
|
||||
}
|
||||
|
||||
i1.PartnerEntityData copyWith(
|
||||
{i2.Uint8List? sharedById,
|
||||
i2.Uint8List? sharedWithId,
|
||||
bool? inTimeline}) =>
|
||||
{String? sharedById, String? sharedWithId, bool? inTimeline}) =>
|
||||
i1.PartnerEntityData(
|
||||
sharedById: sharedById ?? this.sharedById,
|
||||
sharedWithId: sharedWithId ?? this.sharedWithId,
|
||||
@@ -534,20 +531,19 @@ class PartnerEntityData extends i0.DataClass
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(i0.$driftBlobEquality.hash(sharedById),
|
||||
i0.$driftBlobEquality.hash(sharedWithId), inTimeline);
|
||||
int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is i1.PartnerEntityData &&
|
||||
i0.$driftBlobEquality.equals(other.sharedById, this.sharedById) &&
|
||||
i0.$driftBlobEquality.equals(other.sharedWithId, this.sharedWithId) &&
|
||||
other.sharedById == this.sharedById &&
|
||||
other.sharedWithId == this.sharedWithId &&
|
||||
other.inTimeline == this.inTimeline);
|
||||
}
|
||||
|
||||
class PartnerEntityCompanion extends i0.UpdateCompanion<i1.PartnerEntityData> {
|
||||
final i0.Value<i2.Uint8List> sharedById;
|
||||
final i0.Value<i2.Uint8List> sharedWithId;
|
||||
final i0.Value<String> sharedById;
|
||||
final i0.Value<String> sharedWithId;
|
||||
final i0.Value<bool> inTimeline;
|
||||
const PartnerEntityCompanion({
|
||||
this.sharedById = const i0.Value.absent(),
|
||||
@@ -555,14 +551,14 @@ class PartnerEntityCompanion extends i0.UpdateCompanion<i1.PartnerEntityData> {
|
||||
this.inTimeline = const i0.Value.absent(),
|
||||
});
|
||||
PartnerEntityCompanion.insert({
|
||||
required i2.Uint8List sharedById,
|
||||
required i2.Uint8List sharedWithId,
|
||||
required String sharedById,
|
||||
required String sharedWithId,
|
||||
this.inTimeline = const i0.Value.absent(),
|
||||
}) : sharedById = i0.Value(sharedById),
|
||||
sharedWithId = i0.Value(sharedWithId);
|
||||
static i0.Insertable<i1.PartnerEntityData> custom({
|
||||
i0.Expression<i2.Uint8List>? sharedById,
|
||||
i0.Expression<i2.Uint8List>? sharedWithId,
|
||||
i0.Expression<String>? sharedById,
|
||||
i0.Expression<String>? sharedWithId,
|
||||
i0.Expression<bool>? inTimeline,
|
||||
}) {
|
||||
return i0.RawValuesInsertable({
|
||||
@@ -573,8 +569,8 @@ class PartnerEntityCompanion extends i0.UpdateCompanion<i1.PartnerEntityData> {
|
||||
}
|
||||
|
||||
i1.PartnerEntityCompanion copyWith(
|
||||
{i0.Value<i2.Uint8List>? sharedById,
|
||||
i0.Value<i2.Uint8List>? sharedWithId,
|
||||
{i0.Value<String>? sharedById,
|
||||
i0.Value<String>? sharedWithId,
|
||||
i0.Value<bool>? inTimeline}) {
|
||||
return i1.PartnerEntityCompanion(
|
||||
sharedById: sharedById ?? this.sharedById,
|
||||
@@ -587,10 +583,10 @@ class PartnerEntityCompanion extends i0.UpdateCompanion<i1.PartnerEntityData> {
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
if (sharedById.present) {
|
||||
map['shared_by_id'] = i0.Variable<i2.Uint8List>(sharedById.value);
|
||||
map['shared_by_id'] = i0.Variable<String>(sharedById.value);
|
||||
}
|
||||
if (sharedWithId.present) {
|
||||
map['shared_with_id'] = i0.Variable<i2.Uint8List>(sharedWithId.value);
|
||||
map['shared_with_id'] = i0.Variable<String>(sharedWithId.value);
|
||||
}
|
||||
if (inTimeline.present) {
|
||||
map['in_timeline'] = i0.Variable<bool>(inTimeline.value);
|
||||
|
||||
34
mobile/lib/infrastructure/entities/remote_album.entity.dart
Normal file
34
mobile/lib/infrastructure/entities/remote_album.entity.dart
Normal file
@@ -0,0 +1,34 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
||||
|
||||
class RemoteAlbumEntity extends Table with DriftDefaultsMixin {
|
||||
const RemoteAlbumEntity();
|
||||
|
||||
TextColumn get id => text()();
|
||||
|
||||
TextColumn get name => text()();
|
||||
|
||||
TextColumn get description => text()();
|
||||
|
||||
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
||||
|
||||
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
|
||||
|
||||
TextColumn get ownerId =>
|
||||
text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
|
||||
|
||||
TextColumn get thumbnailAssetId => text()
|
||||
.references(RemoteAssetEntity, #id, onDelete: KeyAction.setNull)
|
||||
.nullable()();
|
||||
|
||||
BoolColumn get isActivityEnabled =>
|
||||
boolean().withDefault(const Constant(true))();
|
||||
|
||||
IntColumn get order => intEnum<AssetOrder>()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {id};
|
||||
}
|
||||
946
mobile/lib/infrastructure/entities/remote_album.entity.drift.dart
generated
Normal file
946
mobile/lib/infrastructure/entities/remote_album.entity.drift.dart
generated
Normal file
@@ -0,0 +1,946 @@
|
||||
// dart format width=80
|
||||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
|
||||
as i1;
|
||||
import 'package:immich_mobile/domain/models/album/album.model.dart' as i2;
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart'
|
||||
as i3;
|
||||
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4;
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
|
||||
as i5;
|
||||
import 'package:drift/internal/modular.dart' as i6;
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'
|
||||
as i7;
|
||||
|
||||
typedef $$RemoteAlbumEntityTableCreateCompanionBuilder
|
||||
= i1.RemoteAlbumEntityCompanion Function({
|
||||
required String id,
|
||||
required String name,
|
||||
required String description,
|
||||
i0.Value<DateTime> createdAt,
|
||||
i0.Value<DateTime> updatedAt,
|
||||
required String ownerId,
|
||||
i0.Value<String?> thumbnailAssetId,
|
||||
i0.Value<bool> isActivityEnabled,
|
||||
required i2.AssetOrder order,
|
||||
});
|
||||
typedef $$RemoteAlbumEntityTableUpdateCompanionBuilder
|
||||
= i1.RemoteAlbumEntityCompanion Function({
|
||||
i0.Value<String> id,
|
||||
i0.Value<String> name,
|
||||
i0.Value<String> description,
|
||||
i0.Value<DateTime> createdAt,
|
||||
i0.Value<DateTime> updatedAt,
|
||||
i0.Value<String> ownerId,
|
||||
i0.Value<String?> thumbnailAssetId,
|
||||
i0.Value<bool> isActivityEnabled,
|
||||
i0.Value<i2.AssetOrder> order,
|
||||
});
|
||||
|
||||
final class $$RemoteAlbumEntityTableReferences extends i0.BaseReferences<
|
||||
i0.GeneratedDatabase,
|
||||
i1.$RemoteAlbumEntityTable,
|
||||
i1.RemoteAlbumEntityData> {
|
||||
$$RemoteAlbumEntityTableReferences(
|
||||
super.$_db, super.$_table, super.$_typedResult);
|
||||
|
||||
static i5.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) =>
|
||||
i6.ReadDatabaseContainer(db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity')
|
||||
.createAlias(i0.$_aliasNameGenerator(
|
||||
i6.ReadDatabaseContainer(db)
|
||||
.resultSet<i1.$RemoteAlbumEntityTable>('remote_album_entity')
|
||||
.ownerId,
|
||||
i6.ReadDatabaseContainer(db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity')
|
||||
.id));
|
||||
|
||||
i5.$$UserEntityTableProcessedTableManager get ownerId {
|
||||
final $_column = $_itemColumn<String>('owner_id')!;
|
||||
|
||||
final manager = i5
|
||||
.$$UserEntityTableTableManager(
|
||||
$_db,
|
||||
i6.ReadDatabaseContainer($_db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity'))
|
||||
.filter((f) => f.id.sqlEquals($_column));
|
||||
final item = $_typedResult.readTableOrNull(_ownerIdTable($_db));
|
||||
if (item == null) return manager;
|
||||
return i0.ProcessedTableManager(
|
||||
manager.$state.copyWith(prefetchedData: [item]));
|
||||
}
|
||||
|
||||
static i7.$RemoteAssetEntityTable _thumbnailAssetIdTable(
|
||||
i0.GeneratedDatabase db) =>
|
||||
i6.ReadDatabaseContainer(db)
|
||||
.resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity')
|
||||
.createAlias(i0.$_aliasNameGenerator(
|
||||
i6.ReadDatabaseContainer(db)
|
||||
.resultSet<i1.$RemoteAlbumEntityTable>('remote_album_entity')
|
||||
.thumbnailAssetId,
|
||||
i6.ReadDatabaseContainer(db)
|
||||
.resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity')
|
||||
.id));
|
||||
|
||||
i7.$$RemoteAssetEntityTableProcessedTableManager? get thumbnailAssetId {
|
||||
final $_column = $_itemColumn<String>('thumbnail_asset_id');
|
||||
if ($_column == null) return null;
|
||||
final manager = i7
|
||||
.$$RemoteAssetEntityTableTableManager(
|
||||
$_db,
|
||||
i6.ReadDatabaseContainer($_db)
|
||||
.resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'))
|
||||
.filter((f) => f.id.sqlEquals($_column));
|
||||
final item = $_typedResult.readTableOrNull(_thumbnailAssetIdTable($_db));
|
||||
if (item == null) return manager;
|
||||
return i0.ProcessedTableManager(
|
||||
manager.$state.copyWith(prefetchedData: [item]));
|
||||
}
|
||||
}
|
||||
|
||||
class $$RemoteAlbumEntityTableFilterComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumEntityTable> {
|
||||
$$RemoteAlbumEntityTableFilterComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.ColumnFilters<String> get id => $composableBuilder(
|
||||
column: $table.id, builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnFilters<String> get name => $composableBuilder(
|
||||
column: $table.name, builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnFilters<String> get description => $composableBuilder(
|
||||
column: $table.description,
|
||||
builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnFilters<DateTime> get createdAt => $composableBuilder(
|
||||
column: $table.createdAt, builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnFilters<DateTime> get updatedAt => $composableBuilder(
|
||||
column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnFilters<bool> get isActivityEnabled => $composableBuilder(
|
||||
column: $table.isActivityEnabled,
|
||||
builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnWithTypeConverterFilters<i2.AssetOrder, i2.AssetOrder, int>
|
||||
get order => $composableBuilder(
|
||||
column: $table.order,
|
||||
builder: (column) => i0.ColumnWithTypeConverterFilters(column));
|
||||
|
||||
i5.$$UserEntityTableFilterComposer get ownerId {
|
||||
final i5.$$UserEntityTableFilterComposer composer = $composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.ownerId,
|
||||
referencedTable: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i5.$$UserEntityTableFilterComposer(
|
||||
$db: $db,
|
||||
$table: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
|
||||
i7.$$RemoteAssetEntityTableFilterComposer get thumbnailAssetId {
|
||||
final i7.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.thumbnailAssetId,
|
||||
referencedTable: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i7.$$RemoteAssetEntityTableFilterComposer(
|
||||
$db: $db,
|
||||
$table: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
}
|
||||
|
||||
class $$RemoteAlbumEntityTableOrderingComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumEntityTable> {
|
||||
$$RemoteAlbumEntityTableOrderingComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.ColumnOrderings<String> get id => $composableBuilder(
|
||||
column: $table.id, builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<String> get name => $composableBuilder(
|
||||
column: $table.name, builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<String> get description => $composableBuilder(
|
||||
column: $table.description,
|
||||
builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<DateTime> get createdAt => $composableBuilder(
|
||||
column: $table.createdAt,
|
||||
builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<DateTime> get updatedAt => $composableBuilder(
|
||||
column: $table.updatedAt,
|
||||
builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<bool> get isActivityEnabled => $composableBuilder(
|
||||
column: $table.isActivityEnabled,
|
||||
builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<int> get order => $composableBuilder(
|
||||
column: $table.order, builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i5.$$UserEntityTableOrderingComposer get ownerId {
|
||||
final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.ownerId,
|
||||
referencedTable: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i5.$$UserEntityTableOrderingComposer(
|
||||
$db: $db,
|
||||
$table: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
|
||||
i7.$$RemoteAssetEntityTableOrderingComposer get thumbnailAssetId {
|
||||
final i7.$$RemoteAssetEntityTableOrderingComposer composer =
|
||||
$composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.thumbnailAssetId,
|
||||
referencedTable: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i7.$$RemoteAssetEntityTableOrderingComposer(
|
||||
$db: $db,
|
||||
$table: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i7.$RemoteAssetEntityTable>(
|
||||
'remote_asset_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
}
|
||||
|
||||
class $$RemoteAlbumEntityTableAnnotationComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumEntityTable> {
|
||||
$$RemoteAlbumEntityTableAnnotationComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.GeneratedColumn<String> get id =>
|
||||
$composableBuilder(column: $table.id, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<String> get name =>
|
||||
$composableBuilder(column: $table.name, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<String> get description => $composableBuilder(
|
||||
column: $table.description, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<DateTime> get createdAt =>
|
||||
$composableBuilder(column: $table.createdAt, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<DateTime> get updatedAt =>
|
||||
$composableBuilder(column: $table.updatedAt, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<bool> get isActivityEnabled => $composableBuilder(
|
||||
column: $table.isActivityEnabled, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumnWithTypeConverter<i2.AssetOrder, int> get order =>
|
||||
$composableBuilder(column: $table.order, builder: (column) => column);
|
||||
|
||||
i5.$$UserEntityTableAnnotationComposer get ownerId {
|
||||
final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.ownerId,
|
||||
referencedTable: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i5.$$UserEntityTableAnnotationComposer(
|
||||
$db: $db,
|
||||
$table: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
|
||||
i7.$$RemoteAssetEntityTableAnnotationComposer get thumbnailAssetId {
|
||||
final i7.$$RemoteAssetEntityTableAnnotationComposer composer =
|
||||
$composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.thumbnailAssetId,
|
||||
referencedTable: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i7.$$RemoteAssetEntityTableAnnotationComposer(
|
||||
$db: $db,
|
||||
$table: i6.ReadDatabaseContainer($db)
|
||||
.resultSet<i7.$RemoteAssetEntityTable>(
|
||||
'remote_asset_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
}
|
||||
|
||||
class $$RemoteAlbumEntityTableTableManager extends i0.RootTableManager<
|
||||
i0.GeneratedDatabase,
|
||||
i1.$RemoteAlbumEntityTable,
|
||||
i1.RemoteAlbumEntityData,
|
||||
i1.$$RemoteAlbumEntityTableFilterComposer,
|
||||
i1.$$RemoteAlbumEntityTableOrderingComposer,
|
||||
i1.$$RemoteAlbumEntityTableAnnotationComposer,
|
||||
$$RemoteAlbumEntityTableCreateCompanionBuilder,
|
||||
$$RemoteAlbumEntityTableUpdateCompanionBuilder,
|
||||
(i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences),
|
||||
i1.RemoteAlbumEntityData,
|
||||
i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId})> {
|
||||
$$RemoteAlbumEntityTableTableManager(
|
||||
i0.GeneratedDatabase db, i1.$RemoteAlbumEntityTable table)
|
||||
: super(i0.TableManagerState(
|
||||
db: db,
|
||||
table: table,
|
||||
createFilteringComposer: () =>
|
||||
i1.$$RemoteAlbumEntityTableFilterComposer($db: db, $table: table),
|
||||
createOrderingComposer: () => i1
|
||||
.$$RemoteAlbumEntityTableOrderingComposer($db: db, $table: table),
|
||||
createComputedFieldComposer: () =>
|
||||
i1.$$RemoteAlbumEntityTableAnnotationComposer(
|
||||
$db: db, $table: table),
|
||||
updateCompanionCallback: ({
|
||||
i0.Value<String> id = const i0.Value.absent(),
|
||||
i0.Value<String> name = const i0.Value.absent(),
|
||||
i0.Value<String> description = const i0.Value.absent(),
|
||||
i0.Value<DateTime> createdAt = const i0.Value.absent(),
|
||||
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
||||
i0.Value<String> ownerId = const i0.Value.absent(),
|
||||
i0.Value<String?> thumbnailAssetId = const i0.Value.absent(),
|
||||
i0.Value<bool> isActivityEnabled = const i0.Value.absent(),
|
||||
i0.Value<i2.AssetOrder> order = const i0.Value.absent(),
|
||||
}) =>
|
||||
i1.RemoteAlbumEntityCompanion(
|
||||
id: id,
|
||||
name: name,
|
||||
description: description,
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
ownerId: ownerId,
|
||||
thumbnailAssetId: thumbnailAssetId,
|
||||
isActivityEnabled: isActivityEnabled,
|
||||
order: order,
|
||||
),
|
||||
createCompanionCallback: ({
|
||||
required String id,
|
||||
required String name,
|
||||
required String description,
|
||||
i0.Value<DateTime> createdAt = const i0.Value.absent(),
|
||||
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
||||
required String ownerId,
|
||||
i0.Value<String?> thumbnailAssetId = const i0.Value.absent(),
|
||||
i0.Value<bool> isActivityEnabled = const i0.Value.absent(),
|
||||
required i2.AssetOrder order,
|
||||
}) =>
|
||||
i1.RemoteAlbumEntityCompanion.insert(
|
||||
id: id,
|
||||
name: name,
|
||||
description: description,
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
ownerId: ownerId,
|
||||
thumbnailAssetId: thumbnailAssetId,
|
||||
isActivityEnabled: isActivityEnabled,
|
||||
order: order,
|
||||
),
|
||||
withReferenceMapper: (p0) => p0
|
||||
.map((e) => (
|
||||
e.readTable(table),
|
||||
i1.$$RemoteAlbumEntityTableReferences(db, table, e)
|
||||
))
|
||||
.toList(),
|
||||
prefetchHooksCallback: ({ownerId = false, thumbnailAssetId = false}) {
|
||||
return i0.PrefetchHooks(
|
||||
db: db,
|
||||
explicitlyWatchedTables: [],
|
||||
addJoins: <
|
||||
T extends i0.TableManagerState<
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic>>(state) {
|
||||
if (ownerId) {
|
||||
state = state.withJoin(
|
||||
currentTable: table,
|
||||
currentColumn: table.ownerId,
|
||||
referencedTable:
|
||||
i1.$$RemoteAlbumEntityTableReferences._ownerIdTable(db),
|
||||
referencedColumn: i1.$$RemoteAlbumEntityTableReferences
|
||||
._ownerIdTable(db)
|
||||
.id,
|
||||
) as T;
|
||||
}
|
||||
if (thumbnailAssetId) {
|
||||
state = state.withJoin(
|
||||
currentTable: table,
|
||||
currentColumn: table.thumbnailAssetId,
|
||||
referencedTable: i1.$$RemoteAlbumEntityTableReferences
|
||||
._thumbnailAssetIdTable(db),
|
||||
referencedColumn: i1.$$RemoteAlbumEntityTableReferences
|
||||
._thumbnailAssetIdTable(db)
|
||||
.id,
|
||||
) as T;
|
||||
}
|
||||
|
||||
return state;
|
||||
},
|
||||
getPrefetchedDataCallback: (items) async {
|
||||
return [];
|
||||
},
|
||||
);
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
typedef $$RemoteAlbumEntityTableProcessedTableManager
|
||||
= i0.ProcessedTableManager<
|
||||
i0.GeneratedDatabase,
|
||||
i1.$RemoteAlbumEntityTable,
|
||||
i1.RemoteAlbumEntityData,
|
||||
i1.$$RemoteAlbumEntityTableFilterComposer,
|
||||
i1.$$RemoteAlbumEntityTableOrderingComposer,
|
||||
i1.$$RemoteAlbumEntityTableAnnotationComposer,
|
||||
$$RemoteAlbumEntityTableCreateCompanionBuilder,
|
||||
$$RemoteAlbumEntityTableUpdateCompanionBuilder,
|
||||
(i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences),
|
||||
i1.RemoteAlbumEntityData,
|
||||
i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId})>;
|
||||
|
||||
class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity
|
||||
with i0.TableInfo<$RemoteAlbumEntityTable, i1.RemoteAlbumEntityData> {
|
||||
@override
|
||||
final i0.GeneratedDatabase attachedDatabase;
|
||||
final String? _alias;
|
||||
$RemoteAlbumEntityTable(this.attachedDatabase, [this._alias]);
|
||||
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
|
||||
'id', aliasedName, false,
|
||||
type: i0.DriftSqlType.string, requiredDuringInsert: true);
|
||||
static const i0.VerificationMeta _nameMeta =
|
||||
const i0.VerificationMeta('name');
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> name = i0.GeneratedColumn<String>(
|
||||
'name', aliasedName, false,
|
||||
type: i0.DriftSqlType.string, requiredDuringInsert: true);
|
||||
static const i0.VerificationMeta _descriptionMeta =
|
||||
const i0.VerificationMeta('description');
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> description =
|
||||
i0.GeneratedColumn<String>('description', aliasedName, false,
|
||||
type: i0.DriftSqlType.string, requiredDuringInsert: true);
|
||||
static const i0.VerificationMeta _createdAtMeta =
|
||||
const i0.VerificationMeta('createdAt');
|
||||
@override
|
||||
late final i0.GeneratedColumn<DateTime> createdAt =
|
||||
i0.GeneratedColumn<DateTime>('created_at', aliasedName, false,
|
||||
type: i0.DriftSqlType.dateTime,
|
||||
requiredDuringInsert: false,
|
||||
defaultValue: i4.currentDateAndTime);
|
||||
static const i0.VerificationMeta _updatedAtMeta =
|
||||
const i0.VerificationMeta('updatedAt');
|
||||
@override
|
||||
late final i0.GeneratedColumn<DateTime> updatedAt =
|
||||
i0.GeneratedColumn<DateTime>('updated_at', aliasedName, false,
|
||||
type: i0.DriftSqlType.dateTime,
|
||||
requiredDuringInsert: false,
|
||||
defaultValue: i4.currentDateAndTime);
|
||||
static const i0.VerificationMeta _ownerIdMeta =
|
||||
const i0.VerificationMeta('ownerId');
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> ownerId = i0.GeneratedColumn<String>(
|
||||
'owner_id', aliasedName, false,
|
||||
type: i0.DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||
'REFERENCES user_entity (id) ON DELETE CASCADE'));
|
||||
static const i0.VerificationMeta _thumbnailAssetIdMeta =
|
||||
const i0.VerificationMeta('thumbnailAssetId');
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> thumbnailAssetId =
|
||||
i0.GeneratedColumn<String>('thumbnail_asset_id', aliasedName, true,
|
||||
type: i0.DriftSqlType.string,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||
'REFERENCES remote_asset_entity (id) ON DELETE SET NULL'));
|
||||
static const i0.VerificationMeta _isActivityEnabledMeta =
|
||||
const i0.VerificationMeta('isActivityEnabled');
|
||||
@override
|
||||
late final i0.GeneratedColumn<bool> isActivityEnabled =
|
||||
i0.GeneratedColumn<bool>('is_activity_enabled', aliasedName, false,
|
||||
type: i0.DriftSqlType.bool,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||
'CHECK ("is_activity_enabled" IN (0, 1))'),
|
||||
defaultValue: const i4.Constant(true));
|
||||
@override
|
||||
late final i0.GeneratedColumnWithTypeConverter<i2.AssetOrder, int> order =
|
||||
i0.GeneratedColumn<int>('order', aliasedName, false,
|
||||
type: i0.DriftSqlType.int, requiredDuringInsert: true)
|
||||
.withConverter<i2.AssetOrder>(
|
||||
i1.$RemoteAlbumEntityTable.$converterorder);
|
||||
@override
|
||||
List<i0.GeneratedColumn> get $columns => [
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
ownerId,
|
||||
thumbnailAssetId,
|
||||
isActivityEnabled,
|
||||
order
|
||||
];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@override
|
||||
String get actualTableName => $name;
|
||||
static const String $name = 'remote_album_entity';
|
||||
@override
|
||||
i0.VerificationContext validateIntegrity(
|
||||
i0.Insertable<i1.RemoteAlbumEntityData> instance,
|
||||
{bool isInserting = false}) {
|
||||
final context = i0.VerificationContext();
|
||||
final data = instance.toColumns(true);
|
||||
if (data.containsKey('id')) {
|
||||
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_idMeta);
|
||||
}
|
||||
if (data.containsKey('name')) {
|
||||
context.handle(
|
||||
_nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_nameMeta);
|
||||
}
|
||||
if (data.containsKey('description')) {
|
||||
context.handle(
|
||||
_descriptionMeta,
|
||||
description.isAcceptableOrUnknown(
|
||||
data['description']!, _descriptionMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_descriptionMeta);
|
||||
}
|
||||
if (data.containsKey('created_at')) {
|
||||
context.handle(_createdAtMeta,
|
||||
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta));
|
||||
}
|
||||
if (data.containsKey('updated_at')) {
|
||||
context.handle(_updatedAtMeta,
|
||||
updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta));
|
||||
}
|
||||
if (data.containsKey('owner_id')) {
|
||||
context.handle(_ownerIdMeta,
|
||||
ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_ownerIdMeta);
|
||||
}
|
||||
if (data.containsKey('thumbnail_asset_id')) {
|
||||
context.handle(
|
||||
_thumbnailAssetIdMeta,
|
||||
thumbnailAssetId.isAcceptableOrUnknown(
|
||||
data['thumbnail_asset_id']!, _thumbnailAssetIdMeta));
|
||||
}
|
||||
if (data.containsKey('is_activity_enabled')) {
|
||||
context.handle(
|
||||
_isActivityEnabledMeta,
|
||||
isActivityEnabled.isAcceptableOrUnknown(
|
||||
data['is_activity_enabled']!, _isActivityEnabledMeta));
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@override
|
||||
Set<i0.GeneratedColumn> get $primaryKey => {id};
|
||||
@override
|
||||
i1.RemoteAlbumEntityData map(Map<String, dynamic> data,
|
||||
{String? tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return i1.RemoteAlbumEntityData(
|
||||
id: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!,
|
||||
name: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!,
|
||||
description: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}description'])!,
|
||||
createdAt: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
|
||||
updatedAt: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!,
|
||||
ownerId: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!,
|
||||
thumbnailAssetId: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']),
|
||||
isActivityEnabled: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!,
|
||||
order: i1.$RemoteAlbumEntityTable.$converterorder.fromSql(attachedDatabase
|
||||
.typeMapping
|
||||
.read(i0.DriftSqlType.int, data['${effectivePrefix}order'])!),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
$RemoteAlbumEntityTable createAlias(String alias) {
|
||||
return $RemoteAlbumEntityTable(attachedDatabase, alias);
|
||||
}
|
||||
|
||||
static i0.JsonTypeConverter2<i2.AssetOrder, int, int> $converterorder =
|
||||
const i0.EnumIndexConverter<i2.AssetOrder>(i2.AssetOrder.values);
|
||||
@override
|
||||
bool get withoutRowId => true;
|
||||
@override
|
||||
bool get isStrict => true;
|
||||
}
|
||||
|
||||
class RemoteAlbumEntityData extends i0.DataClass
|
||||
implements i0.Insertable<i1.RemoteAlbumEntityData> {
|
||||
final String id;
|
||||
final String name;
|
||||
final String description;
|
||||
final DateTime createdAt;
|
||||
final DateTime updatedAt;
|
||||
final String ownerId;
|
||||
final String? thumbnailAssetId;
|
||||
final bool isActivityEnabled;
|
||||
final i2.AssetOrder order;
|
||||
const RemoteAlbumEntityData(
|
||||
{required this.id,
|
||||
required this.name,
|
||||
required this.description,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
required this.ownerId,
|
||||
this.thumbnailAssetId,
|
||||
required this.isActivityEnabled,
|
||||
required this.order});
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
map['id'] = i0.Variable<String>(id);
|
||||
map['name'] = i0.Variable<String>(name);
|
||||
map['description'] = i0.Variable<String>(description);
|
||||
map['created_at'] = i0.Variable<DateTime>(createdAt);
|
||||
map['updated_at'] = i0.Variable<DateTime>(updatedAt);
|
||||
map['owner_id'] = i0.Variable<String>(ownerId);
|
||||
if (!nullToAbsent || thumbnailAssetId != null) {
|
||||
map['thumbnail_asset_id'] = i0.Variable<String>(thumbnailAssetId);
|
||||
}
|
||||
map['is_activity_enabled'] = i0.Variable<bool>(isActivityEnabled);
|
||||
{
|
||||
map['order'] = i0.Variable<int>(
|
||||
i1.$RemoteAlbumEntityTable.$converterorder.toSql(order));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
factory RemoteAlbumEntityData.fromJson(Map<String, dynamic> json,
|
||||
{i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return RemoteAlbumEntityData(
|
||||
id: serializer.fromJson<String>(json['id']),
|
||||
name: serializer.fromJson<String>(json['name']),
|
||||
description: serializer.fromJson<String>(json['description']),
|
||||
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
|
||||
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
|
||||
ownerId: serializer.fromJson<String>(json['ownerId']),
|
||||
thumbnailAssetId: serializer.fromJson<String?>(json['thumbnailAssetId']),
|
||||
isActivityEnabled: serializer.fromJson<bool>(json['isActivityEnabled']),
|
||||
order: i1.$RemoteAlbumEntityTable.$converterorder
|
||||
.fromJson(serializer.fromJson<int>(json['order'])),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'id': serializer.toJson<String>(id),
|
||||
'name': serializer.toJson<String>(name),
|
||||
'description': serializer.toJson<String>(description),
|
||||
'createdAt': serializer.toJson<DateTime>(createdAt),
|
||||
'updatedAt': serializer.toJson<DateTime>(updatedAt),
|
||||
'ownerId': serializer.toJson<String>(ownerId),
|
||||
'thumbnailAssetId': serializer.toJson<String?>(thumbnailAssetId),
|
||||
'isActivityEnabled': serializer.toJson<bool>(isActivityEnabled),
|
||||
'order': serializer.toJson<int>(
|
||||
i1.$RemoteAlbumEntityTable.$converterorder.toJson(order)),
|
||||
};
|
||||
}
|
||||
|
||||
i1.RemoteAlbumEntityData copyWith(
|
||||
{String? id,
|
||||
String? name,
|
||||
String? description,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
String? ownerId,
|
||||
i0.Value<String?> thumbnailAssetId = const i0.Value.absent(),
|
||||
bool? isActivityEnabled,
|
||||
i2.AssetOrder? order}) =>
|
||||
i1.RemoteAlbumEntityData(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
description: description ?? this.description,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
ownerId: ownerId ?? this.ownerId,
|
||||
thumbnailAssetId: thumbnailAssetId.present
|
||||
? thumbnailAssetId.value
|
||||
: this.thumbnailAssetId,
|
||||
isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled,
|
||||
order: order ?? this.order,
|
||||
);
|
||||
RemoteAlbumEntityData copyWithCompanion(i1.RemoteAlbumEntityCompanion data) {
|
||||
return RemoteAlbumEntityData(
|
||||
id: data.id.present ? data.id.value : this.id,
|
||||
name: data.name.present ? data.name.value : this.name,
|
||||
description:
|
||||
data.description.present ? data.description.value : this.description,
|
||||
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
|
||||
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
|
||||
ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId,
|
||||
thumbnailAssetId: data.thumbnailAssetId.present
|
||||
? data.thumbnailAssetId.value
|
||||
: this.thumbnailAssetId,
|
||||
isActivityEnabled: data.isActivityEnabled.present
|
||||
? data.isActivityEnabled.value
|
||||
: this.isActivityEnabled,
|
||||
order: data.order.present ? data.order.value : this.order,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('RemoteAlbumEntityData(')
|
||||
..write('id: $id, ')
|
||||
..write('name: $name, ')
|
||||
..write('description: $description, ')
|
||||
..write('createdAt: $createdAt, ')
|
||||
..write('updatedAt: $updatedAt, ')
|
||||
..write('ownerId: $ownerId, ')
|
||||
..write('thumbnailAssetId: $thumbnailAssetId, ')
|
||||
..write('isActivityEnabled: $isActivityEnabled, ')
|
||||
..write('order: $order')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(id, name, description, createdAt, updatedAt,
|
||||
ownerId, thumbnailAssetId, isActivityEnabled, order);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is i1.RemoteAlbumEntityData &&
|
||||
other.id == this.id &&
|
||||
other.name == this.name &&
|
||||
other.description == this.description &&
|
||||
other.createdAt == this.createdAt &&
|
||||
other.updatedAt == this.updatedAt &&
|
||||
other.ownerId == this.ownerId &&
|
||||
other.thumbnailAssetId == this.thumbnailAssetId &&
|
||||
other.isActivityEnabled == this.isActivityEnabled &&
|
||||
other.order == this.order);
|
||||
}
|
||||
|
||||
class RemoteAlbumEntityCompanion
|
||||
extends i0.UpdateCompanion<i1.RemoteAlbumEntityData> {
|
||||
final i0.Value<String> id;
|
||||
final i0.Value<String> name;
|
||||
final i0.Value<String> description;
|
||||
final i0.Value<DateTime> createdAt;
|
||||
final i0.Value<DateTime> updatedAt;
|
||||
final i0.Value<String> ownerId;
|
||||
final i0.Value<String?> thumbnailAssetId;
|
||||
final i0.Value<bool> isActivityEnabled;
|
||||
final i0.Value<i2.AssetOrder> order;
|
||||
const RemoteAlbumEntityCompanion({
|
||||
this.id = const i0.Value.absent(),
|
||||
this.name = const i0.Value.absent(),
|
||||
this.description = const i0.Value.absent(),
|
||||
this.createdAt = const i0.Value.absent(),
|
||||
this.updatedAt = const i0.Value.absent(),
|
||||
this.ownerId = const i0.Value.absent(),
|
||||
this.thumbnailAssetId = const i0.Value.absent(),
|
||||
this.isActivityEnabled = const i0.Value.absent(),
|
||||
this.order = const i0.Value.absent(),
|
||||
});
|
||||
RemoteAlbumEntityCompanion.insert({
|
||||
required String id,
|
||||
required String name,
|
||||
required String description,
|
||||
this.createdAt = const i0.Value.absent(),
|
||||
this.updatedAt = const i0.Value.absent(),
|
||||
required String ownerId,
|
||||
this.thumbnailAssetId = const i0.Value.absent(),
|
||||
this.isActivityEnabled = const i0.Value.absent(),
|
||||
required i2.AssetOrder order,
|
||||
}) : id = i0.Value(id),
|
||||
name = i0.Value(name),
|
||||
description = i0.Value(description),
|
||||
ownerId = i0.Value(ownerId),
|
||||
order = i0.Value(order);
|
||||
static i0.Insertable<i1.RemoteAlbumEntityData> custom({
|
||||
i0.Expression<String>? id,
|
||||
i0.Expression<String>? name,
|
||||
i0.Expression<String>? description,
|
||||
i0.Expression<DateTime>? createdAt,
|
||||
i0.Expression<DateTime>? updatedAt,
|
||||
i0.Expression<String>? ownerId,
|
||||
i0.Expression<String>? thumbnailAssetId,
|
||||
i0.Expression<bool>? isActivityEnabled,
|
||||
i0.Expression<int>? order,
|
||||
}) {
|
||||
return i0.RawValuesInsertable({
|
||||
if (id != null) 'id': id,
|
||||
if (name != null) 'name': name,
|
||||
if (description != null) 'description': description,
|
||||
if (createdAt != null) 'created_at': createdAt,
|
||||
if (updatedAt != null) 'updated_at': updatedAt,
|
||||
if (ownerId != null) 'owner_id': ownerId,
|
||||
if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId,
|
||||
if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled,
|
||||
if (order != null) 'order': order,
|
||||
});
|
||||
}
|
||||
|
||||
i1.RemoteAlbumEntityCompanion copyWith(
|
||||
{i0.Value<String>? id,
|
||||
i0.Value<String>? name,
|
||||
i0.Value<String>? description,
|
||||
i0.Value<DateTime>? createdAt,
|
||||
i0.Value<DateTime>? updatedAt,
|
||||
i0.Value<String>? ownerId,
|
||||
i0.Value<String?>? thumbnailAssetId,
|
||||
i0.Value<bool>? isActivityEnabled,
|
||||
i0.Value<i2.AssetOrder>? order}) {
|
||||
return i1.RemoteAlbumEntityCompanion(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
description: description ?? this.description,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
ownerId: ownerId ?? this.ownerId,
|
||||
thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId,
|
||||
isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled,
|
||||
order: order ?? this.order,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
if (id.present) {
|
||||
map['id'] = i0.Variable<String>(id.value);
|
||||
}
|
||||
if (name.present) {
|
||||
map['name'] = i0.Variable<String>(name.value);
|
||||
}
|
||||
if (description.present) {
|
||||
map['description'] = i0.Variable<String>(description.value);
|
||||
}
|
||||
if (createdAt.present) {
|
||||
map['created_at'] = i0.Variable<DateTime>(createdAt.value);
|
||||
}
|
||||
if (updatedAt.present) {
|
||||
map['updated_at'] = i0.Variable<DateTime>(updatedAt.value);
|
||||
}
|
||||
if (ownerId.present) {
|
||||
map['owner_id'] = i0.Variable<String>(ownerId.value);
|
||||
}
|
||||
if (thumbnailAssetId.present) {
|
||||
map['thumbnail_asset_id'] = i0.Variable<String>(thumbnailAssetId.value);
|
||||
}
|
||||
if (isActivityEnabled.present) {
|
||||
map['is_activity_enabled'] = i0.Variable<bool>(isActivityEnabled.value);
|
||||
}
|
||||
if (order.present) {
|
||||
map['order'] = i0.Variable<int>(
|
||||
i1.$RemoteAlbumEntityTable.$converterorder.toSql(order.value));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('RemoteAlbumEntityCompanion(')
|
||||
..write('id: $id, ')
|
||||
..write('name: $name, ')
|
||||
..write('description: $description, ')
|
||||
..write('createdAt: $createdAt, ')
|
||||
..write('updatedAt: $updatedAt, ')
|
||||
..write('ownerId: $ownerId, ')
|
||||
..write('thumbnailAssetId: $thumbnailAssetId, ')
|
||||
..write('isActivityEnabled: $isActivityEnabled, ')
|
||||
..write('order: $order')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
||||
|
||||
class RemoteAlbumAssetEntity extends Table with DriftDefaultsMixin {
|
||||
const RemoteAlbumAssetEntity();
|
||||
|
||||
TextColumn get assetId =>
|
||||
text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)();
|
||||
|
||||
TextColumn get albumId =>
|
||||
text().references(RemoteAlbumEntity, #id, onDelete: KeyAction.cascade)();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {assetId, albumId};
|
||||
}
|
||||
565
mobile/lib/infrastructure/entities/remote_album_asset.entity.drift.dart
generated
Normal file
565
mobile/lib/infrastructure/entities/remote_album_asset.entity.drift.dart
generated
Normal file
@@ -0,0 +1,565 @@
|
||||
// dart format width=80
|
||||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart'
|
||||
as i1;
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.dart'
|
||||
as i2;
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'
|
||||
as i3;
|
||||
import 'package:drift/internal/modular.dart' as i4;
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
|
||||
as i5;
|
||||
|
||||
typedef $$RemoteAlbumAssetEntityTableCreateCompanionBuilder
|
||||
= i1.RemoteAlbumAssetEntityCompanion Function({
|
||||
required String assetId,
|
||||
required String albumId,
|
||||
});
|
||||
typedef $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder
|
||||
= i1.RemoteAlbumAssetEntityCompanion Function({
|
||||
i0.Value<String> assetId,
|
||||
i0.Value<String> albumId,
|
||||
});
|
||||
|
||||
final class $$RemoteAlbumAssetEntityTableReferences extends i0.BaseReferences<
|
||||
i0.GeneratedDatabase,
|
||||
i1.$RemoteAlbumAssetEntityTable,
|
||||
i1.RemoteAlbumAssetEntityData> {
|
||||
$$RemoteAlbumAssetEntityTableReferences(
|
||||
super.$_db, super.$_table, super.$_typedResult);
|
||||
|
||||
static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) =>
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity')
|
||||
.createAlias(i0.$_aliasNameGenerator(
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i1.$RemoteAlbumAssetEntityTable>(
|
||||
'remote_album_asset_entity')
|
||||
.assetId,
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity')
|
||||
.id));
|
||||
|
||||
i3.$$RemoteAssetEntityTableProcessedTableManager get assetId {
|
||||
final $_column = $_itemColumn<String>('asset_id')!;
|
||||
|
||||
final manager = i3
|
||||
.$$RemoteAssetEntityTableTableManager(
|
||||
$_db,
|
||||
i4.ReadDatabaseContainer($_db)
|
||||
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'))
|
||||
.filter((f) => f.id.sqlEquals($_column));
|
||||
final item = $_typedResult.readTableOrNull(_assetIdTable($_db));
|
||||
if (item == null) return manager;
|
||||
return i0.ProcessedTableManager(
|
||||
manager.$state.copyWith(prefetchedData: [item]));
|
||||
}
|
||||
|
||||
static i5.$RemoteAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) =>
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity')
|
||||
.createAlias(i0.$_aliasNameGenerator(
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i1.$RemoteAlbumAssetEntityTable>(
|
||||
'remote_album_asset_entity')
|
||||
.albumId,
|
||||
i4.ReadDatabaseContainer(db)
|
||||
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity')
|
||||
.id));
|
||||
|
||||
i5.$$RemoteAlbumEntityTableProcessedTableManager get albumId {
|
||||
final $_column = $_itemColumn<String>('album_id')!;
|
||||
|
||||
final manager = i5
|
||||
.$$RemoteAlbumEntityTableTableManager(
|
||||
$_db,
|
||||
i4.ReadDatabaseContainer($_db)
|
||||
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'))
|
||||
.filter((f) => f.id.sqlEquals($_column));
|
||||
final item = $_typedResult.readTableOrNull(_albumIdTable($_db));
|
||||
if (item == null) return manager;
|
||||
return i0.ProcessedTableManager(
|
||||
manager.$state.copyWith(prefetchedData: [item]));
|
||||
}
|
||||
}
|
||||
|
||||
class $$RemoteAlbumAssetEntityTableFilterComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumAssetEntityTable> {
|
||||
$$RemoteAlbumAssetEntityTableFilterComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i3.$$RemoteAssetEntityTableFilterComposer get assetId {
|
||||
final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.assetId,
|
||||
referencedTable: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i3.$$RemoteAssetEntityTableFilterComposer(
|
||||
$db: $db,
|
||||
$table: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
|
||||
i5.$$RemoteAlbumEntityTableFilterComposer get albumId {
|
||||
final i5.$$RemoteAlbumEntityTableFilterComposer composer = $composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.albumId,
|
||||
referencedTable: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i5.$$RemoteAlbumEntityTableFilterComposer(
|
||||
$db: $db,
|
||||
$table: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
}
|
||||
|
||||
class $$RemoteAlbumAssetEntityTableOrderingComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumAssetEntityTable> {
|
||||
$$RemoteAlbumAssetEntityTableOrderingComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i3.$$RemoteAssetEntityTableOrderingComposer get assetId {
|
||||
final i3.$$RemoteAssetEntityTableOrderingComposer composer =
|
||||
$composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.assetId,
|
||||
referencedTable: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i3.$$RemoteAssetEntityTableOrderingComposer(
|
||||
$db: $db,
|
||||
$table: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i3.$RemoteAssetEntityTable>(
|
||||
'remote_asset_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
|
||||
i5.$$RemoteAlbumEntityTableOrderingComposer get albumId {
|
||||
final i5.$$RemoteAlbumEntityTableOrderingComposer composer =
|
||||
$composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.albumId,
|
||||
referencedTable: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i5.$$RemoteAlbumEntityTableOrderingComposer(
|
||||
$db: $db,
|
||||
$table: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$RemoteAlbumEntityTable>(
|
||||
'remote_album_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
}
|
||||
|
||||
class $$RemoteAlbumAssetEntityTableAnnotationComposer
|
||||
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumAssetEntityTable> {
|
||||
$$RemoteAlbumAssetEntityTableAnnotationComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i3.$$RemoteAssetEntityTableAnnotationComposer get assetId {
|
||||
final i3.$$RemoteAssetEntityTableAnnotationComposer composer =
|
||||
$composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.assetId,
|
||||
referencedTable: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i3.$$RemoteAssetEntityTableAnnotationComposer(
|
||||
$db: $db,
|
||||
$table: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i3.$RemoteAssetEntityTable>(
|
||||
'remote_asset_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
|
||||
i5.$$RemoteAlbumEntityTableAnnotationComposer get albumId {
|
||||
final i5.$$RemoteAlbumEntityTableAnnotationComposer composer =
|
||||
$composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.albumId,
|
||||
referencedTable: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder: (joinBuilder,
|
||||
{$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer}) =>
|
||||
i5.$$RemoteAlbumEntityTableAnnotationComposer(
|
||||
$db: $db,
|
||||
$table: i4.ReadDatabaseContainer($db)
|
||||
.resultSet<i5.$RemoteAlbumEntityTable>(
|
||||
'remote_album_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
));
|
||||
return composer;
|
||||
}
|
||||
}
|
||||
|
||||
class $$RemoteAlbumAssetEntityTableTableManager extends i0.RootTableManager<
|
||||
i0.GeneratedDatabase,
|
||||
i1.$RemoteAlbumAssetEntityTable,
|
||||
i1.RemoteAlbumAssetEntityData,
|
||||
i1.$$RemoteAlbumAssetEntityTableFilterComposer,
|
||||
i1.$$RemoteAlbumAssetEntityTableOrderingComposer,
|
||||
i1.$$RemoteAlbumAssetEntityTableAnnotationComposer,
|
||||
$$RemoteAlbumAssetEntityTableCreateCompanionBuilder,
|
||||
$$RemoteAlbumAssetEntityTableUpdateCompanionBuilder,
|
||||
(i1.RemoteAlbumAssetEntityData, i1.$$RemoteAlbumAssetEntityTableReferences),
|
||||
i1.RemoteAlbumAssetEntityData,
|
||||
i0.PrefetchHooks Function({bool assetId, bool albumId})> {
|
||||
$$RemoteAlbumAssetEntityTableTableManager(
|
||||
i0.GeneratedDatabase db, i1.$RemoteAlbumAssetEntityTable table)
|
||||
: super(i0.TableManagerState(
|
||||
db: db,
|
||||
table: table,
|
||||
createFilteringComposer: () =>
|
||||
i1.$$RemoteAlbumAssetEntityTableFilterComposer(
|
||||
$db: db, $table: table),
|
||||
createOrderingComposer: () =>
|
||||
i1.$$RemoteAlbumAssetEntityTableOrderingComposer(
|
||||
$db: db, $table: table),
|
||||
createComputedFieldComposer: () =>
|
||||
i1.$$RemoteAlbumAssetEntityTableAnnotationComposer(
|
||||
$db: db, $table: table),
|
||||
updateCompanionCallback: ({
|
||||
i0.Value<String> assetId = const i0.Value.absent(),
|
||||
i0.Value<String> albumId = const i0.Value.absent(),
|
||||
}) =>
|
||||
i1.RemoteAlbumAssetEntityCompanion(
|
||||
assetId: assetId,
|
||||
albumId: albumId,
|
||||
),
|
||||
createCompanionCallback: ({
|
||||
required String assetId,
|
||||
required String albumId,
|
||||
}) =>
|
||||
i1.RemoteAlbumAssetEntityCompanion.insert(
|
||||
assetId: assetId,
|
||||
albumId: albumId,
|
||||
),
|
||||
withReferenceMapper: (p0) => p0
|
||||
.map((e) => (
|
||||
e.readTable(table),
|
||||
i1.$$RemoteAlbumAssetEntityTableReferences(db, table, e)
|
||||
))
|
||||
.toList(),
|
||||
prefetchHooksCallback: ({assetId = false, albumId = false}) {
|
||||
return i0.PrefetchHooks(
|
||||
db: db,
|
||||
explicitlyWatchedTables: [],
|
||||
addJoins: <
|
||||
T extends i0.TableManagerState<
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic,
|
||||
dynamic>>(state) {
|
||||
if (assetId) {
|
||||
state = state.withJoin(
|
||||
currentTable: table,
|
||||
currentColumn: table.assetId,
|
||||
referencedTable: i1.$$RemoteAlbumAssetEntityTableReferences
|
||||
._assetIdTable(db),
|
||||
referencedColumn: i1.$$RemoteAlbumAssetEntityTableReferences
|
||||
._assetIdTable(db)
|
||||
.id,
|
||||
) as T;
|
||||
}
|
||||
if (albumId) {
|
||||
state = state.withJoin(
|
||||
currentTable: table,
|
||||
currentColumn: table.albumId,
|
||||
referencedTable: i1.$$RemoteAlbumAssetEntityTableReferences
|
||||
._albumIdTable(db),
|
||||
referencedColumn: i1.$$RemoteAlbumAssetEntityTableReferences
|
||||
._albumIdTable(db)
|
||||
.id,
|
||||
) as T;
|
||||
}
|
||||
|
||||
return state;
|
||||
},
|
||||
getPrefetchedDataCallback: (items) async {
|
||||
return [];
|
||||
},
|
||||
);
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
typedef $$RemoteAlbumAssetEntityTableProcessedTableManager
|
||||
= i0.ProcessedTableManager<
|
||||
i0.GeneratedDatabase,
|
||||
i1.$RemoteAlbumAssetEntityTable,
|
||||
i1.RemoteAlbumAssetEntityData,
|
||||
i1.$$RemoteAlbumAssetEntityTableFilterComposer,
|
||||
i1.$$RemoteAlbumAssetEntityTableOrderingComposer,
|
||||
i1.$$RemoteAlbumAssetEntityTableAnnotationComposer,
|
||||
$$RemoteAlbumAssetEntityTableCreateCompanionBuilder,
|
||||
$$RemoteAlbumAssetEntityTableUpdateCompanionBuilder,
|
||||
(
|
||||
i1.RemoteAlbumAssetEntityData,
|
||||
i1.$$RemoteAlbumAssetEntityTableReferences
|
||||
),
|
||||
i1.RemoteAlbumAssetEntityData,
|
||||
i0.PrefetchHooks Function({bool assetId, bool albumId})>;
|
||||
|
||||
class $RemoteAlbumAssetEntityTable extends i2.RemoteAlbumAssetEntity
|
||||
with
|
||||
i0.TableInfo<$RemoteAlbumAssetEntityTable,
|
||||
i1.RemoteAlbumAssetEntityData> {
|
||||
@override
|
||||
final i0.GeneratedDatabase attachedDatabase;
|
||||
final String? _alias;
|
||||
$RemoteAlbumAssetEntityTable(this.attachedDatabase, [this._alias]);
|
||||
static const i0.VerificationMeta _assetIdMeta =
|
||||
const i0.VerificationMeta('assetId');
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> assetId = i0.GeneratedColumn<String>(
|
||||
'asset_id', aliasedName, false,
|
||||
type: i0.DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||
'REFERENCES remote_asset_entity (id) ON DELETE CASCADE'));
|
||||
static const i0.VerificationMeta _albumIdMeta =
|
||||
const i0.VerificationMeta('albumId');
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> albumId = i0.GeneratedColumn<String>(
|
||||
'album_id', aliasedName, false,
|
||||
type: i0.DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||
'REFERENCES remote_album_entity (id) ON DELETE CASCADE'));
|
||||
@override
|
||||
List<i0.GeneratedColumn> get $columns => [assetId, albumId];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@override
|
||||
String get actualTableName => $name;
|
||||
static const String $name = 'remote_album_asset_entity';
|
||||
@override
|
||||
i0.VerificationContext validateIntegrity(
|
||||
i0.Insertable<i1.RemoteAlbumAssetEntityData> instance,
|
||||
{bool isInserting = false}) {
|
||||
final context = i0.VerificationContext();
|
||||
final data = instance.toColumns(true);
|
||||
if (data.containsKey('asset_id')) {
|
||||
context.handle(_assetIdMeta,
|
||||
assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_assetIdMeta);
|
||||
}
|
||||
if (data.containsKey('album_id')) {
|
||||
context.handle(_albumIdMeta,
|
||||
albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_albumIdMeta);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@override
|
||||
Set<i0.GeneratedColumn> get $primaryKey => {assetId, albumId};
|
||||
@override
|
||||
i1.RemoteAlbumAssetEntityData map(Map<String, dynamic> data,
|
||||
{String? tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return i1.RemoteAlbumAssetEntityData(
|
||||
assetId: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!,
|
||||
albumId: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}album_id'])!,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
$RemoteAlbumAssetEntityTable createAlias(String alias) {
|
||||
return $RemoteAlbumAssetEntityTable(attachedDatabase, alias);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get withoutRowId => true;
|
||||
@override
|
||||
bool get isStrict => true;
|
||||
}
|
||||
|
||||
class RemoteAlbumAssetEntityData extends i0.DataClass
|
||||
implements i0.Insertable<i1.RemoteAlbumAssetEntityData> {
|
||||
final String assetId;
|
||||
final String albumId;
|
||||
const RemoteAlbumAssetEntityData(
|
||||
{required this.assetId, required this.albumId});
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
map['asset_id'] = i0.Variable<String>(assetId);
|
||||
map['album_id'] = i0.Variable<String>(albumId);
|
||||
return map;
|
||||
}
|
||||
|
||||
factory RemoteAlbumAssetEntityData.fromJson(Map<String, dynamic> json,
|
||||
{i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return RemoteAlbumAssetEntityData(
|
||||
assetId: serializer.fromJson<String>(json['assetId']),
|
||||
albumId: serializer.fromJson<String>(json['albumId']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'assetId': serializer.toJson<String>(assetId),
|
||||
'albumId': serializer.toJson<String>(albumId),
|
||||
};
|
||||
}
|
||||
|
||||
i1.RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) =>
|
||||
i1.RemoteAlbumAssetEntityData(
|
||||
assetId: assetId ?? this.assetId,
|
||||
albumId: albumId ?? this.albumId,
|
||||
);
|
||||
RemoteAlbumAssetEntityData copyWithCompanion(
|
||||
i1.RemoteAlbumAssetEntityCompanion data) {
|
||||
return RemoteAlbumAssetEntityData(
|
||||
assetId: data.assetId.present ? data.assetId.value : this.assetId,
|
||||
albumId: data.albumId.present ? data.albumId.value : this.albumId,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('RemoteAlbumAssetEntityData(')
|
||||
..write('assetId: $assetId, ')
|
||||
..write('albumId: $albumId')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(assetId, albumId);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is i1.RemoteAlbumAssetEntityData &&
|
||||
other.assetId == this.assetId &&
|
||||
other.albumId == this.albumId);
|
||||
}
|
||||
|
||||
class RemoteAlbumAssetEntityCompanion
|
||||
extends i0.UpdateCompanion<i1.RemoteAlbumAssetEntityData> {
|
||||
final i0.Value<String> assetId;
|
||||
final i0.Value<String> albumId;
|
||||
const RemoteAlbumAssetEntityCompanion({
|
||||
this.assetId = const i0.Value.absent(),
|
||||
this.albumId = const i0.Value.absent(),
|
||||
});
|
||||
RemoteAlbumAssetEntityCompanion.insert({
|
||||
required String assetId,
|
||||
required String albumId,
|
||||
}) : assetId = i0.Value(assetId),
|
||||
albumId = i0.Value(albumId);
|
||||
static i0.Insertable<i1.RemoteAlbumAssetEntityData> custom({
|
||||
i0.Expression<String>? assetId,
|
||||
i0.Expression<String>? albumId,
|
||||
}) {
|
||||
return i0.RawValuesInsertable({
|
||||
if (assetId != null) 'asset_id': assetId,
|
||||
if (albumId != null) 'album_id': albumId,
|
||||
});
|
||||
}
|
||||
|
||||
i1.RemoteAlbumAssetEntityCompanion copyWith(
|
||||
{i0.Value<String>? assetId, i0.Value<String>? albumId}) {
|
||||
return i1.RemoteAlbumAssetEntityCompanion(
|
||||
assetId: assetId ?? this.assetId,
|
||||
albumId: albumId ?? this.albumId,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
if (assetId.present) {
|
||||
map['asset_id'] = i0.Variable<String>(assetId.value);
|
||||
}
|
||||
if (albumId.present) {
|
||||
map['album_id'] = i0.Variable<String>(albumId.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('RemoteAlbumAssetEntityCompanion(')
|
||||
..write('assetId: $assetId, ')
|
||||
..write('albumId: $albumId')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
35
mobile/lib/infrastructure/entities/remote_asset.entity.dart
Normal file
35
mobile/lib/infrastructure/entities/remote_asset.entity.dart
Normal file
@@ -0,0 +1,35 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
||||
|
||||
@TableIndex(
|
||||
name: 'UQ_remote_asset_owner_checksum',
|
||||
columns: {#checksum, #ownerId},
|
||||
unique: true,
|
||||
)
|
||||
class RemoteAssetEntity extends Table
|
||||
with DriftDefaultsMixin, AssetEntityMixin {
|
||||
const RemoteAssetEntity();
|
||||
|
||||
TextColumn get id => text()();
|
||||
|
||||
TextColumn get checksum => text()();
|
||||
|
||||
BoolColumn get isFavorite => boolean().withDefault(const Constant(false))();
|
||||
|
||||
TextColumn get ownerId =>
|
||||
text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
|
||||
|
||||
DateTimeColumn get localDateTime => dateTime().nullable()();
|
||||
|
||||
TextColumn get thumbHash => text().nullable()();
|
||||
|
||||
DateTimeColumn get deletedAt => dateTime().nullable()();
|
||||
|
||||
IntColumn get visibility => intEnum<AssetVisibility>()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {id};
|
||||
}
|
||||
1180
mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart
generated
Normal file
1180
mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -78,7 +78,7 @@ class User {
|
||||
class UserEntity extends Table with DriftDefaultsMixin {
|
||||
const UserEntity();
|
||||
|
||||
BlobColumn get id => blob()();
|
||||
TextColumn get id => text()();
|
||||
TextColumn get name => text()();
|
||||
BoolColumn get isAdmin => boolean().withDefault(const Constant(false))();
|
||||
TextColumn get email => text()();
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
|
||||
as i1;
|
||||
import 'dart:typed_data' as i2;
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as i3;
|
||||
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4;
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as i2;
|
||||
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i3;
|
||||
|
||||
typedef $$UserEntityTableCreateCompanionBuilder = i1.UserEntityCompanion
|
||||
Function({
|
||||
required i2.Uint8List id,
|
||||
required String id,
|
||||
required String name,
|
||||
i0.Value<bool> isAdmin,
|
||||
required String email,
|
||||
@@ -20,7 +19,7 @@ typedef $$UserEntityTableCreateCompanionBuilder = i1.UserEntityCompanion
|
||||
});
|
||||
typedef $$UserEntityTableUpdateCompanionBuilder = i1.UserEntityCompanion
|
||||
Function({
|
||||
i0.Value<i2.Uint8List> id,
|
||||
i0.Value<String> id,
|
||||
i0.Value<String> name,
|
||||
i0.Value<bool> isAdmin,
|
||||
i0.Value<String> email,
|
||||
@@ -39,7 +38,7 @@ class $$UserEntityTableFilterComposer
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.ColumnFilters<i2.Uint8List> get id => $composableBuilder(
|
||||
i0.ColumnFilters<String> get id => $composableBuilder(
|
||||
column: $table.id, builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnFilters<String> get name => $composableBuilder(
|
||||
@@ -76,7 +75,7 @@ class $$UserEntityTableOrderingComposer
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.ColumnOrderings<i2.Uint8List> get id => $composableBuilder(
|
||||
i0.ColumnOrderings<String> get id => $composableBuilder(
|
||||
column: $table.id, builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<String> get name => $composableBuilder(
|
||||
@@ -114,7 +113,7 @@ class $$UserEntityTableAnnotationComposer
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
i0.GeneratedColumn<i2.Uint8List> get id =>
|
||||
i0.GeneratedColumn<String> get id =>
|
||||
$composableBuilder(column: $table.id, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<String> get name =>
|
||||
@@ -167,7 +166,7 @@ class $$UserEntityTableTableManager extends i0.RootTableManager<
|
||||
createComputedFieldComposer: () =>
|
||||
i1.$$UserEntityTableAnnotationComposer($db: db, $table: table),
|
||||
updateCompanionCallback: ({
|
||||
i0.Value<i2.Uint8List> id = const i0.Value.absent(),
|
||||
i0.Value<String> id = const i0.Value.absent(),
|
||||
i0.Value<String> name = const i0.Value.absent(),
|
||||
i0.Value<bool> isAdmin = const i0.Value.absent(),
|
||||
i0.Value<String> email = const i0.Value.absent(),
|
||||
@@ -187,7 +186,7 @@ class $$UserEntityTableTableManager extends i0.RootTableManager<
|
||||
quotaUsageInBytes: quotaUsageInBytes,
|
||||
),
|
||||
createCompanionCallback: ({
|
||||
required i2.Uint8List id,
|
||||
required String id,
|
||||
required String name,
|
||||
i0.Value<bool> isAdmin = const i0.Value.absent(),
|
||||
required String email,
|
||||
@@ -230,7 +229,7 @@ typedef $$UserEntityTableProcessedTableManager = i0.ProcessedTableManager<
|
||||
i1.UserEntityData,
|
||||
i0.PrefetchHooks Function()>;
|
||||
|
||||
class $UserEntityTable extends i3.UserEntity
|
||||
class $UserEntityTable extends i2.UserEntity
|
||||
with i0.TableInfo<$UserEntityTable, i1.UserEntityData> {
|
||||
@override
|
||||
final i0.GeneratedDatabase attachedDatabase;
|
||||
@@ -238,9 +237,9 @@ class $UserEntityTable extends i3.UserEntity
|
||||
$UserEntityTable(this.attachedDatabase, [this._alias]);
|
||||
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
|
||||
@override
|
||||
late final i0.GeneratedColumn<i2.Uint8List> id =
|
||||
i0.GeneratedColumn<i2.Uint8List>('id', aliasedName, false,
|
||||
type: i0.DriftSqlType.blob, requiredDuringInsert: true);
|
||||
late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
|
||||
'id', aliasedName, false,
|
||||
type: i0.DriftSqlType.string, requiredDuringInsert: true);
|
||||
static const i0.VerificationMeta _nameMeta =
|
||||
const i0.VerificationMeta('name');
|
||||
@override
|
||||
@@ -256,7 +255,7 @@ class $UserEntityTable extends i3.UserEntity
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints:
|
||||
i0.GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'),
|
||||
defaultValue: const i4.Constant(false));
|
||||
defaultValue: const i3.Constant(false));
|
||||
static const i0.VerificationMeta _emailMeta =
|
||||
const i0.VerificationMeta('email');
|
||||
@override
|
||||
@@ -276,7 +275,7 @@ class $UserEntityTable extends i3.UserEntity
|
||||
i0.GeneratedColumn<DateTime>('updated_at', aliasedName, false,
|
||||
type: i0.DriftSqlType.dateTime,
|
||||
requiredDuringInsert: false,
|
||||
defaultValue: i4.currentDateAndTime);
|
||||
defaultValue: i3.currentDateAndTime);
|
||||
static const i0.VerificationMeta _quotaSizeInBytesMeta =
|
||||
const i0.VerificationMeta('quotaSizeInBytes');
|
||||
@override
|
||||
@@ -290,7 +289,7 @@ class $UserEntityTable extends i3.UserEntity
|
||||
i0.GeneratedColumn<int>('quota_usage_in_bytes', aliasedName, false,
|
||||
type: i0.DriftSqlType.int,
|
||||
requiredDuringInsert: false,
|
||||
defaultValue: const i4.Constant(0));
|
||||
defaultValue: const i3.Constant(0));
|
||||
@override
|
||||
List<i0.GeneratedColumn> get $columns => [
|
||||
id,
|
||||
@@ -366,7 +365,7 @@ class $UserEntityTable extends i3.UserEntity
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return i1.UserEntityData(
|
||||
id: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.blob, data['${effectivePrefix}id'])!,
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!,
|
||||
name: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!,
|
||||
isAdmin: attachedDatabase.typeMapping
|
||||
@@ -397,7 +396,7 @@ class $UserEntityTable extends i3.UserEntity
|
||||
|
||||
class UserEntityData extends i0.DataClass
|
||||
implements i0.Insertable<i1.UserEntityData> {
|
||||
final i2.Uint8List id;
|
||||
final String id;
|
||||
final String name;
|
||||
final bool isAdmin;
|
||||
final String email;
|
||||
@@ -417,7 +416,7 @@ class UserEntityData extends i0.DataClass
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
map['id'] = i0.Variable<i2.Uint8List>(id);
|
||||
map['id'] = i0.Variable<String>(id);
|
||||
map['name'] = i0.Variable<String>(name);
|
||||
map['is_admin'] = i0.Variable<bool>(isAdmin);
|
||||
map['email'] = i0.Variable<String>(email);
|
||||
@@ -436,7 +435,7 @@ class UserEntityData extends i0.DataClass
|
||||
{i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return UserEntityData(
|
||||
id: serializer.fromJson<i2.Uint8List>(json['id']),
|
||||
id: serializer.fromJson<String>(json['id']),
|
||||
name: serializer.fromJson<String>(json['name']),
|
||||
isAdmin: serializer.fromJson<bool>(json['isAdmin']),
|
||||
email: serializer.fromJson<String>(json['email']),
|
||||
@@ -450,7 +449,7 @@ class UserEntityData extends i0.DataClass
|
||||
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'id': serializer.toJson<i2.Uint8List>(id),
|
||||
'id': serializer.toJson<String>(id),
|
||||
'name': serializer.toJson<String>(name),
|
||||
'isAdmin': serializer.toJson<bool>(isAdmin),
|
||||
'email': serializer.toJson<String>(email),
|
||||
@@ -462,7 +461,7 @@ class UserEntityData extends i0.DataClass
|
||||
}
|
||||
|
||||
i1.UserEntityData copyWith(
|
||||
{i2.Uint8List? id,
|
||||
{String? id,
|
||||
String? name,
|
||||
bool? isAdmin,
|
||||
String? email,
|
||||
@@ -519,13 +518,13 @@ class UserEntityData extends i0.DataClass
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(i0.$driftBlobEquality.hash(id), name, isAdmin,
|
||||
email, profileImagePath, updatedAt, quotaSizeInBytes, quotaUsageInBytes);
|
||||
int get hashCode => Object.hash(id, name, isAdmin, email, profileImagePath,
|
||||
updatedAt, quotaSizeInBytes, quotaUsageInBytes);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is i1.UserEntityData &&
|
||||
i0.$driftBlobEquality.equals(other.id, this.id) &&
|
||||
other.id == this.id &&
|
||||
other.name == this.name &&
|
||||
other.isAdmin == this.isAdmin &&
|
||||
other.email == this.email &&
|
||||
@@ -536,7 +535,7 @@ class UserEntityData extends i0.DataClass
|
||||
}
|
||||
|
||||
class UserEntityCompanion extends i0.UpdateCompanion<i1.UserEntityData> {
|
||||
final i0.Value<i2.Uint8List> id;
|
||||
final i0.Value<String> id;
|
||||
final i0.Value<String> name;
|
||||
final i0.Value<bool> isAdmin;
|
||||
final i0.Value<String> email;
|
||||
@@ -555,7 +554,7 @@ class UserEntityCompanion extends i0.UpdateCompanion<i1.UserEntityData> {
|
||||
this.quotaUsageInBytes = const i0.Value.absent(),
|
||||
});
|
||||
UserEntityCompanion.insert({
|
||||
required i2.Uint8List id,
|
||||
required String id,
|
||||
required String name,
|
||||
this.isAdmin = const i0.Value.absent(),
|
||||
required String email,
|
||||
@@ -567,7 +566,7 @@ class UserEntityCompanion extends i0.UpdateCompanion<i1.UserEntityData> {
|
||||
name = i0.Value(name),
|
||||
email = i0.Value(email);
|
||||
static i0.Insertable<i1.UserEntityData> custom({
|
||||
i0.Expression<i2.Uint8List>? id,
|
||||
i0.Expression<String>? id,
|
||||
i0.Expression<String>? name,
|
||||
i0.Expression<bool>? isAdmin,
|
||||
i0.Expression<String>? email,
|
||||
@@ -589,7 +588,7 @@ class UserEntityCompanion extends i0.UpdateCompanion<i1.UserEntityData> {
|
||||
}
|
||||
|
||||
i1.UserEntityCompanion copyWith(
|
||||
{i0.Value<i2.Uint8List>? id,
|
||||
{i0.Value<String>? id,
|
||||
i0.Value<String>? name,
|
||||
i0.Value<bool>? isAdmin,
|
||||
i0.Value<String>? email,
|
||||
@@ -613,7 +612,7 @@ class UserEntityCompanion extends i0.UpdateCompanion<i1.UserEntityData> {
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, i0.Expression>{};
|
||||
if (id.present) {
|
||||
map['id'] = i0.Variable<i2.Uint8List>(id.value);
|
||||
map['id'] = i0.Variable<String>(id.value);
|
||||
}
|
||||
if (name.present) {
|
||||
map['name'] = i0.Variable<String>(name.value);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user