Compare commits
63 Commits
flutter-3.
...
feat/dav
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65f7b3a86d | ||
|
|
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 |
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
|
|
||||||
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
|
|
||||||
@@ -43,6 +43,7 @@ export interface UploadOptionsDto {
|
|||||||
concurrency: number;
|
concurrency: number;
|
||||||
progress?: boolean;
|
progress?: boolean;
|
||||||
watch?: boolean;
|
watch?: boolean;
|
||||||
|
jsonOutput?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class UploadFile extends File {
|
class UploadFile extends File {
|
||||||
@@ -65,6 +66,9 @@ class UploadFile extends File {
|
|||||||
const uploadBatch = async (files: string[], options: UploadOptionsDto) => {
|
const uploadBatch = async (files: string[], options: UploadOptionsDto) => {
|
||||||
const { newFiles, duplicates } = await checkForDuplicates(files, options);
|
const { newFiles, duplicates } = await checkForDuplicates(files, options);
|
||||||
const newAssets = await uploadFiles(newFiles, 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 updateAlbums([...newAssets, ...duplicates], options);
|
||||||
await deleteFiles(newFiles, options);
|
await deleteFiles(newFiles, options);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -68,6 +68,11 @@ program
|
|||||||
.env('IMMICH_UPLOAD_CONCURRENCY')
|
.env('IMMICH_UPLOAD_CONCURRENCY')
|
||||||
.default(4),
|
.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('--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(new Option('--no-progress', 'Hide progress bars').env('IMMICH_PROGRESS_BAR').default(true))
|
||||||
.addOption(
|
.addOption(
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:8-bookworm@sha256:ff21bc0f8194dc9c105b769aeabf9585fea6a8ed649c0781caeac5cb3c247884
|
image: docker.io/valkey/valkey:8-bookworm@sha256:a19bebed6a91bd5e6e2106fef015f9602a3392deeb7c9ed47548378dcee3dfc2
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:8-bookworm@sha256:ff21bc0f8194dc9c105b769aeabf9585fea6a8ed649c0781caeac5cb3c247884
|
image: docker.io/valkey/valkey:8-bookworm@sha256:a19bebed6a91bd5e6e2106fef015f9602a3392deeb7c9ed47548378dcee3dfc2
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:8-bookworm@sha256:ff21bc0f8194dc9c105b769aeabf9585fea6a8ed649c0781caeac5cb3c247884
|
image: docker.io/valkey/valkey:8-bookworm@sha256:a19bebed6a91bd5e6e2106fef015f9602a3392deeb7c9ed47548378dcee3dfc2
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
|
|||||||
@@ -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.
|
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.
|
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
|
## 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`.
|
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
|
## Mobile Redirect URI
|
||||||
|
|
||||||
|
|||||||
@@ -90,19 +90,22 @@ Usage: immich upload [paths...] [options]
|
|||||||
Upload assets
|
Upload assets
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
paths One or more paths to assets to be uploaded
|
paths One or more paths to assets to be uploaded
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-r, --recursive Recursive (default: false, env: IMMICH_RECURSIVE)
|
-r, --recursive Recursive (default: false, env: IMMICH_RECURSIVE)
|
||||||
-i, --ignore [paths...] Paths to ignore (default: [], env: IMMICH_IGNORE_PATHS)
|
-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, --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)
|
-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 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)
|
-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)
|
-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)
|
-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)
|
-j, --json-output Output detailed information in json format (default: false, env: IMMICH_JSON_OUTPUT)
|
||||||
--help display help for command
|
--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>
|
</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/
|
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
|
### Obtain the API Key
|
||||||
|
|
||||||
The API key can be obtained in the user setting panel on the web interface.
|
The API key can be obtained in the user setting panel on the web interface.
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ import {
|
|||||||
mdiTrashCan,
|
mdiTrashCan,
|
||||||
mdiWeb,
|
mdiWeb,
|
||||||
mdiWrap,
|
mdiWrap,
|
||||||
|
mdiCloudKeyOutline,
|
||||||
|
mdiRegex,
|
||||||
|
mdiCodeJson,
|
||||||
} from '@mdi/js';
|
} from '@mdi/js';
|
||||||
import Layout from '@theme/Layout';
|
import Layout from '@theme/Layout';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@@ -23,6 +26,30 @@ const withLanguage = (date: Date) => (language: string) => date.toLocaleDateStri
|
|||||||
type Item = Omit<TimelineItem, 'done' | 'getDateLabel'> & { date: Date };
|
type Item = Omit<TimelineItem, 'done' | 'getDateLabel'> & { date: Date };
|
||||||
|
|
||||||
const items: Item[] = [
|
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,
|
icon: mdiCrop,
|
||||||
iconColor: 'tomato',
|
iconColor: 'tomato',
|
||||||
@@ -33,7 +60,18 @@ const items: Item[] = [
|
|||||||
url: 'https://github.com/immich-app/immich/pull/17974',
|
url: 'https://github.com/immich-app/immich/pull/17974',
|
||||||
text: '#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,
|
icon: mdiMicrosoftWindows,
|
||||||
|
|||||||
@@ -28,8 +28,10 @@ services:
|
|||||||
extra_hosts:
|
extra_hosts:
|
||||||
- 'auth-server:host-gateway'
|
- 'auth-server:host-gateway'
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
redis:
|
||||||
- database
|
condition: service_started
|
||||||
|
database:
|
||||||
|
condition: service_healthy
|
||||||
ports:
|
ports:
|
||||||
- 2285:2285
|
- 2285:2285
|
||||||
|
|
||||||
@@ -37,7 +39,7 @@ services:
|
|||||||
image: redis:6.2-alpine@sha256:3211c33a618c457e5d241922c975dbc4f446d0bdb2dc75694f5573ef8e2d01fa
|
image: redis:6.2-alpine@sha256:3211c33a618c457e5d241922c975dbc4f446d0bdb2dc75694f5573ef8e2d01fa
|
||||||
|
|
||||||
database:
|
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
|
command: -c fsync=off -c shared_preload_libraries=vchord.so -c config_file=/var/lib/postgresql/data/postgresql.conf
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
@@ -45,3 +47,9 @@ services:
|
|||||||
POSTGRES_DB: immich
|
POSTGRES_DB: immich
|
||||||
ports:
|
ports:
|
||||||
- 5435:5432
|
- 5435:5432
|
||||||
|
healthcheck:
|
||||||
|
test: ['CMD-SHELL', 'pg_isready -U postgres -d immich']
|
||||||
|
interval: 1s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 30
|
||||||
|
start_period: 10s
|
||||||
|
|||||||
@@ -428,6 +428,15 @@ describe('/albums', () => {
|
|||||||
order: AssetOrder.Desc,
|
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', () => {
|
describe('PUT /albums/:id/assets', () => {
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ describe('/timeline', () => {
|
|||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual(
|
expect(body).toEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
{ count: 3, timeBucket: '1970-02-01T00:00:00.000Z' },
|
{ count: 3, timeBucket: '1970-02-01' },
|
||||||
{ count: 1, timeBucket: '1970-01-01T00:00:00.000Z' },
|
{ count: 1, timeBucket: '1970-01-01' },
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -167,7 +167,8 @@ describe('/timeline', () => {
|
|||||||
isImage: [],
|
isImage: [],
|
||||||
isTrashed: [],
|
isTrashed: [],
|
||||||
livePhotoVideoId: [],
|
livePhotoVideoId: [],
|
||||||
localDateTime: [],
|
fileCreatedAt: [],
|
||||||
|
localOffsetHours: [],
|
||||||
ownerId: [],
|
ownerId: [],
|
||||||
projectionType: [],
|
projectionType: [],
|
||||||
ratio: [],
|
ratio: [],
|
||||||
@@ -204,7 +205,8 @@ describe('/timeline', () => {
|
|||||||
isImage: [],
|
isImage: [],
|
||||||
isTrashed: [],
|
isTrashed: [],
|
||||||
livePhotoVideoId: [],
|
livePhotoVideoId: [],
|
||||||
localDateTime: [],
|
fileCreatedAt: [],
|
||||||
|
localOffsetHours: [],
|
||||||
ownerId: [],
|
ownerId: [],
|
||||||
projectionType: [],
|
projectionType: [],
|
||||||
ratio: [],
|
ratio: [],
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ export const loginResponseDto = {
|
|||||||
accessToken: expect.any(String),
|
accessToken: expect.any(String),
|
||||||
name: 'Immich Admin',
|
name: 'Immich Admin',
|
||||||
isAdmin: true,
|
isAdmin: true,
|
||||||
|
isOnboarded: false,
|
||||||
profileImagePath: '',
|
profileImagePath: '',
|
||||||
shouldChangePassword: true,
|
shouldChangePassword: true,
|
||||||
userEmail: 'admin@immich.cloud',
|
userEmail: 'admin@immich.cloud',
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ test.describe('Registration', () => {
|
|||||||
// onboarding
|
// onboarding
|
||||||
await expect(page).toHaveURL('/auth/onboarding');
|
await expect(page).toHaveURL('/auth/onboarding');
|
||||||
await page.getByRole('button', { name: 'Theme' }).click();
|
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: 'Storage Template' }).click();
|
||||||
await page.getByRole('button', { name: 'Done' }).click();
|
await page.getByRole('button', { name: 'Done' }).click();
|
||||||
|
|
||||||
@@ -77,6 +79,13 @@ test.describe('Registration', () => {
|
|||||||
await page.getByLabel('Password').fill('new-password');
|
await page.getByLabel('Password').fill('new-password');
|
||||||
await page.getByRole('button', { name: 'Login' }).click();
|
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
|
// success
|
||||||
await expect(page).toHaveURL(/\/photos/);
|
await expect(page).toHaveURL(/\/photos/);
|
||||||
});
|
});
|
||||||
|
|||||||
80
i18n/en.json
80
i18n/en.json
@@ -26,7 +26,6 @@
|
|||||||
"add_to_album": "Add to album",
|
"add_to_album": "Add to album",
|
||||||
"add_to_album_bottom_sheet_added": "Added to {album}",
|
"add_to_album_bottom_sheet_added": "Added to {album}",
|
||||||
"add_to_album_bottom_sheet_already_exists": "Already in {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_to_shared_album": "Add to shared album",
|
||||||
"add_url": "Add URL",
|
"add_url": "Add URL",
|
||||||
"added_to_archive": "Added to archive",
|
"added_to_archive": "Added to archive",
|
||||||
@@ -44,9 +43,7 @@
|
|||||||
"backup_database_enable_description": "Enable database dumps",
|
"backup_database_enable_description": "Enable database dumps",
|
||||||
"backup_keep_last_amount": "Amount of previous dumps to keep",
|
"backup_keep_last_amount": "Amount of previous dumps to keep",
|
||||||
"backup_settings": "Database Dump Settings",
|
"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.",
|
"backup_settings_description": "Manage database dump settings.",
|
||||||
"check_all": "Check All",
|
|
||||||
"cleanup": "Cleanup",
|
|
||||||
"cleared_jobs": "Cleared jobs for: {job}",
|
"cleared_jobs": "Cleared jobs for: {job}",
|
||||||
"config_set_by_file": "Config is currently set by a config file",
|
"config_set_by_file": "Config is currently set by a config file",
|
||||||
"confirm_delete_library": "Are you sure you want to delete {library} library?",
|
"confirm_delete_library": "Are you sure you want to delete {library} library?",
|
||||||
@@ -62,14 +59,12 @@
|
|||||||
"disable_login": "Disable login",
|
"disable_login": "Disable login",
|
||||||
"duplicate_detection_job_description": "Run machine learning on assets to detect similar images. Relies on Smart Search",
|
"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.",
|
"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",
|
"external_library_management": "External Library Management",
|
||||||
"face_detection": "Face detection",
|
"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.",
|
"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.",
|
"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}",
|
"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.",
|
"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": "Format",
|
||||||
"image_format_description": "WebP produces smaller files than JPEG, but is slower to encode.",
|
"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",
|
"image_fullsize_description": "Full-size image with stripped metadata, used when zoomed in",
|
||||||
@@ -210,8 +205,6 @@
|
|||||||
"oauth_storage_quota_default_description": "Quota in GiB to be used when no claim is provided (Enter 0 for unlimited quota).",
|
"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": "Request Timeout",
|
||||||
"oauth_timeout_description": "Timeout for requests in milliseconds",
|
"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_enable_description": "Login with email and password",
|
||||||
"password_settings": "Password Login",
|
"password_settings": "Password Login",
|
||||||
"password_settings_description": "Manage password login settings",
|
"password_settings_description": "Manage password login settings",
|
||||||
@@ -221,9 +214,6 @@
|
|||||||
"refreshing_all_libraries": "Refreshing all libraries",
|
"refreshing_all_libraries": "Refreshing all libraries",
|
||||||
"registration": "Admin Registration",
|
"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.",
|
"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",
|
"require_password_change_on_login": "Require user to change password on first login",
|
||||||
"reset_settings_to_default": "Reset settings to default",
|
"reset_settings_to_default": "Reset settings to default",
|
||||||
"reset_settings_to_recent_saved": "Reset settings to the recent saved settings",
|
"reset_settings_to_recent_saved": "Reset settings to the recent saved settings",
|
||||||
@@ -264,7 +254,6 @@
|
|||||||
"template_email_invite_album": "Invite Album Template",
|
"template_email_invite_album": "Invite Album Template",
|
||||||
"template_email_preview": "Preview",
|
"template_email_preview": "Preview",
|
||||||
"template_email_settings": "Email Templates",
|
"template_email_settings": "Email Templates",
|
||||||
"template_email_settings_description": "Manage custom email notification templates",
|
|
||||||
"template_email_update_album": "Update Album Template",
|
"template_email_update_album": "Update Album Template",
|
||||||
"template_email_welcome": "Welcome email template",
|
"template_email_welcome": "Welcome email template",
|
||||||
"template_settings": "Notification Templates",
|
"template_settings": "Notification Templates",
|
||||||
@@ -273,7 +262,6 @@
|
|||||||
"theme_custom_css_settings_description": "Cascading Style Sheets allow the design of Immich to be customized.",
|
"theme_custom_css_settings_description": "Cascading Style Sheets allow the design of Immich to be customized.",
|
||||||
"theme_settings": "Theme Settings",
|
"theme_settings": "Theme Settings",
|
||||||
"theme_settings_description": "Manage customization of the Immich web interface",
|
"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": "Generate Thumbnails",
|
||||||
"thumbnail_generation_job_description": "Generate large, small and blurred thumbnails for each asset, as well as thumbnails for each person",
|
"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",
|
"transcoding_acceleration_api": "Acceleration API",
|
||||||
@@ -341,8 +329,6 @@
|
|||||||
"trash_number_of_days_description": "Number of days to keep the assets in trash before permanently removing them",
|
"trash_number_of_days_description": "Number of days to keep the assets in trash before permanently removing them",
|
||||||
"trash_settings": "Trash Settings",
|
"trash_settings": "Trash Settings",
|
||||||
"trash_settings_description": "Manage 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_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": "<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",
|
"user_delete_delay_settings": "Delete delay",
|
||||||
@@ -401,10 +387,6 @@
|
|||||||
"album_remove_user": "Remove user?",
|
"album_remove_user": "Remove user?",
|
||||||
"album_remove_user_confirmation": "Are you sure you want to 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_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": "Album updated",
|
||||||
"album_updated_setting_description": "Receive an email notification when a shared album has new assets",
|
"album_updated_setting_description": "Receive an email notification when a shared album has new assets",
|
||||||
"album_user_left": "Left {album}",
|
"album_user_left": "Left {album}",
|
||||||
@@ -420,6 +402,9 @@
|
|||||||
"album_with_link_access": "Let anyone with the link see photos and people in this album.",
|
"album_with_link_access": "Let anyone with the link see photos and people in this album.",
|
||||||
"albums": "Albums",
|
"albums": "Albums",
|
||||||
"albums_count": "{count, plural, one {{count, number} Album} other {{count, number} 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": "All",
|
||||||
"all_albums": "All albums",
|
"all_albums": "All albums",
|
||||||
"all_people": "All people",
|
"all_people": "All people",
|
||||||
@@ -481,6 +466,8 @@
|
|||||||
"assets_count": "{count, plural, one {# asset} other {# assets}}",
|
"assets_count": "{count, plural, one {# asset} other {# assets}}",
|
||||||
"assets_deleted_permanently": "{count} asset(s) deleted permanently",
|
"assets_deleted_permanently": "{count} asset(s) deleted permanently",
|
||||||
"assets_deleted_permanently_from_server": "{count} asset(s) deleted permanently from the Immich server",
|
"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_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_permanently_deleted_count": "Permanently deleted {count, plural, one {# asset} other {# assets}}",
|
||||||
"assets_removed_count": "Removed {count, plural, one {# asset} other {# assets}}",
|
"assets_removed_count": "Removed {count, plural, one {# asset} other {# assets}}",
|
||||||
@@ -495,6 +482,7 @@
|
|||||||
"authorized_devices": "Authorized Devices",
|
"authorized_devices": "Authorized Devices",
|
||||||
"automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere",
|
"automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere",
|
||||||
"automatic_endpoint_switching_title": "Automatic URL switching",
|
"automatic_endpoint_switching_title": "Automatic URL switching",
|
||||||
|
"autoplay_slideshow": "Autoplay slideshow",
|
||||||
"back": "Back",
|
"back": "Back",
|
||||||
"back_close_deselect": "Back, close, or deselect",
|
"back_close_deselect": "Back, close, or deselect",
|
||||||
"background_location_permission": "Background location permission",
|
"background_location_permission": "Background location permission",
|
||||||
@@ -576,21 +564,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_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.",
|
"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",
|
"buy": "Purchase Immich",
|
||||||
"cache_settings_album_thumbnails": "Library page thumbnails ({count} assets)",
|
|
||||||
"cache_settings_clear_cache_button": "Clear cache",
|
"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_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_clear_button": "CLEAR",
|
||||||
"cache_settings_duplicated_assets_subtitle": "Photos and videos that are black listed by the app",
|
"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_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_album": "Library thumbnails",
|
||||||
"cache_settings_statistics_assets": "{count} assets ({size})",
|
|
||||||
"cache_settings_statistics_full": "Full images",
|
"cache_settings_statistics_full": "Full images",
|
||||||
"cache_settings_statistics_shared": "Shared album thumbnails",
|
"cache_settings_statistics_shared": "Shared album thumbnails",
|
||||||
"cache_settings_statistics_thumbnail": "Thumbnails",
|
"cache_settings_statistics_thumbnail": "Thumbnails",
|
||||||
"cache_settings_statistics_title": "Cache usage",
|
"cache_settings_statistics_title": "Cache usage",
|
||||||
"cache_settings_subtitle": "Control the caching behaviour of the Immich mobile application",
|
"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_subtitle": "Control the local storage behaviour",
|
||||||
"cache_settings_tile_title": "Local Storage",
|
"cache_settings_tile_title": "Local Storage",
|
||||||
"cache_settings_title": "Caching Settings",
|
"cache_settings_title": "Caching Settings",
|
||||||
@@ -622,7 +606,6 @@
|
|||||||
"change_pin_code": "Change PIN code",
|
"change_pin_code": "Change PIN code",
|
||||||
"change_your_password": "Change your password",
|
"change_your_password": "Change your password",
|
||||||
"changed_visibility_successfully": "Changed visibility successfully",
|
"changed_visibility_successfully": "Changed visibility successfully",
|
||||||
"check_all": "Check All",
|
|
||||||
"check_corrupt_asset_backup": "Check for corrupt asset backups",
|
"check_corrupt_asset_backup": "Check for corrupt asset backups",
|
||||||
"check_corrupt_asset_backup_button": "Perform check",
|
"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.",
|
"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.",
|
||||||
@@ -668,7 +651,6 @@
|
|||||||
"contain": "Contain",
|
"contain": "Contain",
|
||||||
"context": "Context",
|
"context": "Context",
|
||||||
"continue": "Continue",
|
"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_create_new_album": "Create new album",
|
||||||
"control_bottom_app_bar_delete_from_immich": "Delete from Immich",
|
"control_bottom_app_bar_delete_from_immich": "Delete from Immich",
|
||||||
"control_bottom_app_bar_delete_from_local": "Delete from device",
|
"control_bottom_app_bar_delete_from_local": "Delete from device",
|
||||||
@@ -717,6 +699,7 @@
|
|||||||
"daily_title_text_date": "E, MMM dd",
|
"daily_title_text_date": "E, MMM dd",
|
||||||
"daily_title_text_date_year": "E, MMM dd, yyyy",
|
"daily_title_text_date_year": "E, MMM dd, yyyy",
|
||||||
"dark": "Dark",
|
"dark": "Dark",
|
||||||
|
"darkTheme": "Toggle dark theme",
|
||||||
"date_after": "Date after",
|
"date_after": "Date after",
|
||||||
"date_and_time": "Date and Time",
|
"date_and_time": "Date and Time",
|
||||||
"date_before": "Date before",
|
"date_before": "Date before",
|
||||||
@@ -779,7 +762,6 @@
|
|||||||
"download_enqueue": "Download enqueued",
|
"download_enqueue": "Download enqueued",
|
||||||
"download_error": "Download Error",
|
"download_error": "Download Error",
|
||||||
"download_failed": "Download failed",
|
"download_failed": "Download failed",
|
||||||
"download_filename": "file: {filename}",
|
|
||||||
"download_finished": "Download finished",
|
"download_finished": "Download finished",
|
||||||
"download_include_embedded_motion_videos": "Embedded videos",
|
"download_include_embedded_motion_videos": "Embedded videos",
|
||||||
"download_include_embedded_motion_videos_description": "Include videos embedded in motion photos as a separate file",
|
"download_include_embedded_motion_videos_description": "Include videos embedded in motion photos as a separate file",
|
||||||
@@ -855,7 +837,6 @@
|
|||||||
"cant_get_number_of_comments": "Can't get number of comments",
|
"cant_get_number_of_comments": "Can't get number of comments",
|
||||||
"cant_search_people": "Can't search people",
|
"cant_search_people": "Can't search people",
|
||||||
"cant_search_places": "Can't search places",
|
"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_assets_to_album": "Error adding assets to album",
|
||||||
"error_adding_users_to_album": "Error adding users to album",
|
"error_adding_users_to_album": "Error adding users to album",
|
||||||
"error_deleting_shared_user": "Error deleting shared user",
|
"error_deleting_shared_user": "Error deleting shared user",
|
||||||
@@ -864,7 +845,6 @@
|
|||||||
"error_removing_assets_from_album": "Error removing assets from album, check console for more details",
|
"error_removing_assets_from_album": "Error removing assets from album, check console for more details",
|
||||||
"error_selecting_all_assets": "Error selecting all assets",
|
"error_selecting_all_assets": "Error selecting all assets",
|
||||||
"exclusion_pattern_already_exists": "This exclusion pattern already exists.",
|
"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_album": "Failed to create album",
|
||||||
"failed_to_create_shared_link": "Failed to create shared link",
|
"failed_to_create_shared_link": "Failed to create shared link",
|
||||||
"failed_to_edit_shared_link": "Failed to edit shared link",
|
"failed_to_edit_shared_link": "Failed to edit shared link",
|
||||||
@@ -883,7 +863,6 @@
|
|||||||
"paths_validation_failed": "{paths, plural, one {# path} other {# paths}} failed validation",
|
"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.",
|
"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",
|
"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_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_assets_to_shared_link": "Unable to add assets to shared link",
|
||||||
"unable_to_add_comment": "Unable to add comment",
|
"unable_to_add_comment": "Unable to add comment",
|
||||||
@@ -902,7 +881,6 @@
|
|||||||
"unable_to_change_visibility": "Unable to change the visibility for {count, plural, one {# person} other {# people}}",
|
"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_complete_oauth_login": "Unable to complete OAuth login",
|
||||||
"unable_to_connect": "Unable to connect",
|
"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_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_admin_account": "Unable to create admin account",
|
||||||
"unable_to_create_api_key": "Unable to create a new API Key",
|
"unable_to_create_api_key": "Unable to create a new API Key",
|
||||||
@@ -926,14 +904,9 @@
|
|||||||
"unable_to_hide_person": "Unable to hide person",
|
"unable_to_hide_person": "Unable to hide person",
|
||||||
"unable_to_link_motion_video": "Unable to link motion video",
|
"unable_to_link_motion_video": "Unable to link motion video",
|
||||||
"unable_to_link_oauth_account": "Unable to link OAuth account",
|
"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_all_devices": "Unable to log out all devices",
|
||||||
"unable_to_log_out_device": "Unable to log out device",
|
"unable_to_log_out_device": "Unable to log out device",
|
||||||
"unable_to_login_with_oauth": "Unable to login with OAuth",
|
"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_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_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",
|
"unable_to_reassign_assets_new_person": "Unable to reassign assets to a new person",
|
||||||
@@ -941,11 +914,9 @@
|
|||||||
"unable_to_remove_album_users": "Unable to remove users from album",
|
"unable_to_remove_album_users": "Unable to remove users from album",
|
||||||
"unable_to_remove_api_key": "Unable to remove API Key",
|
"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_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_library": "Unable to remove library",
|
||||||
"unable_to_remove_partner": "Unable to remove partner",
|
"unable_to_remove_partner": "Unable to remove partner",
|
||||||
"unable_to_remove_reaction": "Unable to remove reaction",
|
"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_password": "Unable to reset password",
|
||||||
"unable_to_reset_pin_code": "Unable to reset PIN code",
|
"unable_to_reset_pin_code": "Unable to reset PIN code",
|
||||||
"unable_to_resolve_duplicate": "Unable to resolve duplicate",
|
"unable_to_resolve_duplicate": "Unable to resolve duplicate",
|
||||||
@@ -1118,6 +1089,12 @@
|
|||||||
"invalid_date_format": "Invalid date format",
|
"invalid_date_format": "Invalid date format",
|
||||||
"invite_people": "Invite People",
|
"invite_people": "Invite People",
|
||||||
"invite_to_album": "Invite to album",
|
"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}}",
|
"items_count": "{count, plural, one {# item} other {# items}}",
|
||||||
"jobs": "Jobs",
|
"jobs": "Jobs",
|
||||||
"keep": "Keep",
|
"keep": "Keep",
|
||||||
@@ -1126,6 +1103,9 @@
|
|||||||
"kept_this_deleted_others": "Kept this asset and deleted {count, plural, one {# asset} other {# assets}}",
|
"kept_this_deleted_others": "Kept this asset and deleted {count, plural, one {# asset} other {# assets}}",
|
||||||
"keyboard_shortcuts": "Keyboard shortcuts",
|
"keyboard_shortcuts": "Keyboard shortcuts",
|
||||||
"language": "Language",
|
"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",
|
"language_setting_description": "Select your preferred language",
|
||||||
"last_seen": "Last seen",
|
"last_seen": "Last seen",
|
||||||
"latest_version": "Latest Version",
|
"latest_version": "Latest Version",
|
||||||
@@ -1161,7 +1141,7 @@
|
|||||||
"location_picker_longitude_error": "Enter a valid longitude",
|
"location_picker_longitude_error": "Enter a valid longitude",
|
||||||
"location_picker_longitude_hint": "Enter your longitude here",
|
"location_picker_longitude_hint": "Enter your longitude here",
|
||||||
"lock": "Lock",
|
"lock": "Lock",
|
||||||
"locked_folder": "Locked folder",
|
"locked_folder": "Locked Folder",
|
||||||
"log_out": "Log out",
|
"log_out": "Log out",
|
||||||
"log_out_all_devices": "Log Out All Devices",
|
"log_out_all_devices": "Log Out All Devices",
|
||||||
"logged_out_all_devices": "Logged out all devices",
|
"logged_out_all_devices": "Logged out all devices",
|
||||||
@@ -1195,7 +1175,7 @@
|
|||||||
"look": "Look",
|
"look": "Look",
|
||||||
"loop_videos": "Loop videos",
|
"loop_videos": "Loop videos",
|
||||||
"loop_videos_description": "Enable to automatically loop a video in the detail viewer.",
|
"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",
|
"main_menu": "Main menu",
|
||||||
"make": "Make",
|
"make": "Make",
|
||||||
"manage_shared_links": "Manage shared links",
|
"manage_shared_links": "Manage shared links",
|
||||||
@@ -1319,15 +1299,15 @@
|
|||||||
"oauth": "OAuth",
|
"oauth": "OAuth",
|
||||||
"official_immich_resources": "Official Immich Resources",
|
"official_immich_resources": "Official Immich Resources",
|
||||||
"offline": "Offline",
|
"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",
|
"ok": "Ok",
|
||||||
"oldest_first": "Oldest first",
|
"oldest_first": "Oldest first",
|
||||||
"on_this_device": "On this device",
|
"on_this_device": "On this device",
|
||||||
"onboarding": "Onboarding",
|
"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_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}",
|
"onboarding_welcome_user": "Welcome, {user}",
|
||||||
"online": "Online",
|
"online": "Online",
|
||||||
"only_favorites": "Only favorites",
|
"only_favorites": "Only favorites",
|
||||||
@@ -1460,7 +1440,7 @@
|
|||||||
"purchase_lifetime_description": "Lifetime purchase",
|
"purchase_lifetime_description": "Lifetime purchase",
|
||||||
"purchase_option_title": "PURCHASE OPTIONS",
|
"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_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_panel_title": "Support the project",
|
||||||
"purchase_per_server": "Per server",
|
"purchase_per_server": "Per server",
|
||||||
"purchase_per_user": "Per user",
|
"purchase_per_user": "Per user",
|
||||||
@@ -1639,6 +1619,7 @@
|
|||||||
"server_info_box_server_url": "Server URL",
|
"server_info_box_server_url": "Server URL",
|
||||||
"server_offline": "Server Offline",
|
"server_offline": "Server Offline",
|
||||||
"server_online": "Server Online",
|
"server_online": "Server Online",
|
||||||
|
"server_privacy": "Server Privacy",
|
||||||
"server_stats": "Server Stats",
|
"server_stats": "Server Stats",
|
||||||
"server_version": "Server Version",
|
"server_version": "Server Version",
|
||||||
"set": "Set",
|
"set": "Set",
|
||||||
@@ -1656,7 +1637,6 @@
|
|||||||
"setting_image_viewer_title": "Images",
|
"setting_image_viewer_title": "Images",
|
||||||
"setting_languages_apply": "Apply",
|
"setting_languages_apply": "Apply",
|
||||||
"setting_languages_subtitle": "Change the app's language",
|
"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_failures_grace_period": "Notify background backup failures: {duration}",
|
||||||
"setting_notifications_notify_hours": "{count} hours",
|
"setting_notifications_notify_hours": "{count} hours",
|
||||||
"setting_notifications_notify_immediately": "immediately",
|
"setting_notifications_notify_immediately": "immediately",
|
||||||
@@ -1843,7 +1823,6 @@
|
|||||||
"to_parent": "Go to parent",
|
"to_parent": "Go to parent",
|
||||||
"to_trash": "Trash",
|
"to_trash": "Trash",
|
||||||
"toggle_settings": "Toggle settings",
|
"toggle_settings": "Toggle settings",
|
||||||
"toggle_theme": "Toggle dark theme",
|
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"total_usage": "Total usage",
|
"total_usage": "Total usage",
|
||||||
"trash": "Trash",
|
"trash": "Trash",
|
||||||
@@ -1865,6 +1844,7 @@
|
|||||||
"unable_to_setup_pin_code": "Unable to setup PIN code",
|
"unable_to_setup_pin_code": "Unable to setup PIN code",
|
||||||
"unarchive": "Unarchive",
|
"unarchive": "Unarchive",
|
||||||
"unarchived_count": "{count, plural, other {Unarchived #}}",
|
"unarchived_count": "{count, plural, other {Unarchived #}}",
|
||||||
|
"undo": "Undo",
|
||||||
"unfavorite": "Unfavorite",
|
"unfavorite": "Unfavorite",
|
||||||
"unhide_person": "Unhide person",
|
"unhide_person": "Unhide person",
|
||||||
"unknown": "Unknown",
|
"unknown": "Unknown",
|
||||||
@@ -1883,8 +1863,6 @@
|
|||||||
"unselect_all_duplicates": "Unselect all duplicates",
|
"unselect_all_duplicates": "Unselect all duplicates",
|
||||||
"unstack": "Un-stack",
|
"unstack": "Un-stack",
|
||||||
"unstacked_assets_count": "Un-stacked {count, plural, one {# asset} other {# assets}}",
|
"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",
|
"up_next": "Up next",
|
||||||
"updated_at": "Updated",
|
"updated_at": "Updated",
|
||||||
"updated_password": "Updated password",
|
"updated_password": "Updated password",
|
||||||
@@ -1912,6 +1890,7 @@
|
|||||||
"user_liked": "{user} liked {type, select, photo {this photo} video {this video} asset {this asset} other {it}}",
|
"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": "PIN Code",
|
||||||
"user_pin_code_settings_description": "Manage your PIN code",
|
"user_pin_code_settings_description": "Manage your PIN code",
|
||||||
|
"user_privacy": "User Privacy",
|
||||||
"user_purchase_settings": "Purchase",
|
"user_purchase_settings": "Purchase",
|
||||||
"user_purchase_settings_description": "Manage your purchase",
|
"user_purchase_settings_description": "Manage your purchase",
|
||||||
"user_role_set": "Set {user} as {role}",
|
"user_role_set": "Set {user} as {role}",
|
||||||
@@ -1927,11 +1906,6 @@
|
|||||||
"version": "Version",
|
"version": "Version",
|
||||||
"version_announcement_closing": "Your friend, Alex",
|
"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_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": "Version History",
|
||||||
"version_history_item": "Installed {version} on {date}",
|
"version_history_item": "Installed {version} on {date}",
|
||||||
"video": "Video",
|
"video": "Video",
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ custom_lint:
|
|||||||
allowed:
|
allowed:
|
||||||
# required / wanted
|
# required / wanted
|
||||||
- 'lib/infrastructure/repositories/album_media.repository.dart'
|
- 'lib/infrastructure/repositories/album_media.repository.dart'
|
||||||
|
- 'lib/infrastructure/repositories/storage.repository.dart'
|
||||||
- 'lib/repositories/{album,asset,file}_media.repository.dart'
|
- 'lib/repositories/{album,asset,file}_media.repository.dart'
|
||||||
# acceptable exceptions for the time being
|
# acceptable exceptions for the time being
|
||||||
- lib/entities/asset.entity.dart # to provide local AssetEntity for now
|
- lib/entities/asset.entity.dart # to provide local AssetEntity for now
|
||||||
|
|||||||
@@ -247,6 +247,7 @@ interface NativeSyncApi {
|
|||||||
fun getAlbums(): List<PlatformAlbum>
|
fun getAlbums(): List<PlatformAlbum>
|
||||||
fun getAssetsCountSince(albumId: String, timestamp: Long): Long
|
fun getAssetsCountSince(albumId: String, timestamp: Long): Long
|
||||||
fun getAssetsForAlbum(albumId: String, updatedTimeCond: Long?): List<PlatformAsset>
|
fun getAssetsForAlbum(albumId: String, updatedTimeCond: Long?): List<PlatformAsset>
|
||||||
|
fun hashPaths(paths: List<String>): List<ByteArray?>
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/** The codec used by NativeSyncApi. */
|
/** The codec used by NativeSyncApi. */
|
||||||
@@ -388,6 +389,23 @@ interface NativeSyncApi {
|
|||||||
channel.setMessageHandler(null)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import android.annotation.SuppressLint
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
|
import android.util.Log
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.security.MessageDigest
|
||||||
|
|
||||||
sealed class AssetResult {
|
sealed class AssetResult {
|
||||||
data class ValidAsset(val asset: PlatformAsset, val albumId: String) : AssetResult()
|
data class ValidAsset(val asset: PlatformAsset, val albumId: String) : AssetResult()
|
||||||
@@ -16,6 +19,8 @@ open class NativeSyncApiImplBase(context: Context) {
|
|||||||
private val ctx: Context = context.applicationContext
|
private val ctx: Context = context.applicationContext
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val TAG = "NativeSyncApiImplBase"
|
||||||
|
|
||||||
const val MEDIA_SELECTION =
|
const val MEDIA_SELECTION =
|
||||||
"(${MediaStore.Files.FileColumns.MEDIA_TYPE} = ? OR ${MediaStore.Files.FileColumns.MEDIA_TYPE} = ?)"
|
"(${MediaStore.Files.FileColumns.MEDIA_TYPE} = ? OR ${MediaStore.Files.FileColumns.MEDIA_TYPE} = ?)"
|
||||||
val MEDIA_SELECTION_ARGS = arrayOf(
|
val MEDIA_SELECTION_ARGS = arrayOf(
|
||||||
@@ -34,6 +39,8 @@ open class NativeSyncApiImplBase(context: Context) {
|
|||||||
MediaStore.MediaColumns.BUCKET_ID,
|
MediaStore.MediaColumns.BUCKET_ID,
|
||||||
MediaStore.MediaColumns.DURATION
|
MediaStore.MediaColumns.DURATION
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const val HASH_BUFFER_SIZE = 2 * 1024 * 1024
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun getCursor(
|
protected fun getCursor(
|
||||||
@@ -174,4 +181,24 @@ open class NativeSyncApiImplBase(context: Context) {
|
|||||||
.mapNotNull { result -> (result as? AssetResult.ValidAsset)?.asset }
|
.mapNotNull { result -> (result as? AssetResult.ValidAsset)?.asset }
|
||||||
.toList()
|
.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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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
@@ -307,6 +307,7 @@ protocol NativeSyncApi {
|
|||||||
func getAlbums() throws -> [PlatformAlbum]
|
func getAlbums() throws -> [PlatformAlbum]
|
||||||
func getAssetsCountSince(albumId: String, timestamp: Int64) throws -> Int64
|
func getAssetsCountSince(albumId: String, timestamp: Int64) throws -> Int64
|
||||||
func getAssetsForAlbum(albumId: String, updatedTimeCond: Int64?) throws -> [PlatformAsset]
|
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`.
|
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
|
||||||
@@ -442,5 +443,22 @@ class NativeSyncApiSetup {
|
|||||||
} else {
|
} else {
|
||||||
getAssetsForAlbumChannel.setMessageHandler(nil)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import Photos
|
import Photos
|
||||||
|
import CryptoKit
|
||||||
|
|
||||||
struct AssetWrapper: Hashable, Equatable {
|
struct AssetWrapper: Hashable, Equatable {
|
||||||
let asset: PlatformAsset
|
let asset: PlatformAsset
|
||||||
@@ -34,6 +35,8 @@ class NativeSyncApiImpl: NativeSyncApi {
|
|||||||
private let changeTokenKey = "immich:changeToken"
|
private let changeTokenKey = "immich:changeToken"
|
||||||
private let albumTypes: [PHAssetCollectionType] = [.album, .smartAlbum]
|
private let albumTypes: [PHAssetCollectionType] = [.album, .smartAlbum]
|
||||||
|
|
||||||
|
private let hashBufferSize = 2 * 1024 * 1024
|
||||||
|
|
||||||
init(with defaults: UserDefaults = .standard) {
|
init(with defaults: UserDefaults = .standard) {
|
||||||
self.defaults = defaults
|
self.defaults = defaults
|
||||||
}
|
}
|
||||||
@@ -243,4 +246,24 @@ class NativeSyncApiImpl: NativeSyncApi {
|
|||||||
}
|
}
|
||||||
return assets
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ const Map<String, Locale> locales = {
|
|||||||
'English (en)': Locale('en'),
|
'English (en)': Locale('en'),
|
||||||
// Additional locales
|
// Additional locales
|
||||||
'Arabic (ar)': Locale('ar'),
|
'Arabic (ar)': Locale('ar'),
|
||||||
|
'Bulgarian (bg)': Locale('bg'),
|
||||||
'Catalan (ca)': Locale('ca'),
|
'Catalan (ca)': Locale('ca'),
|
||||||
'Chinese Simplified (zh_CN)':
|
'Chinese Simplified (zh_CN)':
|
||||||
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'SIMPLIFIED'),
|
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'SIMPLIFIED'),
|
||||||
@@ -31,6 +32,7 @@ const Map<String, Locale> locales = {
|
|||||||
'Mongolian (mn)': Locale('mn'),
|
'Mongolian (mn)': Locale('mn'),
|
||||||
'Norwegian Bokmål (nb_NO)': Locale('nb', 'NO'),
|
'Norwegian Bokmål (nb_NO)': Locale('nb', 'NO'),
|
||||||
'Polish (pl)': Locale('pl'),
|
'Polish (pl)': Locale('pl'),
|
||||||
|
'Brazilian Portuguese (pt_BR)': Locale('pt', 'BR'),
|
||||||
'Portuguese (pt)': Locale('pt'),
|
'Portuguese (pt)': Locale('pt'),
|
||||||
'Romanian (ro)': Locale('ro'),
|
'Romanian (ro)': Locale('ro'),
|
||||||
'Russian (ru)': Locale('ru'),
|
'Russian (ru)': Locale('ru'),
|
||||||
@@ -42,6 +44,8 @@ const Map<String, Locale> locales = {
|
|||||||
'Slovenian (sl)': Locale('sl'),
|
'Slovenian (sl)': Locale('sl'),
|
||||||
'Spanish (es)': Locale('es'),
|
'Spanish (es)': Locale('es'),
|
||||||
'Swedish (sv)': Locale('sv'),
|
'Swedish (sv)': Locale('sv'),
|
||||||
|
'Tamil (ta)': Locale('ta'),
|
||||||
|
'Telugu (te)': Locale('te'),
|
||||||
'Thai (th)': Locale('th'),
|
'Thai (th)': Locale('th'),
|
||||||
'Turkish (tr)': Locale('tr'),
|
'Turkish (tr)': Locale('tr'),
|
||||||
'Ukrainian (uk)': Locale('uk'),
|
'Ukrainian (uk)': Locale('uk'),
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ abstract interface class ILocalAlbumRepository implements IDatabaseRepository {
|
|||||||
String albumId,
|
String albumId,
|
||||||
Iterable<String> assetIdsToKeep,
|
Iterable<String> assetIdsToKeep,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Future<List<LocalAsset>> getAssetsToHash(String albumId);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SortLocalAlbumsBy { id }
|
enum SortLocalAlbumsBy { id }
|
||||||
|
|||||||
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);
|
||||||
|
}
|
||||||
@@ -1,9 +1,17 @@
|
|||||||
part of 'base_asset.model.dart';
|
part of 'base_asset.model.dart';
|
||||||
|
|
||||||
|
enum AssetVisibility {
|
||||||
|
timeline,
|
||||||
|
hidden,
|
||||||
|
archive,
|
||||||
|
locked,
|
||||||
|
}
|
||||||
|
|
||||||
// Model for an asset stored in the server
|
// Model for an asset stored in the server
|
||||||
class Asset extends BaseAsset {
|
class Asset extends BaseAsset {
|
||||||
final String id;
|
final String id;
|
||||||
final String? localId;
|
final String? localId;
|
||||||
|
final AssetVisibility visibility;
|
||||||
|
|
||||||
const Asset({
|
const Asset({
|
||||||
required this.id,
|
required this.id,
|
||||||
@@ -17,6 +25,7 @@ class Asset extends BaseAsset {
|
|||||||
super.height,
|
super.height,
|
||||||
super.durationInSeconds,
|
super.durationInSeconds,
|
||||||
super.isFavorite = false,
|
super.isFavorite = false,
|
||||||
|
this.visibility = AssetVisibility.timeline,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -32,6 +41,7 @@ class Asset extends BaseAsset {
|
|||||||
durationInSeconds: ${durationInSeconds ?? "<NA>"},
|
durationInSeconds: ${durationInSeconds ?? "<NA>"},
|
||||||
localId: ${localId ?? "<NA>"},
|
localId: ${localId ?? "<NA>"},
|
||||||
isFavorite: $isFavorite,
|
isFavorite: $isFavorite,
|
||||||
|
visibility: $visibility,
|
||||||
}''';
|
}''';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,9 +49,13 @@ class Asset extends BaseAsset {
|
|||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (other is! Asset) return false;
|
if (other is! Asset) return false;
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) return true;
|
||||||
return super == other && id == other.id && localId == other.localId;
|
return super == other &&
|
||||||
|
id == other.id &&
|
||||||
|
localId == other.localId &&
|
||||||
|
visibility == other.visibility;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => super.hashCode ^ id.hashCode ^ localId.hashCode;
|
int get hashCode =>
|
||||||
|
super.hashCode ^ id.hashCode ^ localId.hashCode ^ visibility.hashCode;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
enum BackupSelection {
|
enum BackupSelection {
|
||||||
none,
|
none._(1),
|
||||||
selected,
|
selected._(0),
|
||||||
excluded,
|
excluded._(2);
|
||||||
|
|
||||||
|
// Used to sort albums based on the backupSelection
|
||||||
|
// selected -> none -> excluded
|
||||||
|
final int sortOrder;
|
||||||
|
const BackupSelection._(this.sortOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
class LocalAlbum {
|
class LocalAlbum {
|
||||||
final String id;
|
final String id;
|
||||||
final String name;
|
final String name;
|
||||||
final DateTime updatedAt;
|
final DateTime updatedAt;
|
||||||
|
final bool isIosSharedAlbum;
|
||||||
|
|
||||||
final int assetCount;
|
final int assetCount;
|
||||||
final BackupSelection backupSelection;
|
final BackupSelection backupSelection;
|
||||||
@@ -18,6 +24,7 @@ class LocalAlbum {
|
|||||||
required this.updatedAt,
|
required this.updatedAt,
|
||||||
this.assetCount = 0,
|
this.assetCount = 0,
|
||||||
this.backupSelection = BackupSelection.none,
|
this.backupSelection = BackupSelection.none,
|
||||||
|
this.isIosSharedAlbum = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
LocalAlbum copyWith({
|
LocalAlbum copyWith({
|
||||||
@@ -26,6 +33,7 @@ class LocalAlbum {
|
|||||||
DateTime? updatedAt,
|
DateTime? updatedAt,
|
||||||
int? assetCount,
|
int? assetCount,
|
||||||
BackupSelection? backupSelection,
|
BackupSelection? backupSelection,
|
||||||
|
bool? isIosSharedAlbum,
|
||||||
}) {
|
}) {
|
||||||
return LocalAlbum(
|
return LocalAlbum(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
@@ -33,6 +41,7 @@ class LocalAlbum {
|
|||||||
updatedAt: updatedAt ?? this.updatedAt,
|
updatedAt: updatedAt ?? this.updatedAt,
|
||||||
assetCount: assetCount ?? this.assetCount,
|
assetCount: assetCount ?? this.assetCount,
|
||||||
backupSelection: backupSelection ?? this.backupSelection,
|
backupSelection: backupSelection ?? this.backupSelection,
|
||||||
|
isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +54,8 @@ class LocalAlbum {
|
|||||||
other.name == name &&
|
other.name == name &&
|
||||||
other.updatedAt == updatedAt &&
|
other.updatedAt == updatedAt &&
|
||||||
other.assetCount == assetCount &&
|
other.assetCount == assetCount &&
|
||||||
other.backupSelection == backupSelection;
|
other.backupSelection == backupSelection &&
|
||||||
|
other.isIosSharedAlbum == isIosSharedAlbum;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -54,7 +64,8 @@ class LocalAlbum {
|
|||||||
name.hashCode ^
|
name.hashCode ^
|
||||||
updatedAt.hashCode ^
|
updatedAt.hashCode ^
|
||||||
assetCount.hashCode ^
|
assetCount.hashCode ^
|
||||||
backupSelection.hashCode;
|
backupSelection.hashCode ^
|
||||||
|
isIosSharedAlbum.hashCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -65,6 +76,7 @@ name: $name,
|
|||||||
updatedAt: $updatedAt,
|
updatedAt: $updatedAt,
|
||||||
assetCount: $assetCount,
|
assetCount: $assetCount,
|
||||||
backupSelection: $backupSelection,
|
backupSelection: $backupSelection,
|
||||||
|
isIosSharedAlbum: $isIosSharedAlbum
|
||||||
}''';
|
}''';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
121
mobile/lib/domain/services/hash.service.dart
Normal file
121
mobile/lib/domain/services/hash.service.dart
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
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();
|
||||||
|
localAlbums.sort((a, b) {
|
||||||
|
final backupComparison =
|
||||||
|
a.backupSelection.sortOrder.compareTo(b.backupSelection.sortOrder);
|
||||||
|
|
||||||
|
if (backupComparison != 0) {
|
||||||
|
return backupComparison;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local albums come before iCloud albums
|
||||||
|
return (a.isIosSharedAlbum ? 1 : 0).compareTo(b.isIosSharedAlbum ? 1 : 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
for (final (index, hash) in hashes.indexed) {
|
||||||
|
final asset = toHash[index].asset;
|
||||||
|
if (hash?.length == 20) {
|
||||||
|
hashed.add(asset.copyWith(checksum: base64.encode(hash!)));
|
||||||
|
} else {
|
||||||
|
_log.warning("Failed to hash file ${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});
|
||||||
|
}
|
||||||
@@ -365,6 +365,7 @@ extension on Iterable<PlatformAsset> {
|
|||||||
(e) => LocalAsset(
|
(e) => LocalAsset(
|
||||||
id: e.id,
|
id: e.id,
|
||||||
name: e.name,
|
name: e.name,
|
||||||
|
checksum: null,
|
||||||
type: AssetType.values.elementAtOrNull(e.type) ?? AssetType.other,
|
type: AssetType.values.elementAtOrNull(e.type) ?? AssetType.other,
|
||||||
createdAt: e.createdAt == null
|
createdAt: e.createdAt == null
|
||||||
? DateTime.now()
|
? DateTime.now()
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ class SyncStreamService {
|
|||||||
Iterable<dynamic> data,
|
Iterable<dynamic> data,
|
||||||
) async {
|
) async {
|
||||||
_logger.fine("Processing sync data for $type of length ${data.length}");
|
_logger.fine("Processing sync data for $type of length ${data.length}");
|
||||||
// ignore: prefer-switch-expression
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SyncEntityType.userV1:
|
case SyncEntityType.userV1:
|
||||||
return _syncStreamRepository.updateUsersV1(data.cast());
|
return _syncStreamRepository.updateUsersV1(data.cast());
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:worker_manager/worker_manager.dart';
|
|||||||
class BackgroundSyncManager {
|
class BackgroundSyncManager {
|
||||||
Cancelable<void>? _syncTask;
|
Cancelable<void>? _syncTask;
|
||||||
Cancelable<void>? _deviceAlbumSyncTask;
|
Cancelable<void>? _deviceAlbumSyncTask;
|
||||||
|
Cancelable<void>? _hashTask;
|
||||||
|
|
||||||
BackgroundSyncManager();
|
BackgroundSyncManager();
|
||||||
|
|
||||||
@@ -45,6 +46,20 @@ class BackgroundSyncManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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() {
|
Future<void> syncRemote() {
|
||||||
if (_syncTask != null) {
|
if (_syncTask != null) {
|
||||||
return _syncTask!.future;
|
return _syncTask!.future;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ class Album {
|
|||||||
required this.name,
|
required this.name,
|
||||||
required this.createdAt,
|
required this.createdAt,
|
||||||
required this.modifiedAt,
|
required this.modifiedAt,
|
||||||
|
this.description,
|
||||||
this.startDate,
|
this.startDate,
|
||||||
this.endDate,
|
this.endDate,
|
||||||
this.lastModifiedAssetTimestamp,
|
this.lastModifiedAssetTimestamp,
|
||||||
@@ -34,6 +35,7 @@ class Album {
|
|||||||
@Index(unique: false, replace: false, type: IndexType.hash)
|
@Index(unique: false, replace: false, type: IndexType.hash)
|
||||||
String? localId;
|
String? localId;
|
||||||
String name;
|
String name;
|
||||||
|
String? description;
|
||||||
DateTime createdAt;
|
DateTime createdAt;
|
||||||
DateTime modifiedAt;
|
DateTime modifiedAt;
|
||||||
DateTime? startDate;
|
DateTime? startDate;
|
||||||
@@ -108,6 +110,7 @@ class Album {
|
|||||||
remoteId == other.remoteId &&
|
remoteId == other.remoteId &&
|
||||||
localId == other.localId &&
|
localId == other.localId &&
|
||||||
name == other.name &&
|
name == other.name &&
|
||||||
|
description == other.description &&
|
||||||
createdAt.isAtSameMomentAs(other.createdAt) &&
|
createdAt.isAtSameMomentAs(other.createdAt) &&
|
||||||
modifiedAt.isAtSameMomentAs(other.modifiedAt) &&
|
modifiedAt.isAtSameMomentAs(other.modifiedAt) &&
|
||||||
isAtSameMomentAs(startDate, other.startDate) &&
|
isAtSameMomentAs(startDate, other.startDate) &&
|
||||||
@@ -135,6 +138,7 @@ class Album {
|
|||||||
modifiedAt.hashCode ^
|
modifiedAt.hashCode ^
|
||||||
startDate.hashCode ^
|
startDate.hashCode ^
|
||||||
endDate.hashCode ^
|
endDate.hashCode ^
|
||||||
|
description.hashCode ^
|
||||||
lastModifiedAssetTimestamp.hashCode ^
|
lastModifiedAssetTimestamp.hashCode ^
|
||||||
shared.hashCode ^
|
shared.hashCode ^
|
||||||
activityEnabled.hashCode ^
|
activityEnabled.hashCode ^
|
||||||
@@ -150,6 +154,7 @@ class Album {
|
|||||||
name: dto.albumName,
|
name: dto.albumName,
|
||||||
createdAt: dto.createdAt,
|
createdAt: dto.createdAt,
|
||||||
modifiedAt: dto.updatedAt,
|
modifiedAt: dto.updatedAt,
|
||||||
|
description: dto.description,
|
||||||
lastModifiedAssetTimestamp: dto.lastModifiedAssetTimestamp,
|
lastModifiedAssetTimestamp: dto.lastModifiedAssetTimestamp,
|
||||||
shared: dto.shared,
|
shared: dto.shared,
|
||||||
startDate: dto.startDate,
|
startDate: dto.startDate,
|
||||||
@@ -184,7 +189,8 @@ class Album {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => name;
|
String toString() =>
|
||||||
|
'remoteId: $remoteId name: $name description: $description';
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AssetsHelper on IsarCollection<Album> {
|
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',
|
name: r'createdAt',
|
||||||
type: IsarType.dateTime,
|
type: IsarType.dateTime,
|
||||||
),
|
),
|
||||||
r'endDate': PropertySchema(
|
r'description': PropertySchema(
|
||||||
id: 2,
|
id: 2,
|
||||||
|
name: r'description',
|
||||||
|
type: IsarType.string,
|
||||||
|
),
|
||||||
|
r'endDate': PropertySchema(
|
||||||
|
id: 3,
|
||||||
name: r'endDate',
|
name: r'endDate',
|
||||||
type: IsarType.dateTime,
|
type: IsarType.dateTime,
|
||||||
),
|
),
|
||||||
r'lastModifiedAssetTimestamp': PropertySchema(
|
r'lastModifiedAssetTimestamp': PropertySchema(
|
||||||
id: 3,
|
id: 4,
|
||||||
name: r'lastModifiedAssetTimestamp',
|
name: r'lastModifiedAssetTimestamp',
|
||||||
type: IsarType.dateTime,
|
type: IsarType.dateTime,
|
||||||
),
|
),
|
||||||
r'localId': PropertySchema(
|
r'localId': PropertySchema(
|
||||||
id: 4,
|
id: 5,
|
||||||
name: r'localId',
|
name: r'localId',
|
||||||
type: IsarType.string,
|
type: IsarType.string,
|
||||||
),
|
),
|
||||||
r'modifiedAt': PropertySchema(
|
r'modifiedAt': PropertySchema(
|
||||||
id: 5,
|
id: 6,
|
||||||
name: r'modifiedAt',
|
name: r'modifiedAt',
|
||||||
type: IsarType.dateTime,
|
type: IsarType.dateTime,
|
||||||
),
|
),
|
||||||
r'name': PropertySchema(
|
r'name': PropertySchema(
|
||||||
id: 6,
|
id: 7,
|
||||||
name: r'name',
|
name: r'name',
|
||||||
type: IsarType.string,
|
type: IsarType.string,
|
||||||
),
|
),
|
||||||
r'remoteId': PropertySchema(
|
r'remoteId': PropertySchema(
|
||||||
id: 7,
|
id: 8,
|
||||||
name: r'remoteId',
|
name: r'remoteId',
|
||||||
type: IsarType.string,
|
type: IsarType.string,
|
||||||
),
|
),
|
||||||
r'shared': PropertySchema(
|
r'shared': PropertySchema(
|
||||||
id: 8,
|
id: 9,
|
||||||
name: r'shared',
|
name: r'shared',
|
||||||
type: IsarType.bool,
|
type: IsarType.bool,
|
||||||
),
|
),
|
||||||
r'sortOrder': PropertySchema(
|
r'sortOrder': PropertySchema(
|
||||||
id: 9,
|
id: 10,
|
||||||
name: r'sortOrder',
|
name: r'sortOrder',
|
||||||
type: IsarType.byte,
|
type: IsarType.byte,
|
||||||
enumMap: _AlbumsortOrderEnumValueMap,
|
enumMap: _AlbumsortOrderEnumValueMap,
|
||||||
),
|
),
|
||||||
r'startDate': PropertySchema(
|
r'startDate': PropertySchema(
|
||||||
id: 10,
|
id: 11,
|
||||||
name: r'startDate',
|
name: r'startDate',
|
||||||
type: IsarType.dateTime,
|
type: IsarType.dateTime,
|
||||||
)
|
)
|
||||||
@@ -146,6 +151,12 @@ int _albumEstimateSize(
|
|||||||
Map<Type, List<int>> allOffsets,
|
Map<Type, List<int>> allOffsets,
|
||||||
) {
|
) {
|
||||||
var bytesCount = offsets.last;
|
var bytesCount = offsets.last;
|
||||||
|
{
|
||||||
|
final value = object.description;
|
||||||
|
if (value != null) {
|
||||||
|
bytesCount += 3 + value.length * 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
{
|
{
|
||||||
final value = object.localId;
|
final value = object.localId;
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
@@ -170,15 +181,16 @@ void _albumSerialize(
|
|||||||
) {
|
) {
|
||||||
writer.writeBool(offsets[0], object.activityEnabled);
|
writer.writeBool(offsets[0], object.activityEnabled);
|
||||||
writer.writeDateTime(offsets[1], object.createdAt);
|
writer.writeDateTime(offsets[1], object.createdAt);
|
||||||
writer.writeDateTime(offsets[2], object.endDate);
|
writer.writeString(offsets[2], object.description);
|
||||||
writer.writeDateTime(offsets[3], object.lastModifiedAssetTimestamp);
|
writer.writeDateTime(offsets[3], object.endDate);
|
||||||
writer.writeString(offsets[4], object.localId);
|
writer.writeDateTime(offsets[4], object.lastModifiedAssetTimestamp);
|
||||||
writer.writeDateTime(offsets[5], object.modifiedAt);
|
writer.writeString(offsets[5], object.localId);
|
||||||
writer.writeString(offsets[6], object.name);
|
writer.writeDateTime(offsets[6], object.modifiedAt);
|
||||||
writer.writeString(offsets[7], object.remoteId);
|
writer.writeString(offsets[7], object.name);
|
||||||
writer.writeBool(offsets[8], object.shared);
|
writer.writeString(offsets[8], object.remoteId);
|
||||||
writer.writeByte(offsets[9], object.sortOrder.index);
|
writer.writeBool(offsets[9], object.shared);
|
||||||
writer.writeDateTime(offsets[10], object.startDate);
|
writer.writeByte(offsets[10], object.sortOrder.index);
|
||||||
|
writer.writeDateTime(offsets[11], object.startDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
Album _albumDeserialize(
|
Album _albumDeserialize(
|
||||||
@@ -190,16 +202,18 @@ Album _albumDeserialize(
|
|||||||
final object = Album(
|
final object = Album(
|
||||||
activityEnabled: reader.readBool(offsets[0]),
|
activityEnabled: reader.readBool(offsets[0]),
|
||||||
createdAt: reader.readDateTime(offsets[1]),
|
createdAt: reader.readDateTime(offsets[1]),
|
||||||
endDate: reader.readDateTimeOrNull(offsets[2]),
|
description: reader.readStringOrNull(offsets[2]),
|
||||||
lastModifiedAssetTimestamp: reader.readDateTimeOrNull(offsets[3]),
|
endDate: reader.readDateTimeOrNull(offsets[3]),
|
||||||
localId: reader.readStringOrNull(offsets[4]),
|
lastModifiedAssetTimestamp: reader.readDateTimeOrNull(offsets[4]),
|
||||||
modifiedAt: reader.readDateTime(offsets[5]),
|
localId: reader.readStringOrNull(offsets[5]),
|
||||||
name: reader.readString(offsets[6]),
|
modifiedAt: reader.readDateTime(offsets[6]),
|
||||||
remoteId: reader.readStringOrNull(offsets[7]),
|
name: reader.readString(offsets[7]),
|
||||||
shared: reader.readBool(offsets[8]),
|
remoteId: reader.readStringOrNull(offsets[8]),
|
||||||
sortOrder: _AlbumsortOrderValueEnumMap[reader.readByteOrNull(offsets[9])] ??
|
shared: reader.readBool(offsets[9]),
|
||||||
SortOrder.desc,
|
sortOrder:
|
||||||
startDate: reader.readDateTimeOrNull(offsets[10]),
|
_AlbumsortOrderValueEnumMap[reader.readByteOrNull(offsets[10])] ??
|
||||||
|
SortOrder.desc,
|
||||||
|
startDate: reader.readDateTimeOrNull(offsets[11]),
|
||||||
);
|
);
|
||||||
object.id = id;
|
object.id = id;
|
||||||
return object;
|
return object;
|
||||||
@@ -217,23 +231,25 @@ P _albumDeserializeProp<P>(
|
|||||||
case 1:
|
case 1:
|
||||||
return (reader.readDateTime(offset)) as P;
|
return (reader.readDateTime(offset)) as P;
|
||||||
case 2:
|
case 2:
|
||||||
return (reader.readDateTimeOrNull(offset)) as P;
|
return (reader.readStringOrNull(offset)) as P;
|
||||||
case 3:
|
case 3:
|
||||||
return (reader.readDateTimeOrNull(offset)) as P;
|
return (reader.readDateTimeOrNull(offset)) as P;
|
||||||
case 4:
|
case 4:
|
||||||
return (reader.readStringOrNull(offset)) as P;
|
return (reader.readDateTimeOrNull(offset)) as P;
|
||||||
case 5:
|
case 5:
|
||||||
return (reader.readDateTime(offset)) as P;
|
|
||||||
case 6:
|
|
||||||
return (reader.readString(offset)) as P;
|
|
||||||
case 7:
|
|
||||||
return (reader.readStringOrNull(offset)) as P;
|
return (reader.readStringOrNull(offset)) as P;
|
||||||
|
case 6:
|
||||||
|
return (reader.readDateTime(offset)) as P;
|
||||||
|
case 7:
|
||||||
|
return (reader.readString(offset)) as P;
|
||||||
case 8:
|
case 8:
|
||||||
return (reader.readBool(offset)) as P;
|
return (reader.readStringOrNull(offset)) as P;
|
||||||
case 9:
|
case 9:
|
||||||
|
return (reader.readBool(offset)) as P;
|
||||||
|
case 10:
|
||||||
return (_AlbumsortOrderValueEnumMap[reader.readByteOrNull(offset)] ??
|
return (_AlbumsortOrderValueEnumMap[reader.readByteOrNull(offset)] ??
|
||||||
SortOrder.desc) as P;
|
SortOrder.desc) as P;
|
||||||
case 10:
|
case 11:
|
||||||
return (reader.readDateTimeOrNull(offset)) as P;
|
return (reader.readDateTimeOrNull(offset)) as P;
|
||||||
default:
|
default:
|
||||||
throw IsarError('Unknown property with id $propertyId');
|
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() {
|
QueryBuilder<Album, Album, QAfterFilterCondition> endDateIsNull() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
return query.addFilterCondition(const FilterCondition.isNull(
|
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() {
|
QueryBuilder<Album, Album, QAfterSortBy> sortByEndDate() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
return query.addSortBy(r'endDate', Sort.asc);
|
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() {
|
QueryBuilder<Album, Album, QAfterSortBy> thenByEndDate() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
return query.addSortBy(r'endDate', Sort.asc);
|
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() {
|
QueryBuilder<Album, Album, QDistinct> distinctByEndDate() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
return query.addDistinctBy(r'endDate');
|
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() {
|
QueryBuilder<Album, DateTime?, QQueryOperations> endDateProperty() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
return query.addPropertyName(r'endDate');
|
return query.addPropertyName(r'endDate');
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:uuid/parsing.dart';
|
|
||||||
|
|
||||||
extension StringExtension on String {
|
extension StringExtension on String {
|
||||||
String capitalize() {
|
String capitalize() {
|
||||||
return split(" ")
|
return split(" ")
|
||||||
@@ -33,8 +29,3 @@ extension DurationExtension on String {
|
|||||||
return int.parse(this);
|
return int.parse(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension UUIDExtension on String {
|
|
||||||
Uint8List toUuidByte({bool shouldValidate = false}) =>
|
|
||||||
UuidParsing.parseAsByteList(this, validate: shouldValidate);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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/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:immich_mobile/infrastructure/utils/exif.converter.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
|
|
||||||
@@ -90,3 +93,53 @@ class ExifInfo {
|
|||||||
exposureSeconds: exposureSeconds,
|
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
@@ -9,6 +9,8 @@ class LocalAlbumEntity extends Table with DriftDefaultsMixin {
|
|||||||
TextColumn get name => text()();
|
TextColumn get name => text()();
|
||||||
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
|
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
|
||||||
IntColumn get backupSelection => intEnum<BackupSelection>()();
|
IntColumn get backupSelection => intEnum<BackupSelection>()();
|
||||||
|
BoolColumn get isIosSharedAlbum =>
|
||||||
|
boolean().withDefault(const Constant(false))();
|
||||||
|
|
||||||
// Used for mark & sweep
|
// Used for mark & sweep
|
||||||
BoolColumn get marker_ => boolean().nullable()();
|
BoolColumn get marker_ => boolean().nullable()();
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ typedef $$LocalAlbumEntityTableCreateCompanionBuilder
|
|||||||
required String name,
|
required String name,
|
||||||
i0.Value<DateTime> updatedAt,
|
i0.Value<DateTime> updatedAt,
|
||||||
required i2.BackupSelection backupSelection,
|
required i2.BackupSelection backupSelection,
|
||||||
|
i0.Value<bool> isIosSharedAlbum,
|
||||||
i0.Value<bool?> marker_,
|
i0.Value<bool?> marker_,
|
||||||
});
|
});
|
||||||
typedef $$LocalAlbumEntityTableUpdateCompanionBuilder
|
typedef $$LocalAlbumEntityTableUpdateCompanionBuilder
|
||||||
@@ -22,6 +23,7 @@ typedef $$LocalAlbumEntityTableUpdateCompanionBuilder
|
|||||||
i0.Value<String> name,
|
i0.Value<String> name,
|
||||||
i0.Value<DateTime> updatedAt,
|
i0.Value<DateTime> updatedAt,
|
||||||
i0.Value<i2.BackupSelection> backupSelection,
|
i0.Value<i2.BackupSelection> backupSelection,
|
||||||
|
i0.Value<bool> isIosSharedAlbum,
|
||||||
i0.Value<bool?> marker_,
|
i0.Value<bool?> marker_,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -48,6 +50,10 @@ class $$LocalAlbumEntityTableFilterComposer
|
|||||||
column: $table.backupSelection,
|
column: $table.backupSelection,
|
||||||
builder: (column) => i0.ColumnWithTypeConverterFilters(column));
|
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(
|
i0.ColumnFilters<bool> get marker_ => $composableBuilder(
|
||||||
column: $table.marker_, builder: (column) => i0.ColumnFilters(column));
|
column: $table.marker_, builder: (column) => i0.ColumnFilters(column));
|
||||||
}
|
}
|
||||||
@@ -75,6 +81,10 @@ class $$LocalAlbumEntityTableOrderingComposer
|
|||||||
column: $table.backupSelection,
|
column: $table.backupSelection,
|
||||||
builder: (column) => i0.ColumnOrderings(column));
|
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(
|
i0.ColumnOrderings<bool> get marker_ => $composableBuilder(
|
||||||
column: $table.marker_, builder: (column) => i0.ColumnOrderings(column));
|
column: $table.marker_, builder: (column) => i0.ColumnOrderings(column));
|
||||||
}
|
}
|
||||||
@@ -101,6 +111,9 @@ class $$LocalAlbumEntityTableAnnotationComposer
|
|||||||
get backupSelection => $composableBuilder(
|
get backupSelection => $composableBuilder(
|
||||||
column: $table.backupSelection, builder: (column) => column);
|
column: $table.backupSelection, builder: (column) => column);
|
||||||
|
|
||||||
|
i0.GeneratedColumn<bool> get isIosSharedAlbum => $composableBuilder(
|
||||||
|
column: $table.isIosSharedAlbum, builder: (column) => column);
|
||||||
|
|
||||||
i0.GeneratedColumn<bool> get marker_ =>
|
i0.GeneratedColumn<bool> get marker_ =>
|
||||||
$composableBuilder(column: $table.marker_, builder: (column) => column);
|
$composableBuilder(column: $table.marker_, builder: (column) => column);
|
||||||
}
|
}
|
||||||
@@ -139,6 +152,7 @@ class $$LocalAlbumEntityTableTableManager extends i0.RootTableManager<
|
|||||||
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
||||||
i0.Value<i2.BackupSelection> backupSelection =
|
i0.Value<i2.BackupSelection> backupSelection =
|
||||||
const i0.Value.absent(),
|
const i0.Value.absent(),
|
||||||
|
i0.Value<bool> isIosSharedAlbum = const i0.Value.absent(),
|
||||||
i0.Value<bool?> marker_ = const i0.Value.absent(),
|
i0.Value<bool?> marker_ = const i0.Value.absent(),
|
||||||
}) =>
|
}) =>
|
||||||
i1.LocalAlbumEntityCompanion(
|
i1.LocalAlbumEntityCompanion(
|
||||||
@@ -146,6 +160,7 @@ class $$LocalAlbumEntityTableTableManager extends i0.RootTableManager<
|
|||||||
name: name,
|
name: name,
|
||||||
updatedAt: updatedAt,
|
updatedAt: updatedAt,
|
||||||
backupSelection: backupSelection,
|
backupSelection: backupSelection,
|
||||||
|
isIosSharedAlbum: isIosSharedAlbum,
|
||||||
marker_: marker_,
|
marker_: marker_,
|
||||||
),
|
),
|
||||||
createCompanionCallback: ({
|
createCompanionCallback: ({
|
||||||
@@ -153,6 +168,7 @@ class $$LocalAlbumEntityTableTableManager extends i0.RootTableManager<
|
|||||||
required String name,
|
required String name,
|
||||||
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
||||||
required i2.BackupSelection backupSelection,
|
required i2.BackupSelection backupSelection,
|
||||||
|
i0.Value<bool> isIosSharedAlbum = const i0.Value.absent(),
|
||||||
i0.Value<bool?> marker_ = const i0.Value.absent(),
|
i0.Value<bool?> marker_ = const i0.Value.absent(),
|
||||||
}) =>
|
}) =>
|
||||||
i1.LocalAlbumEntityCompanion.insert(
|
i1.LocalAlbumEntityCompanion.insert(
|
||||||
@@ -160,6 +176,7 @@ class $$LocalAlbumEntityTableTableManager extends i0.RootTableManager<
|
|||||||
name: name,
|
name: name,
|
||||||
updatedAt: updatedAt,
|
updatedAt: updatedAt,
|
||||||
backupSelection: backupSelection,
|
backupSelection: backupSelection,
|
||||||
|
isIosSharedAlbum: isIosSharedAlbum,
|
||||||
marker_: marker_,
|
marker_: marker_,
|
||||||
),
|
),
|
||||||
withReferenceMapper: (p0) => p0
|
withReferenceMapper: (p0) => p0
|
||||||
@@ -218,6 +235,16 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity
|
|||||||
type: i0.DriftSqlType.int, requiredDuringInsert: true)
|
type: i0.DriftSqlType.int, requiredDuringInsert: true)
|
||||||
.withConverter<i2.BackupSelection>(
|
.withConverter<i2.BackupSelection>(
|
||||||
i1.$LocalAlbumEntityTable.$converterbackupSelection);
|
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 =
|
static const i0.VerificationMeta _marker_Meta =
|
||||||
const i0.VerificationMeta('marker_');
|
const i0.VerificationMeta('marker_');
|
||||||
@override
|
@override
|
||||||
@@ -229,7 +256,7 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity
|
|||||||
i0.GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))'));
|
i0.GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))'));
|
||||||
@override
|
@override
|
||||||
List<i0.GeneratedColumn> get $columns =>
|
List<i0.GeneratedColumn> get $columns =>
|
||||||
[id, name, updatedAt, backupSelection, marker_];
|
[id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_];
|
||||||
@override
|
@override
|
||||||
String get aliasedName => _alias ?? actualTableName;
|
String get aliasedName => _alias ?? actualTableName;
|
||||||
@override
|
@override
|
||||||
@@ -256,6 +283,12 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity
|
|||||||
context.handle(_updatedAtMeta,
|
context.handle(_updatedAtMeta,
|
||||||
updatedAt.isAcceptableOrUnknown(data['updated_at']!, _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')) {
|
if (data.containsKey('marker')) {
|
||||||
context.handle(_marker_Meta,
|
context.handle(_marker_Meta,
|
||||||
marker_.isAcceptableOrUnknown(data['marker']!, _marker_Meta));
|
marker_.isAcceptableOrUnknown(data['marker']!, _marker_Meta));
|
||||||
@@ -279,6 +312,8 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity
|
|||||||
backupSelection: i1.$LocalAlbumEntityTable.$converterbackupSelection
|
backupSelection: i1.$LocalAlbumEntityTable.$converterbackupSelection
|
||||||
.fromSql(attachedDatabase.typeMapping.read(i0.DriftSqlType.int,
|
.fromSql(attachedDatabase.typeMapping.read(i0.DriftSqlType.int,
|
||||||
data['${effectivePrefix}backup_selection'])!),
|
data['${effectivePrefix}backup_selection'])!),
|
||||||
|
isIosSharedAlbum: attachedDatabase.typeMapping.read(
|
||||||
|
i0.DriftSqlType.bool, data['${effectivePrefix}is_ios_shared_album'])!,
|
||||||
marker_: attachedDatabase.typeMapping
|
marker_: attachedDatabase.typeMapping
|
||||||
.read(i0.DriftSqlType.bool, data['${effectivePrefix}marker']),
|
.read(i0.DriftSqlType.bool, data['${effectivePrefix}marker']),
|
||||||
);
|
);
|
||||||
@@ -305,12 +340,14 @@ class LocalAlbumEntityData extends i0.DataClass
|
|||||||
final String name;
|
final String name;
|
||||||
final DateTime updatedAt;
|
final DateTime updatedAt;
|
||||||
final i2.BackupSelection backupSelection;
|
final i2.BackupSelection backupSelection;
|
||||||
|
final bool isIosSharedAlbum;
|
||||||
final bool? marker_;
|
final bool? marker_;
|
||||||
const LocalAlbumEntityData(
|
const LocalAlbumEntityData(
|
||||||
{required this.id,
|
{required this.id,
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.updatedAt,
|
required this.updatedAt,
|
||||||
required this.backupSelection,
|
required this.backupSelection,
|
||||||
|
required this.isIosSharedAlbum,
|
||||||
this.marker_});
|
this.marker_});
|
||||||
@override
|
@override
|
||||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||||
@@ -323,6 +360,7 @@ class LocalAlbumEntityData extends i0.DataClass
|
|||||||
.$LocalAlbumEntityTable.$converterbackupSelection
|
.$LocalAlbumEntityTable.$converterbackupSelection
|
||||||
.toSql(backupSelection));
|
.toSql(backupSelection));
|
||||||
}
|
}
|
||||||
|
map['is_ios_shared_album'] = i0.Variable<bool>(isIosSharedAlbum);
|
||||||
if (!nullToAbsent || marker_ != null) {
|
if (!nullToAbsent || marker_ != null) {
|
||||||
map['marker'] = i0.Variable<bool>(marker_);
|
map['marker'] = i0.Variable<bool>(marker_);
|
||||||
}
|
}
|
||||||
@@ -338,6 +376,7 @@ class LocalAlbumEntityData extends i0.DataClass
|
|||||||
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
|
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
|
||||||
backupSelection: i1.$LocalAlbumEntityTable.$converterbackupSelection
|
backupSelection: i1.$LocalAlbumEntityTable.$converterbackupSelection
|
||||||
.fromJson(serializer.fromJson<int>(json['backupSelection'])),
|
.fromJson(serializer.fromJson<int>(json['backupSelection'])),
|
||||||
|
isIosSharedAlbum: serializer.fromJson<bool>(json['isIosSharedAlbum']),
|
||||||
marker_: serializer.fromJson<bool?>(json['marker_']),
|
marker_: serializer.fromJson<bool?>(json['marker_']),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -351,6 +390,7 @@ class LocalAlbumEntityData extends i0.DataClass
|
|||||||
'backupSelection': serializer.toJson<int>(i1
|
'backupSelection': serializer.toJson<int>(i1
|
||||||
.$LocalAlbumEntityTable.$converterbackupSelection
|
.$LocalAlbumEntityTable.$converterbackupSelection
|
||||||
.toJson(backupSelection)),
|
.toJson(backupSelection)),
|
||||||
|
'isIosSharedAlbum': serializer.toJson<bool>(isIosSharedAlbum),
|
||||||
'marker_': serializer.toJson<bool?>(marker_),
|
'marker_': serializer.toJson<bool?>(marker_),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -360,12 +400,14 @@ class LocalAlbumEntityData extends i0.DataClass
|
|||||||
String? name,
|
String? name,
|
||||||
DateTime? updatedAt,
|
DateTime? updatedAt,
|
||||||
i2.BackupSelection? backupSelection,
|
i2.BackupSelection? backupSelection,
|
||||||
|
bool? isIosSharedAlbum,
|
||||||
i0.Value<bool?> marker_ = const i0.Value.absent()}) =>
|
i0.Value<bool?> marker_ = const i0.Value.absent()}) =>
|
||||||
i1.LocalAlbumEntityData(
|
i1.LocalAlbumEntityData(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
name: name ?? this.name,
|
name: name ?? this.name,
|
||||||
updatedAt: updatedAt ?? this.updatedAt,
|
updatedAt: updatedAt ?? this.updatedAt,
|
||||||
backupSelection: backupSelection ?? this.backupSelection,
|
backupSelection: backupSelection ?? this.backupSelection,
|
||||||
|
isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum,
|
||||||
marker_: marker_.present ? marker_.value : this.marker_,
|
marker_: marker_.present ? marker_.value : this.marker_,
|
||||||
);
|
);
|
||||||
LocalAlbumEntityData copyWithCompanion(i1.LocalAlbumEntityCompanion data) {
|
LocalAlbumEntityData copyWithCompanion(i1.LocalAlbumEntityCompanion data) {
|
||||||
@@ -376,6 +418,9 @@ class LocalAlbumEntityData extends i0.DataClass
|
|||||||
backupSelection: data.backupSelection.present
|
backupSelection: data.backupSelection.present
|
||||||
? data.backupSelection.value
|
? data.backupSelection.value
|
||||||
: this.backupSelection,
|
: this.backupSelection,
|
||||||
|
isIosSharedAlbum: data.isIosSharedAlbum.present
|
||||||
|
? data.isIosSharedAlbum.value
|
||||||
|
: this.isIosSharedAlbum,
|
||||||
marker_: data.marker_.present ? data.marker_.value : this.marker_,
|
marker_: data.marker_.present ? data.marker_.value : this.marker_,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -387,14 +432,15 @@ class LocalAlbumEntityData extends i0.DataClass
|
|||||||
..write('name: $name, ')
|
..write('name: $name, ')
|
||||||
..write('updatedAt: $updatedAt, ')
|
..write('updatedAt: $updatedAt, ')
|
||||||
..write('backupSelection: $backupSelection, ')
|
..write('backupSelection: $backupSelection, ')
|
||||||
|
..write('isIosSharedAlbum: $isIosSharedAlbum, ')
|
||||||
..write('marker_: $marker_')
|
..write('marker_: $marker_')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode => Object.hash(
|
||||||
Object.hash(id, name, updatedAt, backupSelection, marker_);
|
id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_);
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) ||
|
||||||
@@ -403,6 +449,7 @@ class LocalAlbumEntityData extends i0.DataClass
|
|||||||
other.name == this.name &&
|
other.name == this.name &&
|
||||||
other.updatedAt == this.updatedAt &&
|
other.updatedAt == this.updatedAt &&
|
||||||
other.backupSelection == this.backupSelection &&
|
other.backupSelection == this.backupSelection &&
|
||||||
|
other.isIosSharedAlbum == this.isIosSharedAlbum &&
|
||||||
other.marker_ == this.marker_);
|
other.marker_ == this.marker_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,12 +459,14 @@ class LocalAlbumEntityCompanion
|
|||||||
final i0.Value<String> name;
|
final i0.Value<String> name;
|
||||||
final i0.Value<DateTime> updatedAt;
|
final i0.Value<DateTime> updatedAt;
|
||||||
final i0.Value<i2.BackupSelection> backupSelection;
|
final i0.Value<i2.BackupSelection> backupSelection;
|
||||||
|
final i0.Value<bool> isIosSharedAlbum;
|
||||||
final i0.Value<bool?> marker_;
|
final i0.Value<bool?> marker_;
|
||||||
const LocalAlbumEntityCompanion({
|
const LocalAlbumEntityCompanion({
|
||||||
this.id = const i0.Value.absent(),
|
this.id = const i0.Value.absent(),
|
||||||
this.name = const i0.Value.absent(),
|
this.name = const i0.Value.absent(),
|
||||||
this.updatedAt = const i0.Value.absent(),
|
this.updatedAt = const i0.Value.absent(),
|
||||||
this.backupSelection = const i0.Value.absent(),
|
this.backupSelection = const i0.Value.absent(),
|
||||||
|
this.isIosSharedAlbum = const i0.Value.absent(),
|
||||||
this.marker_ = const i0.Value.absent(),
|
this.marker_ = const i0.Value.absent(),
|
||||||
});
|
});
|
||||||
LocalAlbumEntityCompanion.insert({
|
LocalAlbumEntityCompanion.insert({
|
||||||
@@ -425,6 +474,7 @@ class LocalAlbumEntityCompanion
|
|||||||
required String name,
|
required String name,
|
||||||
this.updatedAt = const i0.Value.absent(),
|
this.updatedAt = const i0.Value.absent(),
|
||||||
required i2.BackupSelection backupSelection,
|
required i2.BackupSelection backupSelection,
|
||||||
|
this.isIosSharedAlbum = const i0.Value.absent(),
|
||||||
this.marker_ = const i0.Value.absent(),
|
this.marker_ = const i0.Value.absent(),
|
||||||
}) : id = i0.Value(id),
|
}) : id = i0.Value(id),
|
||||||
name = i0.Value(name),
|
name = i0.Value(name),
|
||||||
@@ -434,6 +484,7 @@ class LocalAlbumEntityCompanion
|
|||||||
i0.Expression<String>? name,
|
i0.Expression<String>? name,
|
||||||
i0.Expression<DateTime>? updatedAt,
|
i0.Expression<DateTime>? updatedAt,
|
||||||
i0.Expression<int>? backupSelection,
|
i0.Expression<int>? backupSelection,
|
||||||
|
i0.Expression<bool>? isIosSharedAlbum,
|
||||||
i0.Expression<bool>? marker_,
|
i0.Expression<bool>? marker_,
|
||||||
}) {
|
}) {
|
||||||
return i0.RawValuesInsertable({
|
return i0.RawValuesInsertable({
|
||||||
@@ -441,6 +492,7 @@ class LocalAlbumEntityCompanion
|
|||||||
if (name != null) 'name': name,
|
if (name != null) 'name': name,
|
||||||
if (updatedAt != null) 'updated_at': updatedAt,
|
if (updatedAt != null) 'updated_at': updatedAt,
|
||||||
if (backupSelection != null) 'backup_selection': backupSelection,
|
if (backupSelection != null) 'backup_selection': backupSelection,
|
||||||
|
if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum,
|
||||||
if (marker_ != null) 'marker': marker_,
|
if (marker_ != null) 'marker': marker_,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -450,12 +502,14 @@ class LocalAlbumEntityCompanion
|
|||||||
i0.Value<String>? name,
|
i0.Value<String>? name,
|
||||||
i0.Value<DateTime>? updatedAt,
|
i0.Value<DateTime>? updatedAt,
|
||||||
i0.Value<i2.BackupSelection>? backupSelection,
|
i0.Value<i2.BackupSelection>? backupSelection,
|
||||||
|
i0.Value<bool>? isIosSharedAlbum,
|
||||||
i0.Value<bool?>? marker_}) {
|
i0.Value<bool?>? marker_}) {
|
||||||
return i1.LocalAlbumEntityCompanion(
|
return i1.LocalAlbumEntityCompanion(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
name: name ?? this.name,
|
name: name ?? this.name,
|
||||||
updatedAt: updatedAt ?? this.updatedAt,
|
updatedAt: updatedAt ?? this.updatedAt,
|
||||||
backupSelection: backupSelection ?? this.backupSelection,
|
backupSelection: backupSelection ?? this.backupSelection,
|
||||||
|
isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum,
|
||||||
marker_: marker_ ?? this.marker_,
|
marker_: marker_ ?? this.marker_,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -477,6 +531,9 @@ class LocalAlbumEntityCompanion
|
|||||||
.$LocalAlbumEntityTable.$converterbackupSelection
|
.$LocalAlbumEntityTable.$converterbackupSelection
|
||||||
.toSql(backupSelection.value));
|
.toSql(backupSelection.value));
|
||||||
}
|
}
|
||||||
|
if (isIosSharedAlbum.present) {
|
||||||
|
map['is_ios_shared_album'] = i0.Variable<bool>(isIosSharedAlbum.value);
|
||||||
|
}
|
||||||
if (marker_.present) {
|
if (marker_.present) {
|
||||||
map['marker'] = i0.Variable<bool>(marker_.value);
|
map['marker'] = i0.Variable<bool>(marker_.value);
|
||||||
}
|
}
|
||||||
@@ -490,6 +547,7 @@ class LocalAlbumEntityCompanion
|
|||||||
..write('name: $name, ')
|
..write('name: $name, ')
|
||||||
..write('updatedAt: $updatedAt, ')
|
..write('updatedAt: $updatedAt, ')
|
||||||
..write('backupSelection: $backupSelection, ')
|
..write('backupSelection: $backupSelection, ')
|
||||||
|
..write('isIosSharedAlbum: $isIosSharedAlbum, ')
|
||||||
..write('marker_: $marker_')
|
..write('marker_: $marker_')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import 'package:drift/drift.dart';
|
|||||||
import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart';
|
import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart';
|
||||||
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
||||||
|
|
||||||
@TableIndex(name: 'local_asset_checksum', columns: {#checksum})
|
@TableIndex(name: 'idx_local_asset_checksum', columns: {#checksum})
|
||||||
class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
|
class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
|
||||||
const LocalAssetEntity();
|
const LocalAssetEntity();
|
||||||
|
|
||||||
|
|||||||
@@ -231,8 +231,8 @@ typedef $$LocalAssetEntityTableProcessedTableManager = i0.ProcessedTableManager<
|
|||||||
),
|
),
|
||||||
i1.LocalAssetEntityData,
|
i1.LocalAssetEntityData,
|
||||||
i0.PrefetchHooks Function()>;
|
i0.PrefetchHooks Function()>;
|
||||||
i0.Index get localAssetChecksum => i0.Index('local_asset_checksum',
|
i0.Index get idxLocalAssetChecksum => i0.Index('idx_local_asset_checksum',
|
||||||
'CREATE INDEX local_asset_checksum ON local_asset_entity (checksum)');
|
'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)');
|
||||||
|
|
||||||
class $LocalAssetEntityTable extends i3.LocalAssetEntity
|
class $LocalAssetEntityTable extends i3.LocalAssetEntity
|
||||||
with i0.TableInfo<$LocalAssetEntityTable, i1.LocalAssetEntityData> {
|
with i0.TableInfo<$LocalAssetEntityTable, i1.LocalAssetEntityData> {
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
|||||||
class PartnerEntity extends Table with DriftDefaultsMixin {
|
class PartnerEntity extends Table with DriftDefaultsMixin {
|
||||||
const PartnerEntity();
|
const PartnerEntity();
|
||||||
|
|
||||||
BlobColumn get sharedById =>
|
TextColumn get sharedById =>
|
||||||
blob().references(UserEntity, #id, onDelete: KeyAction.cascade)();
|
text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
|
||||||
|
|
||||||
BlobColumn get sharedWithId =>
|
TextColumn get sharedWithId =>
|
||||||
blob().references(UserEntity, #id, onDelete: KeyAction.cascade)();
|
text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
|
||||||
|
|
||||||
BoolColumn get inTimeline => boolean().withDefault(const Constant(false))();
|
BoolColumn get inTimeline => boolean().withDefault(const Constant(false))();
|
||||||
|
|
||||||
|
|||||||
@@ -3,24 +3,23 @@
|
|||||||
import 'package:drift/drift.dart' as i0;
|
import 'package:drift/drift.dart' as i0;
|
||||||
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart'
|
||||||
as i1;
|
as i1;
|
||||||
import 'dart:typed_data' as i2;
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/partner.entity.dart'
|
import 'package:immich_mobile/infrastructure/entities/partner.entity.dart'
|
||||||
as i3;
|
as i2;
|
||||||
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4;
|
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i3;
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
|
||||||
as i5;
|
as i4;
|
||||||
import 'package:drift/internal/modular.dart' as i6;
|
import 'package:drift/internal/modular.dart' as i5;
|
||||||
|
|
||||||
typedef $$PartnerEntityTableCreateCompanionBuilder = i1.PartnerEntityCompanion
|
typedef $$PartnerEntityTableCreateCompanionBuilder = i1.PartnerEntityCompanion
|
||||||
Function({
|
Function({
|
||||||
required i2.Uint8List sharedById,
|
required String sharedById,
|
||||||
required i2.Uint8List sharedWithId,
|
required String sharedWithId,
|
||||||
i0.Value<bool> inTimeline,
|
i0.Value<bool> inTimeline,
|
||||||
});
|
});
|
||||||
typedef $$PartnerEntityTableUpdateCompanionBuilder = i1.PartnerEntityCompanion
|
typedef $$PartnerEntityTableUpdateCompanionBuilder = i1.PartnerEntityCompanion
|
||||||
Function({
|
Function({
|
||||||
i0.Value<i2.Uint8List> sharedById,
|
i0.Value<String> sharedById,
|
||||||
i0.Value<i2.Uint8List> sharedWithId,
|
i0.Value<String> sharedWithId,
|
||||||
i0.Value<bool> inTimeline,
|
i0.Value<bool> inTimeline,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -29,25 +28,25 @@ final class $$PartnerEntityTableReferences extends i0.BaseReferences<
|
|||||||
$$PartnerEntityTableReferences(
|
$$PartnerEntityTableReferences(
|
||||||
super.$_db, super.$_table, super.$_typedResult);
|
super.$_db, super.$_table, super.$_typedResult);
|
||||||
|
|
||||||
static i5.$UserEntityTable _sharedByIdTable(i0.GeneratedDatabase db) =>
|
static i4.$UserEntityTable _sharedByIdTable(i0.GeneratedDatabase db) =>
|
||||||
i6.ReadDatabaseContainer(db)
|
i5.ReadDatabaseContainer(db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity')
|
.resultSet<i4.$UserEntityTable>('user_entity')
|
||||||
.createAlias(i0.$_aliasNameGenerator(
|
.createAlias(i0.$_aliasNameGenerator(
|
||||||
i6.ReadDatabaseContainer(db)
|
i5.ReadDatabaseContainer(db)
|
||||||
.resultSet<i1.$PartnerEntityTable>('partner_entity')
|
.resultSet<i1.$PartnerEntityTable>('partner_entity')
|
||||||
.sharedById,
|
.sharedById,
|
||||||
i6.ReadDatabaseContainer(db)
|
i5.ReadDatabaseContainer(db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity')
|
.resultSet<i4.$UserEntityTable>('user_entity')
|
||||||
.id));
|
.id));
|
||||||
|
|
||||||
i5.$$UserEntityTableProcessedTableManager get sharedById {
|
i4.$$UserEntityTableProcessedTableManager get sharedById {
|
||||||
final $_column = $_itemColumn<i2.Uint8List>('shared_by_id')!;
|
final $_column = $_itemColumn<String>('shared_by_id')!;
|
||||||
|
|
||||||
final manager = i5
|
final manager = i4
|
||||||
.$$UserEntityTableTableManager(
|
.$$UserEntityTableTableManager(
|
||||||
$_db,
|
$_db,
|
||||||
i6.ReadDatabaseContainer($_db)
|
i5.ReadDatabaseContainer($_db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'))
|
.resultSet<i4.$UserEntityTable>('user_entity'))
|
||||||
.filter((f) => f.id.sqlEquals($_column));
|
.filter((f) => f.id.sqlEquals($_column));
|
||||||
final item = $_typedResult.readTableOrNull(_sharedByIdTable($_db));
|
final item = $_typedResult.readTableOrNull(_sharedByIdTable($_db));
|
||||||
if (item == null) return manager;
|
if (item == null) return manager;
|
||||||
@@ -55,25 +54,25 @@ final class $$PartnerEntityTableReferences extends i0.BaseReferences<
|
|||||||
manager.$state.copyWith(prefetchedData: [item]));
|
manager.$state.copyWith(prefetchedData: [item]));
|
||||||
}
|
}
|
||||||
|
|
||||||
static i5.$UserEntityTable _sharedWithIdTable(i0.GeneratedDatabase db) =>
|
static i4.$UserEntityTable _sharedWithIdTable(i0.GeneratedDatabase db) =>
|
||||||
i6.ReadDatabaseContainer(db)
|
i5.ReadDatabaseContainer(db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity')
|
.resultSet<i4.$UserEntityTable>('user_entity')
|
||||||
.createAlias(i0.$_aliasNameGenerator(
|
.createAlias(i0.$_aliasNameGenerator(
|
||||||
i6.ReadDatabaseContainer(db)
|
i5.ReadDatabaseContainer(db)
|
||||||
.resultSet<i1.$PartnerEntityTable>('partner_entity')
|
.resultSet<i1.$PartnerEntityTable>('partner_entity')
|
||||||
.sharedWithId,
|
.sharedWithId,
|
||||||
i6.ReadDatabaseContainer(db)
|
i5.ReadDatabaseContainer(db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity')
|
.resultSet<i4.$UserEntityTable>('user_entity')
|
||||||
.id));
|
.id));
|
||||||
|
|
||||||
i5.$$UserEntityTableProcessedTableManager get sharedWithId {
|
i4.$$UserEntityTableProcessedTableManager get sharedWithId {
|
||||||
final $_column = $_itemColumn<i2.Uint8List>('shared_with_id')!;
|
final $_column = $_itemColumn<String>('shared_with_id')!;
|
||||||
|
|
||||||
final manager = i5
|
final manager = i4
|
||||||
.$$UserEntityTableTableManager(
|
.$$UserEntityTableTableManager(
|
||||||
$_db,
|
$_db,
|
||||||
i6.ReadDatabaseContainer($_db)
|
i5.ReadDatabaseContainer($_db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'))
|
.resultSet<i4.$UserEntityTable>('user_entity'))
|
||||||
.filter((f) => f.id.sqlEquals($_column));
|
.filter((f) => f.id.sqlEquals($_column));
|
||||||
final item = $_typedResult.readTableOrNull(_sharedWithIdTable($_db));
|
final item = $_typedResult.readTableOrNull(_sharedWithIdTable($_db));
|
||||||
if (item == null) return manager;
|
if (item == null) return manager;
|
||||||
@@ -94,20 +93,20 @@ class $$PartnerEntityTableFilterComposer
|
|||||||
i0.ColumnFilters<bool> get inTimeline => $composableBuilder(
|
i0.ColumnFilters<bool> get inTimeline => $composableBuilder(
|
||||||
column: $table.inTimeline, builder: (column) => i0.ColumnFilters(column));
|
column: $table.inTimeline, builder: (column) => i0.ColumnFilters(column));
|
||||||
|
|
||||||
i5.$$UserEntityTableFilterComposer get sharedById {
|
i4.$$UserEntityTableFilterComposer get sharedById {
|
||||||
final i5.$$UserEntityTableFilterComposer composer = $composerBuilder(
|
final i4.$$UserEntityTableFilterComposer composer = $composerBuilder(
|
||||||
composer: this,
|
composer: this,
|
||||||
getCurrentColumn: (t) => t.sharedById,
|
getCurrentColumn: (t) => t.sharedById,
|
||||||
referencedTable: i6.ReadDatabaseContainer($db)
|
referencedTable: i5.ReadDatabaseContainer($db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
getReferencedColumn: (t) => t.id,
|
getReferencedColumn: (t) => t.id,
|
||||||
builder: (joinBuilder,
|
builder: (joinBuilder,
|
||||||
{$addJoinBuilderToRootComposer,
|
{$addJoinBuilderToRootComposer,
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
$removeJoinBuilderFromRootComposer}) =>
|
||||||
i5.$$UserEntityTableFilterComposer(
|
i4.$$UserEntityTableFilterComposer(
|
||||||
$db: $db,
|
$db: $db,
|
||||||
$table: i6.ReadDatabaseContainer($db)
|
$table: i5.ReadDatabaseContainer($db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
joinBuilder: joinBuilder,
|
joinBuilder: joinBuilder,
|
||||||
$removeJoinBuilderFromRootComposer:
|
$removeJoinBuilderFromRootComposer:
|
||||||
@@ -116,20 +115,20 @@ class $$PartnerEntityTableFilterComposer
|
|||||||
return composer;
|
return composer;
|
||||||
}
|
}
|
||||||
|
|
||||||
i5.$$UserEntityTableFilterComposer get sharedWithId {
|
i4.$$UserEntityTableFilterComposer get sharedWithId {
|
||||||
final i5.$$UserEntityTableFilterComposer composer = $composerBuilder(
|
final i4.$$UserEntityTableFilterComposer composer = $composerBuilder(
|
||||||
composer: this,
|
composer: this,
|
||||||
getCurrentColumn: (t) => t.sharedWithId,
|
getCurrentColumn: (t) => t.sharedWithId,
|
||||||
referencedTable: i6.ReadDatabaseContainer($db)
|
referencedTable: i5.ReadDatabaseContainer($db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
getReferencedColumn: (t) => t.id,
|
getReferencedColumn: (t) => t.id,
|
||||||
builder: (joinBuilder,
|
builder: (joinBuilder,
|
||||||
{$addJoinBuilderToRootComposer,
|
{$addJoinBuilderToRootComposer,
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
$removeJoinBuilderFromRootComposer}) =>
|
||||||
i5.$$UserEntityTableFilterComposer(
|
i4.$$UserEntityTableFilterComposer(
|
||||||
$db: $db,
|
$db: $db,
|
||||||
$table: i6.ReadDatabaseContainer($db)
|
$table: i5.ReadDatabaseContainer($db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
joinBuilder: joinBuilder,
|
joinBuilder: joinBuilder,
|
||||||
$removeJoinBuilderFromRootComposer:
|
$removeJoinBuilderFromRootComposer:
|
||||||
@@ -152,20 +151,20 @@ class $$PartnerEntityTableOrderingComposer
|
|||||||
column: $table.inTimeline,
|
column: $table.inTimeline,
|
||||||
builder: (column) => i0.ColumnOrderings(column));
|
builder: (column) => i0.ColumnOrderings(column));
|
||||||
|
|
||||||
i5.$$UserEntityTableOrderingComposer get sharedById {
|
i4.$$UserEntityTableOrderingComposer get sharedById {
|
||||||
final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder(
|
final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder(
|
||||||
composer: this,
|
composer: this,
|
||||||
getCurrentColumn: (t) => t.sharedById,
|
getCurrentColumn: (t) => t.sharedById,
|
||||||
referencedTable: i6.ReadDatabaseContainer($db)
|
referencedTable: i5.ReadDatabaseContainer($db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
getReferencedColumn: (t) => t.id,
|
getReferencedColumn: (t) => t.id,
|
||||||
builder: (joinBuilder,
|
builder: (joinBuilder,
|
||||||
{$addJoinBuilderToRootComposer,
|
{$addJoinBuilderToRootComposer,
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
$removeJoinBuilderFromRootComposer}) =>
|
||||||
i5.$$UserEntityTableOrderingComposer(
|
i4.$$UserEntityTableOrderingComposer(
|
||||||
$db: $db,
|
$db: $db,
|
||||||
$table: i6.ReadDatabaseContainer($db)
|
$table: i5.ReadDatabaseContainer($db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
joinBuilder: joinBuilder,
|
joinBuilder: joinBuilder,
|
||||||
$removeJoinBuilderFromRootComposer:
|
$removeJoinBuilderFromRootComposer:
|
||||||
@@ -174,20 +173,20 @@ class $$PartnerEntityTableOrderingComposer
|
|||||||
return composer;
|
return composer;
|
||||||
}
|
}
|
||||||
|
|
||||||
i5.$$UserEntityTableOrderingComposer get sharedWithId {
|
i4.$$UserEntityTableOrderingComposer get sharedWithId {
|
||||||
final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder(
|
final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder(
|
||||||
composer: this,
|
composer: this,
|
||||||
getCurrentColumn: (t) => t.sharedWithId,
|
getCurrentColumn: (t) => t.sharedWithId,
|
||||||
referencedTable: i6.ReadDatabaseContainer($db)
|
referencedTable: i5.ReadDatabaseContainer($db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
getReferencedColumn: (t) => t.id,
|
getReferencedColumn: (t) => t.id,
|
||||||
builder: (joinBuilder,
|
builder: (joinBuilder,
|
||||||
{$addJoinBuilderToRootComposer,
|
{$addJoinBuilderToRootComposer,
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
$removeJoinBuilderFromRootComposer}) =>
|
||||||
i5.$$UserEntityTableOrderingComposer(
|
i4.$$UserEntityTableOrderingComposer(
|
||||||
$db: $db,
|
$db: $db,
|
||||||
$table: i6.ReadDatabaseContainer($db)
|
$table: i5.ReadDatabaseContainer($db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
joinBuilder: joinBuilder,
|
joinBuilder: joinBuilder,
|
||||||
$removeJoinBuilderFromRootComposer:
|
$removeJoinBuilderFromRootComposer:
|
||||||
@@ -209,20 +208,20 @@ class $$PartnerEntityTableAnnotationComposer
|
|||||||
i0.GeneratedColumn<bool> get inTimeline => $composableBuilder(
|
i0.GeneratedColumn<bool> get inTimeline => $composableBuilder(
|
||||||
column: $table.inTimeline, builder: (column) => column);
|
column: $table.inTimeline, builder: (column) => column);
|
||||||
|
|
||||||
i5.$$UserEntityTableAnnotationComposer get sharedById {
|
i4.$$UserEntityTableAnnotationComposer get sharedById {
|
||||||
final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
|
final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
|
||||||
composer: this,
|
composer: this,
|
||||||
getCurrentColumn: (t) => t.sharedById,
|
getCurrentColumn: (t) => t.sharedById,
|
||||||
referencedTable: i6.ReadDatabaseContainer($db)
|
referencedTable: i5.ReadDatabaseContainer($db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
getReferencedColumn: (t) => t.id,
|
getReferencedColumn: (t) => t.id,
|
||||||
builder: (joinBuilder,
|
builder: (joinBuilder,
|
||||||
{$addJoinBuilderToRootComposer,
|
{$addJoinBuilderToRootComposer,
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
$removeJoinBuilderFromRootComposer}) =>
|
||||||
i5.$$UserEntityTableAnnotationComposer(
|
i4.$$UserEntityTableAnnotationComposer(
|
||||||
$db: $db,
|
$db: $db,
|
||||||
$table: i6.ReadDatabaseContainer($db)
|
$table: i5.ReadDatabaseContainer($db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
joinBuilder: joinBuilder,
|
joinBuilder: joinBuilder,
|
||||||
$removeJoinBuilderFromRootComposer:
|
$removeJoinBuilderFromRootComposer:
|
||||||
@@ -231,20 +230,20 @@ class $$PartnerEntityTableAnnotationComposer
|
|||||||
return composer;
|
return composer;
|
||||||
}
|
}
|
||||||
|
|
||||||
i5.$$UserEntityTableAnnotationComposer get sharedWithId {
|
i4.$$UserEntityTableAnnotationComposer get sharedWithId {
|
||||||
final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
|
final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
|
||||||
composer: this,
|
composer: this,
|
||||||
getCurrentColumn: (t) => t.sharedWithId,
|
getCurrentColumn: (t) => t.sharedWithId,
|
||||||
referencedTable: i6.ReadDatabaseContainer($db)
|
referencedTable: i5.ReadDatabaseContainer($db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
getReferencedColumn: (t) => t.id,
|
getReferencedColumn: (t) => t.id,
|
||||||
builder: (joinBuilder,
|
builder: (joinBuilder,
|
||||||
{$addJoinBuilderToRootComposer,
|
{$addJoinBuilderToRootComposer,
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
$removeJoinBuilderFromRootComposer}) =>
|
||||||
i5.$$UserEntityTableAnnotationComposer(
|
i4.$$UserEntityTableAnnotationComposer(
|
||||||
$db: $db,
|
$db: $db,
|
||||||
$table: i6.ReadDatabaseContainer($db)
|
$table: i5.ReadDatabaseContainer($db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
joinBuilder: joinBuilder,
|
joinBuilder: joinBuilder,
|
||||||
$removeJoinBuilderFromRootComposer:
|
$removeJoinBuilderFromRootComposer:
|
||||||
@@ -278,8 +277,8 @@ class $$PartnerEntityTableTableManager extends i0.RootTableManager<
|
|||||||
createComputedFieldComposer: () =>
|
createComputedFieldComposer: () =>
|
||||||
i1.$$PartnerEntityTableAnnotationComposer($db: db, $table: table),
|
i1.$$PartnerEntityTableAnnotationComposer($db: db, $table: table),
|
||||||
updateCompanionCallback: ({
|
updateCompanionCallback: ({
|
||||||
i0.Value<i2.Uint8List> sharedById = const i0.Value.absent(),
|
i0.Value<String> sharedById = const i0.Value.absent(),
|
||||||
i0.Value<i2.Uint8List> sharedWithId = const i0.Value.absent(),
|
i0.Value<String> sharedWithId = const i0.Value.absent(),
|
||||||
i0.Value<bool> inTimeline = const i0.Value.absent(),
|
i0.Value<bool> inTimeline = const i0.Value.absent(),
|
||||||
}) =>
|
}) =>
|
||||||
i1.PartnerEntityCompanion(
|
i1.PartnerEntityCompanion(
|
||||||
@@ -288,8 +287,8 @@ class $$PartnerEntityTableTableManager extends i0.RootTableManager<
|
|||||||
inTimeline: inTimeline,
|
inTimeline: inTimeline,
|
||||||
),
|
),
|
||||||
createCompanionCallback: ({
|
createCompanionCallback: ({
|
||||||
required i2.Uint8List sharedById,
|
required String sharedById,
|
||||||
required i2.Uint8List sharedWithId,
|
required String sharedWithId,
|
||||||
i0.Value<bool> inTimeline = const i0.Value.absent(),
|
i0.Value<bool> inTimeline = const i0.Value.absent(),
|
||||||
}) =>
|
}) =>
|
||||||
i1.PartnerEntityCompanion.insert(
|
i1.PartnerEntityCompanion.insert(
|
||||||
@@ -366,7 +365,7 @@ typedef $$PartnerEntityTableProcessedTableManager = i0.ProcessedTableManager<
|
|||||||
i1.PartnerEntityData,
|
i1.PartnerEntityData,
|
||||||
i0.PrefetchHooks Function({bool sharedById, bool sharedWithId})>;
|
i0.PrefetchHooks Function({bool sharedById, bool sharedWithId})>;
|
||||||
|
|
||||||
class $PartnerEntityTable extends i3.PartnerEntity
|
class $PartnerEntityTable extends i2.PartnerEntity
|
||||||
with i0.TableInfo<$PartnerEntityTable, i1.PartnerEntityData> {
|
with i0.TableInfo<$PartnerEntityTable, i1.PartnerEntityData> {
|
||||||
@override
|
@override
|
||||||
final i0.GeneratedDatabase attachedDatabase;
|
final i0.GeneratedDatabase attachedDatabase;
|
||||||
@@ -375,18 +374,18 @@ class $PartnerEntityTable extends i3.PartnerEntity
|
|||||||
static const i0.VerificationMeta _sharedByIdMeta =
|
static const i0.VerificationMeta _sharedByIdMeta =
|
||||||
const i0.VerificationMeta('sharedById');
|
const i0.VerificationMeta('sharedById');
|
||||||
@override
|
@override
|
||||||
late final i0.GeneratedColumn<i2.Uint8List> sharedById =
|
late final i0.GeneratedColumn<String> sharedById = i0.GeneratedColumn<String>(
|
||||||
i0.GeneratedColumn<i2.Uint8List>('shared_by_id', aliasedName, false,
|
'shared_by_id', aliasedName, false,
|
||||||
type: i0.DriftSqlType.blob,
|
type: i0.DriftSqlType.string,
|
||||||
requiredDuringInsert: true,
|
requiredDuringInsert: true,
|
||||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||||
'REFERENCES user_entity (id) ON DELETE CASCADE'));
|
'REFERENCES user_entity (id) ON DELETE CASCADE'));
|
||||||
static const i0.VerificationMeta _sharedWithIdMeta =
|
static const i0.VerificationMeta _sharedWithIdMeta =
|
||||||
const i0.VerificationMeta('sharedWithId');
|
const i0.VerificationMeta('sharedWithId');
|
||||||
@override
|
@override
|
||||||
late final i0.GeneratedColumn<i2.Uint8List> sharedWithId =
|
late final i0.GeneratedColumn<String> sharedWithId =
|
||||||
i0.GeneratedColumn<i2.Uint8List>('shared_with_id', aliasedName, false,
|
i0.GeneratedColumn<String>('shared_with_id', aliasedName, false,
|
||||||
type: i0.DriftSqlType.blob,
|
type: i0.DriftSqlType.string,
|
||||||
requiredDuringInsert: true,
|
requiredDuringInsert: true,
|
||||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||||
'REFERENCES user_entity (id) ON DELETE CASCADE'));
|
'REFERENCES user_entity (id) ON DELETE CASCADE'));
|
||||||
@@ -399,7 +398,7 @@ class $PartnerEntityTable extends i3.PartnerEntity
|
|||||||
requiredDuringInsert: false,
|
requiredDuringInsert: false,
|
||||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||||
'CHECK ("in_timeline" IN (0, 1))'),
|
'CHECK ("in_timeline" IN (0, 1))'),
|
||||||
defaultValue: const i4.Constant(false));
|
defaultValue: const i3.Constant(false));
|
||||||
@override
|
@override
|
||||||
List<i0.GeneratedColumn> get $columns =>
|
List<i0.GeneratedColumn> get $columns =>
|
||||||
[sharedById, sharedWithId, inTimeline];
|
[sharedById, sharedWithId, inTimeline];
|
||||||
@@ -445,10 +444,10 @@ class $PartnerEntityTable extends i3.PartnerEntity
|
|||||||
i1.PartnerEntityData map(Map<String, dynamic> data, {String? tablePrefix}) {
|
i1.PartnerEntityData map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||||
return i1.PartnerEntityData(
|
return i1.PartnerEntityData(
|
||||||
sharedById: attachedDatabase.typeMapping
|
sharedById: attachedDatabase.typeMapping.read(
|
||||||
.read(i0.DriftSqlType.blob, data['${effectivePrefix}shared_by_id'])!,
|
i0.DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!,
|
||||||
sharedWithId: attachedDatabase.typeMapping.read(
|
sharedWithId: attachedDatabase.typeMapping.read(
|
||||||
i0.DriftSqlType.blob, data['${effectivePrefix}shared_with_id'])!,
|
i0.DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!,
|
||||||
inTimeline: attachedDatabase.typeMapping
|
inTimeline: attachedDatabase.typeMapping
|
||||||
.read(i0.DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!,
|
.read(i0.DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!,
|
||||||
);
|
);
|
||||||
@@ -467,8 +466,8 @@ class $PartnerEntityTable extends i3.PartnerEntity
|
|||||||
|
|
||||||
class PartnerEntityData extends i0.DataClass
|
class PartnerEntityData extends i0.DataClass
|
||||||
implements i0.Insertable<i1.PartnerEntityData> {
|
implements i0.Insertable<i1.PartnerEntityData> {
|
||||||
final i2.Uint8List sharedById;
|
final String sharedById;
|
||||||
final i2.Uint8List sharedWithId;
|
final String sharedWithId;
|
||||||
final bool inTimeline;
|
final bool inTimeline;
|
||||||
const PartnerEntityData(
|
const PartnerEntityData(
|
||||||
{required this.sharedById,
|
{required this.sharedById,
|
||||||
@@ -477,8 +476,8 @@ class PartnerEntityData extends i0.DataClass
|
|||||||
@override
|
@override
|
||||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||||
final map = <String, i0.Expression>{};
|
final map = <String, i0.Expression>{};
|
||||||
map['shared_by_id'] = i0.Variable<i2.Uint8List>(sharedById);
|
map['shared_by_id'] = i0.Variable<String>(sharedById);
|
||||||
map['shared_with_id'] = i0.Variable<i2.Uint8List>(sharedWithId);
|
map['shared_with_id'] = i0.Variable<String>(sharedWithId);
|
||||||
map['in_timeline'] = i0.Variable<bool>(inTimeline);
|
map['in_timeline'] = i0.Variable<bool>(inTimeline);
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
@@ -487,8 +486,8 @@ class PartnerEntityData extends i0.DataClass
|
|||||||
{i0.ValueSerializer? serializer}) {
|
{i0.ValueSerializer? serializer}) {
|
||||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||||
return PartnerEntityData(
|
return PartnerEntityData(
|
||||||
sharedById: serializer.fromJson<i2.Uint8List>(json['sharedById']),
|
sharedById: serializer.fromJson<String>(json['sharedById']),
|
||||||
sharedWithId: serializer.fromJson<i2.Uint8List>(json['sharedWithId']),
|
sharedWithId: serializer.fromJson<String>(json['sharedWithId']),
|
||||||
inTimeline: serializer.fromJson<bool>(json['inTimeline']),
|
inTimeline: serializer.fromJson<bool>(json['inTimeline']),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -496,16 +495,14 @@ class PartnerEntityData extends i0.DataClass
|
|||||||
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
||||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||||
return <String, dynamic>{
|
return <String, dynamic>{
|
||||||
'sharedById': serializer.toJson<i2.Uint8List>(sharedById),
|
'sharedById': serializer.toJson<String>(sharedById),
|
||||||
'sharedWithId': serializer.toJson<i2.Uint8List>(sharedWithId),
|
'sharedWithId': serializer.toJson<String>(sharedWithId),
|
||||||
'inTimeline': serializer.toJson<bool>(inTimeline),
|
'inTimeline': serializer.toJson<bool>(inTimeline),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
i1.PartnerEntityData copyWith(
|
i1.PartnerEntityData copyWith(
|
||||||
{i2.Uint8List? sharedById,
|
{String? sharedById, String? sharedWithId, bool? inTimeline}) =>
|
||||||
i2.Uint8List? sharedWithId,
|
|
||||||
bool? inTimeline}) =>
|
|
||||||
i1.PartnerEntityData(
|
i1.PartnerEntityData(
|
||||||
sharedById: sharedById ?? this.sharedById,
|
sharedById: sharedById ?? this.sharedById,
|
||||||
sharedWithId: sharedWithId ?? this.sharedWithId,
|
sharedWithId: sharedWithId ?? this.sharedWithId,
|
||||||
@@ -534,20 +531,19 @@ class PartnerEntityData extends i0.DataClass
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(i0.$driftBlobEquality.hash(sharedById),
|
int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline);
|
||||||
i0.$driftBlobEquality.hash(sharedWithId), inTimeline);
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) ||
|
||||||
(other is i1.PartnerEntityData &&
|
(other is i1.PartnerEntityData &&
|
||||||
i0.$driftBlobEquality.equals(other.sharedById, this.sharedById) &&
|
other.sharedById == this.sharedById &&
|
||||||
i0.$driftBlobEquality.equals(other.sharedWithId, this.sharedWithId) &&
|
other.sharedWithId == this.sharedWithId &&
|
||||||
other.inTimeline == this.inTimeline);
|
other.inTimeline == this.inTimeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
class PartnerEntityCompanion extends i0.UpdateCompanion<i1.PartnerEntityData> {
|
class PartnerEntityCompanion extends i0.UpdateCompanion<i1.PartnerEntityData> {
|
||||||
final i0.Value<i2.Uint8List> sharedById;
|
final i0.Value<String> sharedById;
|
||||||
final i0.Value<i2.Uint8List> sharedWithId;
|
final i0.Value<String> sharedWithId;
|
||||||
final i0.Value<bool> inTimeline;
|
final i0.Value<bool> inTimeline;
|
||||||
const PartnerEntityCompanion({
|
const PartnerEntityCompanion({
|
||||||
this.sharedById = const i0.Value.absent(),
|
this.sharedById = const i0.Value.absent(),
|
||||||
@@ -555,14 +551,14 @@ class PartnerEntityCompanion extends i0.UpdateCompanion<i1.PartnerEntityData> {
|
|||||||
this.inTimeline = const i0.Value.absent(),
|
this.inTimeline = const i0.Value.absent(),
|
||||||
});
|
});
|
||||||
PartnerEntityCompanion.insert({
|
PartnerEntityCompanion.insert({
|
||||||
required i2.Uint8List sharedById,
|
required String sharedById,
|
||||||
required i2.Uint8List sharedWithId,
|
required String sharedWithId,
|
||||||
this.inTimeline = const i0.Value.absent(),
|
this.inTimeline = const i0.Value.absent(),
|
||||||
}) : sharedById = i0.Value(sharedById),
|
}) : sharedById = i0.Value(sharedById),
|
||||||
sharedWithId = i0.Value(sharedWithId);
|
sharedWithId = i0.Value(sharedWithId);
|
||||||
static i0.Insertable<i1.PartnerEntityData> custom({
|
static i0.Insertable<i1.PartnerEntityData> custom({
|
||||||
i0.Expression<i2.Uint8List>? sharedById,
|
i0.Expression<String>? sharedById,
|
||||||
i0.Expression<i2.Uint8List>? sharedWithId,
|
i0.Expression<String>? sharedWithId,
|
||||||
i0.Expression<bool>? inTimeline,
|
i0.Expression<bool>? inTimeline,
|
||||||
}) {
|
}) {
|
||||||
return i0.RawValuesInsertable({
|
return i0.RawValuesInsertable({
|
||||||
@@ -573,8 +569,8 @@ class PartnerEntityCompanion extends i0.UpdateCompanion<i1.PartnerEntityData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
i1.PartnerEntityCompanion copyWith(
|
i1.PartnerEntityCompanion copyWith(
|
||||||
{i0.Value<i2.Uint8List>? sharedById,
|
{i0.Value<String>? sharedById,
|
||||||
i0.Value<i2.Uint8List>? sharedWithId,
|
i0.Value<String>? sharedWithId,
|
||||||
i0.Value<bool>? inTimeline}) {
|
i0.Value<bool>? inTimeline}) {
|
||||||
return i1.PartnerEntityCompanion(
|
return i1.PartnerEntityCompanion(
|
||||||
sharedById: sharedById ?? this.sharedById,
|
sharedById: sharedById ?? this.sharedById,
|
||||||
@@ -587,10 +583,10 @@ class PartnerEntityCompanion extends i0.UpdateCompanion<i1.PartnerEntityData> {
|
|||||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||||
final map = <String, i0.Expression>{};
|
final map = <String, i0.Expression>{};
|
||||||
if (sharedById.present) {
|
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) {
|
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) {
|
if (inTimeline.present) {
|
||||||
map['in_timeline'] = i0.Variable<bool>(inTimeline.value);
|
map['in_timeline'] = i0.Variable<bool>(inTimeline.value);
|
||||||
|
|||||||
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};
|
||||||
|
}
|
||||||
1076
mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart
generated
Normal file
1076
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 {
|
class UserEntity extends Table with DriftDefaultsMixin {
|
||||||
const UserEntity();
|
const UserEntity();
|
||||||
|
|
||||||
BlobColumn get id => blob()();
|
TextColumn get id => text()();
|
||||||
TextColumn get name => text()();
|
TextColumn get name => text()();
|
||||||
BoolColumn get isAdmin => boolean().withDefault(const Constant(false))();
|
BoolColumn get isAdmin => boolean().withDefault(const Constant(false))();
|
||||||
TextColumn get email => text()();
|
TextColumn get email => text()();
|
||||||
|
|||||||
@@ -3,13 +3,12 @@
|
|||||||
import 'package:drift/drift.dart' as i0;
|
import 'package:drift/drift.dart' as i0;
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
|
||||||
as i1;
|
as i1;
|
||||||
import 'dart:typed_data' as i2;
|
import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as i2;
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as i3;
|
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i3;
|
||||||
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4;
|
|
||||||
|
|
||||||
typedef $$UserEntityTableCreateCompanionBuilder = i1.UserEntityCompanion
|
typedef $$UserEntityTableCreateCompanionBuilder = i1.UserEntityCompanion
|
||||||
Function({
|
Function({
|
||||||
required i2.Uint8List id,
|
required String id,
|
||||||
required String name,
|
required String name,
|
||||||
i0.Value<bool> isAdmin,
|
i0.Value<bool> isAdmin,
|
||||||
required String email,
|
required String email,
|
||||||
@@ -20,7 +19,7 @@ typedef $$UserEntityTableCreateCompanionBuilder = i1.UserEntityCompanion
|
|||||||
});
|
});
|
||||||
typedef $$UserEntityTableUpdateCompanionBuilder = i1.UserEntityCompanion
|
typedef $$UserEntityTableUpdateCompanionBuilder = i1.UserEntityCompanion
|
||||||
Function({
|
Function({
|
||||||
i0.Value<i2.Uint8List> id,
|
i0.Value<String> id,
|
||||||
i0.Value<String> name,
|
i0.Value<String> name,
|
||||||
i0.Value<bool> isAdmin,
|
i0.Value<bool> isAdmin,
|
||||||
i0.Value<String> email,
|
i0.Value<String> email,
|
||||||
@@ -39,7 +38,7 @@ class $$UserEntityTableFilterComposer
|
|||||||
super.$addJoinBuilderToRootComposer,
|
super.$addJoinBuilderToRootComposer,
|
||||||
super.$removeJoinBuilderFromRootComposer,
|
super.$removeJoinBuilderFromRootComposer,
|
||||||
});
|
});
|
||||||
i0.ColumnFilters<i2.Uint8List> get id => $composableBuilder(
|
i0.ColumnFilters<String> get id => $composableBuilder(
|
||||||
column: $table.id, builder: (column) => i0.ColumnFilters(column));
|
column: $table.id, builder: (column) => i0.ColumnFilters(column));
|
||||||
|
|
||||||
i0.ColumnFilters<String> get name => $composableBuilder(
|
i0.ColumnFilters<String> get name => $composableBuilder(
|
||||||
@@ -76,7 +75,7 @@ class $$UserEntityTableOrderingComposer
|
|||||||
super.$addJoinBuilderToRootComposer,
|
super.$addJoinBuilderToRootComposer,
|
||||||
super.$removeJoinBuilderFromRootComposer,
|
super.$removeJoinBuilderFromRootComposer,
|
||||||
});
|
});
|
||||||
i0.ColumnOrderings<i2.Uint8List> get id => $composableBuilder(
|
i0.ColumnOrderings<String> get id => $composableBuilder(
|
||||||
column: $table.id, builder: (column) => i0.ColumnOrderings(column));
|
column: $table.id, builder: (column) => i0.ColumnOrderings(column));
|
||||||
|
|
||||||
i0.ColumnOrderings<String> get name => $composableBuilder(
|
i0.ColumnOrderings<String> get name => $composableBuilder(
|
||||||
@@ -114,7 +113,7 @@ class $$UserEntityTableAnnotationComposer
|
|||||||
super.$addJoinBuilderToRootComposer,
|
super.$addJoinBuilderToRootComposer,
|
||||||
super.$removeJoinBuilderFromRootComposer,
|
super.$removeJoinBuilderFromRootComposer,
|
||||||
});
|
});
|
||||||
i0.GeneratedColumn<i2.Uint8List> get id =>
|
i0.GeneratedColumn<String> get id =>
|
||||||
$composableBuilder(column: $table.id, builder: (column) => column);
|
$composableBuilder(column: $table.id, builder: (column) => column);
|
||||||
|
|
||||||
i0.GeneratedColumn<String> get name =>
|
i0.GeneratedColumn<String> get name =>
|
||||||
@@ -167,7 +166,7 @@ class $$UserEntityTableTableManager extends i0.RootTableManager<
|
|||||||
createComputedFieldComposer: () =>
|
createComputedFieldComposer: () =>
|
||||||
i1.$$UserEntityTableAnnotationComposer($db: db, $table: table),
|
i1.$$UserEntityTableAnnotationComposer($db: db, $table: table),
|
||||||
updateCompanionCallback: ({
|
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<String> name = const i0.Value.absent(),
|
||||||
i0.Value<bool> isAdmin = const i0.Value.absent(),
|
i0.Value<bool> isAdmin = const i0.Value.absent(),
|
||||||
i0.Value<String> email = const i0.Value.absent(),
|
i0.Value<String> email = const i0.Value.absent(),
|
||||||
@@ -187,7 +186,7 @@ class $$UserEntityTableTableManager extends i0.RootTableManager<
|
|||||||
quotaUsageInBytes: quotaUsageInBytes,
|
quotaUsageInBytes: quotaUsageInBytes,
|
||||||
),
|
),
|
||||||
createCompanionCallback: ({
|
createCompanionCallback: ({
|
||||||
required i2.Uint8List id,
|
required String id,
|
||||||
required String name,
|
required String name,
|
||||||
i0.Value<bool> isAdmin = const i0.Value.absent(),
|
i0.Value<bool> isAdmin = const i0.Value.absent(),
|
||||||
required String email,
|
required String email,
|
||||||
@@ -230,7 +229,7 @@ typedef $$UserEntityTableProcessedTableManager = i0.ProcessedTableManager<
|
|||||||
i1.UserEntityData,
|
i1.UserEntityData,
|
||||||
i0.PrefetchHooks Function()>;
|
i0.PrefetchHooks Function()>;
|
||||||
|
|
||||||
class $UserEntityTable extends i3.UserEntity
|
class $UserEntityTable extends i2.UserEntity
|
||||||
with i0.TableInfo<$UserEntityTable, i1.UserEntityData> {
|
with i0.TableInfo<$UserEntityTable, i1.UserEntityData> {
|
||||||
@override
|
@override
|
||||||
final i0.GeneratedDatabase attachedDatabase;
|
final i0.GeneratedDatabase attachedDatabase;
|
||||||
@@ -238,9 +237,9 @@ class $UserEntityTable extends i3.UserEntity
|
|||||||
$UserEntityTable(this.attachedDatabase, [this._alias]);
|
$UserEntityTable(this.attachedDatabase, [this._alias]);
|
||||||
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
|
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
|
||||||
@override
|
@override
|
||||||
late final i0.GeneratedColumn<i2.Uint8List> id =
|
late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
|
||||||
i0.GeneratedColumn<i2.Uint8List>('id', aliasedName, false,
|
'id', aliasedName, false,
|
||||||
type: i0.DriftSqlType.blob, requiredDuringInsert: true);
|
type: i0.DriftSqlType.string, requiredDuringInsert: true);
|
||||||
static const i0.VerificationMeta _nameMeta =
|
static const i0.VerificationMeta _nameMeta =
|
||||||
const i0.VerificationMeta('name');
|
const i0.VerificationMeta('name');
|
||||||
@override
|
@override
|
||||||
@@ -256,7 +255,7 @@ class $UserEntityTable extends i3.UserEntity
|
|||||||
requiredDuringInsert: false,
|
requiredDuringInsert: false,
|
||||||
defaultConstraints:
|
defaultConstraints:
|
||||||
i0.GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'),
|
i0.GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'),
|
||||||
defaultValue: const i4.Constant(false));
|
defaultValue: const i3.Constant(false));
|
||||||
static const i0.VerificationMeta _emailMeta =
|
static const i0.VerificationMeta _emailMeta =
|
||||||
const i0.VerificationMeta('email');
|
const i0.VerificationMeta('email');
|
||||||
@override
|
@override
|
||||||
@@ -276,7 +275,7 @@ class $UserEntityTable extends i3.UserEntity
|
|||||||
i0.GeneratedColumn<DateTime>('updated_at', aliasedName, false,
|
i0.GeneratedColumn<DateTime>('updated_at', aliasedName, false,
|
||||||
type: i0.DriftSqlType.dateTime,
|
type: i0.DriftSqlType.dateTime,
|
||||||
requiredDuringInsert: false,
|
requiredDuringInsert: false,
|
||||||
defaultValue: i4.currentDateAndTime);
|
defaultValue: i3.currentDateAndTime);
|
||||||
static const i0.VerificationMeta _quotaSizeInBytesMeta =
|
static const i0.VerificationMeta _quotaSizeInBytesMeta =
|
||||||
const i0.VerificationMeta('quotaSizeInBytes');
|
const i0.VerificationMeta('quotaSizeInBytes');
|
||||||
@override
|
@override
|
||||||
@@ -290,7 +289,7 @@ class $UserEntityTable extends i3.UserEntity
|
|||||||
i0.GeneratedColumn<int>('quota_usage_in_bytes', aliasedName, false,
|
i0.GeneratedColumn<int>('quota_usage_in_bytes', aliasedName, false,
|
||||||
type: i0.DriftSqlType.int,
|
type: i0.DriftSqlType.int,
|
||||||
requiredDuringInsert: false,
|
requiredDuringInsert: false,
|
||||||
defaultValue: const i4.Constant(0));
|
defaultValue: const i3.Constant(0));
|
||||||
@override
|
@override
|
||||||
List<i0.GeneratedColumn> get $columns => [
|
List<i0.GeneratedColumn> get $columns => [
|
||||||
id,
|
id,
|
||||||
@@ -366,7 +365,7 @@ class $UserEntityTable extends i3.UserEntity
|
|||||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||||
return i1.UserEntityData(
|
return i1.UserEntityData(
|
||||||
id: attachedDatabase.typeMapping
|
id: attachedDatabase.typeMapping
|
||||||
.read(i0.DriftSqlType.blob, data['${effectivePrefix}id'])!,
|
.read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!,
|
||||||
name: attachedDatabase.typeMapping
|
name: attachedDatabase.typeMapping
|
||||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!,
|
.read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!,
|
||||||
isAdmin: attachedDatabase.typeMapping
|
isAdmin: attachedDatabase.typeMapping
|
||||||
@@ -397,7 +396,7 @@ class $UserEntityTable extends i3.UserEntity
|
|||||||
|
|
||||||
class UserEntityData extends i0.DataClass
|
class UserEntityData extends i0.DataClass
|
||||||
implements i0.Insertable<i1.UserEntityData> {
|
implements i0.Insertable<i1.UserEntityData> {
|
||||||
final i2.Uint8List id;
|
final String id;
|
||||||
final String name;
|
final String name;
|
||||||
final bool isAdmin;
|
final bool isAdmin;
|
||||||
final String email;
|
final String email;
|
||||||
@@ -417,7 +416,7 @@ class UserEntityData extends i0.DataClass
|
|||||||
@override
|
@override
|
||||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||||
final map = <String, i0.Expression>{};
|
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['name'] = i0.Variable<String>(name);
|
||||||
map['is_admin'] = i0.Variable<bool>(isAdmin);
|
map['is_admin'] = i0.Variable<bool>(isAdmin);
|
||||||
map['email'] = i0.Variable<String>(email);
|
map['email'] = i0.Variable<String>(email);
|
||||||
@@ -436,7 +435,7 @@ class UserEntityData extends i0.DataClass
|
|||||||
{i0.ValueSerializer? serializer}) {
|
{i0.ValueSerializer? serializer}) {
|
||||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||||
return UserEntityData(
|
return UserEntityData(
|
||||||
id: serializer.fromJson<i2.Uint8List>(json['id']),
|
id: serializer.fromJson<String>(json['id']),
|
||||||
name: serializer.fromJson<String>(json['name']),
|
name: serializer.fromJson<String>(json['name']),
|
||||||
isAdmin: serializer.fromJson<bool>(json['isAdmin']),
|
isAdmin: serializer.fromJson<bool>(json['isAdmin']),
|
||||||
email: serializer.fromJson<String>(json['email']),
|
email: serializer.fromJson<String>(json['email']),
|
||||||
@@ -450,7 +449,7 @@ class UserEntityData extends i0.DataClass
|
|||||||
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
||||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||||
return <String, dynamic>{
|
return <String, dynamic>{
|
||||||
'id': serializer.toJson<i2.Uint8List>(id),
|
'id': serializer.toJson<String>(id),
|
||||||
'name': serializer.toJson<String>(name),
|
'name': serializer.toJson<String>(name),
|
||||||
'isAdmin': serializer.toJson<bool>(isAdmin),
|
'isAdmin': serializer.toJson<bool>(isAdmin),
|
||||||
'email': serializer.toJson<String>(email),
|
'email': serializer.toJson<String>(email),
|
||||||
@@ -462,7 +461,7 @@ class UserEntityData extends i0.DataClass
|
|||||||
}
|
}
|
||||||
|
|
||||||
i1.UserEntityData copyWith(
|
i1.UserEntityData copyWith(
|
||||||
{i2.Uint8List? id,
|
{String? id,
|
||||||
String? name,
|
String? name,
|
||||||
bool? isAdmin,
|
bool? isAdmin,
|
||||||
String? email,
|
String? email,
|
||||||
@@ -519,13 +518,13 @@ class UserEntityData extends i0.DataClass
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(i0.$driftBlobEquality.hash(id), name, isAdmin,
|
int get hashCode => Object.hash(id, name, isAdmin, email, profileImagePath,
|
||||||
email, profileImagePath, updatedAt, quotaSizeInBytes, quotaUsageInBytes);
|
updatedAt, quotaSizeInBytes, quotaUsageInBytes);
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) ||
|
||||||
(other is i1.UserEntityData &&
|
(other is i1.UserEntityData &&
|
||||||
i0.$driftBlobEquality.equals(other.id, this.id) &&
|
other.id == this.id &&
|
||||||
other.name == this.name &&
|
other.name == this.name &&
|
||||||
other.isAdmin == this.isAdmin &&
|
other.isAdmin == this.isAdmin &&
|
||||||
other.email == this.email &&
|
other.email == this.email &&
|
||||||
@@ -536,7 +535,7 @@ class UserEntityData extends i0.DataClass
|
|||||||
}
|
}
|
||||||
|
|
||||||
class UserEntityCompanion extends i0.UpdateCompanion<i1.UserEntityData> {
|
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<String> name;
|
||||||
final i0.Value<bool> isAdmin;
|
final i0.Value<bool> isAdmin;
|
||||||
final i0.Value<String> email;
|
final i0.Value<String> email;
|
||||||
@@ -555,7 +554,7 @@ class UserEntityCompanion extends i0.UpdateCompanion<i1.UserEntityData> {
|
|||||||
this.quotaUsageInBytes = const i0.Value.absent(),
|
this.quotaUsageInBytes = const i0.Value.absent(),
|
||||||
});
|
});
|
||||||
UserEntityCompanion.insert({
|
UserEntityCompanion.insert({
|
||||||
required i2.Uint8List id,
|
required String id,
|
||||||
required String name,
|
required String name,
|
||||||
this.isAdmin = const i0.Value.absent(),
|
this.isAdmin = const i0.Value.absent(),
|
||||||
required String email,
|
required String email,
|
||||||
@@ -567,7 +566,7 @@ class UserEntityCompanion extends i0.UpdateCompanion<i1.UserEntityData> {
|
|||||||
name = i0.Value(name),
|
name = i0.Value(name),
|
||||||
email = i0.Value(email);
|
email = i0.Value(email);
|
||||||
static i0.Insertable<i1.UserEntityData> custom({
|
static i0.Insertable<i1.UserEntityData> custom({
|
||||||
i0.Expression<i2.Uint8List>? id,
|
i0.Expression<String>? id,
|
||||||
i0.Expression<String>? name,
|
i0.Expression<String>? name,
|
||||||
i0.Expression<bool>? isAdmin,
|
i0.Expression<bool>? isAdmin,
|
||||||
i0.Expression<String>? email,
|
i0.Expression<String>? email,
|
||||||
@@ -589,7 +588,7 @@ class UserEntityCompanion extends i0.UpdateCompanion<i1.UserEntityData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
i1.UserEntityCompanion copyWith(
|
i1.UserEntityCompanion copyWith(
|
||||||
{i0.Value<i2.Uint8List>? id,
|
{i0.Value<String>? id,
|
||||||
i0.Value<String>? name,
|
i0.Value<String>? name,
|
||||||
i0.Value<bool>? isAdmin,
|
i0.Value<bool>? isAdmin,
|
||||||
i0.Value<String>? email,
|
i0.Value<String>? email,
|
||||||
@@ -613,7 +612,7 @@ class UserEntityCompanion extends i0.UpdateCompanion<i1.UserEntityData> {
|
|||||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||||
final map = <String, i0.Expression>{};
|
final map = <String, i0.Expression>{};
|
||||||
if (id.present) {
|
if (id.present) {
|
||||||
map['id'] = i0.Variable<i2.Uint8List>(id.value);
|
map['id'] = i0.Variable<String>(id.value);
|
||||||
}
|
}
|
||||||
if (name.present) {
|
if (name.present) {
|
||||||
map['name'] = i0.Variable<String>(name.value);
|
map['name'] = i0.Variable<String>(name.value);
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
|||||||
class UserMetadataEntity extends Table with DriftDefaultsMixin {
|
class UserMetadataEntity extends Table with DriftDefaultsMixin {
|
||||||
const UserMetadataEntity();
|
const UserMetadataEntity();
|
||||||
|
|
||||||
BlobColumn get userId =>
|
TextColumn get userId =>
|
||||||
blob().references(UserEntity, #id, onDelete: KeyAction.cascade)();
|
text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
|
||||||
TextColumn get preferences => text().map(userPreferenceConverter)();
|
TextColumn get preferences => text().map(userPreferenceConverter)();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -3,23 +3,22 @@
|
|||||||
import 'package:drift/drift.dart' as i0;
|
import 'package:drift/drift.dart' as i0;
|
||||||
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart'
|
||||||
as i1;
|
as i1;
|
||||||
import 'dart:typed_data' as i2;
|
import 'package:immich_mobile/domain/models/user_metadata.model.dart' as i2;
|
||||||
import 'package:immich_mobile/domain/models/user_metadata.model.dart' as i3;
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart'
|
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart'
|
||||||
as i4;
|
as i3;
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
|
||||||
as i5;
|
as i4;
|
||||||
import 'package:drift/internal/modular.dart' as i6;
|
import 'package:drift/internal/modular.dart' as i5;
|
||||||
|
|
||||||
typedef $$UserMetadataEntityTableCreateCompanionBuilder
|
typedef $$UserMetadataEntityTableCreateCompanionBuilder
|
||||||
= i1.UserMetadataEntityCompanion Function({
|
= i1.UserMetadataEntityCompanion Function({
|
||||||
required i2.Uint8List userId,
|
required String userId,
|
||||||
required i3.UserPreferences preferences,
|
required i2.UserPreferences preferences,
|
||||||
});
|
});
|
||||||
typedef $$UserMetadataEntityTableUpdateCompanionBuilder
|
typedef $$UserMetadataEntityTableUpdateCompanionBuilder
|
||||||
= i1.UserMetadataEntityCompanion Function({
|
= i1.UserMetadataEntityCompanion Function({
|
||||||
i0.Value<i2.Uint8List> userId,
|
i0.Value<String> userId,
|
||||||
i0.Value<i3.UserPreferences> preferences,
|
i0.Value<i2.UserPreferences> preferences,
|
||||||
});
|
});
|
||||||
|
|
||||||
final class $$UserMetadataEntityTableReferences extends i0.BaseReferences<
|
final class $$UserMetadataEntityTableReferences extends i0.BaseReferences<
|
||||||
@@ -29,26 +28,26 @@ final class $$UserMetadataEntityTableReferences extends i0.BaseReferences<
|
|||||||
$$UserMetadataEntityTableReferences(
|
$$UserMetadataEntityTableReferences(
|
||||||
super.$_db, super.$_table, super.$_typedResult);
|
super.$_db, super.$_table, super.$_typedResult);
|
||||||
|
|
||||||
static i5.$UserEntityTable _userIdTable(i0.GeneratedDatabase db) =>
|
static i4.$UserEntityTable _userIdTable(i0.GeneratedDatabase db) =>
|
||||||
i6.ReadDatabaseContainer(db)
|
i5.ReadDatabaseContainer(db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity')
|
.resultSet<i4.$UserEntityTable>('user_entity')
|
||||||
.createAlias(i0.$_aliasNameGenerator(
|
.createAlias(i0.$_aliasNameGenerator(
|
||||||
i6.ReadDatabaseContainer(db)
|
i5.ReadDatabaseContainer(db)
|
||||||
.resultSet<i1.$UserMetadataEntityTable>(
|
.resultSet<i1.$UserMetadataEntityTable>(
|
||||||
'user_metadata_entity')
|
'user_metadata_entity')
|
||||||
.userId,
|
.userId,
|
||||||
i6.ReadDatabaseContainer(db)
|
i5.ReadDatabaseContainer(db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity')
|
.resultSet<i4.$UserEntityTable>('user_entity')
|
||||||
.id));
|
.id));
|
||||||
|
|
||||||
i5.$$UserEntityTableProcessedTableManager get userId {
|
i4.$$UserEntityTableProcessedTableManager get userId {
|
||||||
final $_column = $_itemColumn<i2.Uint8List>('user_id')!;
|
final $_column = $_itemColumn<String>('user_id')!;
|
||||||
|
|
||||||
final manager = i5
|
final manager = i4
|
||||||
.$$UserEntityTableTableManager(
|
.$$UserEntityTableTableManager(
|
||||||
$_db,
|
$_db,
|
||||||
i6.ReadDatabaseContainer($_db)
|
i5.ReadDatabaseContainer($_db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'))
|
.resultSet<i4.$UserEntityTable>('user_entity'))
|
||||||
.filter((f) => f.id.sqlEquals($_column));
|
.filter((f) => f.id.sqlEquals($_column));
|
||||||
final item = $_typedResult.readTableOrNull(_userIdTable($_db));
|
final item = $_typedResult.readTableOrNull(_userIdTable($_db));
|
||||||
if (item == null) return manager;
|
if (item == null) return manager;
|
||||||
@@ -66,26 +65,26 @@ class $$UserMetadataEntityTableFilterComposer
|
|||||||
super.$addJoinBuilderToRootComposer,
|
super.$addJoinBuilderToRootComposer,
|
||||||
super.$removeJoinBuilderFromRootComposer,
|
super.$removeJoinBuilderFromRootComposer,
|
||||||
});
|
});
|
||||||
i0.ColumnWithTypeConverterFilters<i3.UserPreferences, i3.UserPreferences,
|
i0.ColumnWithTypeConverterFilters<i2.UserPreferences, i2.UserPreferences,
|
||||||
String>
|
String>
|
||||||
get preferences => $composableBuilder(
|
get preferences => $composableBuilder(
|
||||||
column: $table.preferences,
|
column: $table.preferences,
|
||||||
builder: (column) => i0.ColumnWithTypeConverterFilters(column));
|
builder: (column) => i0.ColumnWithTypeConverterFilters(column));
|
||||||
|
|
||||||
i5.$$UserEntityTableFilterComposer get userId {
|
i4.$$UserEntityTableFilterComposer get userId {
|
||||||
final i5.$$UserEntityTableFilterComposer composer = $composerBuilder(
|
final i4.$$UserEntityTableFilterComposer composer = $composerBuilder(
|
||||||
composer: this,
|
composer: this,
|
||||||
getCurrentColumn: (t) => t.userId,
|
getCurrentColumn: (t) => t.userId,
|
||||||
referencedTable: i6.ReadDatabaseContainer($db)
|
referencedTable: i5.ReadDatabaseContainer($db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
getReferencedColumn: (t) => t.id,
|
getReferencedColumn: (t) => t.id,
|
||||||
builder: (joinBuilder,
|
builder: (joinBuilder,
|
||||||
{$addJoinBuilderToRootComposer,
|
{$addJoinBuilderToRootComposer,
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
$removeJoinBuilderFromRootComposer}) =>
|
||||||
i5.$$UserEntityTableFilterComposer(
|
i4.$$UserEntityTableFilterComposer(
|
||||||
$db: $db,
|
$db: $db,
|
||||||
$table: i6.ReadDatabaseContainer($db)
|
$table: i5.ReadDatabaseContainer($db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
joinBuilder: joinBuilder,
|
joinBuilder: joinBuilder,
|
||||||
$removeJoinBuilderFromRootComposer:
|
$removeJoinBuilderFromRootComposer:
|
||||||
@@ -108,20 +107,20 @@ class $$UserMetadataEntityTableOrderingComposer
|
|||||||
column: $table.preferences,
|
column: $table.preferences,
|
||||||
builder: (column) => i0.ColumnOrderings(column));
|
builder: (column) => i0.ColumnOrderings(column));
|
||||||
|
|
||||||
i5.$$UserEntityTableOrderingComposer get userId {
|
i4.$$UserEntityTableOrderingComposer get userId {
|
||||||
final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder(
|
final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder(
|
||||||
composer: this,
|
composer: this,
|
||||||
getCurrentColumn: (t) => t.userId,
|
getCurrentColumn: (t) => t.userId,
|
||||||
referencedTable: i6.ReadDatabaseContainer($db)
|
referencedTable: i5.ReadDatabaseContainer($db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
getReferencedColumn: (t) => t.id,
|
getReferencedColumn: (t) => t.id,
|
||||||
builder: (joinBuilder,
|
builder: (joinBuilder,
|
||||||
{$addJoinBuilderToRootComposer,
|
{$addJoinBuilderToRootComposer,
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
$removeJoinBuilderFromRootComposer}) =>
|
||||||
i5.$$UserEntityTableOrderingComposer(
|
i4.$$UserEntityTableOrderingComposer(
|
||||||
$db: $db,
|
$db: $db,
|
||||||
$table: i6.ReadDatabaseContainer($db)
|
$table: i5.ReadDatabaseContainer($db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
joinBuilder: joinBuilder,
|
joinBuilder: joinBuilder,
|
||||||
$removeJoinBuilderFromRootComposer:
|
$removeJoinBuilderFromRootComposer:
|
||||||
@@ -140,24 +139,24 @@ class $$UserMetadataEntityTableAnnotationComposer
|
|||||||
super.$addJoinBuilderToRootComposer,
|
super.$addJoinBuilderToRootComposer,
|
||||||
super.$removeJoinBuilderFromRootComposer,
|
super.$removeJoinBuilderFromRootComposer,
|
||||||
});
|
});
|
||||||
i0.GeneratedColumnWithTypeConverter<i3.UserPreferences, String>
|
i0.GeneratedColumnWithTypeConverter<i2.UserPreferences, String>
|
||||||
get preferences => $composableBuilder(
|
get preferences => $composableBuilder(
|
||||||
column: $table.preferences, builder: (column) => column);
|
column: $table.preferences, builder: (column) => column);
|
||||||
|
|
||||||
i5.$$UserEntityTableAnnotationComposer get userId {
|
i4.$$UserEntityTableAnnotationComposer get userId {
|
||||||
final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
|
final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
|
||||||
composer: this,
|
composer: this,
|
||||||
getCurrentColumn: (t) => t.userId,
|
getCurrentColumn: (t) => t.userId,
|
||||||
referencedTable: i6.ReadDatabaseContainer($db)
|
referencedTable: i5.ReadDatabaseContainer($db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
getReferencedColumn: (t) => t.id,
|
getReferencedColumn: (t) => t.id,
|
||||||
builder: (joinBuilder,
|
builder: (joinBuilder,
|
||||||
{$addJoinBuilderToRootComposer,
|
{$addJoinBuilderToRootComposer,
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
$removeJoinBuilderFromRootComposer}) =>
|
||||||
i5.$$UserEntityTableAnnotationComposer(
|
i4.$$UserEntityTableAnnotationComposer(
|
||||||
$db: $db,
|
$db: $db,
|
||||||
$table: i6.ReadDatabaseContainer($db)
|
$table: i5.ReadDatabaseContainer($db)
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
joinBuilder: joinBuilder,
|
joinBuilder: joinBuilder,
|
||||||
$removeJoinBuilderFromRootComposer:
|
$removeJoinBuilderFromRootComposer:
|
||||||
@@ -193,16 +192,16 @@ class $$UserMetadataEntityTableTableManager extends i0.RootTableManager<
|
|||||||
i1.$$UserMetadataEntityTableAnnotationComposer(
|
i1.$$UserMetadataEntityTableAnnotationComposer(
|
||||||
$db: db, $table: table),
|
$db: db, $table: table),
|
||||||
updateCompanionCallback: ({
|
updateCompanionCallback: ({
|
||||||
i0.Value<i2.Uint8List> userId = const i0.Value.absent(),
|
i0.Value<String> userId = const i0.Value.absent(),
|
||||||
i0.Value<i3.UserPreferences> preferences = const i0.Value.absent(),
|
i0.Value<i2.UserPreferences> preferences = const i0.Value.absent(),
|
||||||
}) =>
|
}) =>
|
||||||
i1.UserMetadataEntityCompanion(
|
i1.UserMetadataEntityCompanion(
|
||||||
userId: userId,
|
userId: userId,
|
||||||
preferences: preferences,
|
preferences: preferences,
|
||||||
),
|
),
|
||||||
createCompanionCallback: ({
|
createCompanionCallback: ({
|
||||||
required i2.Uint8List userId,
|
required String userId,
|
||||||
required i3.UserPreferences preferences,
|
required i2.UserPreferences preferences,
|
||||||
}) =>
|
}) =>
|
||||||
i1.UserMetadataEntityCompanion.insert(
|
i1.UserMetadataEntityCompanion.insert(
|
||||||
userId: userId,
|
userId: userId,
|
||||||
@@ -267,7 +266,7 @@ typedef $$UserMetadataEntityTableProcessedTableManager
|
|||||||
i1.UserMetadataEntityData,
|
i1.UserMetadataEntityData,
|
||||||
i0.PrefetchHooks Function({bool userId})>;
|
i0.PrefetchHooks Function({bool userId})>;
|
||||||
|
|
||||||
class $UserMetadataEntityTable extends i4.UserMetadataEntity
|
class $UserMetadataEntityTable extends i3.UserMetadataEntity
|
||||||
with i0.TableInfo<$UserMetadataEntityTable, i1.UserMetadataEntityData> {
|
with i0.TableInfo<$UserMetadataEntityTable, i1.UserMetadataEntityData> {
|
||||||
@override
|
@override
|
||||||
final i0.GeneratedDatabase attachedDatabase;
|
final i0.GeneratedDatabase attachedDatabase;
|
||||||
@@ -276,18 +275,18 @@ class $UserMetadataEntityTable extends i4.UserMetadataEntity
|
|||||||
static const i0.VerificationMeta _userIdMeta =
|
static const i0.VerificationMeta _userIdMeta =
|
||||||
const i0.VerificationMeta('userId');
|
const i0.VerificationMeta('userId');
|
||||||
@override
|
@override
|
||||||
late final i0.GeneratedColumn<i2.Uint8List> userId =
|
late final i0.GeneratedColumn<String> userId = i0.GeneratedColumn<String>(
|
||||||
i0.GeneratedColumn<i2.Uint8List>('user_id', aliasedName, false,
|
'user_id', aliasedName, false,
|
||||||
type: i0.DriftSqlType.blob,
|
type: i0.DriftSqlType.string,
|
||||||
requiredDuringInsert: true,
|
requiredDuringInsert: true,
|
||||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||||
'REFERENCES user_entity (id) ON DELETE CASCADE'));
|
'REFERENCES user_entity (id) ON DELETE CASCADE'));
|
||||||
@override
|
@override
|
||||||
late final i0.GeneratedColumnWithTypeConverter<i3.UserPreferences, String>
|
late final i0.GeneratedColumnWithTypeConverter<i2.UserPreferences, String>
|
||||||
preferences = i0.GeneratedColumn<String>(
|
preferences = i0.GeneratedColumn<String>(
|
||||||
'preferences', aliasedName, false,
|
'preferences', aliasedName, false,
|
||||||
type: i0.DriftSqlType.string, requiredDuringInsert: true)
|
type: i0.DriftSqlType.string, requiredDuringInsert: true)
|
||||||
.withConverter<i3.UserPreferences>(
|
.withConverter<i2.UserPreferences>(
|
||||||
i1.$UserMetadataEntityTable.$converterpreferences);
|
i1.$UserMetadataEntityTable.$converterpreferences);
|
||||||
@override
|
@override
|
||||||
List<i0.GeneratedColumn> get $columns => [userId, preferences];
|
List<i0.GeneratedColumn> get $columns => [userId, preferences];
|
||||||
@@ -319,7 +318,7 @@ class $UserMetadataEntityTable extends i4.UserMetadataEntity
|
|||||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||||
return i1.UserMetadataEntityData(
|
return i1.UserMetadataEntityData(
|
||||||
userId: attachedDatabase.typeMapping
|
userId: attachedDatabase.typeMapping
|
||||||
.read(i0.DriftSqlType.blob, data['${effectivePrefix}user_id'])!,
|
.read(i0.DriftSqlType.string, data['${effectivePrefix}user_id'])!,
|
||||||
preferences: i1.$UserMetadataEntityTable.$converterpreferences.fromSql(
|
preferences: i1.$UserMetadataEntityTable.$converterpreferences.fromSql(
|
||||||
attachedDatabase.typeMapping.read(
|
attachedDatabase.typeMapping.read(
|
||||||
i0.DriftSqlType.string, data['${effectivePrefix}preferences'])!),
|
i0.DriftSqlType.string, data['${effectivePrefix}preferences'])!),
|
||||||
@@ -331,8 +330,8 @@ class $UserMetadataEntityTable extends i4.UserMetadataEntity
|
|||||||
return $UserMetadataEntityTable(attachedDatabase, alias);
|
return $UserMetadataEntityTable(attachedDatabase, alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
static i0.JsonTypeConverter2<i3.UserPreferences, String, Object?>
|
static i0.JsonTypeConverter2<i2.UserPreferences, String, Object?>
|
||||||
$converterpreferences = i4.userPreferenceConverter;
|
$converterpreferences = i3.userPreferenceConverter;
|
||||||
@override
|
@override
|
||||||
bool get withoutRowId => true;
|
bool get withoutRowId => true;
|
||||||
@override
|
@override
|
||||||
@@ -341,14 +340,14 @@ class $UserMetadataEntityTable extends i4.UserMetadataEntity
|
|||||||
|
|
||||||
class UserMetadataEntityData extends i0.DataClass
|
class UserMetadataEntityData extends i0.DataClass
|
||||||
implements i0.Insertable<i1.UserMetadataEntityData> {
|
implements i0.Insertable<i1.UserMetadataEntityData> {
|
||||||
final i2.Uint8List userId;
|
final String userId;
|
||||||
final i3.UserPreferences preferences;
|
final i2.UserPreferences preferences;
|
||||||
const UserMetadataEntityData(
|
const UserMetadataEntityData(
|
||||||
{required this.userId, required this.preferences});
|
{required this.userId, required this.preferences});
|
||||||
@override
|
@override
|
||||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||||
final map = <String, i0.Expression>{};
|
final map = <String, i0.Expression>{};
|
||||||
map['user_id'] = i0.Variable<i2.Uint8List>(userId);
|
map['user_id'] = i0.Variable<String>(userId);
|
||||||
{
|
{
|
||||||
map['preferences'] = i0.Variable<String>(
|
map['preferences'] = i0.Variable<String>(
|
||||||
i1.$UserMetadataEntityTable.$converterpreferences.toSql(preferences));
|
i1.$UserMetadataEntityTable.$converterpreferences.toSql(preferences));
|
||||||
@@ -360,7 +359,7 @@ class UserMetadataEntityData extends i0.DataClass
|
|||||||
{i0.ValueSerializer? serializer}) {
|
{i0.ValueSerializer? serializer}) {
|
||||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||||
return UserMetadataEntityData(
|
return UserMetadataEntityData(
|
||||||
userId: serializer.fromJson<i2.Uint8List>(json['userId']),
|
userId: serializer.fromJson<String>(json['userId']),
|
||||||
preferences: i1.$UserMetadataEntityTable.$converterpreferences
|
preferences: i1.$UserMetadataEntityTable.$converterpreferences
|
||||||
.fromJson(serializer.fromJson<Object?>(json['preferences'])),
|
.fromJson(serializer.fromJson<Object?>(json['preferences'])),
|
||||||
);
|
);
|
||||||
@@ -369,7 +368,7 @@ class UserMetadataEntityData extends i0.DataClass
|
|||||||
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
||||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||||
return <String, dynamic>{
|
return <String, dynamic>{
|
||||||
'userId': serializer.toJson<i2.Uint8List>(userId),
|
'userId': serializer.toJson<String>(userId),
|
||||||
'preferences': serializer.toJson<Object?>(i1
|
'preferences': serializer.toJson<Object?>(i1
|
||||||
.$UserMetadataEntityTable.$converterpreferences
|
.$UserMetadataEntityTable.$converterpreferences
|
||||||
.toJson(preferences)),
|
.toJson(preferences)),
|
||||||
@@ -377,7 +376,7 @@ class UserMetadataEntityData extends i0.DataClass
|
|||||||
}
|
}
|
||||||
|
|
||||||
i1.UserMetadataEntityData copyWith(
|
i1.UserMetadataEntityData copyWith(
|
||||||
{i2.Uint8List? userId, i3.UserPreferences? preferences}) =>
|
{String? userId, i2.UserPreferences? preferences}) =>
|
||||||
i1.UserMetadataEntityData(
|
i1.UserMetadataEntityData(
|
||||||
userId: userId ?? this.userId,
|
userId: userId ?? this.userId,
|
||||||
preferences: preferences ?? this.preferences,
|
preferences: preferences ?? this.preferences,
|
||||||
@@ -401,31 +400,30 @@ class UserMetadataEntityData extends i0.DataClass
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode => Object.hash(userId, preferences);
|
||||||
Object.hash(i0.$driftBlobEquality.hash(userId), preferences);
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) ||
|
||||||
(other is i1.UserMetadataEntityData &&
|
(other is i1.UserMetadataEntityData &&
|
||||||
i0.$driftBlobEquality.equals(other.userId, this.userId) &&
|
other.userId == this.userId &&
|
||||||
other.preferences == this.preferences);
|
other.preferences == this.preferences);
|
||||||
}
|
}
|
||||||
|
|
||||||
class UserMetadataEntityCompanion
|
class UserMetadataEntityCompanion
|
||||||
extends i0.UpdateCompanion<i1.UserMetadataEntityData> {
|
extends i0.UpdateCompanion<i1.UserMetadataEntityData> {
|
||||||
final i0.Value<i2.Uint8List> userId;
|
final i0.Value<String> userId;
|
||||||
final i0.Value<i3.UserPreferences> preferences;
|
final i0.Value<i2.UserPreferences> preferences;
|
||||||
const UserMetadataEntityCompanion({
|
const UserMetadataEntityCompanion({
|
||||||
this.userId = const i0.Value.absent(),
|
this.userId = const i0.Value.absent(),
|
||||||
this.preferences = const i0.Value.absent(),
|
this.preferences = const i0.Value.absent(),
|
||||||
});
|
});
|
||||||
UserMetadataEntityCompanion.insert({
|
UserMetadataEntityCompanion.insert({
|
||||||
required i2.Uint8List userId,
|
required String userId,
|
||||||
required i3.UserPreferences preferences,
|
required i2.UserPreferences preferences,
|
||||||
}) : userId = i0.Value(userId),
|
}) : userId = i0.Value(userId),
|
||||||
preferences = i0.Value(preferences);
|
preferences = i0.Value(preferences);
|
||||||
static i0.Insertable<i1.UserMetadataEntityData> custom({
|
static i0.Insertable<i1.UserMetadataEntityData> custom({
|
||||||
i0.Expression<i2.Uint8List>? userId,
|
i0.Expression<String>? userId,
|
||||||
i0.Expression<String>? preferences,
|
i0.Expression<String>? preferences,
|
||||||
}) {
|
}) {
|
||||||
return i0.RawValuesInsertable({
|
return i0.RawValuesInsertable({
|
||||||
@@ -435,8 +433,7 @@ class UserMetadataEntityCompanion
|
|||||||
}
|
}
|
||||||
|
|
||||||
i1.UserMetadataEntityCompanion copyWith(
|
i1.UserMetadataEntityCompanion copyWith(
|
||||||
{i0.Value<i2.Uint8List>? userId,
|
{i0.Value<String>? userId, i0.Value<i2.UserPreferences>? preferences}) {
|
||||||
i0.Value<i3.UserPreferences>? preferences}) {
|
|
||||||
return i1.UserMetadataEntityCompanion(
|
return i1.UserMetadataEntityCompanion(
|
||||||
userId: userId ?? this.userId,
|
userId: userId ?? this.userId,
|
||||||
preferences: preferences ?? this.preferences,
|
preferences: preferences ?? this.preferences,
|
||||||
@@ -447,7 +444,7 @@ class UserMetadataEntityCompanion
|
|||||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||||
final map = <String, i0.Expression>{};
|
final map = <String, i0.Expression>{};
|
||||||
if (userId.present) {
|
if (userId.present) {
|
||||||
map['user_id'] = i0.Variable<i2.Uint8List>(userId.value);
|
map['user_id'] = i0.Variable<String>(userId.value);
|
||||||
}
|
}
|
||||||
if (preferences.present) {
|
if (preferences.present) {
|
||||||
map['preferences'] = i0.Variable<String>(i1
|
map['preferences'] = i0.Variable<String>(i1
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ import 'dart:async';
|
|||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:drift_flutter/drift_flutter.dart';
|
import 'package:drift_flutter/drift_flutter.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/partner.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/partner.entity.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/entities/user.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
@@ -36,6 +38,8 @@ class IsarDatabaseRepository implements IDatabaseRepository {
|
|||||||
LocalAlbumEntity,
|
LocalAlbumEntity,
|
||||||
LocalAssetEntity,
|
LocalAssetEntity,
|
||||||
LocalAlbumAssetEntity,
|
LocalAlbumAssetEntity,
|
||||||
|
RemoteAssetEntity,
|
||||||
|
RemoteExifEntity,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
class Drift extends $Drift implements IDatabaseRepository {
|
class Drift extends $Drift implements IDatabaseRepository {
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.d
|
|||||||
as i5;
|
as i5;
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart'
|
||||||
as i6;
|
as i6;
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'
|
||||||
|
as i7;
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'
|
||||||
|
as i8;
|
||||||
|
|
||||||
abstract class $Drift extends i0.GeneratedDatabase {
|
abstract class $Drift extends i0.GeneratedDatabase {
|
||||||
$Drift(i0.QueryExecutor e) : super(e);
|
$Drift(i0.QueryExecutor e) : super(e);
|
||||||
@@ -28,6 +32,10 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
|||||||
i5.$LocalAssetEntityTable(this);
|
i5.$LocalAssetEntityTable(this);
|
||||||
late final i6.$LocalAlbumAssetEntityTable localAlbumAssetEntity =
|
late final i6.$LocalAlbumAssetEntityTable localAlbumAssetEntity =
|
||||||
i6.$LocalAlbumAssetEntityTable(this);
|
i6.$LocalAlbumAssetEntityTable(this);
|
||||||
|
late final i7.$RemoteAssetEntityTable remoteAssetEntity =
|
||||||
|
i7.$RemoteAssetEntityTable(this);
|
||||||
|
late final i8.$RemoteExifEntityTable remoteExifEntity =
|
||||||
|
i8.$RemoteExifEntityTable(this);
|
||||||
@override
|
@override
|
||||||
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
|
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
|
||||||
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
|
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
|
||||||
@@ -39,7 +47,10 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
|||||||
localAlbumEntity,
|
localAlbumEntity,
|
||||||
localAssetEntity,
|
localAssetEntity,
|
||||||
localAlbumAssetEntity,
|
localAlbumAssetEntity,
|
||||||
i5.localAssetChecksum
|
remoteAssetEntity,
|
||||||
|
remoteExifEntity,
|
||||||
|
i5.idxLocalAssetChecksum,
|
||||||
|
i7.uQRemoteAssetOwnerChecksum
|
||||||
];
|
];
|
||||||
@override
|
@override
|
||||||
i0.StreamQueryUpdateRules get streamUpdateRules =>
|
i0.StreamQueryUpdateRules get streamUpdateRules =>
|
||||||
@@ -83,6 +94,20 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
|||||||
kind: i0.UpdateKind.delete),
|
kind: i0.UpdateKind.delete),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
i0.WritePropagation(
|
||||||
|
on: i0.TableUpdateQuery.onTableName('user_entity',
|
||||||
|
limitUpdateKind: i0.UpdateKind.delete),
|
||||||
|
result: [
|
||||||
|
i0.TableUpdate('remote_asset_entity', kind: i0.UpdateKind.delete),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
i0.WritePropagation(
|
||||||
|
on: i0.TableUpdateQuery.onTableName('remote_asset_entity',
|
||||||
|
limitUpdateKind: i0.UpdateKind.delete),
|
||||||
|
result: [
|
||||||
|
i0.TableUpdate('remote_exif_entity', kind: i0.UpdateKind.delete),
|
||||||
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@override
|
@override
|
||||||
@@ -105,4 +130,8 @@ class $DriftManager {
|
|||||||
i5.$$LocalAssetEntityTableTableManager(_db, _db.localAssetEntity);
|
i5.$$LocalAssetEntityTableTableManager(_db, _db.localAssetEntity);
|
||||||
i6.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i6
|
i6.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i6
|
||||||
.$$LocalAlbumAssetEntityTableTableManager(_db, _db.localAlbumAssetEntity);
|
.$$LocalAlbumAssetEntityTableTableManager(_db, _db.localAlbumAssetEntity);
|
||||||
|
i7.$$RemoteAssetEntityTableTableManager get remoteAssetEntity =>
|
||||||
|
i7.$$RemoteAssetEntityTableTableManager(_db, _db.remoteAssetEntity);
|
||||||
|
i8.$$RemoteExifEntityTableTableManager get remoteExifEntity =>
|
||||||
|
i8.$$RemoteExifEntityTableTableManager(_db, _db.remoteExifEntity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,12 +98,24 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
|||||||
name: localAlbum.name,
|
name: localAlbum.name,
|
||||||
updatedAt: Value(localAlbum.updatedAt),
|
updatedAt: Value(localAlbum.updatedAt),
|
||||||
backupSelection: localAlbum.backupSelection,
|
backupSelection: localAlbum.backupSelection,
|
||||||
|
isIosSharedAlbum: Value(localAlbum.isIosSharedAlbum),
|
||||||
);
|
);
|
||||||
|
|
||||||
return _db.transaction(() async {
|
return _db.transaction(() async {
|
||||||
await _db.localAlbumEntity
|
await _db.localAlbumEntity
|
||||||
.insertOne(companion, onConflict: DoUpdate((_) => companion));
|
.insertOne(companion, onConflict: DoUpdate((_) => companion));
|
||||||
await _addAssets(localAlbum.id, toUpsert);
|
if (toUpsert.isNotEmpty) {
|
||||||
|
await _upsertAssets(toUpsert);
|
||||||
|
await _db.localAlbumAssetEntity.insertAll(
|
||||||
|
toUpsert.map(
|
||||||
|
(a) => LocalAlbumAssetEntityCompanion.insert(
|
||||||
|
assetId: a.id,
|
||||||
|
albumId: localAlbum.id,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
mode: InsertMode.insertOrIgnore,
|
||||||
|
);
|
||||||
|
}
|
||||||
await _removeAssets(localAlbum.id, toDelete);
|
await _removeAssets(localAlbum.id, toDelete);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -122,6 +134,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
|||||||
name: album.name,
|
name: album.name,
|
||||||
updatedAt: Value(album.updatedAt),
|
updatedAt: Value(album.updatedAt),
|
||||||
backupSelection: album.backupSelection,
|
backupSelection: album.backupSelection,
|
||||||
|
isIosSharedAlbum: Value(album.isIosSharedAlbum),
|
||||||
marker_: const Value(null),
|
marker_: const Value(null),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -226,21 +239,52 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _addAssets(String albumId, Iterable<LocalAsset> assets) {
|
@override
|
||||||
if (assets.isEmpty) {
|
Future<List<LocalAsset>> getAssetsToHash(String albumId) {
|
||||||
|
final query = _db.localAlbumAssetEntity.select().join(
|
||||||
|
[
|
||||||
|
innerJoin(
|
||||||
|
_db.localAssetEntity,
|
||||||
|
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
..where(
|
||||||
|
_db.localAlbumAssetEntity.albumId.equals(albumId) &
|
||||||
|
_db.localAssetEntity.checksum.isNull(),
|
||||||
|
)
|
||||||
|
..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]);
|
||||||
|
|
||||||
|
return query
|
||||||
|
.map((row) => row.readTable(_db.localAssetEntity).toDto())
|
||||||
|
.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _upsertAssets(Iterable<LocalAsset> localAssets) {
|
||||||
|
if (localAssets.isEmpty) {
|
||||||
return Future.value();
|
return Future.value();
|
||||||
}
|
}
|
||||||
return transaction(() async {
|
|
||||||
await _upsertAssets(assets);
|
return _db.batch((batch) async {
|
||||||
await _db.localAlbumAssetEntity.insertAll(
|
for (final asset in localAssets) {
|
||||||
assets.map(
|
final companion = LocalAssetEntityCompanion.insert(
|
||||||
(a) => LocalAlbumAssetEntityCompanion.insert(
|
name: asset.name,
|
||||||
assetId: a.id,
|
type: asset.type,
|
||||||
albumId: albumId,
|
createdAt: Value(asset.createdAt),
|
||||||
|
updatedAt: Value(asset.updatedAt),
|
||||||
|
durationInSeconds: Value.absentIfNull(asset.durationInSeconds),
|
||||||
|
id: asset.id,
|
||||||
|
checksum: const Value(null),
|
||||||
|
);
|
||||||
|
batch.insert<$LocalAssetEntityTable, LocalAssetEntityData>(
|
||||||
|
_db.localAssetEntity,
|
||||||
|
companion,
|
||||||
|
onConflict: DoUpdate(
|
||||||
|
(_) => companion,
|
||||||
|
where: (old) => old.updatedAt.isNotValue(asset.updatedAt),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
mode: InsertMode.insertOrIgnore,
|
}
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,40 +345,14 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
|
|||||||
return query.map((row) => row.read(assetId)!).get();
|
return query.map((row) => row.read(assetId)!).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _upsertAssets(Iterable<LocalAsset> localAssets) {
|
|
||||||
if (localAssets.isEmpty) {
|
|
||||||
return Future.value();
|
|
||||||
}
|
|
||||||
|
|
||||||
return _db.batch((batch) async {
|
|
||||||
batch.insertAllOnConflictUpdate(
|
|
||||||
_db.localAssetEntity,
|
|
||||||
localAssets.map(
|
|
||||||
(a) => LocalAssetEntityCompanion.insert(
|
|
||||||
name: a.name,
|
|
||||||
type: a.type,
|
|
||||||
createdAt: Value(a.createdAt),
|
|
||||||
updatedAt: Value(a.updatedAt),
|
|
||||||
durationInSeconds: Value.absentIfNull(a.durationInSeconds),
|
|
||||||
id: a.id,
|
|
||||||
checksum: Value.absentIfNull(a.checksum),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _deleteAssets(Iterable<String> ids) {
|
Future<void> _deleteAssets(Iterable<String> ids) {
|
||||||
if (ids.isEmpty) {
|
if (ids.isEmpty) {
|
||||||
return Future.value();
|
return Future.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
return _db.batch(
|
return _db.batch((batch) {
|
||||||
(batch) => batch.deleteWhere(
|
batch.deleteWhere(_db.localAssetEntity, (f) => f.id.isIn(ids));
|
||||||
_db.localAssetEntity,
|
});
|
||||||
(f) => f.id.isIn(ids),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:immich_mobile/domain/interfaces/local_asset.interface.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
|
|
||||||
|
class DriftLocalAssetRepository extends DriftDatabaseRepository
|
||||||
|
implements ILocalAssetRepository {
|
||||||
|
final Drift _db;
|
||||||
|
const DriftLocalAssetRepository(this._db) : super(_db);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> updateHashes(Iterable<LocalAsset> hashes) {
|
||||||
|
if (hashes.isEmpty) {
|
||||||
|
return Future.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _db.batch((batch) async {
|
||||||
|
for (final asset in hashes) {
|
||||||
|
batch.update(
|
||||||
|
_db.localAssetEntity,
|
||||||
|
LocalAssetEntityCompanion(checksum: Value(asset.checksum)),
|
||||||
|
where: (e) => e.id.equals(asset.id),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:immich_mobile/domain/interfaces/storage.interface.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:photo_manager/photo_manager.dart';
|
||||||
|
|
||||||
|
class StorageRepository implements IStorageRepository {
|
||||||
|
final _log = Logger('StorageRepository');
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<File?> getFileForAsset(LocalAsset asset) async {
|
||||||
|
File? file;
|
||||||
|
try {
|
||||||
|
final entity = await AssetEntity.fromId(asset.id);
|
||||||
|
file = await entity?.originFile;
|
||||||
|
if (file == null) {
|
||||||
|
_log.warning(
|
||||||
|
"Cannot get file for asset ${asset.id}, name: ${asset.name}, created on: ${asset.createdAt}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error, stackTrace) {
|
||||||
|
_log.warning(
|
||||||
|
"Error getting file for asset ${asset.id}, name: ${asset.name}, created on: ${asset.createdAt}",
|
||||||
|
error,
|
||||||
|
stackTrace,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import 'package:http/http.dart' as http;
|
|||||||
import 'package:immich_mobile/constants/constants.dart';
|
import 'package:immich_mobile/constants/constants.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/sync_api.interface.dart';
|
import 'package:immich_mobile/domain/interfaces/sync_api.interface.dart';
|
||||||
import 'package:immich_mobile/domain/models/sync_event.model.dart';
|
import 'package:immich_mobile/domain/models/sync_event.model.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart';
|
||||||
import 'package:immich_mobile/services/api.service.dart';
|
import 'package:immich_mobile/services/api.service.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
@@ -105,6 +106,7 @@ class SyncApiRepository implements ISyncApiRepository {
|
|||||||
stopwatch.stop();
|
stopwatch.stop();
|
||||||
_logger
|
_logger
|
||||||
.info("Remote Sync completed in ${stopwatch.elapsed.inMilliseconds}ms");
|
.info("Remote Sync completed in ${stopwatch.elapsed.inMilliseconds}ms");
|
||||||
|
DLog.log("Remote Sync completed in ${stopwatch.elapsed.inMilliseconds}ms");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<SyncEvent> _parseLines(List<String> lines) {
|
List<SyncEvent> _parseLines(List<String> lines) {
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:immich_mobile/domain/interfaces/sync_stream.interface.dart';
|
import 'package:immich_mobile/domain/interfaces/sync_stream.interface.dart';
|
||||||
import 'package:immich_mobile/extensions/string_extensions.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart' as api show AssetVisibility;
|
||||||
|
import 'package:openapi/api.dart' hide AssetVisibility;
|
||||||
|
|
||||||
class DriftSyncStreamRepository extends DriftDatabaseRepository
|
class DriftSyncStreamRepository extends DriftDatabaseRepository
|
||||||
implements ISyncStreamRepository {
|
implements ISyncStreamRepository {
|
||||||
@@ -22,7 +24,7 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
for (final user in data) {
|
for (final user in data) {
|
||||||
batch.delete(
|
batch.delete(
|
||||||
_db.userEntity,
|
_db.userEntity,
|
||||||
UserEntityCompanion(id: Value(user.userId.toUuidByte())),
|
UserEntityCompanion(id: Value(user.userId)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -44,7 +46,7 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
|
|
||||||
batch.insert(
|
batch.insert(
|
||||||
_db.userEntity,
|
_db.userEntity,
|
||||||
companion.copyWith(id: Value(user.id.toUuidByte())),
|
companion.copyWith(id: Value(user.id)),
|
||||||
onConflict: DoUpdate((_) => companion),
|
onConflict: DoUpdate((_) => companion),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -63,8 +65,8 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
batch.delete(
|
batch.delete(
|
||||||
_db.partnerEntity,
|
_db.partnerEntity,
|
||||||
PartnerEntityCompanion(
|
PartnerEntityCompanion(
|
||||||
sharedById: Value(partner.sharedById.toUuidByte()),
|
sharedById: Value(partner.sharedById),
|
||||||
sharedWithId: Value(partner.sharedWithId.toUuidByte()),
|
sharedWithId: Value(partner.sharedWithId),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -86,8 +88,8 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
batch.insert(
|
batch.insert(
|
||||||
_db.partnerEntity,
|
_db.partnerEntity,
|
||||||
companion.copyWith(
|
companion.copyWith(
|
||||||
sharedById: Value(partner.sharedById.toUuidByte()),
|
sharedById: Value(partner.sharedById),
|
||||||
sharedWithId: Value(partner.sharedWithId.toUuidByte()),
|
sharedWithId: Value(partner.sharedWithId),
|
||||||
),
|
),
|
||||||
onConflict: DoUpdate((_) => companion),
|
onConflict: DoUpdate((_) => companion),
|
||||||
);
|
);
|
||||||
@@ -99,36 +101,153 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assets
|
|
||||||
@override
|
|
||||||
Future<void> updateAssetsV1(Iterable<SyncAssetV1> data) async {
|
|
||||||
debugPrint("updateAssetsV1 - ${data.length}");
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> deleteAssetsV1(Iterable<SyncAssetDeleteV1> data) async {
|
Future<void> deleteAssetsV1(Iterable<SyncAssetDeleteV1> data) async {
|
||||||
debugPrint("deleteAssetsV1 - ${data.length}");
|
try {
|
||||||
|
await _deleteAssetsV1(data);
|
||||||
|
} catch (e, s) {
|
||||||
|
_logger.severe('Error while processing deleteAssetsV1', e, s);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Partner Assets
|
|
||||||
@override
|
@override
|
||||||
Future<void> updatePartnerAssetsV1(Iterable<SyncAssetV1> data) async {
|
Future<void> updateAssetsV1(Iterable<SyncAssetV1> data) async {
|
||||||
debugPrint("updatePartnerAssetsV1 - ${data.length}");
|
try {
|
||||||
|
await _updateAssetsV1(data);
|
||||||
|
} catch (e, s) {
|
||||||
|
_logger.severe('Error while processing updateAssetsV1', e, s);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> deletePartnerAssetsV1(Iterable<SyncAssetDeleteV1> data) async {
|
Future<void> deletePartnerAssetsV1(Iterable<SyncAssetDeleteV1> data) async {
|
||||||
debugPrint("deletePartnerAssetsV1 - ${data.length}");
|
try {
|
||||||
|
await _deleteAssetsV1(data);
|
||||||
|
} catch (e, s) {
|
||||||
|
_logger.severe('Error while processing deletePartnerAssetsV1', e, s);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> updatePartnerAssetsV1(Iterable<SyncAssetV1> data) async {
|
||||||
|
try {
|
||||||
|
await _updateAssetsV1(data);
|
||||||
|
} catch (e, s) {
|
||||||
|
_logger.severe('Error while processing updatePartnerAssetsV1', e, s);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// EXIF
|
|
||||||
@override
|
@override
|
||||||
Future<void> updateAssetsExifV1(Iterable<SyncAssetExifV1> data) async {
|
Future<void> updateAssetsExifV1(Iterable<SyncAssetExifV1> data) async {
|
||||||
debugPrint("updateAssetsExifV1 - ${data.length}");
|
try {
|
||||||
|
await _updateAssetExifV1(data);
|
||||||
|
} catch (e, s) {
|
||||||
|
_logger.severe('Error while processing updateAssetsExifV1', e, s);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> updatePartnerAssetsExifV1(Iterable<SyncAssetExifV1> data) async {
|
Future<void> updatePartnerAssetsExifV1(Iterable<SyncAssetExifV1> data) async {
|
||||||
debugPrint("updatePartnerAssetsExifV1 - ${data.length}");
|
try {
|
||||||
|
await _updateAssetExifV1(data);
|
||||||
|
} catch (e, s) {
|
||||||
|
_logger.severe('Error while processing updatePartnerAssetsExifV1', e, s);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _updateAssetsV1(Iterable<SyncAssetV1> data) =>
|
||||||
|
_db.batch((batch) {
|
||||||
|
for (final asset in data) {
|
||||||
|
final companion = RemoteAssetEntityCompanion(
|
||||||
|
name: Value(asset.originalFileName),
|
||||||
|
type: Value(asset.type.toAssetType()),
|
||||||
|
createdAt: Value.absentIfNull(asset.fileCreatedAt),
|
||||||
|
updatedAt: Value.absentIfNull(asset.fileModifiedAt),
|
||||||
|
durationInSeconds: const Value(0),
|
||||||
|
checksum: Value(asset.checksum),
|
||||||
|
isFavorite: Value(asset.isFavorite),
|
||||||
|
ownerId: Value(asset.ownerId),
|
||||||
|
localDateTime: Value(asset.localDateTime),
|
||||||
|
thumbHash: Value(asset.thumbhash),
|
||||||
|
deletedAt: Value(asset.deletedAt),
|
||||||
|
visibility: Value(asset.visibility.toAssetVisibility()),
|
||||||
|
);
|
||||||
|
|
||||||
|
batch.insert(
|
||||||
|
_db.remoteAssetEntity,
|
||||||
|
companion.copyWith(id: Value(asset.id)),
|
||||||
|
onConflict: DoUpdate((_) => companion),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<void> _deleteAssetsV1(Iterable<SyncAssetDeleteV1> assets) =>
|
||||||
|
_db.batch((batch) {
|
||||||
|
for (final asset in assets) {
|
||||||
|
batch.delete(
|
||||||
|
_db.remoteAssetEntity,
|
||||||
|
RemoteAssetEntityCompanion(id: Value(asset.assetId)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<void> _updateAssetExifV1(Iterable<SyncAssetExifV1> data) =>
|
||||||
|
_db.batch((batch) {
|
||||||
|
for (final exif in data) {
|
||||||
|
final companion = RemoteExifEntityCompanion(
|
||||||
|
city: Value(exif.city),
|
||||||
|
state: Value(exif.state),
|
||||||
|
country: Value(exif.country),
|
||||||
|
dateTimeOriginal: Value(exif.dateTimeOriginal),
|
||||||
|
description: Value(exif.description),
|
||||||
|
height: Value(exif.exifImageHeight),
|
||||||
|
width: Value(exif.exifImageWidth),
|
||||||
|
exposureTime: Value(exif.exposureTime),
|
||||||
|
fNumber: Value(exif.fNumber),
|
||||||
|
fileSize: Value(exif.fileSizeInByte),
|
||||||
|
focalLength: Value(exif.focalLength),
|
||||||
|
latitude: Value(exif.latitude),
|
||||||
|
longitude: Value(exif.longitude),
|
||||||
|
iso: Value(exif.iso),
|
||||||
|
make: Value(exif.make),
|
||||||
|
model: Value(exif.model),
|
||||||
|
orientation: Value(exif.orientation),
|
||||||
|
timeZone: Value(exif.timeZone),
|
||||||
|
rating: Value(exif.rating),
|
||||||
|
projectionType: Value(exif.projectionType),
|
||||||
|
);
|
||||||
|
|
||||||
|
batch.insert(
|
||||||
|
_db.remoteExifEntity,
|
||||||
|
companion.copyWith(assetId: Value(exif.assetId)),
|
||||||
|
onConflict: DoUpdate((_) => companion),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
extension on AssetTypeEnum {
|
||||||
|
AssetType toAssetType() => switch (this) {
|
||||||
|
AssetTypeEnum.IMAGE => AssetType.image,
|
||||||
|
AssetTypeEnum.VIDEO => AssetType.video,
|
||||||
|
AssetTypeEnum.AUDIO => AssetType.audio,
|
||||||
|
AssetTypeEnum.OTHER => AssetType.other,
|
||||||
|
_ => throw Exception('Unknown AssetType value: $this'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
extension on api.AssetVisibility {
|
||||||
|
AssetVisibility toAssetVisibility() => switch (this) {
|
||||||
|
api.AssetVisibility.timeline => AssetVisibility.timeline,
|
||||||
|
api.AssetVisibility.hidden => AssetVisibility.hidden,
|
||||||
|
api.AssetVisibility.archive => AssetVisibility.archive,
|
||||||
|
api.AssetVisibility.locked => AssetVisibility.locked,
|
||||||
|
_ => throw Exception('Unknown AssetVisibility value: $this'),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ abstract interface class IDownloadRepository {
|
|||||||
void Function(TaskProgressUpdate)? onTaskProgress;
|
void Function(TaskProgressUpdate)? onTaskProgress;
|
||||||
|
|
||||||
Future<List<TaskRecord>> getLiveVideoTasks();
|
Future<List<TaskRecord>> getLiveVideoTasks();
|
||||||
Future<bool> download(DownloadTask task);
|
Future<List<bool>> downloadAll(List<DownloadTask> tasks);
|
||||||
|
|
||||||
Future<bool> cancel(String id);
|
Future<bool> cancel(String id);
|
||||||
Future<void> deleteAllTrackingRecords();
|
Future<void> deleteAllTrackingRecords();
|
||||||
Future<void> deleteRecordsWithIds(List<String> id);
|
Future<void> deleteRecordsWithIds(List<String> id);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ abstract class ITimelineRepository {
|
|||||||
Album album,
|
Album album,
|
||||||
GroupAssetsBy groupAssetsBy,
|
GroupAssetsBy groupAssetsBy,
|
||||||
);
|
);
|
||||||
Stream<RenderList> watchAllVideosTimeline();
|
Stream<RenderList> watchAllVideosTimeline(String userId);
|
||||||
|
|
||||||
Stream<RenderList> watchHomeTimeline(
|
Stream<RenderList> watchHomeTimeline(
|
||||||
String userId,
|
String userId,
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ import 'package:immich_mobile/providers/db.provider.dart';
|
|||||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||||
import 'package:immich_mobile/providers/locale_provider.dart';
|
import 'package:immich_mobile/providers/locale_provider.dart';
|
||||||
import 'package:immich_mobile/providers/theme.provider.dart';
|
import 'package:immich_mobile/providers/theme.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
|
||||||
import 'package:immich_mobile/routing/app_navigation_observer.dart';
|
import 'package:immich_mobile/routing/app_navigation_observer.dart';
|
||||||
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/services/background.service.dart';
|
import 'package:immich_mobile/services/background.service.dart';
|
||||||
import 'package:immich_mobile/services/local_notification.service.dart';
|
import 'package:immich_mobile/services/local_notification.service.dart';
|
||||||
import 'package:immich_mobile/theme/dynamic_theme.dart';
|
import 'package:immich_mobile/theme/dynamic_theme.dart';
|
||||||
@@ -89,18 +89,6 @@ Future<void> initApp() async {
|
|||||||
|
|
||||||
initializeTimeZones();
|
initializeTimeZones();
|
||||||
|
|
||||||
FileDownloader().configureNotification(
|
|
||||||
running: TaskNotification(
|
|
||||||
'downloading_media'.tr(),
|
|
||||||
'file: {filename}',
|
|
||||||
),
|
|
||||||
complete: TaskNotification(
|
|
||||||
'download_finished'.tr(),
|
|
||||||
'file: {filename}',
|
|
||||||
),
|
|
||||||
progressBar: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
await FileDownloader().trackTasksInGroup(
|
await FileDownloader().trackTasksInGroup(
|
||||||
downloadGroupLivePhoto,
|
downloadGroupLivePhoto,
|
||||||
markDownloadedComplete: false,
|
markDownloadedComplete: false,
|
||||||
@@ -167,10 +155,27 @@ class ImmichAppState extends ConsumerState<ImmichApp>
|
|||||||
await ref.read(localNotificationService).setup();
|
await ref.read(localNotificationService).setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _configureFileDownloaderNotifications() {
|
||||||
|
FileDownloader().configureNotification(
|
||||||
|
running: TaskNotification(
|
||||||
|
'downloading_media'.tr(),
|
||||||
|
'${'file_name'.tr()}: {filename}',
|
||||||
|
),
|
||||||
|
complete: TaskNotification(
|
||||||
|
'download_finished'.tr(),
|
||||||
|
'${'file_name'.tr()}: {filename}',
|
||||||
|
),
|
||||||
|
progressBar: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeDependencies() {
|
void didChangeDependencies() {
|
||||||
super.didChangeDependencies();
|
super.didChangeDependencies();
|
||||||
Intl.defaultLocale = context.locale.toLanguageTag();
|
Intl.defaultLocale = context.locale.toLanguageTag();
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
_configureFileDownloaderNotifications();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -3,18 +3,23 @@ import 'dart:convert';
|
|||||||
class AlbumViewerPageState {
|
class AlbumViewerPageState {
|
||||||
final bool isEditAlbum;
|
final bool isEditAlbum;
|
||||||
final String editTitleText;
|
final String editTitleText;
|
||||||
|
final String editDescriptionText;
|
||||||
|
|
||||||
AlbumViewerPageState({
|
AlbumViewerPageState({
|
||||||
required this.isEditAlbum,
|
required this.isEditAlbum,
|
||||||
required this.editTitleText,
|
required this.editTitleText,
|
||||||
|
required this.editDescriptionText,
|
||||||
});
|
});
|
||||||
|
|
||||||
AlbumViewerPageState copyWith({
|
AlbumViewerPageState copyWith({
|
||||||
bool? isEditAlbum,
|
bool? isEditAlbum,
|
||||||
String? editTitleText,
|
String? editTitleText,
|
||||||
|
String? editDescriptionText,
|
||||||
}) {
|
}) {
|
||||||
return AlbumViewerPageState(
|
return AlbumViewerPageState(
|
||||||
isEditAlbum: isEditAlbum ?? this.isEditAlbum,
|
isEditAlbum: isEditAlbum ?? this.isEditAlbum,
|
||||||
editTitleText: editTitleText ?? this.editTitleText,
|
editTitleText: editTitleText ?? this.editTitleText,
|
||||||
|
editDescriptionText: editDescriptionText ?? this.editDescriptionText,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,6 +28,7 @@ class AlbumViewerPageState {
|
|||||||
|
|
||||||
result.addAll({'isEditAlbum': isEditAlbum});
|
result.addAll({'isEditAlbum': isEditAlbum});
|
||||||
result.addAll({'editTitleText': editTitleText});
|
result.addAll({'editTitleText': editTitleText});
|
||||||
|
result.addAll({'editDescriptionText': editDescriptionText});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -31,6 +37,7 @@ class AlbumViewerPageState {
|
|||||||
return AlbumViewerPageState(
|
return AlbumViewerPageState(
|
||||||
isEditAlbum: map['isEditAlbum'] ?? false,
|
isEditAlbum: map['isEditAlbum'] ?? false,
|
||||||
editTitleText: map['editTitleText'] ?? '',
|
editTitleText: map['editTitleText'] ?? '',
|
||||||
|
editDescriptionText: map['editDescriptionText'] ?? '',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +48,7 @@ class AlbumViewerPageState {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() =>
|
String toString() =>
|
||||||
'AlbumViewerPageState(isEditAlbum: $isEditAlbum, editTitleText: $editTitleText)';
|
'AlbumViewerPageState(isEditAlbum: $isEditAlbum, editTitleText: $editTitleText, editDescriptionText: $editDescriptionText)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
@@ -49,9 +56,13 @@ class AlbumViewerPageState {
|
|||||||
|
|
||||||
return other is AlbumViewerPageState &&
|
return other is AlbumViewerPageState &&
|
||||||
other.isEditAlbum == isEditAlbum &&
|
other.isEditAlbum == isEditAlbum &&
|
||||||
other.editTitleText == editTitleText;
|
other.editTitleText == editTitleText &&
|
||||||
|
other.editDescriptionText == editDescriptionText;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => isEditAlbum.hashCode ^ editTitleText.hashCode;
|
int get hashCode =>
|
||||||
|
isEditAlbum.hashCode ^
|
||||||
|
editTitleText.hashCode ^
|
||||||
|
editDescriptionText.hashCode;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class AssetSelectionState {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() =>
|
String toString() =>
|
||||||
'SelectionAssetState(hasRemote: $hasRemote, hasMerged: $hasMerged, hasMerged: $hasMerged, selectedCount: $selectedCount)';
|
'SelectionAssetState(hasRemote: $hasRemote, hasLocal: $hasLocal, hasMerged: $hasMerged, selectedCount: $selectedCount)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant AssetSelectionState other) {
|
bool operator ==(covariant AssetSelectionState other) {
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ class AlbumControlButton extends ConsumerWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(left: 16.0, top: 8, bottom: 16),
|
padding: const EdgeInsets.only(left: 16.0),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 36,
|
||||||
child: ListView(
|
child: ListView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -30,15 +30,12 @@ class AlbumDateRange extends ConsumerWidget {
|
|||||||
final (startDate, endDate, shared) = data;
|
final (startDate, endDate, shared) = data;
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: shared
|
padding: const EdgeInsets.only(left: 16.0),
|
||||||
? const EdgeInsets.only(
|
|
||||||
left: 16.0,
|
|
||||||
bottom: 0.0,
|
|
||||||
)
|
|
||||||
: const EdgeInsets.only(left: 16.0, bottom: 8.0),
|
|
||||||
child: Text(
|
child: Text(
|
||||||
_getDateRangeText(startDate, endDate),
|
_getDateRangeText(startDate, endDate),
|
||||||
style: context.textTheme.labelLarge,
|
style: context.textTheme.labelLarge?.copyWith(
|
||||||
|
color: context.colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
45
mobile/lib/pages/album/album_description.dart
Normal file
45
mobile/lib/pages/album/album_description.dart
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
import 'package:immich_mobile/providers/album/current_album.provider.dart';
|
||||||
|
import 'package:immich_mobile/widgets/album/album_viewer_editable_description.dart';
|
||||||
|
import 'package:immich_mobile/providers/auth.provider.dart';
|
||||||
|
|
||||||
|
class AlbumDescription extends ConsumerWidget {
|
||||||
|
const AlbumDescription({super.key, required this.descriptionFocusNode});
|
||||||
|
|
||||||
|
final FocusNode descriptionFocusNode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final userId = ref.watch(authProvider).userId;
|
||||||
|
final (isOwner, isRemote, albumDescription) = ref.watch(
|
||||||
|
currentAlbumProvider.select((album) {
|
||||||
|
if (album == null) {
|
||||||
|
return const (false, false, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (album.ownerId == userId, album.isRemote, album.description);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isOwner && isRemote) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8, right: 8),
|
||||||
|
child: AlbumViewerEditableDescription(
|
||||||
|
albumDescription: albumDescription ?? 'add_a_description'.tr(),
|
||||||
|
descriptionFocusNode: descriptionFocusNode,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 16, right: 8),
|
||||||
|
child: Text(
|
||||||
|
albumDescription ?? 'add_a_description'.tr(),
|
||||||
|
style: context.textTheme.bodyLarge,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,7 +36,7 @@ class AlbumSharedUserIcons extends HookConsumerWidget {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 50,
|
height: 50,
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
padding: const EdgeInsets.only(left: 16),
|
padding: const EdgeInsets.only(left: 16, bottom: 8),
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
itemBuilder: ((context, index) {
|
itemBuilder: ((context, index) {
|
||||||
return Padding(
|
return Padding(
|
||||||
|
|||||||
@@ -19,7 +19,11 @@ class AlbumTitle extends ConsumerWidget {
|
|||||||
return const (false, false, '');
|
return const (false, false, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
return (album.ownerId == userId, album.isRemote, album.name);
|
return (
|
||||||
|
album.ownerId == userId,
|
||||||
|
album.isRemote,
|
||||||
|
album.name,
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -35,7 +39,12 @@ class AlbumTitle extends ConsumerWidget {
|
|||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(left: 16, right: 8),
|
padding: const EdgeInsets.only(left: 16, right: 8),
|
||||||
child: Text(albumName, style: context.textTheme.headlineMedium),
|
child: Text(
|
||||||
|
albumName,
|
||||||
|
style: context.textTheme.headlineLarge?.copyWith(
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,10 +10,12 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
|||||||
import 'package:immich_mobile/models/albums/asset_selection_page_result.model.dart';
|
import 'package:immich_mobile/models/albums/asset_selection_page_result.model.dart';
|
||||||
import 'package:immich_mobile/pages/album/album_control_button.dart';
|
import 'package:immich_mobile/pages/album/album_control_button.dart';
|
||||||
import 'package:immich_mobile/pages/album/album_date_range.dart';
|
import 'package:immich_mobile/pages/album/album_date_range.dart';
|
||||||
|
import 'package:immich_mobile/pages/album/album_description.dart';
|
||||||
import 'package:immich_mobile/pages/album/album_shared_user_icons.dart';
|
import 'package:immich_mobile/pages/album/album_shared_user_icons.dart';
|
||||||
import 'package:immich_mobile/pages/album/album_title.dart';
|
import 'package:immich_mobile/pages/album/album_title.dart';
|
||||||
import 'package:immich_mobile/providers/album/album.provider.dart';
|
import 'package:immich_mobile/providers/album/album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/album/current_album.provider.dart';
|
import 'package:immich_mobile/providers/album/current_album.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart';
|
||||||
import 'package:immich_mobile/providers/timeline.provider.dart';
|
import 'package:immich_mobile/providers/timeline.provider.dart';
|
||||||
import 'package:immich_mobile/utils/immich_loading_overlay.dart';
|
import 'package:immich_mobile/utils/immich_loading_overlay.dart';
|
||||||
import 'package:immich_mobile/providers/multiselect.provider.dart';
|
import 'package:immich_mobile/providers/multiselect.provider.dart';
|
||||||
@@ -35,6 +37,7 @@ class AlbumViewer extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final titleFocusNode = useFocusNode();
|
final titleFocusNode = useFocusNode();
|
||||||
|
final descriptionFocusNode = useFocusNode();
|
||||||
final userId = ref.watch(authProvider).userId;
|
final userId = ref.watch(authProvider).userId;
|
||||||
final isMultiselecting = ref.watch(multiselectProvider);
|
final isMultiselecting = ref.watch(multiselectProvider);
|
||||||
final isProcessing = useProcessingOverlay();
|
final isProcessing = useProcessingOverlay();
|
||||||
@@ -93,6 +96,7 @@ class AlbumViewer extends HookConsumerWidget {
|
|||||||
|
|
||||||
onActivitiesPressed() {
|
onActivitiesPressed() {
|
||||||
if (album.remoteId != null) {
|
if (album.remoteId != null) {
|
||||||
|
ref.read(currentAssetProvider.notifier).set(null);
|
||||||
context.pushRoute(
|
context.pushRoute(
|
||||||
const ActivitiesRoute(),
|
const ActivitiesRoute(),
|
||||||
);
|
);
|
||||||
@@ -104,23 +108,44 @@ class AlbumViewer extends HookConsumerWidget {
|
|||||||
MultiselectGrid(
|
MultiselectGrid(
|
||||||
key: const ValueKey("albumViewerMultiselectGrid"),
|
key: const ValueKey("albumViewerMultiselectGrid"),
|
||||||
renderListProvider: albumTimelineProvider(album.id),
|
renderListProvider: albumTimelineProvider(album.id),
|
||||||
topWidget: Column(
|
topWidget: Container(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
decoration: BoxDecoration(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
gradient: LinearGradient(
|
||||||
children: [
|
begin: Alignment.topCenter,
|
||||||
AlbumTitle(
|
end: Alignment.bottomCenter,
|
||||||
key: const ValueKey("albumTitle"),
|
colors: [
|
||||||
titleFocusNode: titleFocusNode,
|
context.primaryColor.withValues(alpha: 0.06),
|
||||||
|
context.primaryColor.withValues(alpha: 0.04),
|
||||||
|
Colors.indigo.withValues(alpha: 0.02),
|
||||||
|
Colors.transparent,
|
||||||
|
],
|
||||||
|
stops: const [0.0, 0.3, 0.7, 1.0],
|
||||||
),
|
),
|
||||||
const AlbumDateRange(),
|
),
|
||||||
const AlbumSharedUserIcons(),
|
child: Column(
|
||||||
if (album.isRemote)
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
AlbumControlButton(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
key: const ValueKey("albumControlButton"),
|
children: [
|
||||||
onAddPhotosPressed: onAddPhotosPressed,
|
const SizedBox(height: 32),
|
||||||
onAddUsersPressed: onAddUsersPressed,
|
const AlbumDateRange(),
|
||||||
|
AlbumTitle(
|
||||||
|
key: const ValueKey("albumTitle"),
|
||||||
|
titleFocusNode: titleFocusNode,
|
||||||
),
|
),
|
||||||
],
|
AlbumDescription(
|
||||||
|
key: const ValueKey("albumDescription"),
|
||||||
|
descriptionFocusNode: descriptionFocusNode,
|
||||||
|
),
|
||||||
|
const AlbumSharedUserIcons(),
|
||||||
|
if (album.isRemote)
|
||||||
|
AlbumControlButton(
|
||||||
|
key: const ValueKey("albumControlButton"),
|
||||||
|
onAddPhotosPressed: onAddPhotosPressed,
|
||||||
|
onAddUsersPressed: onAddUsersPressed,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
onRemoveFromAlbum: onRemoveFromAlbumPressed,
|
onRemoveFromAlbum: onRemoveFromAlbumPressed,
|
||||||
editEnabled: album.ownerId == userId,
|
editEnabled: album.ownerId == userId,
|
||||||
@@ -134,6 +159,7 @@ class AlbumViewer extends HookConsumerWidget {
|
|||||||
child: AlbumViewerAppbar(
|
child: AlbumViewerAppbar(
|
||||||
key: const ValueKey("albumViewerAppbar"),
|
key: const ValueKey("albumViewerAppbar"),
|
||||||
titleFocusNode: titleFocusNode,
|
titleFocusNode: titleFocusNode,
|
||||||
|
descriptionFocusNode: descriptionFocusNode,
|
||||||
userId: userId,
|
userId: userId,
|
||||||
onAddPhotos: onAddPhotosPressed,
|
onAddPhotos: onAddPhotosPressed,
|
||||||
onAddUsers: onAddUsersPressed,
|
onAddUsers: onAddUsersPressed,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import 'package:immich_mobile/providers/album/album.provider.dart';
|
|||||||
import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart';
|
import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart';
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
|
import 'package:immich_mobile/utils/translation.dart';
|
||||||
import 'package:immich_mobile/widgets/album/album_thumbnail_card.dart';
|
import 'package:immich_mobile/widgets/album/album_thumbnail_card.dart';
|
||||||
import 'package:immich_mobile/widgets/common/immich_app_bar.dart';
|
import 'package:immich_mobile/widgets/common/immich_app_bar.dart';
|
||||||
import 'package:immich_mobile/widgets/common/immich_thumbnail.dart';
|
import 'package:immich_mobile/widgets/common/immich_thumbnail.dart';
|
||||||
@@ -229,13 +230,11 @@ class AlbumsPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
subtitle: sorted[index].ownerId != null
|
subtitle: sorted[index].ownerId != null
|
||||||
? Text(
|
? Text(
|
||||||
'${(sorted[index].assetCount == 1 ? 'album_thumbnail_card_item'.tr() : 'album_thumbnail_card_items'.tr(
|
'${t('items_count', {
|
||||||
namedArgs: {
|
'count': sorted[index].assetCount,
|
||||||
'count': sorted[index]
|
})} • ${sorted[index].ownerId != userId ? t('shared_by_user', {
|
||||||
.assetCount
|
'user': sorted[index].ownerName!,
|
||||||
.toString(),
|
}) : 'owned'.tr()}',
|
||||||
},
|
|
||||||
))} • ${sorted[index].ownerId != userId ? 'album_thumbnail_shared_by'.tr(namedArgs: {'user': sorted[index].ownerName!}) : 'owned'.tr()}',
|
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style:
|
style:
|
||||||
context.textTheme.bodyMedium?.copyWith(
|
context.textTheme.bodyMedium?.copyWith(
|
||||||
|
|||||||
@@ -8,9 +8,11 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
|||||||
import 'package:immich_mobile/models/albums/asset_selection_page_result.model.dart';
|
import 'package:immich_mobile/models/albums/asset_selection_page_result.model.dart';
|
||||||
import 'package:immich_mobile/providers/album/album.provider.dart';
|
import 'package:immich_mobile/providers/album/album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/album/album_title.provider.dart';
|
import 'package:immich_mobile/providers/album/album_title.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/album/album_viewer.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/widgets/album/album_action_filled_button.dart';
|
import 'package:immich_mobile/widgets/album/album_action_filled_button.dart';
|
||||||
import 'package:immich_mobile/widgets/album/album_title_text_field.dart';
|
import 'package:immich_mobile/widgets/album/album_title_text_field.dart';
|
||||||
|
import 'package:immich_mobile/widgets/album/album_viewer_editable_description.dart';
|
||||||
import 'package:immich_mobile/widgets/album/shared_album_thumbnail_image.dart';
|
import 'package:immich_mobile/widgets/album/shared_album_thumbnail_image.dart';
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
@@ -28,6 +30,7 @@ class CreateAlbumPage extends HookConsumerWidget {
|
|||||||
final albumTitleController =
|
final albumTitleController =
|
||||||
useTextEditingController.fromValue(TextEditingValue.empty);
|
useTextEditingController.fromValue(TextEditingValue.empty);
|
||||||
final albumTitleTextFieldFocusNode = useFocusNode();
|
final albumTitleTextFieldFocusNode = useFocusNode();
|
||||||
|
final albumDescriptionTextFieldFocusNode = useFocusNode();
|
||||||
final isAlbumTitleTextFieldFocus = useState(false);
|
final isAlbumTitleTextFieldFocus = useState(false);
|
||||||
final isAlbumTitleEmpty = useState(true);
|
final isAlbumTitleEmpty = useState(true);
|
||||||
final selectedAssets = useState<Set<Asset>>(
|
final selectedAssets = useState<Set<Asset>>(
|
||||||
@@ -36,6 +39,7 @@ class CreateAlbumPage extends HookConsumerWidget {
|
|||||||
|
|
||||||
void onBackgroundTapped() {
|
void onBackgroundTapped() {
|
||||||
albumTitleTextFieldFocusNode.unfocus();
|
albumTitleTextFieldFocusNode.unfocus();
|
||||||
|
albumDescriptionTextFieldFocusNode.unfocus();
|
||||||
isAlbumTitleTextFieldFocus.value = false;
|
isAlbumTitleTextFieldFocus.value = false;
|
||||||
|
|
||||||
if (albumTitleController.text.isEmpty) {
|
if (albumTitleController.text.isEmpty) {
|
||||||
@@ -77,6 +81,19 @@ class CreateAlbumPage extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildDescriptionInputField() {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
right: 10,
|
||||||
|
left: 10,
|
||||||
|
),
|
||||||
|
child: AlbumViewerEditableDescription(
|
||||||
|
albumDescription: '',
|
||||||
|
descriptionFocusNode: albumDescriptionTextFieldFocusNode,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
buildTitle() {
|
buildTitle() {
|
||||||
if (selectedAssets.value.isEmpty) {
|
if (selectedAssets.value.isEmpty) {
|
||||||
return SliverToBoxAdapter(
|
return SliverToBoxAdapter(
|
||||||
@@ -178,18 +195,18 @@ class CreateAlbumPage extends HookConsumerWidget {
|
|||||||
return const SliverToBoxAdapter();
|
return const SliverToBoxAdapter();
|
||||||
}
|
}
|
||||||
|
|
||||||
createNonSharedAlbum() async {
|
Future<void> createAlbum() async {
|
||||||
onBackgroundTapped();
|
onBackgroundTapped();
|
||||||
var newAlbum = await ref.watch(albumProvider.notifier).createAlbum(
|
var newAlbum = await ref.watch(albumProvider.notifier).createAlbum(
|
||||||
ref.watch(albumTitleProvider),
|
ref.read(albumTitleProvider),
|
||||||
selectedAssets.value,
|
selectedAssets.value,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (newAlbum != null) {
|
if (newAlbum != null) {
|
||||||
ref.watch(albumProvider.notifier).refreshRemoteAlbums();
|
ref.read(albumProvider.notifier).refreshRemoteAlbums();
|
||||||
selectedAssets.value = {};
|
selectedAssets.value = {};
|
||||||
ref.watch(albumTitleProvider.notifier).clearAlbumTitle();
|
ref.read(albumTitleProvider.notifier).clearAlbumTitle();
|
||||||
|
ref.read(albumViewerProvider.notifier).disableEditAlbum();
|
||||||
context.replaceRoute(AlbumViewerRoute(albumId: newAlbum.id));
|
context.replaceRoute(AlbumViewerRoute(albumId: newAlbum.id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -211,9 +228,8 @@ class CreateAlbumPage extends HookConsumerWidget {
|
|||||||
).tr(),
|
).tr(),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: albumTitleController.text.isNotEmpty
|
onPressed:
|
||||||
? createNonSharedAlbum
|
albumTitleController.text.isNotEmpty ? createAlbum : null,
|
||||||
: null,
|
|
||||||
child: Text(
|
child: Text(
|
||||||
'create'.tr(),
|
'create'.tr(),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -237,10 +253,11 @@ class CreateAlbumPage extends HookConsumerWidget {
|
|||||||
pinned: true,
|
pinned: true,
|
||||||
floating: false,
|
floating: false,
|
||||||
bottom: PreferredSize(
|
bottom: PreferredSize(
|
||||||
preferredSize: const Size.fromHeight(96.0),
|
preferredSize: const Size.fromHeight(125.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
buildTitleInputField(),
|
buildTitleInputField(),
|
||||||
|
buildDescriptionInputField(),
|
||||||
if (selectedAssets.value.isNotEmpty) buildControlButton(),
|
if (selectedAssets.value.isNotEmpty) buildControlButton(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ enum SettingSection {
|
|||||||
"backup_setting_subtitle",
|
"backup_setting_subtitle",
|
||||||
),
|
),
|
||||||
languages(
|
languages(
|
||||||
'setting_languages_title',
|
'language',
|
||||||
Icons.language,
|
Icons.language,
|
||||||
"setting_languages_subtitle",
|
"setting_languages_subtitle",
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -72,7 +72,9 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.replaceRoute(const TabControllerRoute());
|
if (context.router.current.name != ShareIntentRoute.name) {
|
||||||
|
context.replaceRoute(const TabControllerRoute());
|
||||||
|
}
|
||||||
|
|
||||||
final hasPermission =
|
final hasPermission =
|
||||||
await ref.read(galleryPermissionNotifier.notifier).hasPermission;
|
await ref.read(galleryPermissionNotifier.notifier).hasPermission;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import 'package:immich_mobile/extensions/theme_extensions.dart';
|
|||||||
import 'package:immich_mobile/models/folder/recursive_folder.model.dart';
|
import 'package:immich_mobile/models/folder/recursive_folder.model.dart';
|
||||||
import 'package:immich_mobile/models/folder/root_folder.model.dart';
|
import 'package:immich_mobile/models/folder/root_folder.model.dart';
|
||||||
import 'package:immich_mobile/pages/common/large_leading_tile.dart';
|
import 'package:immich_mobile/pages/common/large_leading_tile.dart';
|
||||||
|
import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart';
|
||||||
import 'package:immich_mobile/providers/folder.provider.dart';
|
import 'package:immich_mobile/providers/folder.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/utils/bytes_units.dart';
|
import 'package:immich_mobile/utils/bytes_units.dart';
|
||||||
@@ -219,12 +220,15 @@ class FolderContent extends HookConsumerWidget {
|
|||||||
list.allAssets!.isNotEmpty)
|
list.allAssets!.isNotEmpty)
|
||||||
...list.allAssets!.map(
|
...list.allAssets!.map(
|
||||||
(asset) => LargeLeadingTile(
|
(asset) => LargeLeadingTile(
|
||||||
onTap: () => context.pushRoute(
|
onTap: () {
|
||||||
GalleryViewerRoute(
|
ref.read(currentAssetProvider.notifier).set(asset);
|
||||||
renderList: list,
|
context.pushRoute(
|
||||||
initialIndex: list.allAssets!.indexOf(asset),
|
GalleryViewerRoute(
|
||||||
),
|
renderList: list,
|
||||||
),
|
initialIndex: list.allAssets!.indexOf(asset),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
leading: ClipRRect(
|
leading: ClipRRect(
|
||||||
borderRadius: const BorderRadius.all(
|
borderRadius: const BorderRadius.all(
|
||||||
Radius.circular(15),
|
Radius.circular(15),
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ class PeopleCollectionPage extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
error: (error, stack) => const Text("error"),
|
error: (error, stack) => const Text("error"),
|
||||||
loading: () => const CircularProgressIndicator(),
|
loading: () => const Center(child: CircularProgressIndicator()),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -395,6 +395,7 @@ class _MapWithMarker extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
style.widgetWhen(
|
style.widgetWhen(
|
||||||
onData: (style) => MapLibreMap(
|
onData: (style) => MapLibreMap(
|
||||||
|
attributionButtonMargins: const Point(8, kToolbarHeight),
|
||||||
initialCameraPosition: CameraPosition(
|
initialCameraPosition: CameraPosition(
|
||||||
target: initialLocation ?? const LatLng(0, 0),
|
target: initialLocation ?? const LatLng(0, 0),
|
||||||
zoom: initialLocation != null ? 12 : 0,
|
zoom: initialLocation != null ? 12 : 0,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
|||||||
import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart';
|
import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart';
|
||||||
import 'package:immich_mobile/pages/common/large_leading_tile.dart';
|
import 'package:immich_mobile/pages/common/large_leading_tile.dart';
|
||||||
import 'package:immich_mobile/providers/asset_viewer/share_intent_upload.provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/share_intent_upload.provider.dart';
|
||||||
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/utils/url_helper.dart';
|
import 'package:immich_mobile/utils/url_helper.dart';
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
@@ -20,6 +21,11 @@ class ShareIntentPage extends HookConsumerWidget {
|
|||||||
final currentEndpoint = getServerUrl() ?? '--';
|
final currentEndpoint = getServerUrl() ?? '--';
|
||||||
final candidates = ref.watch(shareIntentUploadProvider);
|
final candidates = ref.watch(shareIntentUploadProvider);
|
||||||
final isUploaded = useState(false);
|
final isUploaded = useState(false);
|
||||||
|
useOnAppLifecycleStateChange((previous, current) {
|
||||||
|
if (current == AppLifecycleState.resumed) {
|
||||||
|
isUploaded.value = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
void removeAttachment(ShareIntentAttachment attachment) {
|
void removeAttachment(ShareIntentAttachment attachment) {
|
||||||
ref.read(shareIntentUploadProvider.notifier).removeAttachment(attachment);
|
ref.read(shareIntentUploadProvider.notifier).removeAttachment(attachment);
|
||||||
@@ -66,6 +72,14 @@ class ShareIntentPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
leading: IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
context.navigateTo(
|
||||||
|
const TabControllerRoute(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
body: ListView.builder(
|
body: ListView.builder(
|
||||||
itemCount: attachments.length,
|
itemCount: attachments.length,
|
||||||
|
|||||||
31
mobile/lib/platform/native_sync_api.g.dart
generated
31
mobile/lib/platform/native_sync_api.g.dart
generated
@@ -498,4 +498,35 @@ class NativeSyncApi {
|
|||||||
return (pigeonVar_replyList[0] as List<Object?>?)!.cast<PlatformAsset>();
|
return (pigeonVar_replyList[0] as List<Object?>?)!.cast<PlatformAsset>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<Uint8List?>> hashPaths(List<String> paths) async {
|
||||||
|
final String pigeonVar_channelName =
|
||||||
|
'dev.flutter.pigeon.immich_mobile.NativeSyncApi.hashPaths$pigeonVar_messageChannelSuffix';
|
||||||
|
final BasicMessageChannel<Object?> pigeonVar_channel =
|
||||||
|
BasicMessageChannel<Object?>(
|
||||||
|
pigeonVar_channelName,
|
||||||
|
pigeonChannelCodec,
|
||||||
|
binaryMessenger: pigeonVar_binaryMessenger,
|
||||||
|
);
|
||||||
|
final Future<Object?> pigeonVar_sendFuture =
|
||||||
|
pigeonVar_channel.send(<Object?>[paths]);
|
||||||
|
final List<Object?>? pigeonVar_replyList =
|
||||||
|
await pigeonVar_sendFuture as List<Object?>?;
|
||||||
|
if (pigeonVar_replyList == null) {
|
||||||
|
throw _createConnectionError(pigeonVar_channelName);
|
||||||
|
} else if (pigeonVar_replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: pigeonVar_replyList[0]! as String,
|
||||||
|
message: pigeonVar_replyList[1] as String?,
|
||||||
|
details: pigeonVar_replyList[2],
|
||||||
|
);
|
||||||
|
} else if (pigeonVar_replyList[0] == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'null-error',
|
||||||
|
message: 'Host platform returned null value for non-null return value.',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (pigeonVar_replyList[0] as List<Object?>?)!.cast<Uint8List?>();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:immich_mobile/domain/models/log.model.dart';
|
import 'package:immich_mobile/domain/models/log.model.dart';
|
||||||
@@ -15,7 +16,6 @@ abstract final class DLog {
|
|||||||
static Stream<List<LogMessage>> watchLog() {
|
static Stream<List<LogMessage>> watchLog() {
|
||||||
final db = Isar.getInstance();
|
final db = Isar.getInstance();
|
||||||
if (db == null) {
|
if (db == null) {
|
||||||
debugPrint('Isar is not initialized');
|
|
||||||
return const Stream.empty();
|
return const Stream.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +30,6 @@ abstract final class DLog {
|
|||||||
static void clearLog() {
|
static void clearLog() {
|
||||||
final db = Isar.getInstance();
|
final db = Isar.getInstance();
|
||||||
if (db == null) {
|
if (db == null) {
|
||||||
debugPrint('Isar is not initialized');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +39,9 @@ abstract final class DLog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void log(String message, [Object? error, StackTrace? stackTrace]) {
|
static void log(String message, [Object? error, StackTrace? stackTrace]) {
|
||||||
debugPrint('[$kDevLoggerTag] [${DateTime.now()}] $message');
|
if (!Platform.environment.containsKey('FLUTTER_TEST')) {
|
||||||
|
debugPrint('[$kDevLoggerTag] [${DateTime.now()}] $message');
|
||||||
|
}
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
debugPrint('Error: $error');
|
debugPrint('Error: $error');
|
||||||
}
|
}
|
||||||
@@ -50,7 +51,6 @@ abstract final class DLog {
|
|||||||
|
|
||||||
final isar = Isar.getInstance();
|
final isar = Isar.getInstance();
|
||||||
if (isar == null) {
|
if (isar == null) {
|
||||||
debugPrint('Isar is not initialized');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ final _features = [
|
|||||||
icon: Icons.photo_library_rounded,
|
icon: Icons.photo_library_rounded,
|
||||||
onTap: (_, ref) => ref.read(backgroundSyncProvider).syncLocal(full: true),
|
onTap: (_, ref) => ref.read(backgroundSyncProvider).syncLocal(full: true),
|
||||||
),
|
),
|
||||||
|
_Feature(
|
||||||
|
name: 'Hash Local Assets',
|
||||||
|
icon: Icons.numbers_outlined,
|
||||||
|
onTap: (_, ref) => ref.read(backgroundSyncProvider).hashAssets(),
|
||||||
|
),
|
||||||
_Feature(
|
_Feature(
|
||||||
name: 'Sync Remote',
|
name: 'Sync Remote',
|
||||||
icon: Icons.refresh_rounded,
|
icon: Icons.refresh_rounded,
|
||||||
@@ -53,11 +58,38 @@ final _features = [
|
|||||||
await db.localAlbumAssetEntity.deleteAll();
|
await db.localAlbumAssetEntity.deleteAll();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
_Feature(
|
||||||
|
name: 'Clear Remote Data',
|
||||||
|
icon: Icons.delete_sweep_rounded,
|
||||||
|
onTap: (_, ref) async {
|
||||||
|
final db = ref.read(driftProvider);
|
||||||
|
await db.remoteAssetEntity.deleteAll();
|
||||||
|
await db.remoteExifEntity.deleteAll();
|
||||||
|
},
|
||||||
|
),
|
||||||
_Feature(
|
_Feature(
|
||||||
name: 'Local Media Summary',
|
name: 'Local Media Summary',
|
||||||
icon: Icons.table_chart_rounded,
|
icon: Icons.table_chart_rounded,
|
||||||
onTap: (ctx, _) => ctx.pushRoute(const LocalMediaSummaryRoute()),
|
onTap: (ctx, _) => ctx.pushRoute(const LocalMediaSummaryRoute()),
|
||||||
),
|
),
|
||||||
|
_Feature(
|
||||||
|
name: 'Remote Media Summary',
|
||||||
|
icon: Icons.summarize_rounded,
|
||||||
|
onTap: (ctx, _) => ctx.pushRoute(const RemoteMediaSummaryRoute()),
|
||||||
|
),
|
||||||
|
_Feature(
|
||||||
|
name: 'Reset Sqlite',
|
||||||
|
icon: Icons.table_view_rounded,
|
||||||
|
onTap: (_, ref) async {
|
||||||
|
final drift = ref.read(driftProvider);
|
||||||
|
// ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member
|
||||||
|
final migrator = drift.createMigrator();
|
||||||
|
for (final entity in drift.allSchemaEntities) {
|
||||||
|
await migrator.drop(entity);
|
||||||
|
await migrator.create(entity);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
|
|||||||
@@ -1,14 +1,48 @@
|
|||||||
|
// ignore_for_file: prefer-single-widget-per-file
|
||||||
|
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/domain/models/local_album.model.dart';
|
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||||
|
|
||||||
final _stats = [
|
class _Stat {
|
||||||
|
const _Stat({required this.name, required this.load});
|
||||||
|
|
||||||
|
final String name;
|
||||||
|
final Future<int> Function(Drift _) load;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _Summary extends StatelessWidget {
|
||||||
|
final String name;
|
||||||
|
final Future<int> countFuture;
|
||||||
|
|
||||||
|
const _Summary({required this.name, required this.countFuture});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FutureBuilder<int>(
|
||||||
|
future: countFuture,
|
||||||
|
builder: (ctx, snapshot) {
|
||||||
|
final Widget subtitle;
|
||||||
|
|
||||||
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
|
subtitle = const CircularProgressIndicator();
|
||||||
|
} else if (snapshot.hasError) {
|
||||||
|
subtitle = const Icon(Icons.error_rounded);
|
||||||
|
} else {
|
||||||
|
subtitle = Text('${snapshot.data ?? 0}');
|
||||||
|
}
|
||||||
|
return ListTile(title: Text(name), trailing: subtitle);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final _localStats = [
|
||||||
_Stat(
|
_Stat(
|
||||||
name: 'Local Assets',
|
name: 'Local Assets',
|
||||||
load: (db) => db.managers.localAssetEntity.count(),
|
load: (db) => db.managers.localAssetEntity.count(),
|
||||||
@@ -36,11 +70,11 @@ class LocalMediaSummaryPage extends StatelessWidget {
|
|||||||
slivers: [
|
slivers: [
|
||||||
SliverList.builder(
|
SliverList.builder(
|
||||||
itemBuilder: (_, index) {
|
itemBuilder: (_, index) {
|
||||||
final stat = _stats[index];
|
final stat = _localStats[index];
|
||||||
final countFuture = stat.load(db);
|
final countFuture = stat.load(db);
|
||||||
return _Summary(name: stat.name, countFuture: countFuture);
|
return _Summary(name: stat.name, countFuture: countFuture);
|
||||||
},
|
},
|
||||||
itemCount: _stats.length,
|
itemCount: _localStats.length,
|
||||||
),
|
),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -59,9 +93,8 @@ class LocalMediaSummaryPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: albumsFuture,
|
future: albumsFuture,
|
||||||
initialData: <LocalAlbum>[],
|
|
||||||
builder: (_, snap) {
|
builder: (_, snap) {
|
||||||
final albums = snap.data!;
|
final albums = snap.data ?? [];
|
||||||
if (albums.isEmpty) {
|
if (albums.isEmpty) {
|
||||||
return const SliverToBoxAdapter(child: SizedBox.shrink());
|
return const SliverToBoxAdapter(child: SizedBox.shrink());
|
||||||
}
|
}
|
||||||
@@ -90,36 +123,43 @@ class LocalMediaSummaryPage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore: prefer-single-widget-per-file
|
final _remoteStats = [
|
||||||
class _Summary extends StatelessWidget {
|
_Stat(
|
||||||
final String name;
|
name: 'Remote Assets',
|
||||||
final Future<int> countFuture;
|
load: (db) => db.managers.remoteAssetEntity.count(),
|
||||||
|
),
|
||||||
|
_Stat(
|
||||||
|
name: 'Exif Entities',
|
||||||
|
load: (db) => db.managers.remoteExifEntity.count(),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
const _Summary({required this.name, required this.countFuture});
|
@RoutePage()
|
||||||
|
class RemoteMediaSummaryPage extends StatelessWidget {
|
||||||
|
const RemoteMediaSummaryPage({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return FutureBuilder<int>(
|
return Scaffold(
|
||||||
future: countFuture,
|
appBar: AppBar(title: const Text('Remote Media Summary')),
|
||||||
builder: (ctx, snapshot) {
|
body: Consumer(
|
||||||
final Widget subtitle;
|
builder: (ctx, ref, __) {
|
||||||
|
final db = ref.watch(driftProvider);
|
||||||
|
|
||||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
return CustomScrollView(
|
||||||
subtitle = const CircularProgressIndicator();
|
slivers: [
|
||||||
} else if (snapshot.hasError) {
|
SliverList.builder(
|
||||||
subtitle = const Icon(Icons.error_rounded);
|
itemBuilder: (_, index) {
|
||||||
} else {
|
final stat = _remoteStats[index];
|
||||||
subtitle = Text('${snapshot.data ?? 0}');
|
final countFuture = stat.load(db);
|
||||||
}
|
return _Summary(name: stat.name, countFuture: countFuture);
|
||||||
return ListTile(title: Text(name), trailing: subtitle);
|
},
|
||||||
},
|
itemCount: _remoteStats.length,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _Stat {
|
|
||||||
const _Stat({required this.name, required this.load});
|
|
||||||
|
|
||||||
final String name;
|
|
||||||
final Future<int> Function(Drift _) load;
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,13 @@ import 'package:immich_mobile/entities/album.entity.dart';
|
|||||||
|
|
||||||
class AlbumViewerNotifier extends StateNotifier<AlbumViewerPageState> {
|
class AlbumViewerNotifier extends StateNotifier<AlbumViewerPageState> {
|
||||||
AlbumViewerNotifier(this.ref)
|
AlbumViewerNotifier(this.ref)
|
||||||
: super(AlbumViewerPageState(editTitleText: "", isEditAlbum: false));
|
: super(
|
||||||
|
AlbumViewerPageState(
|
||||||
|
editTitleText: "",
|
||||||
|
isEditAlbum: false,
|
||||||
|
editDescriptionText: "",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
final Ref ref;
|
final Ref ref;
|
||||||
|
|
||||||
@@ -21,12 +27,24 @@ class AlbumViewerNotifier extends StateNotifier<AlbumViewerPageState> {
|
|||||||
state = state.copyWith(editTitleText: newTitle);
|
state = state.copyWith(editTitleText: newTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setEditDescriptionText(String newDescription) {
|
||||||
|
state = state.copyWith(editDescriptionText: newDescription);
|
||||||
|
}
|
||||||
|
|
||||||
void remoteEditTitleText() {
|
void remoteEditTitleText() {
|
||||||
state = state.copyWith(editTitleText: "");
|
state = state.copyWith(editTitleText: "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void remoteEditDescriptionText() {
|
||||||
|
state = state.copyWith(editDescriptionText: "");
|
||||||
|
}
|
||||||
|
|
||||||
void resetState() {
|
void resetState() {
|
||||||
state = state.copyWith(editTitleText: "", isEditAlbum: false);
|
state = state.copyWith(
|
||||||
|
editTitleText: "",
|
||||||
|
isEditAlbum: false,
|
||||||
|
editDescriptionText: "",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> changeAlbumTitle(
|
Future<bool> changeAlbumTitle(
|
||||||
@@ -46,6 +64,28 @@ class AlbumViewerNotifier extends StateNotifier<AlbumViewerPageState> {
|
|||||||
state = state.copyWith(editTitleText: "", isEditAlbum: false);
|
state = state.copyWith(editTitleText: "", isEditAlbum: false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> changeAlbumDescription(
|
||||||
|
Album album,
|
||||||
|
String newAlbumDescription,
|
||||||
|
) async {
|
||||||
|
AlbumService service = ref.watch(albumServiceProvider);
|
||||||
|
|
||||||
|
bool isSuccess = await service.changeDescriptionAlbum(
|
||||||
|
album,
|
||||||
|
newAlbumDescription,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isSuccess) {
|
||||||
|
state = state.copyWith(editDescriptionText: "", isEditAlbum: false);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
state = state.copyWith(editDescriptionText: "", isEditAlbum: false);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final albumViewerProvider =
|
final albumViewerProvider =
|
||||||
|
|||||||
@@ -140,6 +140,10 @@ class DownloadStateNotifier extends StateNotifier<DownloadState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<bool>> downloadAllAsset(List<Asset> assets) async {
|
||||||
|
return await _downloadService.downloadAll(assets);
|
||||||
|
}
|
||||||
|
|
||||||
void downloadAsset(Asset asset, BuildContext context) async {
|
void downloadAsset(Asset asset, BuildContext context) async {
|
||||||
await _downloadService.download(asset);
|
await _downloadService.download(asset);
|
||||||
}
|
}
|
||||||
|
|||||||
8
mobile/lib/providers/infrastructure/asset.provider.dart
Normal file
8
mobile/lib/providers/infrastructure/asset.provider.dart
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/interfaces/local_asset.interface.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||||
|
|
||||||
|
final localAssetRepository = Provider<ILocalAssetRepository>(
|
||||||
|
(ref) => DriftLocalAssetRepository(ref.watch(driftProvider)),
|
||||||
|
);
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/interfaces/storage.interface.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
||||||
|
|
||||||
|
final storageRepositoryProvider = Provider<IStorageRepository>(
|
||||||
|
(ref) => StorageRepository(),
|
||||||
|
);
|
||||||
@@ -1,13 +1,16 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/services/hash.service.dart';
|
||||||
import 'package:immich_mobile/domain/services/local_sync.service.dart';
|
import 'package:immich_mobile/domain/services/local_sync.service.dart';
|
||||||
import 'package:immich_mobile/domain/services/sync_stream.service.dart';
|
import 'package:immich_mobile/domain/services/sync_stream.service.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/sync_api.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/sync_api.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart';
|
||||||
import 'package:immich_mobile/providers/api.provider.dart';
|
import 'package:immich_mobile/providers/api.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/cancel.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/cancel.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/storage.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/store.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/store.provider.dart';
|
||||||
|
|
||||||
final syncStreamServiceProvider = Provider(
|
final syncStreamServiceProvider = Provider(
|
||||||
@@ -33,3 +36,12 @@ final localSyncServiceProvider = Provider(
|
|||||||
storeService: ref.watch(storeServiceProvider),
|
storeService: ref.watch(storeServiceProvider),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final hashServiceProvider = Provider(
|
||||||
|
(ref) => HashService(
|
||||||
|
localAlbumRepository: ref.watch(localAlbumRepository),
|
||||||
|
localAssetRepository: ref.watch(localAssetRepository),
|
||||||
|
storageRepository: ref.watch(storageRepositoryProvider),
|
||||||
|
nativeSyncApi: ref.watch(nativeSyncApiProvider),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository {
|
|||||||
String name, {
|
String name, {
|
||||||
required Iterable<String> assetIds,
|
required Iterable<String> assetIds,
|
||||||
Iterable<String> sharedUserIds = const [],
|
Iterable<String> sharedUserIds = const [],
|
||||||
|
String? description,
|
||||||
}) async {
|
}) async {
|
||||||
final users = sharedUserIds.map(
|
final users = sharedUserIds.map(
|
||||||
(id) => AlbumUserCreateDto(userId: id, role: AlbumUserRole.editor),
|
(id) => AlbumUserCreateDto(userId: id, role: AlbumUserRole.editor),
|
||||||
@@ -44,6 +45,7 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository {
|
|||||||
_api.createAlbum(
|
_api.createAlbum(
|
||||||
CreateAlbumDto(
|
CreateAlbumDto(
|
||||||
albumName: name,
|
albumName: name,
|
||||||
|
description: description,
|
||||||
assetIds: assetIds.toList(),
|
assetIds: assetIds.toList(),
|
||||||
albumUsers: users.toList(),
|
albumUsers: users.toList(),
|
||||||
),
|
),
|
||||||
@@ -161,6 +163,7 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository {
|
|||||||
lastModifiedAssetTimestamp: dto.lastModifiedAssetTimestamp,
|
lastModifiedAssetTimestamp: dto.lastModifiedAssetTimestamp,
|
||||||
shared: dto.shared,
|
shared: dto.shared,
|
||||||
startDate: dto.startDate,
|
startDate: dto.startDate,
|
||||||
|
description: dto.description,
|
||||||
endDate: dto.endDate,
|
endDate: dto.endDate,
|
||||||
activityEnabled: dto.isActivityEnabled,
|
activityEnabled: dto.isActivityEnabled,
|
||||||
sortOrder: dto.order == AssetOrder.asc ? SortOrder.asc : SortOrder.desc,
|
sortOrder: dto.order == AssetOrder.asc ? SortOrder.asc : SortOrder.desc,
|
||||||
@@ -174,6 +177,7 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository {
|
|||||||
album.sharedUsers.addAll(users.map(entity.User.fromDto));
|
album.sharedUsers.addAll(users.map(entity.User.fromDto));
|
||||||
final assets = dto.assets.map(Asset.remote).toList();
|
final assets = dto.assets.map(Asset.remote).toList();
|
||||||
album.assets.addAll(assets);
|
album.assets.addAll(assets);
|
||||||
|
|
||||||
return album;
|
return album;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,30 @@ class AlbumMediaRepository implements IAlbumMediaRepository {
|
|||||||
bool get useCustomFilter =>
|
bool get useCustomFilter =>
|
||||||
Store.get(StoreKey.photoManagerCustomFilter, false);
|
Store.get(StoreKey.photoManagerCustomFilter, false);
|
||||||
|
|
||||||
|
FilterOptionGroup? _getAlbumFilter({
|
||||||
|
DateTimeCond? updateTimeCond,
|
||||||
|
bool? containsPathModified,
|
||||||
|
List<OrderOption>? orderBy,
|
||||||
|
}) =>
|
||||||
|
useCustomFilter
|
||||||
|
? FilterOptionGroup(
|
||||||
|
imageOption: const FilterOption(
|
||||||
|
needTitle: true,
|
||||||
|
sizeConstraint: SizeConstraint(ignoreSize: true),
|
||||||
|
),
|
||||||
|
videoOption: const FilterOption(
|
||||||
|
needTitle: true,
|
||||||
|
sizeConstraint: SizeConstraint(ignoreSize: true),
|
||||||
|
durationConstraint: DurationConstraint(allowNullable: true),
|
||||||
|
),
|
||||||
|
containsPathModified: containsPathModified ?? false,
|
||||||
|
createTimeCond: DateTimeCond.def().copyWith(ignore: true),
|
||||||
|
updateTimeCond:
|
||||||
|
updateTimeCond ?? DateTimeCond.def().copyWith(ignore: true),
|
||||||
|
orders: orderBy ?? [],
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<Album>> getAll() async {
|
Future<List<Album>> getAll() async {
|
||||||
final filter = useCustomFilter
|
final filter = useCustomFilter
|
||||||
@@ -30,7 +54,8 @@ class AlbumMediaRepository implements IAlbumMediaRepository {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<String>> getAssetIds(String albumId) async {
|
Future<List<String>> getAssetIds(String albumId) async {
|
||||||
final album = await AssetPathEntity.fromId(albumId);
|
final album =
|
||||||
|
await AssetPathEntity.fromId(albumId, filterOption: _getAlbumFilter());
|
||||||
final List<AssetEntity> assets =
|
final List<AssetEntity> assets =
|
||||||
await album.getAssetListRange(start: 0, end: 0x7fffffffffffffff);
|
await album.getAssetListRange(start: 0, end: 0x7fffffffffffffff);
|
||||||
return assets.map((e) => e.id).toList();
|
return assets.map((e) => e.id).toList();
|
||||||
@@ -38,7 +63,8 @@ class AlbumMediaRepository implements IAlbumMediaRepository {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<int> getAssetCount(String albumId) async {
|
Future<int> getAssetCount(String albumId) async {
|
||||||
final album = await AssetPathEntity.fromId(albumId);
|
final album =
|
||||||
|
await AssetPathEntity.fromId(albumId, filterOption: _getAlbumFilter());
|
||||||
return album.assetCountAsync;
|
return album.assetCountAsync;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,17 +79,14 @@ class AlbumMediaRepository implements IAlbumMediaRepository {
|
|||||||
}) async {
|
}) async {
|
||||||
final onDevice = await AssetPathEntity.fromId(
|
final onDevice = await AssetPathEntity.fromId(
|
||||||
albumId,
|
albumId,
|
||||||
filterOption: FilterOptionGroup(
|
filterOption: _getAlbumFilter(
|
||||||
imageOption: const FilterOption(needTitle: true),
|
|
||||||
videoOption: const FilterOption(needTitle: true),
|
|
||||||
containsPathModified: true,
|
|
||||||
updateTimeCond: modifiedFrom == null && modifiedUntil == null
|
updateTimeCond: modifiedFrom == null && modifiedUntil == null
|
||||||
? null
|
? null
|
||||||
: DateTimeCond(
|
: DateTimeCond(
|
||||||
min: modifiedFrom ?? DateTime.utc(-271820),
|
min: modifiedFrom ?? DateTime.utc(-271820),
|
||||||
max: modifiedUntil ?? DateTime.utc(275760),
|
max: modifiedUntil ?? DateTime.utc(275760),
|
||||||
),
|
),
|
||||||
orders: orderByModificationDate
|
orderBy: orderByModificationDate
|
||||||
? [const OrderOption(type: OrderOptionType.updateDate)]
|
? [const OrderOption(type: OrderOptionType.updateDate)]
|
||||||
: [],
|
: [],
|
||||||
),
|
),
|
||||||
@@ -80,7 +103,10 @@ class AlbumMediaRepository implements IAlbumMediaRepository {
|
|||||||
DateTime? modifiedFrom,
|
DateTime? modifiedFrom,
|
||||||
DateTime? modifiedUntil,
|
DateTime? modifiedUntil,
|
||||||
}) async {
|
}) async {
|
||||||
final assetPathEntity = await AssetPathEntity.fromId(id);
|
final assetPathEntity = await AssetPathEntity.fromId(
|
||||||
|
id,
|
||||||
|
filterOption: _getAlbumFilter(containsPathModified: true),
|
||||||
|
);
|
||||||
return _toAlbum(assetPathEntity);
|
return _toAlbum(assetPathEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
import 'package:immich_mobile/entities/album.entity.dart';
|
import 'package:immich_mobile/entities/album.entity.dart';
|
||||||
@@ -8,17 +9,22 @@ import 'package:immich_mobile/entities/etag.entity.dart';
|
|||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
import 'package:immich_mobile/interfaces/auth.interface.dart';
|
import 'package:immich_mobile/interfaces/auth.interface.dart';
|
||||||
import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart';
|
import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart';
|
||||||
import 'package:immich_mobile/providers/db.provider.dart';
|
import 'package:immich_mobile/providers/db.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/database.repository.dart';
|
import 'package:immich_mobile/repositories/database.repository.dart';
|
||||||
|
|
||||||
final authRepositoryProvider = Provider<IAuthRepository>(
|
final authRepositoryProvider = Provider<IAuthRepository>(
|
||||||
(ref) => AuthRepository(ref.watch(dbProvider)),
|
(ref) =>
|
||||||
|
AuthRepository(ref.watch(dbProvider), drift: ref.watch(driftProvider)),
|
||||||
);
|
);
|
||||||
|
|
||||||
class AuthRepository extends DatabaseRepository implements IAuthRepository {
|
class AuthRepository extends DatabaseRepository implements IAuthRepository {
|
||||||
AuthRepository(super.db);
|
final Drift _drift;
|
||||||
|
|
||||||
|
AuthRepository(super.db, {required Drift drift}) : _drift = drift;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> clearLocalData() {
|
Future<void> clearLocalData() {
|
||||||
@@ -29,6 +35,8 @@ class AuthRepository extends DatabaseRepository implements IAuthRepository {
|
|||||||
db.albums.clear(),
|
db.albums.clear(),
|
||||||
db.eTags.clear(),
|
db.eTags.clear(),
|
||||||
db.users.clear(),
|
db.users.clear(),
|
||||||
|
_drift.remoteAssetEntity.deleteAll(),
|
||||||
|
_drift.remoteExifEntity.deleteAll(),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ class DownloadRepository implements IDownloadRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> download(DownloadTask task) {
|
Future<List<bool>> downloadAll(List<DownloadTask> tasks) {
|
||||||
return FileDownloader().enqueue(task);
|
return FileDownloader().enqueueAll(tasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -98,8 +98,10 @@ class TimelineRepository extends DatabaseRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<RenderList> watchAllVideosTimeline() {
|
Stream<RenderList> watchAllVideosTimeline(String userId) {
|
||||||
final query = db.assets
|
final query = db.assets
|
||||||
|
.where()
|
||||||
|
.ownerIdEqualToAnyChecksum(fastHash(userId))
|
||||||
.filter()
|
.filter()
|
||||||
.isTrashedEqualTo(false)
|
.isTrashedEqualTo(false)
|
||||||
.visibilityEqualTo(AssetVisibilityEnum.timeline)
|
.visibilityEqualTo(AssetVisibilityEnum.timeline)
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ import 'package:immich_mobile/pages/search/recently_taken.page.dart';
|
|||||||
import 'package:immich_mobile/pages/search/search.page.dart';
|
import 'package:immich_mobile/pages/search/search.page.dart';
|
||||||
import 'package:immich_mobile/pages/share_intent/share_intent.page.dart';
|
import 'package:immich_mobile/pages/share_intent/share_intent.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/dev/feat_in_development.page.dart';
|
import 'package:immich_mobile/presentation/pages/dev/feat_in_development.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/dev/local_media_stat.page.dart';
|
import 'package:immich_mobile/presentation/pages/dev/media_stat.page.dart';
|
||||||
import 'package:immich_mobile/providers/api.provider.dart';
|
import 'package:immich_mobile/providers/api.provider.dart';
|
||||||
import 'package:immich_mobile/providers/gallery_permission.provider.dart';
|
import 'package:immich_mobile/providers/gallery_permission.provider.dart';
|
||||||
import 'package:immich_mobile/routing/auth_guard.dart';
|
import 'package:immich_mobile/routing/auth_guard.dart';
|
||||||
@@ -326,5 +326,9 @@ class AppRouter extends RootStackRouter {
|
|||||||
page: LocalMediaSummaryRoute.page,
|
page: LocalMediaSummaryRoute.page,
|
||||||
guards: [_authGuard, _duplicateGuard],
|
guards: [_authGuard, _duplicateGuard],
|
||||||
),
|
),
|
||||||
|
AutoRoute(
|
||||||
|
page: RemoteMediaSummaryRoute.page,
|
||||||
|
guards: [_authGuard, _duplicateGuard],
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1356,6 +1356,22 @@ class RecentlyTakenRoute extends PageRouteInfo<void> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [RemoteMediaSummaryPage]
|
||||||
|
class RemoteMediaSummaryRoute extends PageRouteInfo<void> {
|
||||||
|
const RemoteMediaSummaryRoute({List<PageRouteInfo>? children})
|
||||||
|
: super(RemoteMediaSummaryRoute.name, initialChildren: children);
|
||||||
|
|
||||||
|
static const String name = 'RemoteMediaSummaryRoute';
|
||||||
|
|
||||||
|
static PageInfo page = PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
return const RemoteMediaSummaryPage();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [SearchPage]
|
/// [SearchPage]
|
||||||
class SearchRoute extends PageRouteInfo<SearchRouteArgs> {
|
class SearchRoute extends PageRouteInfo<SearchRouteArgs> {
|
||||||
|
|||||||
@@ -422,6 +422,25 @@ class AlbumService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> changeDescriptionAlbum(
|
||||||
|
Album album,
|
||||||
|
String newAlbumDescription,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
final updatedAlbum = await _albumApiRepository.update(
|
||||||
|
album.remoteId!,
|
||||||
|
description: newAlbumDescription,
|
||||||
|
);
|
||||||
|
|
||||||
|
album.description = updatedAlbum.description;
|
||||||
|
await _albumRepository.update(album);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint("Error changeDescriptionAlbum ${e.toString()}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<Album?> getAlbumByName(
|
Future<Album?> getAlbumByName(
|
||||||
String name, {
|
String name, {
|
||||||
bool? remote,
|
bool? remote,
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ class AppSettingsService {
|
|||||||
return Store.get(setting.storeKey, setting.defaultValue);
|
return Store.get(setting.storeKey, setting.defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSetting<T>(AppSettingsEnum<T> setting, T value) {
|
Future<void> setSetting<T>(AppSettingsEnum<T> setting, T value) {
|
||||||
Store.put(setting.storeKey, value);
|
return Store.put(setting.storeKey, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,9 +159,19 @@ class DownloadService {
|
|||||||
return await FileDownloader().cancelTaskWithId(id);
|
return await FileDownloader().cancelTaskWithId(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<bool>> downloadAll(List<Asset> assets) async {
|
||||||
|
return await _downloadRepository
|
||||||
|
.downloadAll(assets.expand(_createDownloadTasks).toList());
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> download(Asset asset) async {
|
Future<void> download(Asset asset) async {
|
||||||
|
final tasks = _createDownloadTasks(asset);
|
||||||
|
await _downloadRepository.downloadAll(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<DownloadTask> _createDownloadTasks(Asset asset) {
|
||||||
if (asset.isImage && asset.livePhotoVideoId != null && Platform.isIOS) {
|
if (asset.isImage && asset.livePhotoVideoId != null && Platform.isIOS) {
|
||||||
await _downloadRepository.download(
|
return [
|
||||||
_buildDownloadTask(
|
_buildDownloadTask(
|
||||||
asset.remoteId!,
|
asset.remoteId!,
|
||||||
asset.fileName,
|
asset.fileName,
|
||||||
@@ -171,9 +181,6 @@ class DownloadService {
|
|||||||
id: asset.remoteId!,
|
id: asset.remoteId!,
|
||||||
).toJson(),
|
).toJson(),
|
||||||
),
|
),
|
||||||
);
|
|
||||||
|
|
||||||
await _downloadRepository.download(
|
|
||||||
_buildDownloadTask(
|
_buildDownloadTask(
|
||||||
asset.livePhotoVideoId!,
|
asset.livePhotoVideoId!,
|
||||||
asset.fileName
|
asset.fileName
|
||||||
@@ -185,16 +192,20 @@ class DownloadService {
|
|||||||
id: asset.remoteId!,
|
id: asset.remoteId!,
|
||||||
).toJson(),
|
).toJson(),
|
||||||
),
|
),
|
||||||
);
|
];
|
||||||
} else {
|
|
||||||
await _downloadRepository.download(
|
|
||||||
_buildDownloadTask(
|
|
||||||
asset.remoteId!,
|
|
||||||
asset.fileName,
|
|
||||||
group: asset.isImage ? downloadGroupImage : downloadGroupVideo,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (asset.remoteId == null) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
_buildDownloadTask(
|
||||||
|
asset.remoteId!,
|
||||||
|
asset.fileName,
|
||||||
|
group: asset.isImage ? downloadGroupImage : downloadGroupVideo,
|
||||||
|
),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadTask _buildDownloadTask(
|
DownloadTask _buildDownloadTask(
|
||||||
|
|||||||
@@ -451,6 +451,7 @@ class SyncService {
|
|||||||
final usersToLink = await _userRepository.getByUserIds(userIdsToAdd);
|
final usersToLink = await _userRepository.getByUserIds(userIdsToAdd);
|
||||||
|
|
||||||
album.name = dto.name;
|
album.name = dto.name;
|
||||||
|
album.description = dto.description;
|
||||||
album.shared = dto.shared;
|
album.shared = dto.shared;
|
||||||
album.createdAt = dto.createdAt;
|
album.createdAt = dto.createdAt;
|
||||||
album.modifiedAt = dto.modifiedAt;
|
album.modifiedAt = dto.modifiedAt;
|
||||||
@@ -643,6 +644,7 @@ class SyncService {
|
|||||||
toUpdate.isEmpty &&
|
toUpdate.isEmpty &&
|
||||||
toDelete.isEmpty &&
|
toDelete.isEmpty &&
|
||||||
dbAlbum.name == deviceAlbum.name &&
|
dbAlbum.name == deviceAlbum.name &&
|
||||||
|
dbAlbum.description == deviceAlbum.description &&
|
||||||
dbAlbum.modifiedAt.isAtSameMomentAs(deviceAlbum.modifiedAt)) {
|
dbAlbum.modifiedAt.isAtSameMomentAs(deviceAlbum.modifiedAt)) {
|
||||||
// changes only affeted excluded albums
|
// changes only affeted excluded albums
|
||||||
_log.info(
|
_log.info(
|
||||||
@@ -670,6 +672,7 @@ class SyncService {
|
|||||||
deleteCandidates.addAll(toDelete);
|
deleteCandidates.addAll(toDelete);
|
||||||
existing.addAll(existingInDb);
|
existing.addAll(existingInDb);
|
||||||
dbAlbum.name = deviceAlbum.name;
|
dbAlbum.name = deviceAlbum.name;
|
||||||
|
dbAlbum.description = deviceAlbum.description;
|
||||||
dbAlbum.modifiedAt = deviceAlbum.modifiedAt;
|
dbAlbum.modifiedAt = deviceAlbum.modifiedAt;
|
||||||
if (dbAlbum.thumbnail.value != null &&
|
if (dbAlbum.thumbnail.value != null &&
|
||||||
toDelete.contains(dbAlbum.thumbnail.value)) {
|
toDelete.contains(dbAlbum.thumbnail.value)) {
|
||||||
@@ -943,6 +946,7 @@ class SyncService {
|
|||||||
Album dbAlbum,
|
Album dbAlbum,
|
||||||
) async {
|
) async {
|
||||||
return deviceAlbum.name != dbAlbum.name ||
|
return deviceAlbum.name != dbAlbum.name ||
|
||||||
|
deviceAlbum.description != dbAlbum.description ||
|
||||||
!deviceAlbum.modifiedAt.isAtSameMomentAs(dbAlbum.modifiedAt) ||
|
!deviceAlbum.modifiedAt.isAtSameMomentAs(dbAlbum.modifiedAt) ||
|
||||||
await _albumMediaRepository.getAssetCount(deviceAlbum.localId!) !=
|
await _albumMediaRepository.getAssetCount(deviceAlbum.localId!) !=
|
||||||
(await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))
|
(await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))
|
||||||
@@ -1101,6 +1105,7 @@ class SyncService {
|
|||||||
bool _hasRemoteAlbumChanged(Album remoteAlbum, Album dbAlbum) {
|
bool _hasRemoteAlbumChanged(Album remoteAlbum, Album dbAlbum) {
|
||||||
return remoteAlbum.remoteAssetCount != dbAlbum.assetCount ||
|
return remoteAlbum.remoteAssetCount != dbAlbum.assetCount ||
|
||||||
remoteAlbum.name != dbAlbum.name ||
|
remoteAlbum.name != dbAlbum.name ||
|
||||||
|
remoteAlbum.description != dbAlbum.description ||
|
||||||
remoteAlbum.remoteThumbnailAssetId != dbAlbum.thumbnail.value?.remoteId ||
|
remoteAlbum.remoteThumbnailAssetId != dbAlbum.thumbnail.value?.remoteId ||
|
||||||
remoteAlbum.shared != dbAlbum.shared ||
|
remoteAlbum.shared != dbAlbum.shared ||
|
||||||
remoteAlbum.remoteUsers.length != dbAlbum.sharedUsers.length ||
|
remoteAlbum.remoteUsers.length != dbAlbum.sharedUsers.length ||
|
||||||
|
|||||||
@@ -75,7 +75,9 @@ class TimelineService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Stream<RenderList> watchAllVideosTimeline() {
|
Stream<RenderList> watchAllVideosTimeline() {
|
||||||
return _timelineRepository.watchAllVideosTimeline();
|
final user = _userService.getMyUser();
|
||||||
|
|
||||||
|
return _timelineRepository.watchAllVideosTimeline(user.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<RenderList> getTimelineFromAssets(
|
Future<RenderList> getTimelineFromAssets(
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
// ignore_for_file: avoid-unsafe-collection-methods
|
// ignore_for_file: avoid-unsafe-collection-methods
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
|
import 'package:immich_mobile/domain/utils/background_sync.dart';
|
||||||
import 'package:immich_mobile/entities/album.entity.dart';
|
import 'package:immich_mobile/entities/album.entity.dart';
|
||||||
import 'package:immich_mobile/entities/android_device_asset.entity.dart';
|
import 'package:immich_mobile/entities/android_device_asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
@@ -13,14 +16,16 @@ import 'package:immich_mobile/entities/ios_device_asset.entity.dart';
|
|||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/device_asset.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/device_asset.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
import 'package:immich_mobile/utils/diff.dart';
|
import 'package:immich_mobile/utils/diff.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
// ignore: import_rule_photo_manager
|
// ignore: import_rule_photo_manager
|
||||||
import 'package:photo_manager/photo_manager.dart';
|
import 'package:photo_manager/photo_manager.dart';
|
||||||
|
|
||||||
const int targetVersion = 11;
|
const int targetVersion = 12;
|
||||||
|
|
||||||
Future<void> migrateDatabaseIfNeeded(Isar db) async {
|
Future<void> migrateDatabaseIfNeeded(Isar db) async {
|
||||||
final int version = Store.get(StoreKey.version, targetVersion);
|
final int version = Store.get(StoreKey.version, targetVersion);
|
||||||
@@ -45,7 +50,15 @@ Future<void> migrateDatabaseIfNeeded(Isar db) async {
|
|||||||
await _migrateDeviceAsset(db);
|
await _migrateDeviceAsset(db);
|
||||||
}
|
}
|
||||||
|
|
||||||
final shouldTruncate = version < 8 && version < targetVersion;
|
if (version < 12 && (!kReleaseMode)) {
|
||||||
|
final backgroundSync = BackgroundSyncManager();
|
||||||
|
await backgroundSync.syncLocal();
|
||||||
|
final drift = Drift();
|
||||||
|
await _migrateDeviceAssetToSqlite(db, drift);
|
||||||
|
await drift.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
final shouldTruncate = version < 8 || version < targetVersion;
|
||||||
if (shouldTruncate) {
|
if (shouldTruncate) {
|
||||||
await _migrateTo(db, targetVersion);
|
await _migrateTo(db, targetVersion);
|
||||||
}
|
}
|
||||||
@@ -154,6 +167,28 @@ Future<void> _migrateDeviceAsset(Isar db) async {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _migrateDeviceAssetToSqlite(Isar db, Drift drift) async {
|
||||||
|
final isarDeviceAssets =
|
||||||
|
await db.deviceAssetEntitys.where().sortByAssetId().findAll();
|
||||||
|
await drift.batch((batch) {
|
||||||
|
for (final deviceAsset in isarDeviceAssets) {
|
||||||
|
final companion = LocalAssetEntityCompanion(
|
||||||
|
updatedAt: Value(deviceAsset.modifiedTime),
|
||||||
|
id: Value(deviceAsset.assetId),
|
||||||
|
checksum: Value(base64.encode(deviceAsset.hash)),
|
||||||
|
);
|
||||||
|
batch.insert<$LocalAssetEntityTable, LocalAssetEntityData>(
|
||||||
|
drift.localAssetEntity,
|
||||||
|
companion,
|
||||||
|
onConflict: DoUpdate(
|
||||||
|
(_) => companion,
|
||||||
|
where: (old) => old.updatedAt.equals(deviceAsset.modifiedTime),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
class _DeviceAsset {
|
class _DeviceAsset {
|
||||||
final String assetId;
|
final String assetId;
|
||||||
final List<int>? hash;
|
final List<int>? hash;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ dynamic upgradeDto(dynamic value, String targetType) {
|
|||||||
addDefault(value, 'tags', TagsResponse().toJson());
|
addDefault(value, 'tags', TagsResponse().toJson());
|
||||||
addDefault(value, 'sharedLinks', SharedLinksResponse().toJson());
|
addDefault(value, 'sharedLinks', SharedLinksResponse().toJson());
|
||||||
addDefault(value, 'cast', CastResponse().toJson());
|
addDefault(value, 'cast', CastResponse().toJson());
|
||||||
|
addDefault(value, 'albums', {'defaultAssetOrder': 'desc'});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'ServerConfigDto':
|
case 'ServerConfigDto':
|
||||||
@@ -42,6 +43,11 @@ dynamic upgradeDto(dynamic value, String targetType) {
|
|||||||
addDefault(value, 'profileChangedAt', DateTime.now().toIso8601String());
|
addDefault(value, 'profileChangedAt', DateTime.now().toIso8601String());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'LoginResponseDto':
|
||||||
|
if (value is Map) {
|
||||||
|
addDefault(value, 'isOnboarded', false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ String t(String key, [Map<String, Object>? args]) {
|
|||||||
try {
|
try {
|
||||||
String message = key.tr();
|
String message = key.tr();
|
||||||
if (args != null) {
|
if (args != null) {
|
||||||
return MessageFormat(message).format(args);
|
return MessageFormat(message, locale: Intl.defaultLocale ?? 'en')
|
||||||
|
.format(args);
|
||||||
}
|
}
|
||||||
return message;
|
return message;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user