Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 023d2195f9 | |||
| c482bdfae7 | |||
| 0dadfc52dd | |||
| df4a27e8a7 | |||
| 1f9813a28e | |||
| bbfff45058 | |||
| 87dd09d103 | |||
| dd94ad17aa | |||
| a87c2e82cd | |||
| a11ab4c3f7 | |||
| ebf2f9fd7b | |||
| 683af67344 | |||
| d149d6fa3f | |||
| 8c5269c002 | |||
| cf91d9bdfc | |||
| 5579554532 | |||
| 7e35e6985e | |||
| 56756baea2 | |||
| d5923241b5 | |||
| cc471806fe | |||
| 4ce9bce414 | |||
| 2f5d75ce21 |
@@ -74,7 +74,7 @@ install_dependencies() {
|
|||||||
(
|
(
|
||||||
cd "${IMMICH_WORKSPACE}" || exit 1
|
cd "${IMMICH_WORKSPACE}" || exit 1
|
||||||
export CI=1 FROZEN=1 OFFLINE=1
|
export CI=1 FROZEN=1 OFFLINE=1
|
||||||
run_cmd make setup-web-dev setup-server-dev
|
run_cmd make setup-dev
|
||||||
)
|
)
|
||||||
log ""
|
log ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,8 @@ cd "${IMMICH_WORKSPACE}/server" || (
|
|||||||
exit 1
|
exit 1
|
||||||
)
|
)
|
||||||
|
|
||||||
CI=1 pnpm install
|
|
||||||
while true; do
|
while true; do
|
||||||
run_cmd pnpm exec nest start --debug "0.0.0.0:9230" --watch
|
run_cmd node ./node_modules/.bin/nest start --debug "0.0.0.0:9230" --watch
|
||||||
log "Nest API Server crashed with exit code $?. Respawning in 3s ..."
|
log "Nest API Server crashed with exit code $?. Respawning in 3s ..."
|
||||||
sleep 3
|
sleep 3
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ until curl --output /dev/null --silent --head --fail "http://127.0.0.1:${IMMICH_
|
|||||||
done
|
done
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
run_cmd pnpm exec vite dev --host 0.0.0.0 --port "${DEV_PORT}"
|
run_cmd node ./node_modules/.bin/vite dev --host 0.0.0.0 --port "${DEV_PORT}"
|
||||||
log "Web crashed with exit code $?. Respawning in 3s ..."
|
log "Web crashed with exit code $?. Respawning in 3s ..."
|
||||||
sleep 3
|
sleep 3
|
||||||
done
|
done
|
||||||
|
|||||||
+17
-17
@@ -1,41 +1,41 @@
|
|||||||
.vscode/
|
.vscode/
|
||||||
.github/
|
.github/
|
||||||
.git/
|
.git/
|
||||||
.env*
|
|
||||||
*.log
|
|
||||||
*.tmp
|
|
||||||
*.temp
|
|
||||||
|
|
||||||
**/Dockerfile
|
|
||||||
**/node_modules/
|
|
||||||
**/.pnpm-store/
|
|
||||||
**/dist/
|
|
||||||
**/coverage/
|
|
||||||
**/build/
|
|
||||||
|
|
||||||
design/
|
design/
|
||||||
docker/
|
docker/
|
||||||
|
Dockerfile
|
||||||
!docker/scripts
|
!docker/scripts
|
||||||
|
|
||||||
docs/
|
docs/
|
||||||
!docs/package.json
|
!docs/package.json
|
||||||
!docs/package-lock.json
|
!docs/package-lock.json
|
||||||
|
|
||||||
e2e/
|
e2e/
|
||||||
!e2e/package.json
|
!e2e/package.json
|
||||||
!e2e/package-lock.json
|
!e2e/package-lock.json
|
||||||
|
|
||||||
fastlane/
|
fastlane/
|
||||||
machine-learning/
|
machine-learning/
|
||||||
misc/
|
misc/
|
||||||
mobile/
|
mobile/
|
||||||
|
|
||||||
open-api/typescript-sdk/build/
|
cli/coverage/
|
||||||
!open-api/typescript-sdk/package.json
|
cli/dist/
|
||||||
!open-api/typescript-sdk/package-lock.json
|
cli/node_modules/
|
||||||
|
cli/Dockerfile
|
||||||
|
|
||||||
|
open-api/typescript-sdk/build/
|
||||||
|
open-api/typescript-sdk/node_modules/
|
||||||
|
|
||||||
|
server/coverage/
|
||||||
|
server/node_modules/
|
||||||
server/upload/
|
server/upload/
|
||||||
server/src/queries
|
server/src/queries
|
||||||
|
server/dist/
|
||||||
server/www/
|
server/www/
|
||||||
|
server/Dockerfile
|
||||||
|
|
||||||
|
web/node_modules/
|
||||||
|
web/coverage/
|
||||||
web/.svelte-kit
|
web/.svelte-kit
|
||||||
|
web/build/
|
||||||
|
web/.env
|
||||||
|
web/Dockerfile
|
||||||
|
|||||||
Generated
+28
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": ".github",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"devDependencies": {
|
||||||
|
"prettier": "^3.5.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prettier": {
|
||||||
|
"version": "3.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
|
||||||
|
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"prettier": "bin/prettier.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
-15
@@ -33,24 +33,21 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup pnpm
|
# Setup .npmrc file to publish to npm
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
|
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
cache: 'pnpm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Setup typescript-sdk
|
- name: Prepare SDK
|
||||||
run: pnpm install && pnpm run build
|
run: npm ci --prefix ../open-api/typescript-sdk/
|
||||||
working-directory: ./open-api/typescript-sdk
|
- name: Build SDK
|
||||||
|
run: npm run build --prefix ../open-api/typescript-sdk/
|
||||||
- run: pnpm install --frozen-lockfile
|
- run: npm ci
|
||||||
- run: pnpm build
|
- run: npm run build
|
||||||
- run: pnpm publish
|
- run: npm publish
|
||||||
if: ${{ github.event_name == 'release' }}
|
if: ${{ github.event_name == 'release' }}
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
|
uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@@ -63,7 +63,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
|
uses: github/codeql-action/autobuild@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||||
@@ -76,6 +76,6 @@ jobs:
|
|||||||
# ./location_of_script_within_repo/buildscript.sh
|
# ./location_of_script_within_repo/buildscript.sh
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
|
uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||||
with:
|
with:
|
||||||
category: '/language:${{matrix.language}}'
|
category: '/language:${{matrix.language}}'
|
||||||
|
|||||||
@@ -53,24 +53,21 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup pnpm
|
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './docs/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run install
|
- name: Run npm install
|
||||||
run: pnpm install
|
run: npm ci
|
||||||
|
|
||||||
- name: Check formatting
|
- name: Check formatting
|
||||||
run: pnpm format
|
run: npm run format
|
||||||
|
|
||||||
- name: Run build
|
- name: Run build
|
||||||
run: pnpm build
|
run: npm run build
|
||||||
|
|
||||||
- name: Upload build output
|
- name: Upload build output
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Fix formatting
|
- name: Fix formatting
|
||||||
run: make install-all && make format-all
|
run: make install-all && make format-all
|
||||||
|
|||||||
@@ -20,21 +20,18 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup pnpm
|
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
|
||||||
|
|
||||||
# Setup .npmrc file to publish to npm
|
# Setup .npmrc file to publish to npm
|
||||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './open-api/typescript-sdk/.nvmrc'
|
node-version-file: './open-api/typescript-sdk/.nvmrc'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
cache: 'pnpm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: pnpm install --frozen-lockfile
|
run: npm ci
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm build
|
run: npm run build
|
||||||
- name: Publish
|
- name: Publish
|
||||||
run: pnpm publish
|
run: npm publish
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./mobile
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
@@ -56,27 +59,23 @@ jobs:
|
|||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: dart pub get
|
run: dart pub get
|
||||||
working-directory: ./mobile
|
|
||||||
|
|
||||||
- name: Install DCM
|
- name: Install DCM
|
||||||
run: |
|
# TODO: Move to upstream after https://github.com/CQLabs/setup-dcm/pull/235 merges
|
||||||
sudo apt-get update
|
uses: bo0tzz/setup-dcm@b4952ab813659c03513b57bd78bfe3f634171f8a
|
||||||
wget -qO- https://dcm.dev/pgp-key.public | sudo gpg --dearmor -o /usr/share/keyrings/dcm.gpg
|
with:
|
||||||
echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
sudo apt-get update
|
version: auto
|
||||||
sudo apt-get install dcm
|
working-directory: ./mobile
|
||||||
|
|
||||||
- name: Generate translation file
|
- name: Generate translation file
|
||||||
run: make translation
|
run: make translation
|
||||||
working-directory: ./mobile
|
|
||||||
|
|
||||||
- name: Run Build Runner
|
- name: Run Build Runner
|
||||||
run: make build
|
run: make build
|
||||||
working-directory: ./mobile
|
|
||||||
|
|
||||||
- name: Generate platform API
|
- name: Generate platform API
|
||||||
run: make pigeon
|
run: make pigeon
|
||||||
working-directory: ./mobile
|
|
||||||
|
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||||
@@ -98,19 +97,16 @@ jobs:
|
|||||||
|
|
||||||
- name: Run dart analyze
|
- name: Run dart analyze
|
||||||
run: dart analyze --fatal-infos
|
run: dart analyze --fatal-infos
|
||||||
working-directory: ./mobile
|
|
||||||
|
|
||||||
- name: Run dart format
|
- name: Run dart format
|
||||||
run: dart format lib/ --set-exit-if-changed
|
run: dart format lib/ --set-exit-if-changed
|
||||||
working-directory: ./mobile
|
|
||||||
|
|
||||||
- name: Run dart custom_lint
|
- name: Run dart custom_lint
|
||||||
run: dart run custom_lint
|
run: dart run custom_lint
|
||||||
working-directory: ./mobile
|
|
||||||
|
|
||||||
|
# TODO: Use https://github.com/CQLabs/dcm-action
|
||||||
- name: Run DCM
|
- name: Run DCM
|
||||||
run: dcm analyze lib --fatal-style --fatal-warnings
|
run: dcm analyze lib --fatal-style --fatal-warnings
|
||||||
working-directory: ./mobile
|
|
||||||
|
|
||||||
zizmor:
|
zizmor:
|
||||||
name: zizmor
|
name: zizmor
|
||||||
@@ -134,7 +130,7 @@ jobs:
|
|||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Upload SARIF file
|
- name: Upload SARIF file
|
||||||
uses: github/codeql-action/upload-sarif@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
|
uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||||
with:
|
with:
|
||||||
sarif_file: results.sarif
|
sarif_file: results.sarif
|
||||||
category: zizmor
|
category: zizmor
|
||||||
|
|||||||
+85
-129
@@ -80,33 +80,30 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup pnpm
|
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run package manager install
|
- name: Run npm install
|
||||||
run: pnpm install
|
run: npm ci
|
||||||
|
|
||||||
- name: Run linter
|
- name: Run linter
|
||||||
run: pnpm lint
|
run: npm run lint
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run formatter
|
- name: Run formatter
|
||||||
run: pnpm format
|
run: npm run format
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run tsc
|
- name: Run tsc
|
||||||
run: pnpm check
|
run: npm run check
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run small tests & coverage
|
- name: Run small tests & coverage
|
||||||
run: pnpm test
|
run: npm test
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
cli-unit-tests:
|
cli-unit-tests:
|
||||||
@@ -126,37 +123,34 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup pnpm
|
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Setup typescript-sdk
|
- name: Setup typescript-sdk
|
||||||
run: pnpm install && pnpm run build
|
run: npm ci && npm run build
|
||||||
working-directory: ./open-api/typescript-sdk
|
working-directory: ./open-api/typescript-sdk
|
||||||
|
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: pnpm install
|
run: npm ci
|
||||||
|
|
||||||
- name: Run linter
|
- name: Run linter
|
||||||
run: pnpm lint
|
run: npm run lint
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run formatter
|
- name: Run formatter
|
||||||
run: pnpm format
|
run: npm run format
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run tsc
|
- name: Run tsc
|
||||||
run: pnpm check
|
run: npm run check
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run unit tests & coverage
|
- name: Run unit tests & coverage
|
||||||
run: pnpm test
|
run: npm run test
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
cli-unit-tests-win:
|
cli-unit-tests-win:
|
||||||
@@ -176,30 +170,27 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup pnpm
|
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Setup typescript-sdk
|
- name: Setup typescript-sdk
|
||||||
run: pnpm install --frozen-lockfile && pnpm build
|
run: npm ci && npm run build
|
||||||
working-directory: ./open-api/typescript-sdk
|
working-directory: ./open-api/typescript-sdk
|
||||||
|
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: pnpm install --frozen-lockfile
|
run: npm ci
|
||||||
|
|
||||||
# Skip linter & formatter in Windows test.
|
# Skip linter & formatter in Windows test.
|
||||||
- name: Run tsc
|
- name: Run tsc
|
||||||
run: pnpm check
|
run: npm run check
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run unit tests & coverage
|
- name: Run unit tests & coverage
|
||||||
run: pnpm test
|
run: npm run test
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
web-lint:
|
web-lint:
|
||||||
@@ -219,33 +210,30 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup pnpm
|
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run setup typescript-sdk
|
- name: Run setup typescript-sdk
|
||||||
run: pnpm install --frozen-lockfile && pnpm build
|
run: npm ci && npm run build
|
||||||
working-directory: ./open-api/typescript-sdk
|
working-directory: ./open-api/typescript-sdk
|
||||||
|
|
||||||
- name: Run npm install
|
- name: Run npm install
|
||||||
run: pnpm rebuild && pnpm install --frozen-lockfile
|
run: npm ci
|
||||||
|
|
||||||
- name: Run linter
|
- name: Run linter
|
||||||
run: pnpm lint:p
|
run: npm run lint:p
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run formatter
|
- name: Run formatter
|
||||||
run: pnpm format
|
run: npm run format
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run svelte checks
|
- name: Run svelte checks
|
||||||
run: pnpm check:svelte
|
run: npm run check:svelte
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
web-unit-tests:
|
web-unit-tests:
|
||||||
@@ -265,29 +253,26 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup pnpm
|
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run setup typescript-sdk
|
- name: Run setup typescript-sdk
|
||||||
run: pnpm install --frozen-lockfile && pnpm build
|
run: npm ci && npm run build
|
||||||
working-directory: ./open-api/typescript-sdk
|
working-directory: ./open-api/typescript-sdk
|
||||||
|
|
||||||
- name: Run npm install
|
- name: Run npm install
|
||||||
run: pnpm install --frozen-lockfile
|
run: npm ci
|
||||||
|
|
||||||
- name: Run tsc
|
- name: Run tsc
|
||||||
run: pnpm check:typescript
|
run: npm run check:typescript
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run unit tests & coverage
|
- name: Run unit tests & coverage
|
||||||
run: pnpm test
|
run: npm run test
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
i18n-tests:
|
i18n-tests:
|
||||||
@@ -303,21 +288,18 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup pnpm
|
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm --filter=immich-web install --frozen-lockfile
|
run: npm --prefix=web ci
|
||||||
|
|
||||||
- name: Format
|
- name: Format
|
||||||
run: pnpm --filter=immich-web format:i18n
|
run: npm --prefix=web run format:i18n
|
||||||
|
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||||
@@ -352,35 +334,32 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup pnpm
|
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run setup typescript-sdk
|
- name: Run setup typescript-sdk
|
||||||
run: pnpm install --frozen-lockfile && pnpm build
|
run: npm ci && npm run build
|
||||||
working-directory: ./open-api/typescript-sdk
|
working-directory: ./open-api/typescript-sdk
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install --frozen-lockfile
|
run: npm ci
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run linter
|
- name: Run linter
|
||||||
run: pnpm lint
|
run: npm run lint
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run formatter
|
- name: Run formatter
|
||||||
run: pnpm format
|
run: npm run format
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run tsc
|
- name: Run tsc
|
||||||
run: pnpm check
|
run: npm run check
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
server-medium-tests:
|
server-medium-tests:
|
||||||
@@ -400,21 +379,18 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup pnpm
|
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run npm install
|
- name: Run npm install
|
||||||
run: pnpm install --frozen-lockfile
|
run: npm ci
|
||||||
|
|
||||||
- name: Run medium tests
|
- name: Run medium tests
|
||||||
run: pnpm test:medium
|
run: npm run test:medium
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
e2e-tests-server-cli:
|
e2e-tests-server-cli:
|
||||||
@@ -438,33 +414,25 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
|
|
||||||
- name: Setup pnpm
|
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run setup typescript-sdk
|
- name: Run setup typescript-sdk
|
||||||
run: pnpm install --frozen-lockfile && pnpm build
|
run: npm ci && npm run build
|
||||||
working-directory: ./open-api/typescript-sdk
|
working-directory: ./open-api/typescript-sdk
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run setup web
|
|
||||||
run: pnpm install --frozen-lockfile && pnpm exec svelte-kit sync
|
|
||||||
working-directory: ./web
|
|
||||||
if: ${{ !cancelled() }}
|
|
||||||
|
|
||||||
- name: Run setup cli
|
- name: Run setup cli
|
||||||
run: pnpm install --frozen-lockfile && pnpm build
|
run: npm ci && npm run build
|
||||||
working-directory: ./cli
|
working-directory: ./cli
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install --frozen-lockfile
|
run: npm ci
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Docker build
|
- name: Docker build
|
||||||
@@ -472,7 +440,7 @@ jobs:
|
|||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Run e2e tests (api & cli)
|
- name: Run e2e tests (api & cli)
|
||||||
run: pnpm test
|
run: npm run test
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
e2e-tests-web:
|
e2e-tests-web:
|
||||||
@@ -496,23 +464,20 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
|
|
||||||
- name: Setup pnpm
|
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run setup typescript-sdk
|
- name: Run setup typescript-sdk
|
||||||
run: pnpm install --frozen-lockfile && pnpm build
|
run: npm ci && npm run build
|
||||||
working-directory: ./open-api/typescript-sdk
|
working-directory: ./open-api/typescript-sdk
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install --frozen-lockfile
|
run: npm ci
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
- name: Install Playwright Browsers
|
- name: Install Playwright Browsers
|
||||||
@@ -619,21 +584,18 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup pnpm
|
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './.github/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Run npm install
|
- name: Run npm install
|
||||||
run: pnpm install --frozen-lockfile
|
run: npm ci
|
||||||
|
|
||||||
- name: Run formatter
|
- name: Run formatter
|
||||||
run: pnpm format
|
run: npm run format
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
shellcheck:
|
shellcheck:
|
||||||
@@ -665,21 +627,18 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup pnpm
|
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Install server dependencies
|
- name: Install server dependencies
|
||||||
run: pnpm --filter immich install --frozen-lockfile
|
run: npm --prefix=server ci
|
||||||
|
|
||||||
- name: Build the app
|
- name: Build the app
|
||||||
run: pnpm --filter immich build
|
run: npm --prefix=server run build
|
||||||
|
|
||||||
- name: Run API generation
|
- name: Run API generation
|
||||||
run: make open-api
|
run: make open-api
|
||||||
@@ -731,31 +690,28 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup pnpm
|
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
|
||||||
- name: Install server dependencies
|
- name: Install server dependencies
|
||||||
run: pnpm install --frozen-lockfile
|
run: npm ci
|
||||||
|
|
||||||
- name: Build the app
|
- name: Build the app
|
||||||
run: pnpm build
|
run: npm run build
|
||||||
|
|
||||||
- name: Run existing migrations
|
- name: Run existing migrations
|
||||||
run: pnpm migrations:run
|
run: npm run migrations:run
|
||||||
|
|
||||||
- name: Test npm run schema:reset command works
|
- name: Test npm run schema:reset command works
|
||||||
run: pnpm schema:reset
|
run: npm run schema:reset
|
||||||
|
|
||||||
- name: Generate new migrations
|
- name: Generate new migrations
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: pnpm migrations:generate src/TestMigration
|
run: npm run migrations:generate src/TestMigration
|
||||||
|
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||||
@@ -774,7 +730,7 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
- name: Run SQL generation
|
- name: Run SQL generation
|
||||||
run: pnpm sync:sql
|
run: npm run sync:sql
|
||||||
env:
|
env:
|
||||||
DB_URL: postgres://postgres:postgres@localhost:5432/immich
|
DB_URL: postgres://postgres:postgres@localhost:5432/immich
|
||||||
|
|
||||||
|
|||||||
@@ -24,4 +24,3 @@ mobile/android/fastlane/report.xml
|
|||||||
mobile/ios/fastlane/report.xml
|
mobile/ios/fastlane/report.xml
|
||||||
|
|
||||||
vite.config.js.timestamp-*
|
vite.config.js.timestamp-*
|
||||||
.pnpm-store
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ open-api-typescript:
|
|||||||
cd ./open-api && bash ./bin/generate-open-api.sh typescript
|
cd ./open-api && bash ./bin/generate-open-api.sh typescript
|
||||||
|
|
||||||
sql:
|
sql:
|
||||||
pnpm --filter immich run sync:sql
|
npm --prefix server run sync:sql
|
||||||
|
|
||||||
attach-server:
|
attach-server:
|
||||||
docker exec -it docker_immich-server_1 sh
|
docker exec -it docker_immich-server_1 sh
|
||||||
@@ -50,40 +50,31 @@ renovate:
|
|||||||
|
|
||||||
MODULES = e2e server web cli sdk docs .github
|
MODULES = e2e server web cli sdk docs .github
|
||||||
|
|
||||||
# directory to package name mapping function
|
|
||||||
# cli = @immich/cli
|
|
||||||
# docs = documentation
|
|
||||||
# e2e = immich-e2e
|
|
||||||
# open-api/typescript-sdk = @immich/sdk
|
|
||||||
# server = immich
|
|
||||||
# web = immich-web
|
|
||||||
map-package = $(subst sdk,@immich/sdk,$(subst cli,@immich/cli,$(subst docs,documentation,$(subst e2e,immich-e2e,$(subst server,immich,$(subst web,immich-web,$1))))))
|
|
||||||
|
|
||||||
audit-%:
|
audit-%:
|
||||||
pnpm --filter $(call map-package,$*) audit fix
|
npm --prefix $(subst sdk,open-api/typescript-sdk,$*) audit fix
|
||||||
install-%:
|
install-%:
|
||||||
pnpm --filter $(call map-package,$*) install $(if $(FROZEN),--frozen-lockfile) $(if $(OFFLINE),--offline)
|
npm --prefix $(subst sdk,open-api/typescript-sdk,$*) i
|
||||||
|
ci-%:
|
||||||
|
npm --prefix $(subst sdk,open-api/typescript-sdk,$*) ci
|
||||||
build-cli: build-sdk
|
build-cli: build-sdk
|
||||||
build-web: build-sdk
|
build-web: build-sdk
|
||||||
build-%: install-%
|
build-%: install-%
|
||||||
pnpm --filter $(call map-package,$*) run build
|
npm --prefix $(subst sdk,open-api/typescript-sdk,$*) run build
|
||||||
format-%:
|
format-%:
|
||||||
pnpm --filter $(call map-package,$*) run format:fix
|
npm --prefix $* run format:fix
|
||||||
lint-%:
|
lint-%:
|
||||||
pnpm --filter $(call map-package,$*) run lint:fix
|
npm --prefix $* run lint:fix
|
||||||
lint-web:
|
|
||||||
pnpm --filter $(call map-package,$*) run lint:p
|
|
||||||
check-%:
|
check-%:
|
||||||
pnpm --filter $(call map-package,$*) run check
|
npm --prefix $* run check
|
||||||
check-web:
|
check-web:
|
||||||
pnpm --filter immich-web run check:typescript
|
npm --prefix web run check:typescript
|
||||||
pnpm --filter immich-web run check:svelte
|
npm --prefix web run check:svelte
|
||||||
test-%:
|
test-%:
|
||||||
pnpm --filter $(call map-package,$*) run test
|
npm --prefix $* run test
|
||||||
test-e2e:
|
test-e2e:
|
||||||
docker compose -f ./e2e/docker-compose.yml build
|
docker compose -f ./e2e/docker-compose.yml build
|
||||||
pnpm --filter immich-e2e run test
|
npm --prefix e2e run test
|
||||||
pnpm --filter immich-e2e run test:web
|
npm --prefix e2e run test:web
|
||||||
test-medium:
|
test-medium:
|
||||||
docker run \
|
docker run \
|
||||||
--rm \
|
--rm \
|
||||||
@@ -93,28 +84,19 @@ test-medium:
|
|||||||
-v ./server/tsconfig.json:/usr/src/app/tsconfig.json \
|
-v ./server/tsconfig.json:/usr/src/app/tsconfig.json \
|
||||||
-e NODE_ENV=development \
|
-e NODE_ENV=development \
|
||||||
immich-server:latest \
|
immich-server:latest \
|
||||||
-c "pnpm test:medium -- --run"
|
-c "npm ci && npm run test:medium -- --run"
|
||||||
test-medium-dev:
|
test-medium-dev:
|
||||||
docker exec -it immich_server /bin/sh -c "pnpm run test:medium"
|
docker exec -it immich_server /bin/sh -c "npm run test:medium"
|
||||||
|
|
||||||
install-all:
|
build-all: $(foreach M,$(filter-out e2e .github,$(MODULES)),build-$M) ;
|
||||||
pnpm -r --filter '!documentation' install
|
install-all: $(foreach M,$(MODULES),install-$M) ;
|
||||||
|
ci-all: $(foreach M,$(filter-out .github,$(MODULES)),ci-$M) ;
|
||||||
build-all: $(foreach M,$(filter-out e2e docs .github,$(MODULES)),build-$M) ;
|
check-all: $(foreach M,$(filter-out sdk cli docs .github,$(MODULES)),check-$M) ;
|
||||||
|
lint-all: $(foreach M,$(filter-out sdk docs .github,$(MODULES)),lint-$M) ;
|
||||||
check-all:
|
format-all: $(foreach M,$(filter-out sdk,$(MODULES)),format-$M) ;
|
||||||
pnpm -r --filter '!documentation' run "/^(check|check\:svelte|check\:typescript)$/"
|
audit-all: $(foreach M,$(MODULES),audit-$M) ;
|
||||||
lint-all:
|
hygiene-all: lint-all format-all check-all sql audit-all;
|
||||||
pnpm -r --filter '!documentation' run lint:fix
|
test-all: $(foreach M,$(filter-out sdk docs .github,$(MODULES)),test-$M) ;
|
||||||
format-all:
|
|
||||||
pnpm -r --filter '!documentation' run format:fix
|
|
||||||
audit-all:
|
|
||||||
pnpm -r --filter '!documentation' audit fix
|
|
||||||
hygiene-all: audit-all
|
|
||||||
pnpm -r --filter '!documentation' run "/(format:fix|check|check:svelte|check:typescript|sql)/"
|
|
||||||
|
|
||||||
test-all:
|
|
||||||
pnpm -r --filter '!documentation' run "/^test/"
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
find . -name "node_modules" -type d -prune -exec rm -rf {} +
|
find . -name "node_modules" -type d -prune -exec rm -rf {} +
|
||||||
@@ -124,5 +106,4 @@ clean:
|
|||||||
command -v docker >/dev/null 2>&1 && docker compose -f ./docker/docker-compose.dev.yml rm -v -f || true
|
command -v docker >/dev/null 2>&1 && docker compose -f ./docker/docker-compose.dev.yml rm -v -f || true
|
||||||
command -v docker >/dev/null 2>&1 && docker compose -f ./e2e/docker-compose.yml rm -v -f || true
|
command -v docker >/dev/null 2>&1 && docker compose -f ./e2e/docker-compose.yml rm -v -f || true
|
||||||
|
|
||||||
setup-server-dev: install-server
|
setup-dev: install-server install-sdk build-sdk install-web
|
||||||
setup-web-dev: install-sdk build-sdk install-web
|
|
||||||
+4
-8
@@ -6,10 +6,8 @@ Please see the [Immich CLI documentation](https://immich.app/docs/features/comma
|
|||||||
|
|
||||||
Before building the CLI, you must build the immich server and the open-api client. To build the server run the following in the server folder:
|
Before building the CLI, you must build the immich server and the open-api client. To build the server run the following in the server folder:
|
||||||
|
|
||||||
# if you don't have node installed
|
$ npm install
|
||||||
$ npm install -g pnpm
|
$ npm run build
|
||||||
$ pnpm install
|
|
||||||
$ pnpm build
|
|
||||||
|
|
||||||
Then, to build the open-api client run the following in the open-api folder:
|
Then, to build the open-api client run the following in the open-api folder:
|
||||||
|
|
||||||
@@ -17,10 +15,8 @@ Then, to build the open-api client run the following in the open-api folder:
|
|||||||
|
|
||||||
To run the Immich CLI from source, run the following in the cli folder:
|
To run the Immich CLI from source, run the following in the cli folder:
|
||||||
|
|
||||||
# if you don't have node installed
|
$ npm install
|
||||||
$ npm install -g pnpm
|
$ npm run build
|
||||||
$ pnpm install
|
|
||||||
$ pnpm build
|
|
||||||
$ ts-node .
|
$ ts-node .
|
||||||
|
|
||||||
You'll need ts-node, the easiest way to install it is to use npm:
|
You'll need ts-node, the easiest way to install it is to use npm:
|
||||||
|
|||||||
Generated
+4617
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,7 @@ name: immich-dev
|
|||||||
services:
|
services:
|
||||||
immich-server:
|
immich-server:
|
||||||
container_name: immich_server
|
container_name: immich_server
|
||||||
command: ['/usr/src/app/server/bin/immich-dev']
|
command: ['/usr/src/app/bin/immich-dev']
|
||||||
image: immich-server-dev:latest
|
image: immich-server-dev:latest
|
||||||
# extends:
|
# extends:
|
||||||
# file: hwaccel.transcoding.yml
|
# file: hwaccel.transcoding.yml
|
||||||
@@ -27,18 +27,14 @@ services:
|
|||||||
target: dev
|
target: dev
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- ../Makefile:/usr/src/app/Makefile
|
- ../server:/usr/src/app
|
||||||
- ../package.json:/usr/src/app/package.json
|
- ../open-api:/usr/src/open-api
|
||||||
- ../pnpm-lock.yaml:/usr/src/app/pnpm-lock.yaml
|
- ${UPLOAD_LOCATION}/photos:/usr/src/app/upload
|
||||||
- ../pnpm-workspace.yaml:/usr/src/app/pnpm-workspace.yaml
|
- ${UPLOAD_LOCATION}/photos/upload:/usr/src/app/upload/upload
|
||||||
- ../server:/usr/src/app/server
|
- /usr/src/app/node_modules
|
||||||
- ../open-api:/usr/src/app/open-api
|
|
||||||
- ${UPLOAD_LOCATION}/photos:/usr/src/app/server/upload
|
|
||||||
- ${UPLOAD_LOCATION}/photos/upload:/usr/src/app/server/upload/upload
|
|
||||||
- /etc/localtime:/etc/localtime:ro
|
- /etc/localtime:/etc/localtime:ro
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
# user: ${UID:-1000}:${GID:-1000}
|
|
||||||
environment:
|
environment:
|
||||||
IMMICH_REPOSITORY: immich-app/immich
|
IMMICH_REPOSITORY: immich-app/immich
|
||||||
IMMICH_REPOSITORY_URL: https://github.com/immich-app/immich
|
IMMICH_REPOSITORY_URL: https://github.com/immich-app/immich
|
||||||
@@ -72,25 +68,20 @@ services:
|
|||||||
image: immich-web-dev:latest
|
image: immich-web-dev:latest
|
||||||
# Needed for rootless docker setup, see https://github.com/moby/moby/issues/45919
|
# Needed for rootless docker setup, see https://github.com/moby/moby/issues/45919
|
||||||
# user: 0:0
|
# user: 0:0
|
||||||
# user: ${UID:-1000}:${GID:-1000}
|
|
||||||
build:
|
build:
|
||||||
context: ../
|
context: ../web
|
||||||
dockerfile: web/Dockerfile
|
command: ['/usr/src/app/bin/immich-web']
|
||||||
command: ['/usr/src/app/web/bin/immich-web']
|
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
- 24678:24678
|
- 24678:24678
|
||||||
volumes:
|
volumes:
|
||||||
- ../Makefile:/usr/src/app/Makefile
|
- ../web:/usr/src/app
|
||||||
- ../package.json:/usr/src/app/package.json
|
- ../i18n:/usr/src/i18n
|
||||||
- ../pnpm-lock.yaml:/usr/src/app/pnpm-lock.yaml
|
- ../open-api/:/usr/src/open-api/
|
||||||
- ../pnpm-workspace.yaml:/usr/src/app/pnpm-workspace.yaml
|
|
||||||
- ../web:/usr/src/app/web
|
|
||||||
- ../i18n:/usr/src/app/i18n
|
|
||||||
- ../open-api:/usr/src/app/open-api
|
|
||||||
# - ../../ui:/usr/ui
|
# - ../../ui:/usr/ui
|
||||||
|
- /usr/src/app/node_modules
|
||||||
ulimits:
|
ulimits:
|
||||||
nofile:
|
nofile:
|
||||||
soft: 1048576
|
soft: 1048576
|
||||||
|
|||||||
@@ -1,7 +1,2 @@
|
|||||||
build/
|
build/
|
||||||
.docusaurus/
|
.docusaurus/
|
||||||
|
|
||||||
# Ignore files for PNPM, NPM and YARN
|
|
||||||
pnpm-lock.yaml
|
|
||||||
package-lock.json
|
|
||||||
yarn.lock
|
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ This website is built using [Docusaurus](https://docusaurus.io/), a modern stati
|
|||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
```
|
```
|
||||||
$ pnpm install
|
$ npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
### Local Development
|
### Local Development
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ Once you have a new OAuth client application configured, Immich can be configure
|
|||||||
| Scope | string | openid email profile | Full list of scopes to send with the request (space delimited) |
|
| Scope | string | openid email profile | Full list of scopes to send with the request (space delimited) |
|
||||||
| Signing Algorithm | string | RS256 | The algorithm used to sign the id token (examples: RS256, HS256) |
|
| Signing Algorithm | string | RS256 | The algorithm used to sign the id token (examples: RS256, HS256) |
|
||||||
| Storage Label Claim | string | preferred_username | Claim mapping for the user's storage label**¹** |
|
| Storage Label Claim | string | preferred_username | Claim mapping for the user's storage label**¹** |
|
||||||
|
| Role Claim | string | immich_role | Claim mapping for the user's role. (should return "user" or "admin")**¹** |
|
||||||
| Storage Quota Claim | string | immich_quota | Claim mapping for the user's storage**¹** |
|
| Storage Quota Claim | string | immich_quota | Claim mapping for the user's storage**¹** |
|
||||||
| Default Storage Quota (GiB) | number | 0 | Default quota for user without storage quota claim (Enter 0 for unlimited quota) |
|
| Default Storage Quota (GiB) | number | 0 | Default quota for user without storage quota claim (Enter 0 for unlimited quota) |
|
||||||
| Button Text | string | Login with OAuth | Text for the OAuth button on the web |
|
| Button Text | string | Login with OAuth | Text for the OAuth button on the web |
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ When the Dev Container starts, it automatically:
|
|||||||
|
|
||||||
1. **Runs post-create script** (`container-server-post-create.sh`):
|
1. **Runs post-create script** (`container-server-post-create.sh`):
|
||||||
- Adjusts file permissions for the `node` user
|
- Adjusts file permissions for the `node` user
|
||||||
- Installs dependencies: `pnpm install` in all packages
|
- Installs dependencies: `npm install` in all packages
|
||||||
- Builds TypeScript SDK: `npm run build` in `open-api/typescript-sdk`
|
- Builds TypeScript SDK: `npm run build` in `open-api/typescript-sdk`
|
||||||
|
|
||||||
2. **Starts development servers** via VS Code tasks:
|
2. **Starts development servers** via VS Code tasks:
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ If you only want to do web development connected to an existing, remote backend,
|
|||||||
|
|
||||||
1. Build the Immich SDK - `cd open-api/typescript-sdk && npm i && npm run build && cd -`
|
1. Build the Immich SDK - `cd open-api/typescript-sdk && npm i && npm run build && cd -`
|
||||||
2. Enter the web directory - `cd web/`
|
2. Enter the web directory - `cd web/`
|
||||||
3. Install web dependencies - `pnpm i`
|
3. Install web dependencies - `npm i`
|
||||||
4. Start the web development server
|
4. Start the web development server
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
### Unit tests
|
### Unit tests
|
||||||
|
|
||||||
Unit are run by calling `npm run test` from the `server/` directory.
|
Unit are run by calling `npm run test` from the `server/` directory.
|
||||||
You need to run `pnpm install` (in `server/`) before _once_.
|
You need to run `npm install` (in `server/`) before _once_.
|
||||||
|
|
||||||
### End to end tests
|
### End to end tests
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ For the full list, refer to the [Immich source code](https://github.com/immich-a
|
|||||||
| `HEIC` | `.heic` | :white_check_mark: | |
|
| `HEIC` | `.heic` | :white_check_mark: | |
|
||||||
| `HEIF` | `.heif` | :white_check_mark: | |
|
| `HEIF` | `.heif` | :white_check_mark: | |
|
||||||
| `JPEG 2000` | `.jp2` | :white_check_mark: | |
|
| `JPEG 2000` | `.jp2` | :white_check_mark: | |
|
||||||
| `JPEG` | `.webp` `.jpg` `.jpe` `.insp` | :white_check_mark: | |
|
| `JPEG` | `.jpeg` `.jpg` `.jpe` `.insp` | :white_check_mark: | |
|
||||||
| `JPEG XL` | `.jxl` | :white_check_mark: | |
|
| `JPEG XL` | `.jxl` | :white_check_mark: | |
|
||||||
| `PNG` | `.png` | :white_check_mark: | |
|
| `PNG` | `.png` | :white_check_mark: | |
|
||||||
| `PSD` | `.psd` | :white_check_mark: | Adobe Photoshop |
|
| `PSD` | `.psd` | :white_check_mark: | Adobe Photoshop |
|
||||||
|
|||||||
Generated
+20545
File diff suppressed because it is too large
Load Diff
@@ -36,7 +36,7 @@ services:
|
|||||||
- 2285:2285
|
- 2285:2285
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:6.2-alpine@sha256:3211c33a618c457e5d241922c975dbc4f446d0bdb2dc75694f5573ef8e2d01fa
|
image: redis:6.2-alpine@sha256:03fd052257735b41cd19f3d8ae9782926bf9b704fb6a9dc5e29f9ccfbe8827f0
|
||||||
|
|
||||||
database:
|
database:
|
||||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:3aef84a0a4fabbda17ef115c3019ba0c914ec73e9f6e59203674322d858b8eea
|
image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:3aef84a0a4fabbda17ef115c3019ba0c914ec73e9f6e59203674322d858b8eea
|
||||||
|
|||||||
Generated
+7409
File diff suppressed because it is too large
Load Diff
@@ -227,6 +227,21 @@ describe(`/oauth`, () => {
|
|||||||
expect(user.storageLabel).toBe('user-username');
|
expect(user.storageLabel).toBe('user-username');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should set the admin status from a role claim', async () => {
|
||||||
|
const callbackParams = await loginWithOAuth(OAuthUser.WITH_ROLE);
|
||||||
|
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
||||||
|
expect(status).toBe(201);
|
||||||
|
expect(body).toMatchObject({
|
||||||
|
accessToken: expect.any(String),
|
||||||
|
userId: expect.any(String),
|
||||||
|
userEmail: 'oauth-with-role@immich.app',
|
||||||
|
isAdmin: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = await getMyUser({ headers: asBearerAuth(body.accessToken) });
|
||||||
|
expect(user.isAdmin).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it('should work with RS256 signed tokens', async () => {
|
it('should work with RS256 signed tokens', async () => {
|
||||||
await setupOAuth(admin.accessToken, {
|
await setupOAuth(admin.accessToken, {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export enum OAuthUser {
|
|||||||
NO_NAME = 'no-name',
|
NO_NAME = 'no-name',
|
||||||
WITH_QUOTA = 'with-quota',
|
WITH_QUOTA = 'with-quota',
|
||||||
WITH_USERNAME = 'with-username',
|
WITH_USERNAME = 'with-username',
|
||||||
|
WITH_ROLE = 'with-role',
|
||||||
}
|
}
|
||||||
|
|
||||||
const claims = [
|
const claims = [
|
||||||
@@ -34,6 +35,12 @@ const claims = [
|
|||||||
preferred_username: 'user-quota',
|
preferred_username: 'user-quota',
|
||||||
immich_quota: 25,
|
immich_quota: 25,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
sub: OAuthUser.WITH_ROLE,
|
||||||
|
email: 'oauth-with-role@immich.app',
|
||||||
|
email_verified: true,
|
||||||
|
immich_role: 'admin',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const withDefaultClaims = (sub: string) => ({
|
const withDefaultClaims = (sub: string) => ({
|
||||||
@@ -64,7 +71,15 @@ const setup = async () => {
|
|||||||
claims: {
|
claims: {
|
||||||
openid: ['sub'],
|
openid: ['sub'],
|
||||||
email: ['email', 'email_verified'],
|
email: ['email', 'email_verified'],
|
||||||
profile: ['name', 'given_name', 'family_name', 'preferred_username', 'immich_quota', 'immich_username'],
|
profile: [
|
||||||
|
'name',
|
||||||
|
'given_name',
|
||||||
|
'family_name',
|
||||||
|
'preferred_username',
|
||||||
|
'immich_quota',
|
||||||
|
'immich_username',
|
||||||
|
'immich_role',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
features: {
|
features: {
|
||||||
jwtUserinfo: {
|
jwtUserinfo: {
|
||||||
|
|||||||
+1
-1
@@ -79,7 +79,7 @@ export const tempDir = tmpdir();
|
|||||||
export const asBearerAuth = (accessToken: string) => ({ Authorization: `Bearer ${accessToken}` });
|
export const asBearerAuth = (accessToken: string) => ({ Authorization: `Bearer ${accessToken}` });
|
||||||
export const asKeyAuth = (key: string) => ({ 'x-api-key': key });
|
export const asKeyAuth = (key: string) => ({ 'x-api-key': key });
|
||||||
export const immichCli = (args: string[]) =>
|
export const immichCli = (args: string[]) =>
|
||||||
executeCommand('pnpm', ['exec', 'immich', '-d', `/${tempDir}/immich/`, ...args], { cwd: '../cli' }).promise;
|
executeCommand('node', ['node_modules/.bin/immich', '-d', `/${tempDir}/immich/`, ...args]).promise;
|
||||||
export const immichAdmin = (args: string[]) =>
|
export const immichAdmin = (args: string[]) =>
|
||||||
executeCommand('docker', ['exec', '-i', 'immich-e2e-server', '/bin/bash', '-c', `immich-admin ${args.join(' ')}`]);
|
executeCommand('docker', ['exec', '-i', 'immich-e2e-server', '/bin/bash', '-c', `immich-admin ${args.join(' ')}`]);
|
||||||
export const specialCharStrings = ["'", '"', ',', '{', '}', '*'];
|
export const specialCharStrings = ["'", '"', ',', '{', '}', '*'];
|
||||||
|
|||||||
@@ -196,6 +196,8 @@
|
|||||||
"oauth_mobile_redirect_uri": "Mobile redirect URI",
|
"oauth_mobile_redirect_uri": "Mobile redirect URI",
|
||||||
"oauth_mobile_redirect_uri_override": "Mobile redirect URI override",
|
"oauth_mobile_redirect_uri_override": "Mobile redirect URI override",
|
||||||
"oauth_mobile_redirect_uri_override_description": "Enable when OAuth provider does not allow a mobile URI, like ''{callback}''",
|
"oauth_mobile_redirect_uri_override_description": "Enable when OAuth provider does not allow a mobile URI, like ''{callback}''",
|
||||||
|
"oauth_role_claim": "Role Claim",
|
||||||
|
"oauth_role_claim_description": "Automatically grant admin access based on the presence of this claim. The claim may have either 'user' or 'admin'.",
|
||||||
"oauth_settings": "OAuth",
|
"oauth_settings": "OAuth",
|
||||||
"oauth_settings_description": "Manage OAuth login settings",
|
"oauth_settings_description": "Manage OAuth login settings",
|
||||||
"oauth_settings_more_details": "For more details about this feature, refer to the <link>docs</link>.",
|
"oauth_settings_more_details": "For more details about this feature, refer to the <link>docs</link>.",
|
||||||
|
|||||||
+1
-1331
File diff suppressed because one or more lines are too long
@@ -18,7 +18,7 @@ const String kSecuredPinCode = "secured_pin_code";
|
|||||||
|
|
||||||
// Timeline constants
|
// Timeline constants
|
||||||
const int kTimelineNoneSegmentSize = 120;
|
const int kTimelineNoneSegmentSize = 120;
|
||||||
const int kTimelineAssetLoadBatchSize = 256;
|
const int kTimelineAssetLoadBatchSize = 1024;
|
||||||
const int kTimelineAssetLoadOppositeSize = 64;
|
const int kTimelineAssetLoadOppositeSize = 64;
|
||||||
|
|
||||||
// Widget keys
|
// Widget keys
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
// Model for a stack stored in the server
|
||||||
|
class Stack {
|
||||||
|
final String id;
|
||||||
|
final DateTime createdAt;
|
||||||
|
final DateTime updatedAt;
|
||||||
|
final String ownerId;
|
||||||
|
final String primaryAssetId;
|
||||||
|
|
||||||
|
const Stack({
|
||||||
|
required this.id,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
required this.ownerId,
|
||||||
|
required this.primaryAssetId,
|
||||||
|
});
|
||||||
|
|
||||||
|
Stack copyWith({
|
||||||
|
String? id,
|
||||||
|
DateTime? createdAt,
|
||||||
|
DateTime? updatedAt,
|
||||||
|
String? ownerId,
|
||||||
|
String? primaryAssetId,
|
||||||
|
}) {
|
||||||
|
return Stack(
|
||||||
|
id: id ?? this.id,
|
||||||
|
createdAt: createdAt ?? this.createdAt,
|
||||||
|
updatedAt: updatedAt ?? this.updatedAt,
|
||||||
|
ownerId: ownerId ?? this.ownerId,
|
||||||
|
primaryAssetId: primaryAssetId ?? this.primaryAssetId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return <String, dynamic>{
|
||||||
|
'id': id,
|
||||||
|
'createdAt': createdAt.millisecondsSinceEpoch,
|
||||||
|
'updatedAt': updatedAt.millisecondsSinceEpoch,
|
||||||
|
'ownerId': ownerId,
|
||||||
|
'primaryAssetId': primaryAssetId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory Stack.fromMap(Map<String, dynamic> map) {
|
||||||
|
return Stack(
|
||||||
|
id: map['id'] as String,
|
||||||
|
createdAt: DateTime.fromMillisecondsSinceEpoch(map['createdAt'] as int),
|
||||||
|
updatedAt: DateTime.fromMillisecondsSinceEpoch(map['updatedAt'] as int),
|
||||||
|
ownerId: map['ownerId'] as String,
|
||||||
|
primaryAssetId: map['primaryAssetId'] as String,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String toJson() => json.encode(toMap());
|
||||||
|
|
||||||
|
factory Stack.fromJson(String source) =>
|
||||||
|
Stack.fromMap(json.decode(source) as Map<String, dynamic>);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'Stack(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, ownerId: $ownerId, primaryAssetId: $primaryAssetId)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(covariant Stack other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
|
return other.id == id &&
|
||||||
|
other.createdAt == createdAt &&
|
||||||
|
other.updatedAt == updatedAt &&
|
||||||
|
other.ownerId == ownerId &&
|
||||||
|
other.primaryAssetId == primaryAssetId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return id.hashCode ^
|
||||||
|
createdAt.hashCode ^
|
||||||
|
updatedAt.hashCode ^
|
||||||
|
ownerId.hashCode ^
|
||||||
|
primaryAssetId.hashCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -154,6 +154,25 @@ class SyncStreamService {
|
|||||||
return _syncStreamRepository.updateMemoryAssetsV1(data.cast());
|
return _syncStreamRepository.updateMemoryAssetsV1(data.cast());
|
||||||
case SyncEntityType.memoryToAssetDeleteV1:
|
case SyncEntityType.memoryToAssetDeleteV1:
|
||||||
return _syncStreamRepository.deleteMemoryAssetsV1(data.cast());
|
return _syncStreamRepository.deleteMemoryAssetsV1(data.cast());
|
||||||
|
case SyncEntityType.stackV1:
|
||||||
|
return _syncStreamRepository.updateStacksV1(data.cast());
|
||||||
|
case SyncEntityType.stackDeleteV1:
|
||||||
|
return _syncStreamRepository.deleteStacksV1(data.cast());
|
||||||
|
case SyncEntityType.partnerStackV1:
|
||||||
|
return _syncStreamRepository.updateStacksV1(
|
||||||
|
data.cast(),
|
||||||
|
debugLabel: 'partner',
|
||||||
|
);
|
||||||
|
case SyncEntityType.partnerStackBackfillV1:
|
||||||
|
return _syncStreamRepository.updateStacksV1(
|
||||||
|
data.cast(),
|
||||||
|
debugLabel: 'partner backfill',
|
||||||
|
);
|
||||||
|
case SyncEntityType.partnerStackDeleteV1:
|
||||||
|
return _syncStreamRepository.deleteStacksV1(
|
||||||
|
data.cast(),
|
||||||
|
debugLabel: 'partner',
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
_logger.warning("Unknown sync data type: $type");
|
_logger.warning("Unknown sync data type: $type");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,16 +42,66 @@ class TimelineFactory {
|
|||||||
|
|
||||||
TimelineService localAlbum({required String albumId}) => TimelineService(
|
TimelineService localAlbum({required String albumId}) => TimelineService(
|
||||||
assetSource: (offset, count) => _timelineRepository
|
assetSource: (offset, count) => _timelineRepository
|
||||||
.getLocalBucketAssets(albumId, offset: offset, count: count),
|
.getLocalAlbumBucketAssets(albumId, offset: offset, count: count),
|
||||||
bucketSource: () =>
|
bucketSource: () => _timelineRepository.watchLocalAlbumBucket(
|
||||||
_timelineRepository.watchLocalBucket(albumId, groupBy: groupBy),
|
albumId,
|
||||||
|
groupBy: groupBy,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
TimelineService remoteAlbum({required String albumId}) => TimelineService(
|
TimelineService remoteAlbum({required String albumId}) => TimelineService(
|
||||||
assetSource: (offset, count) => _timelineRepository
|
assetSource: (offset, count) => _timelineRepository
|
||||||
.getRemoteBucketAssets(albumId, offset: offset, count: count),
|
.getRemoteAlbumBucketAssets(albumId, offset: offset, count: count),
|
||||||
|
bucketSource: () => _timelineRepository.watchRemoteAlbumBucket(
|
||||||
|
albumId,
|
||||||
|
groupBy: groupBy,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
TimelineService remoteAssets(List<String> timelineUsers) => TimelineService(
|
||||||
|
assetSource: (offset, count) => _timelineRepository
|
||||||
|
.getRemoteBucketAssets(timelineUsers, offset: offset, count: count),
|
||||||
|
bucketSource: () => _timelineRepository.watchRemoteBucket(
|
||||||
|
timelineUsers,
|
||||||
|
groupBy: GroupAssetsBy.month,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
TimelineService favorite(String userId) => TimelineService(
|
||||||
|
assetSource: (offset, count) => _timelineRepository
|
||||||
|
.getFavoriteBucketAssets(userId, offset: offset, count: count),
|
||||||
bucketSource: () =>
|
bucketSource: () =>
|
||||||
_timelineRepository.watchRemoteBucket(albumId, groupBy: groupBy),
|
_timelineRepository.watchFavoriteBucket(userId, groupBy: groupBy),
|
||||||
|
);
|
||||||
|
|
||||||
|
TimelineService trash(String userId) => TimelineService(
|
||||||
|
assetSource: (offset, count) => _timelineRepository
|
||||||
|
.getTrashBucketAssets(userId, offset: offset, count: count),
|
||||||
|
bucketSource: () =>
|
||||||
|
_timelineRepository.watchTrashBucket(userId, groupBy: groupBy),
|
||||||
|
);
|
||||||
|
|
||||||
|
TimelineService archive(String userId) => TimelineService(
|
||||||
|
assetSource: (offset, count) => _timelineRepository
|
||||||
|
.getArchiveBucketAssets(userId, offset: offset, count: count),
|
||||||
|
bucketSource: () =>
|
||||||
|
_timelineRepository.watchArchiveBucket(userId, groupBy: groupBy),
|
||||||
|
);
|
||||||
|
|
||||||
|
TimelineService lockedFolder(String userId) => TimelineService(
|
||||||
|
assetSource: (offset, count) => _timelineRepository
|
||||||
|
.getLockedFolderBucketAssets(userId, offset: offset, count: count),
|
||||||
|
bucketSource: () => _timelineRepository.watchLockedFolderBucket(
|
||||||
|
userId,
|
||||||
|
groupBy: groupBy,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
TimelineService video(String userId) => TimelineService(
|
||||||
|
assetSource: (offset, count) => _timelineRepository
|
||||||
|
.getVideoBucketAssets(userId, offset: offset, count: count),
|
||||||
|
bucketSource: () =>
|
||||||
|
_timelineRepository.watchVideoBucket(userId, groupBy: groupBy),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import 'remote_asset.entity.dart';
|
||||||
|
import 'local_asset.entity.dart';
|
||||||
|
|
||||||
|
-- TRIGGERS ON local_asset_entity
|
||||||
|
|
||||||
|
-- Find and update the remote_id in local_asset_entity and local_id in remote_asset_entity when checksum is set
|
||||||
|
CREATE TRIGGER IF NOT EXISTS tr_local_asset_update_checksum_set_ids
|
||||||
|
AFTER UPDATE OF checksum ON local_asset_entity
|
||||||
|
FOR EACH ROW
|
||||||
|
WHEN NEW.checksum IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
UPDATE local_asset_entity
|
||||||
|
SET remote_id = (SELECT id FROM remote_asset_entity WHERE checksum = NEW.checksum LIMIT 1)
|
||||||
|
WHERE id = NEW.id;
|
||||||
|
|
||||||
|
UPDATE remote_asset_entity
|
||||||
|
SET local_id = (SELECT id FROM local_asset_entity WHERE checksum = NEW.checksum ORDER BY id ASC LIMIT 1)
|
||||||
|
WHERE checksum = NEW.checksum;
|
||||||
|
END;
|
||||||
|
|
||||||
|
-- When a local asset is updated, relink remote assets that had a checksum match
|
||||||
|
CREATE TRIGGER IF NOT EXISTS tr_local_asset_update_old_checksum_set_remote_asset_local_id
|
||||||
|
AFTER UPDATE OF checksum ON local_asset_entity
|
||||||
|
FOR EACH ROW
|
||||||
|
WHEN OLD.checksum IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
UPDATE remote_asset_entity
|
||||||
|
SET local_id = (SELECT id FROM local_asset_entity WHERE checksum = OLD.checksum ORDER BY id ASC LIMIT 1)
|
||||||
|
WHERE checksum = OLD.checksum;
|
||||||
|
END;
|
||||||
|
|
||||||
|
-- remote_asset_entity.checksum is a 1..* relationship with local_asset_entity.checksum.
|
||||||
|
-- When a local asset is deleted, update remote assets that had a checksum match
|
||||||
|
-- to ensure their local_id is set to the first matching local asset or NULL
|
||||||
|
CREATE TRIGGER IF NOT EXISTS tr_local_asset_delete_update_remote_asset_local_id
|
||||||
|
AFTER DELETE ON local_asset_entity
|
||||||
|
FOR EACH ROW
|
||||||
|
WHEN OLD.checksum IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
UPDATE remote_asset_entity
|
||||||
|
SET local_id = (SELECT id FROM local_asset_entity WHERE checksum = OLD.checksum ORDER BY id ASC LIMIT 1)
|
||||||
|
WHERE checksum = OLD.checksum;
|
||||||
|
END;
|
||||||
|
|
||||||
|
|
||||||
|
-- TRIGGERS ON remote_asset_entity
|
||||||
|
|
||||||
|
-- Find and update local_id in remote_asset_entity when a new remote asset is inserted
|
||||||
|
CREATE TRIGGER IF NOT EXISTS tr_remote_asset_insert_set_local_id
|
||||||
|
AFTER INSERT ON remote_asset_entity
|
||||||
|
FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
UPDATE remote_asset_entity
|
||||||
|
SET local_id = (SELECT id FROM local_asset_entity WHERE checksum = NEW.checksum ORDER BY id ASC LIMIT 1)
|
||||||
|
WHERE id = NEW.id;
|
||||||
|
|
||||||
|
UPDATE local_asset_entity SET remote_id = NEW.id WHERE checksum = NEW.checksum;
|
||||||
|
END;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// dart format width=80
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
import 'package:drift/drift.dart' as i0;
|
||||||
|
|
||||||
|
i0.Trigger get trLocalAssetUpdateChecksumSetIds => i0.Trigger(
|
||||||
|
'CREATE TRIGGER IF NOT EXISTS tr_local_asset_update_checksum_set_ids AFTER UPDATE OF checksum ON local_asset_entity WHEN NEW.checksum IS NOT NULL BEGIN UPDATE local_asset_entity SET remote_id = (SELECT id FROM remote_asset_entity WHERE checksum = NEW.checksum LIMIT 1) WHERE id = NEW.id;UPDATE remote_asset_entity SET local_id = (SELECT id FROM local_asset_entity WHERE checksum = NEW.checksum ORDER BY id ASC LIMIT 1) WHERE checksum = NEW.checksum;END',
|
||||||
|
'tr_local_asset_update_checksum_set_ids');
|
||||||
|
i0.Trigger get trLocalAssetUpdateOldChecksumSetRemoteAssetLocalId => i0.Trigger(
|
||||||
|
'CREATE TRIGGER IF NOT EXISTS tr_local_asset_update_old_checksum_set_remote_asset_local_id AFTER UPDATE OF checksum ON local_asset_entity WHEN OLD.checksum IS NOT NULL BEGIN UPDATE remote_asset_entity SET local_id = (SELECT id FROM local_asset_entity WHERE checksum = OLD.checksum ORDER BY id ASC LIMIT 1) WHERE checksum = OLD.checksum;END',
|
||||||
|
'tr_local_asset_update_old_checksum_set_remote_asset_local_id');
|
||||||
|
i0.Trigger get trLocalAssetDeleteUpdateRemoteAssetLocalId => i0.Trigger(
|
||||||
|
'CREATE TRIGGER IF NOT EXISTS tr_local_asset_delete_update_remote_asset_local_id AFTER DELETE ON local_asset_entity WHEN OLD.checksum IS NOT NULL BEGIN UPDATE remote_asset_entity SET local_id = (SELECT id FROM local_asset_entity WHERE checksum = OLD.checksum ORDER BY id ASC LIMIT 1) WHERE checksum = OLD.checksum;END',
|
||||||
|
'tr_local_asset_delete_update_remote_asset_local_id');
|
||||||
|
i0.Trigger get trRemoteAssetInsertSetLocalId => i0.Trigger(
|
||||||
|
'CREATE TRIGGER IF NOT EXISTS tr_remote_asset_insert_set_local_id AFTER INSERT ON remote_asset_entity BEGIN UPDATE remote_asset_entity SET local_id = (SELECT id FROM local_asset_entity WHERE checksum = NEW.checksum ORDER BY id ASC LIMIT 1) WHERE id = NEW.id;UPDATE local_asset_entity SET remote_id = NEW.id WHERE checksum = NEW.checksum;END',
|
||||||
|
'tr_remote_asset_insert_set_local_id');
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.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';
|
||||||
|
|
||||||
@@ -9,6 +10,11 @@ class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
|
|||||||
const LocalAssetEntity();
|
const LocalAssetEntity();
|
||||||
|
|
||||||
TextColumn get id => text()();
|
TextColumn get id => text()();
|
||||||
|
|
||||||
|
TextColumn get remoteId => text()
|
||||||
|
.nullable()
|
||||||
|
.references(RemoteAssetEntity, #id, onDelete: KeyAction.setNull)();
|
||||||
|
|
||||||
TextColumn get checksum => text().nullable()();
|
TextColumn get checksum => text().nullable()();
|
||||||
|
|
||||||
// Only used during backup to mirror the favorite status of the asset in the server
|
// Only used during backup to mirror the favorite status of the asset in the server
|
||||||
@@ -30,6 +36,6 @@ extension LocalAssetEntityDataDomainEx on LocalAssetEntityData {
|
|||||||
isFavorite: isFavorite,
|
isFavorite: isFavorite,
|
||||||
height: height,
|
height: height,
|
||||||
width: width,
|
width: width,
|
||||||
remoteId: null,
|
remoteId: remoteId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+195
-15
@@ -7,6 +7,9 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart' as i2;
|
|||||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'
|
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'
|
||||||
as i3;
|
as i3;
|
||||||
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4;
|
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4;
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'
|
||||||
|
as i5;
|
||||||
|
import 'package:drift/internal/modular.dart' as i6;
|
||||||
|
|
||||||
typedef $$LocalAssetEntityTableCreateCompanionBuilder
|
typedef $$LocalAssetEntityTableCreateCompanionBuilder
|
||||||
= i1.LocalAssetEntityCompanion Function({
|
= i1.LocalAssetEntityCompanion Function({
|
||||||
@@ -18,6 +21,7 @@ typedef $$LocalAssetEntityTableCreateCompanionBuilder
|
|||||||
i0.Value<int?> height,
|
i0.Value<int?> height,
|
||||||
i0.Value<int?> durationInSeconds,
|
i0.Value<int?> durationInSeconds,
|
||||||
required String id,
|
required String id,
|
||||||
|
i0.Value<String?> remoteId,
|
||||||
i0.Value<String?> checksum,
|
i0.Value<String?> checksum,
|
||||||
i0.Value<bool> isFavorite,
|
i0.Value<bool> isFavorite,
|
||||||
});
|
});
|
||||||
@@ -31,10 +35,43 @@ typedef $$LocalAssetEntityTableUpdateCompanionBuilder
|
|||||||
i0.Value<int?> height,
|
i0.Value<int?> height,
|
||||||
i0.Value<int?> durationInSeconds,
|
i0.Value<int?> durationInSeconds,
|
||||||
i0.Value<String> id,
|
i0.Value<String> id,
|
||||||
|
i0.Value<String?> remoteId,
|
||||||
i0.Value<String?> checksum,
|
i0.Value<String?> checksum,
|
||||||
i0.Value<bool> isFavorite,
|
i0.Value<bool> isFavorite,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final class $$LocalAssetEntityTableReferences extends i0.BaseReferences<
|
||||||
|
i0.GeneratedDatabase, i1.$LocalAssetEntityTable, i1.LocalAssetEntityData> {
|
||||||
|
$$LocalAssetEntityTableReferences(
|
||||||
|
super.$_db, super.$_table, super.$_typedResult);
|
||||||
|
|
||||||
|
static i5.$RemoteAssetEntityTable _remoteIdTable(i0.GeneratedDatabase db) =>
|
||||||
|
i6.ReadDatabaseContainer(db)
|
||||||
|
.resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity')
|
||||||
|
.createAlias(i0.$_aliasNameGenerator(
|
||||||
|
i6.ReadDatabaseContainer(db)
|
||||||
|
.resultSet<i1.$LocalAssetEntityTable>('local_asset_entity')
|
||||||
|
.remoteId,
|
||||||
|
i6.ReadDatabaseContainer(db)
|
||||||
|
.resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity')
|
||||||
|
.id));
|
||||||
|
|
||||||
|
i5.$$RemoteAssetEntityTableProcessedTableManager? get remoteId {
|
||||||
|
final $_column = $_itemColumn<String>('remote_id');
|
||||||
|
if ($_column == null) return null;
|
||||||
|
final manager = i5
|
||||||
|
.$$RemoteAssetEntityTableTableManager(
|
||||||
|
$_db,
|
||||||
|
i6.ReadDatabaseContainer($_db)
|
||||||
|
.resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity'))
|
||||||
|
.filter((f) => f.id.sqlEquals($_column));
|
||||||
|
final item = $_typedResult.readTableOrNull(_remoteIdTable($_db));
|
||||||
|
if (item == null) return manager;
|
||||||
|
return i0.ProcessedTableManager(
|
||||||
|
manager.$state.copyWith(prefetchedData: [item]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class $$LocalAssetEntityTableFilterComposer
|
class $$LocalAssetEntityTableFilterComposer
|
||||||
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAssetEntityTable> {
|
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAssetEntityTable> {
|
||||||
$$LocalAssetEntityTableFilterComposer({
|
$$LocalAssetEntityTableFilterComposer({
|
||||||
@@ -76,6 +113,28 @@ class $$LocalAssetEntityTableFilterComposer
|
|||||||
|
|
||||||
i0.ColumnFilters<bool> get isFavorite => $composableBuilder(
|
i0.ColumnFilters<bool> get isFavorite => $composableBuilder(
|
||||||
column: $table.isFavorite, builder: (column) => i0.ColumnFilters(column));
|
column: $table.isFavorite, builder: (column) => i0.ColumnFilters(column));
|
||||||
|
|
||||||
|
i5.$$RemoteAssetEntityTableFilterComposer get remoteId {
|
||||||
|
final i5.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder(
|
||||||
|
composer: this,
|
||||||
|
getCurrentColumn: (t) => t.remoteId,
|
||||||
|
referencedTable: i6.ReadDatabaseContainer($db)
|
||||||
|
.resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||||
|
getReferencedColumn: (t) => t.id,
|
||||||
|
builder: (joinBuilder,
|
||||||
|
{$addJoinBuilderToRootComposer,
|
||||||
|
$removeJoinBuilderFromRootComposer}) =>
|
||||||
|
i5.$$RemoteAssetEntityTableFilterComposer(
|
||||||
|
$db: $db,
|
||||||
|
$table: i6.ReadDatabaseContainer($db)
|
||||||
|
.resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||||
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
|
joinBuilder: joinBuilder,
|
||||||
|
$removeJoinBuilderFromRootComposer:
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
));
|
||||||
|
return composer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class $$LocalAssetEntityTableOrderingComposer
|
class $$LocalAssetEntityTableOrderingComposer
|
||||||
@@ -120,6 +179,30 @@ class $$LocalAssetEntityTableOrderingComposer
|
|||||||
i0.ColumnOrderings<bool> get isFavorite => $composableBuilder(
|
i0.ColumnOrderings<bool> get isFavorite => $composableBuilder(
|
||||||
column: $table.isFavorite,
|
column: $table.isFavorite,
|
||||||
builder: (column) => i0.ColumnOrderings(column));
|
builder: (column) => i0.ColumnOrderings(column));
|
||||||
|
|
||||||
|
i5.$$RemoteAssetEntityTableOrderingComposer get remoteId {
|
||||||
|
final i5.$$RemoteAssetEntityTableOrderingComposer composer =
|
||||||
|
$composerBuilder(
|
||||||
|
composer: this,
|
||||||
|
getCurrentColumn: (t) => t.remoteId,
|
||||||
|
referencedTable: i6.ReadDatabaseContainer($db)
|
||||||
|
.resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||||
|
getReferencedColumn: (t) => t.id,
|
||||||
|
builder: (joinBuilder,
|
||||||
|
{$addJoinBuilderToRootComposer,
|
||||||
|
$removeJoinBuilderFromRootComposer}) =>
|
||||||
|
i5.$$RemoteAssetEntityTableOrderingComposer(
|
||||||
|
$db: $db,
|
||||||
|
$table: i6.ReadDatabaseContainer($db)
|
||||||
|
.resultSet<i5.$RemoteAssetEntityTable>(
|
||||||
|
'remote_asset_entity'),
|
||||||
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
|
joinBuilder: joinBuilder,
|
||||||
|
$removeJoinBuilderFromRootComposer:
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
));
|
||||||
|
return composer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class $$LocalAssetEntityTableAnnotationComposer
|
class $$LocalAssetEntityTableAnnotationComposer
|
||||||
@@ -160,6 +243,30 @@ class $$LocalAssetEntityTableAnnotationComposer
|
|||||||
|
|
||||||
i0.GeneratedColumn<bool> get isFavorite => $composableBuilder(
|
i0.GeneratedColumn<bool> get isFavorite => $composableBuilder(
|
||||||
column: $table.isFavorite, builder: (column) => column);
|
column: $table.isFavorite, builder: (column) => column);
|
||||||
|
|
||||||
|
i5.$$RemoteAssetEntityTableAnnotationComposer get remoteId {
|
||||||
|
final i5.$$RemoteAssetEntityTableAnnotationComposer composer =
|
||||||
|
$composerBuilder(
|
||||||
|
composer: this,
|
||||||
|
getCurrentColumn: (t) => t.remoteId,
|
||||||
|
referencedTable: i6.ReadDatabaseContainer($db)
|
||||||
|
.resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||||
|
getReferencedColumn: (t) => t.id,
|
||||||
|
builder: (joinBuilder,
|
||||||
|
{$addJoinBuilderToRootComposer,
|
||||||
|
$removeJoinBuilderFromRootComposer}) =>
|
||||||
|
i5.$$RemoteAssetEntityTableAnnotationComposer(
|
||||||
|
$db: $db,
|
||||||
|
$table: i6.ReadDatabaseContainer($db)
|
||||||
|
.resultSet<i5.$RemoteAssetEntityTable>(
|
||||||
|
'remote_asset_entity'),
|
||||||
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
|
joinBuilder: joinBuilder,
|
||||||
|
$removeJoinBuilderFromRootComposer:
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
));
|
||||||
|
return composer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
|
class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
|
||||||
@@ -171,13 +278,9 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
|
|||||||
i1.$$LocalAssetEntityTableAnnotationComposer,
|
i1.$$LocalAssetEntityTableAnnotationComposer,
|
||||||
$$LocalAssetEntityTableCreateCompanionBuilder,
|
$$LocalAssetEntityTableCreateCompanionBuilder,
|
||||||
$$LocalAssetEntityTableUpdateCompanionBuilder,
|
$$LocalAssetEntityTableUpdateCompanionBuilder,
|
||||||
(
|
(i1.LocalAssetEntityData, i1.$$LocalAssetEntityTableReferences),
|
||||||
i1.LocalAssetEntityData,
|
|
||||||
i0.BaseReferences<i0.GeneratedDatabase, i1.$LocalAssetEntityTable,
|
|
||||||
i1.LocalAssetEntityData>
|
|
||||||
),
|
|
||||||
i1.LocalAssetEntityData,
|
i1.LocalAssetEntityData,
|
||||||
i0.PrefetchHooks Function()> {
|
i0.PrefetchHooks Function({bool remoteId})> {
|
||||||
$$LocalAssetEntityTableTableManager(
|
$$LocalAssetEntityTableTableManager(
|
||||||
i0.GeneratedDatabase db, i1.$LocalAssetEntityTable table)
|
i0.GeneratedDatabase db, i1.$LocalAssetEntityTable table)
|
||||||
: super(i0.TableManagerState(
|
: super(i0.TableManagerState(
|
||||||
@@ -199,6 +302,7 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
|
|||||||
i0.Value<int?> height = const i0.Value.absent(),
|
i0.Value<int?> height = const i0.Value.absent(),
|
||||||
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
|
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
|
||||||
i0.Value<String> id = const i0.Value.absent(),
|
i0.Value<String> id = const i0.Value.absent(),
|
||||||
|
i0.Value<String?> remoteId = const i0.Value.absent(),
|
||||||
i0.Value<String?> checksum = const i0.Value.absent(),
|
i0.Value<String?> checksum = const i0.Value.absent(),
|
||||||
i0.Value<bool> isFavorite = const i0.Value.absent(),
|
i0.Value<bool> isFavorite = const i0.Value.absent(),
|
||||||
}) =>
|
}) =>
|
||||||
@@ -211,6 +315,7 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
|
|||||||
height: height,
|
height: height,
|
||||||
durationInSeconds: durationInSeconds,
|
durationInSeconds: durationInSeconds,
|
||||||
id: id,
|
id: id,
|
||||||
|
remoteId: remoteId,
|
||||||
checksum: checksum,
|
checksum: checksum,
|
||||||
isFavorite: isFavorite,
|
isFavorite: isFavorite,
|
||||||
),
|
),
|
||||||
@@ -223,6 +328,7 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
|
|||||||
i0.Value<int?> height = const i0.Value.absent(),
|
i0.Value<int?> height = const i0.Value.absent(),
|
||||||
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
|
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
|
||||||
required String id,
|
required String id,
|
||||||
|
i0.Value<String?> remoteId = const i0.Value.absent(),
|
||||||
i0.Value<String?> checksum = const i0.Value.absent(),
|
i0.Value<String?> checksum = const i0.Value.absent(),
|
||||||
i0.Value<bool> isFavorite = const i0.Value.absent(),
|
i0.Value<bool> isFavorite = const i0.Value.absent(),
|
||||||
}) =>
|
}) =>
|
||||||
@@ -235,13 +341,52 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
|
|||||||
height: height,
|
height: height,
|
||||||
durationInSeconds: durationInSeconds,
|
durationInSeconds: durationInSeconds,
|
||||||
id: id,
|
id: id,
|
||||||
|
remoteId: remoteId,
|
||||||
checksum: checksum,
|
checksum: checksum,
|
||||||
isFavorite: isFavorite,
|
isFavorite: isFavorite,
|
||||||
),
|
),
|
||||||
withReferenceMapper: (p0) => p0
|
withReferenceMapper: (p0) => p0
|
||||||
.map((e) => (e.readTable(table), i0.BaseReferences(db, table, e)))
|
.map((e) => (
|
||||||
|
e.readTable(table),
|
||||||
|
i1.$$LocalAssetEntityTableReferences(db, table, e)
|
||||||
|
))
|
||||||
.toList(),
|
.toList(),
|
||||||
prefetchHooksCallback: null,
|
prefetchHooksCallback: ({remoteId = false}) {
|
||||||
|
return i0.PrefetchHooks(
|
||||||
|
db: db,
|
||||||
|
explicitlyWatchedTables: [],
|
||||||
|
addJoins: <
|
||||||
|
T extends i0.TableManagerState<
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic>>(state) {
|
||||||
|
if (remoteId) {
|
||||||
|
state = state.withJoin(
|
||||||
|
currentTable: table,
|
||||||
|
currentColumn: table.remoteId,
|
||||||
|
referencedTable:
|
||||||
|
i1.$$LocalAssetEntityTableReferences._remoteIdTable(db),
|
||||||
|
referencedColumn: i1.$$LocalAssetEntityTableReferences
|
||||||
|
._remoteIdTable(db)
|
||||||
|
.id,
|
||||||
|
) as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
},
|
||||||
|
getPrefetchedDataCallback: (items) async {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,13 +399,9 @@ typedef $$LocalAssetEntityTableProcessedTableManager = i0.ProcessedTableManager<
|
|||||||
i1.$$LocalAssetEntityTableAnnotationComposer,
|
i1.$$LocalAssetEntityTableAnnotationComposer,
|
||||||
$$LocalAssetEntityTableCreateCompanionBuilder,
|
$$LocalAssetEntityTableCreateCompanionBuilder,
|
||||||
$$LocalAssetEntityTableUpdateCompanionBuilder,
|
$$LocalAssetEntityTableUpdateCompanionBuilder,
|
||||||
(
|
(i1.LocalAssetEntityData, i1.$$LocalAssetEntityTableReferences),
|
||||||
i1.LocalAssetEntityData,
|
|
||||||
i0.BaseReferences<i0.GeneratedDatabase, i1.$LocalAssetEntityTable,
|
|
||||||
i1.LocalAssetEntityData>
|
|
||||||
),
|
|
||||||
i1.LocalAssetEntityData,
|
i1.LocalAssetEntityData,
|
||||||
i0.PrefetchHooks Function()>;
|
i0.PrefetchHooks Function({bool remoteId})>;
|
||||||
i0.Index get idxLocalAssetChecksum => i0.Index('idx_local_asset_checksum',
|
i0.Index get idxLocalAssetChecksum => i0.Index('idx_local_asset_checksum',
|
||||||
'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)');
|
'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)');
|
||||||
|
|
||||||
@@ -321,6 +462,15 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
|
|||||||
late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
|
late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
|
||||||
'id', aliasedName, false,
|
'id', aliasedName, false,
|
||||||
type: i0.DriftSqlType.string, requiredDuringInsert: true);
|
type: i0.DriftSqlType.string, requiredDuringInsert: true);
|
||||||
|
static const i0.VerificationMeta _remoteIdMeta =
|
||||||
|
const i0.VerificationMeta('remoteId');
|
||||||
|
@override
|
||||||
|
late final i0.GeneratedColumn<String> remoteId = i0.GeneratedColumn<String>(
|
||||||
|
'remote_id', aliasedName, true,
|
||||||
|
type: i0.DriftSqlType.string,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||||
|
'REFERENCES remote_asset_entity (id) ON DELETE SET NULL'));
|
||||||
static const i0.VerificationMeta _checksumMeta =
|
static const i0.VerificationMeta _checksumMeta =
|
||||||
const i0.VerificationMeta('checksum');
|
const i0.VerificationMeta('checksum');
|
||||||
@override
|
@override
|
||||||
@@ -347,6 +497,7 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
|
|||||||
height,
|
height,
|
||||||
durationInSeconds,
|
durationInSeconds,
|
||||||
id,
|
id,
|
||||||
|
remoteId,
|
||||||
checksum,
|
checksum,
|
||||||
isFavorite
|
isFavorite
|
||||||
];
|
];
|
||||||
@@ -394,6 +545,10 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
|
|||||||
} else if (isInserting) {
|
} else if (isInserting) {
|
||||||
context.missing(_idMeta);
|
context.missing(_idMeta);
|
||||||
}
|
}
|
||||||
|
if (data.containsKey('remote_id')) {
|
||||||
|
context.handle(_remoteIdMeta,
|
||||||
|
remoteId.isAcceptableOrUnknown(data['remote_id']!, _remoteIdMeta));
|
||||||
|
}
|
||||||
if (data.containsKey('checksum')) {
|
if (data.containsKey('checksum')) {
|
||||||
context.handle(_checksumMeta,
|
context.handle(_checksumMeta,
|
||||||
checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta));
|
checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta));
|
||||||
@@ -431,6 +586,8 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
|
|||||||
i0.DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']),
|
i0.DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']),
|
||||||
id: attachedDatabase.typeMapping
|
id: attachedDatabase.typeMapping
|
||||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!,
|
.read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!,
|
||||||
|
remoteId: attachedDatabase.typeMapping
|
||||||
|
.read(i0.DriftSqlType.string, data['${effectivePrefix}remote_id']),
|
||||||
checksum: attachedDatabase.typeMapping
|
checksum: attachedDatabase.typeMapping
|
||||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}checksum']),
|
.read(i0.DriftSqlType.string, data['${effectivePrefix}checksum']),
|
||||||
isFavorite: attachedDatabase.typeMapping
|
isFavorite: attachedDatabase.typeMapping
|
||||||
@@ -461,6 +618,7 @@ class LocalAssetEntityData extends i0.DataClass
|
|||||||
final int? height;
|
final int? height;
|
||||||
final int? durationInSeconds;
|
final int? durationInSeconds;
|
||||||
final String id;
|
final String id;
|
||||||
|
final String? remoteId;
|
||||||
final String? checksum;
|
final String? checksum;
|
||||||
final bool isFavorite;
|
final bool isFavorite;
|
||||||
const LocalAssetEntityData(
|
const LocalAssetEntityData(
|
||||||
@@ -472,6 +630,7 @@ class LocalAssetEntityData extends i0.DataClass
|
|||||||
this.height,
|
this.height,
|
||||||
this.durationInSeconds,
|
this.durationInSeconds,
|
||||||
required this.id,
|
required this.id,
|
||||||
|
this.remoteId,
|
||||||
this.checksum,
|
this.checksum,
|
||||||
required this.isFavorite});
|
required this.isFavorite});
|
||||||
@override
|
@override
|
||||||
@@ -494,6 +653,9 @@ class LocalAssetEntityData extends i0.DataClass
|
|||||||
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds);
|
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds);
|
||||||
}
|
}
|
||||||
map['id'] = i0.Variable<String>(id);
|
map['id'] = i0.Variable<String>(id);
|
||||||
|
if (!nullToAbsent || remoteId != null) {
|
||||||
|
map['remote_id'] = i0.Variable<String>(remoteId);
|
||||||
|
}
|
||||||
if (!nullToAbsent || checksum != null) {
|
if (!nullToAbsent || checksum != null) {
|
||||||
map['checksum'] = i0.Variable<String>(checksum);
|
map['checksum'] = i0.Variable<String>(checksum);
|
||||||
}
|
}
|
||||||
@@ -514,6 +676,7 @@ class LocalAssetEntityData extends i0.DataClass
|
|||||||
height: serializer.fromJson<int?>(json['height']),
|
height: serializer.fromJson<int?>(json['height']),
|
||||||
durationInSeconds: serializer.fromJson<int?>(json['durationInSeconds']),
|
durationInSeconds: serializer.fromJson<int?>(json['durationInSeconds']),
|
||||||
id: serializer.fromJson<String>(json['id']),
|
id: serializer.fromJson<String>(json['id']),
|
||||||
|
remoteId: serializer.fromJson<String?>(json['remoteId']),
|
||||||
checksum: serializer.fromJson<String?>(json['checksum']),
|
checksum: serializer.fromJson<String?>(json['checksum']),
|
||||||
isFavorite: serializer.fromJson<bool>(json['isFavorite']),
|
isFavorite: serializer.fromJson<bool>(json['isFavorite']),
|
||||||
);
|
);
|
||||||
@@ -531,6 +694,7 @@ class LocalAssetEntityData extends i0.DataClass
|
|||||||
'height': serializer.toJson<int?>(height),
|
'height': serializer.toJson<int?>(height),
|
||||||
'durationInSeconds': serializer.toJson<int?>(durationInSeconds),
|
'durationInSeconds': serializer.toJson<int?>(durationInSeconds),
|
||||||
'id': serializer.toJson<String>(id),
|
'id': serializer.toJson<String>(id),
|
||||||
|
'remoteId': serializer.toJson<String?>(remoteId),
|
||||||
'checksum': serializer.toJson<String?>(checksum),
|
'checksum': serializer.toJson<String?>(checksum),
|
||||||
'isFavorite': serializer.toJson<bool>(isFavorite),
|
'isFavorite': serializer.toJson<bool>(isFavorite),
|
||||||
};
|
};
|
||||||
@@ -545,6 +709,7 @@ class LocalAssetEntityData extends i0.DataClass
|
|||||||
i0.Value<int?> height = const i0.Value.absent(),
|
i0.Value<int?> height = const i0.Value.absent(),
|
||||||
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
|
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
|
||||||
String? id,
|
String? id,
|
||||||
|
i0.Value<String?> remoteId = const i0.Value.absent(),
|
||||||
i0.Value<String?> checksum = const i0.Value.absent(),
|
i0.Value<String?> checksum = const i0.Value.absent(),
|
||||||
bool? isFavorite}) =>
|
bool? isFavorite}) =>
|
||||||
i1.LocalAssetEntityData(
|
i1.LocalAssetEntityData(
|
||||||
@@ -558,6 +723,7 @@ class LocalAssetEntityData extends i0.DataClass
|
|||||||
? durationInSeconds.value
|
? durationInSeconds.value
|
||||||
: this.durationInSeconds,
|
: this.durationInSeconds,
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
|
remoteId: remoteId.present ? remoteId.value : this.remoteId,
|
||||||
checksum: checksum.present ? checksum.value : this.checksum,
|
checksum: checksum.present ? checksum.value : this.checksum,
|
||||||
isFavorite: isFavorite ?? this.isFavorite,
|
isFavorite: isFavorite ?? this.isFavorite,
|
||||||
);
|
);
|
||||||
@@ -573,6 +739,7 @@ class LocalAssetEntityData extends i0.DataClass
|
|||||||
? data.durationInSeconds.value
|
? data.durationInSeconds.value
|
||||||
: this.durationInSeconds,
|
: this.durationInSeconds,
|
||||||
id: data.id.present ? data.id.value : this.id,
|
id: data.id.present ? data.id.value : this.id,
|
||||||
|
remoteId: data.remoteId.present ? data.remoteId.value : this.remoteId,
|
||||||
checksum: data.checksum.present ? data.checksum.value : this.checksum,
|
checksum: data.checksum.present ? data.checksum.value : this.checksum,
|
||||||
isFavorite:
|
isFavorite:
|
||||||
data.isFavorite.present ? data.isFavorite.value : this.isFavorite,
|
data.isFavorite.present ? data.isFavorite.value : this.isFavorite,
|
||||||
@@ -590,6 +757,7 @@ class LocalAssetEntityData extends i0.DataClass
|
|||||||
..write('height: $height, ')
|
..write('height: $height, ')
|
||||||
..write('durationInSeconds: $durationInSeconds, ')
|
..write('durationInSeconds: $durationInSeconds, ')
|
||||||
..write('id: $id, ')
|
..write('id: $id, ')
|
||||||
|
..write('remoteId: $remoteId, ')
|
||||||
..write('checksum: $checksum, ')
|
..write('checksum: $checksum, ')
|
||||||
..write('isFavorite: $isFavorite')
|
..write('isFavorite: $isFavorite')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
@@ -598,7 +766,7 @@ class LocalAssetEntityData extends i0.DataClass
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(name, type, createdAt, updatedAt, width,
|
int get hashCode => Object.hash(name, type, createdAt, updatedAt, width,
|
||||||
height, durationInSeconds, id, checksum, isFavorite);
|
height, durationInSeconds, id, remoteId, checksum, isFavorite);
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) ||
|
||||||
@@ -611,6 +779,7 @@ class LocalAssetEntityData extends i0.DataClass
|
|||||||
other.height == this.height &&
|
other.height == this.height &&
|
||||||
other.durationInSeconds == this.durationInSeconds &&
|
other.durationInSeconds == this.durationInSeconds &&
|
||||||
other.id == this.id &&
|
other.id == this.id &&
|
||||||
|
other.remoteId == this.remoteId &&
|
||||||
other.checksum == this.checksum &&
|
other.checksum == this.checksum &&
|
||||||
other.isFavorite == this.isFavorite);
|
other.isFavorite == this.isFavorite);
|
||||||
}
|
}
|
||||||
@@ -625,6 +794,7 @@ class LocalAssetEntityCompanion
|
|||||||
final i0.Value<int?> height;
|
final i0.Value<int?> height;
|
||||||
final i0.Value<int?> durationInSeconds;
|
final i0.Value<int?> durationInSeconds;
|
||||||
final i0.Value<String> id;
|
final i0.Value<String> id;
|
||||||
|
final i0.Value<String?> remoteId;
|
||||||
final i0.Value<String?> checksum;
|
final i0.Value<String?> checksum;
|
||||||
final i0.Value<bool> isFavorite;
|
final i0.Value<bool> isFavorite;
|
||||||
const LocalAssetEntityCompanion({
|
const LocalAssetEntityCompanion({
|
||||||
@@ -636,6 +806,7 @@ class LocalAssetEntityCompanion
|
|||||||
this.height = const i0.Value.absent(),
|
this.height = const i0.Value.absent(),
|
||||||
this.durationInSeconds = const i0.Value.absent(),
|
this.durationInSeconds = const i0.Value.absent(),
|
||||||
this.id = const i0.Value.absent(),
|
this.id = const i0.Value.absent(),
|
||||||
|
this.remoteId = const i0.Value.absent(),
|
||||||
this.checksum = const i0.Value.absent(),
|
this.checksum = const i0.Value.absent(),
|
||||||
this.isFavorite = const i0.Value.absent(),
|
this.isFavorite = const i0.Value.absent(),
|
||||||
});
|
});
|
||||||
@@ -648,6 +819,7 @@ class LocalAssetEntityCompanion
|
|||||||
this.height = const i0.Value.absent(),
|
this.height = const i0.Value.absent(),
|
||||||
this.durationInSeconds = const i0.Value.absent(),
|
this.durationInSeconds = const i0.Value.absent(),
|
||||||
required String id,
|
required String id,
|
||||||
|
this.remoteId = const i0.Value.absent(),
|
||||||
this.checksum = const i0.Value.absent(),
|
this.checksum = const i0.Value.absent(),
|
||||||
this.isFavorite = const i0.Value.absent(),
|
this.isFavorite = const i0.Value.absent(),
|
||||||
}) : name = i0.Value(name),
|
}) : name = i0.Value(name),
|
||||||
@@ -662,6 +834,7 @@ class LocalAssetEntityCompanion
|
|||||||
i0.Expression<int>? height,
|
i0.Expression<int>? height,
|
||||||
i0.Expression<int>? durationInSeconds,
|
i0.Expression<int>? durationInSeconds,
|
||||||
i0.Expression<String>? id,
|
i0.Expression<String>? id,
|
||||||
|
i0.Expression<String>? remoteId,
|
||||||
i0.Expression<String>? checksum,
|
i0.Expression<String>? checksum,
|
||||||
i0.Expression<bool>? isFavorite,
|
i0.Expression<bool>? isFavorite,
|
||||||
}) {
|
}) {
|
||||||
@@ -674,6 +847,7 @@ class LocalAssetEntityCompanion
|
|||||||
if (height != null) 'height': height,
|
if (height != null) 'height': height,
|
||||||
if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds,
|
if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds,
|
||||||
if (id != null) 'id': id,
|
if (id != null) 'id': id,
|
||||||
|
if (remoteId != null) 'remote_id': remoteId,
|
||||||
if (checksum != null) 'checksum': checksum,
|
if (checksum != null) 'checksum': checksum,
|
||||||
if (isFavorite != null) 'is_favorite': isFavorite,
|
if (isFavorite != null) 'is_favorite': isFavorite,
|
||||||
});
|
});
|
||||||
@@ -688,6 +862,7 @@ class LocalAssetEntityCompanion
|
|||||||
i0.Value<int?>? height,
|
i0.Value<int?>? height,
|
||||||
i0.Value<int?>? durationInSeconds,
|
i0.Value<int?>? durationInSeconds,
|
||||||
i0.Value<String>? id,
|
i0.Value<String>? id,
|
||||||
|
i0.Value<String?>? remoteId,
|
||||||
i0.Value<String?>? checksum,
|
i0.Value<String?>? checksum,
|
||||||
i0.Value<bool>? isFavorite}) {
|
i0.Value<bool>? isFavorite}) {
|
||||||
return i1.LocalAssetEntityCompanion(
|
return i1.LocalAssetEntityCompanion(
|
||||||
@@ -699,6 +874,7 @@ class LocalAssetEntityCompanion
|
|||||||
height: height ?? this.height,
|
height: height ?? this.height,
|
||||||
durationInSeconds: durationInSeconds ?? this.durationInSeconds,
|
durationInSeconds: durationInSeconds ?? this.durationInSeconds,
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
|
remoteId: remoteId ?? this.remoteId,
|
||||||
checksum: checksum ?? this.checksum,
|
checksum: checksum ?? this.checksum,
|
||||||
isFavorite: isFavorite ?? this.isFavorite,
|
isFavorite: isFavorite ?? this.isFavorite,
|
||||||
);
|
);
|
||||||
@@ -732,6 +908,9 @@ class LocalAssetEntityCompanion
|
|||||||
if (id.present) {
|
if (id.present) {
|
||||||
map['id'] = i0.Variable<String>(id.value);
|
map['id'] = i0.Variable<String>(id.value);
|
||||||
}
|
}
|
||||||
|
if (remoteId.present) {
|
||||||
|
map['remote_id'] = i0.Variable<String>(remoteId.value);
|
||||||
|
}
|
||||||
if (checksum.present) {
|
if (checksum.present) {
|
||||||
map['checksum'] = i0.Variable<String>(checksum.value);
|
map['checksum'] = i0.Variable<String>(checksum.value);
|
||||||
}
|
}
|
||||||
@@ -752,6 +931,7 @@ class LocalAssetEntityCompanion
|
|||||||
..write('height: $height, ')
|
..write('height: $height, ')
|
||||||
..write('durationInSeconds: $durationInSeconds, ')
|
..write('durationInSeconds: $durationInSeconds, ')
|
||||||
..write('id: $id, ')
|
..write('id: $id, ')
|
||||||
|
..write('remoteId: $remoteId, ')
|
||||||
..write('checksum: $checksum, ')
|
..write('checksum: $checksum, ')
|
||||||
..write('isFavorite: $isFavorite')
|
..write('isFavorite: $isFavorite')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ mergedAsset: SELECT * FROM
|
|||||||
(
|
(
|
||||||
SELECT
|
SELECT
|
||||||
rae.id as remote_id,
|
rae.id as remote_id,
|
||||||
lae.id as local_id,
|
rae.local_id as local_id,
|
||||||
rae.name,
|
rae.name,
|
||||||
rae."type",
|
rae."type",
|
||||||
rae.created_at,
|
rae.created_at,
|
||||||
@@ -19,13 +19,11 @@ mergedAsset: SELECT * FROM
|
|||||||
rae.owner_id
|
rae.owner_id
|
||||||
FROM
|
FROM
|
||||||
remote_asset_entity rae
|
remote_asset_entity rae
|
||||||
LEFT JOIN
|
|
||||||
local_asset_entity lae ON rae.checksum = lae.checksum
|
|
||||||
WHERE
|
WHERE
|
||||||
rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id in ?
|
rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id in ?
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT
|
SELECT
|
||||||
NULL as remote_id,
|
lae.remote_id as remote_id,
|
||||||
lae.id as local_id,
|
lae.id as local_id,
|
||||||
lae.name,
|
lae.name,
|
||||||
lae."type",
|
lae."type",
|
||||||
@@ -40,10 +38,8 @@ mergedAsset: SELECT * FROM
|
|||||||
NULL as owner_id
|
NULL as owner_id
|
||||||
FROM
|
FROM
|
||||||
local_asset_entity lae
|
local_asset_entity lae
|
||||||
LEFT JOIN
|
|
||||||
remote_asset_entity rae ON rae.checksum = lae.checksum
|
|
||||||
WHERE
|
WHERE
|
||||||
rae.id IS NULL
|
lae.remote_id IS NULL
|
||||||
)
|
)
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
LIMIT $limit;
|
LIMIT $limit;
|
||||||
@@ -62,8 +58,6 @@ FROM
|
|||||||
rae.created_at
|
rae.created_at
|
||||||
FROM
|
FROM
|
||||||
remote_asset_entity rae
|
remote_asset_entity rae
|
||||||
LEFT JOIN
|
|
||||||
local_asset_entity lae ON rae.checksum = lae.checksum
|
|
||||||
WHERE
|
WHERE
|
||||||
rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id in ?
|
rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id in ?
|
||||||
UNION ALL
|
UNION ALL
|
||||||
@@ -72,10 +66,8 @@ FROM
|
|||||||
lae.created_at
|
lae.created_at
|
||||||
FROM
|
FROM
|
||||||
local_asset_entity lae
|
local_asset_entity lae
|
||||||
LEFT JOIN
|
|
||||||
remote_asset_entity rae ON rae.checksum = lae.checksum
|
|
||||||
WHERE
|
WHERE
|
||||||
rae.id IS NULL
|
lae.remote_id IS NULL
|
||||||
)
|
)
|
||||||
GROUP BY bucket_date
|
GROUP BY bucket_date
|
||||||
ORDER BY bucket_date DESC;
|
ORDER BY bucket_date DESC;
|
||||||
|
|||||||
+2
-2
@@ -18,7 +18,7 @@ class MergedAssetDrift extends i1.ModularAccessor {
|
|||||||
final generatedlimit = $write(limit, startIndex: $arrayStartIndex);
|
final generatedlimit = $write(limit, startIndex: $arrayStartIndex);
|
||||||
$arrayStartIndex += generatedlimit.amountOfVariables;
|
$arrayStartIndex += generatedlimit.amountOfVariables;
|
||||||
return customSelect(
|
return customSelect(
|
||||||
'SELECT * FROM (SELECT rae.id AS remote_id, lae.id AS local_id, rae.name, rae.type, rae.created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id FROM remote_asset_entity AS rae LEFT JOIN local_asset_entity AS lae ON rae.checksum = lae.checksum WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar1) UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum WHERE rae.id IS NULL) ORDER BY created_at DESC ${generatedlimit.sql}',
|
'SELECT * FROM (SELECT rae.id AS remote_id, rae.local_id AS local_id, rae.name, rae.type, rae.created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id FROM remote_asset_entity AS rae WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar1) UNION ALL SELECT lae.remote_id AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id FROM local_asset_entity AS lae WHERE lae.remote_id IS NULL) ORDER BY created_at DESC ${generatedlimit.sql}',
|
||||||
variables: [
|
variables: [
|
||||||
for (var $ in var1) i0.Variable<String>($),
|
for (var $ in var1) i0.Variable<String>($),
|
||||||
...generatedlimit.introducedVariables
|
...generatedlimit.introducedVariables
|
||||||
@@ -51,7 +51,7 @@ class MergedAssetDrift extends i1.ModularAccessor {
|
|||||||
final expandedvar2 = $expandVar($arrayStartIndex, var2.length);
|
final expandedvar2 = $expandVar($arrayStartIndex, var2.length);
|
||||||
$arrayStartIndex += var2.length;
|
$arrayStartIndex += var2.length;
|
||||||
return customSelect(
|
return customSelect(
|
||||||
'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.name, rae.created_at FROM remote_asset_entity AS rae LEFT JOIN local_asset_entity AS lae ON rae.checksum = lae.checksum WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar2) UNION ALL SELECT lae.name, lae.created_at FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum WHERE rae.id IS NULL) GROUP BY bucket_date ORDER BY bucket_date DESC',
|
'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.name, rae.created_at FROM remote_asset_entity AS rae WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar2) UNION ALL SELECT lae.name, lae.created_at FROM local_asset_entity AS lae WHERE lae.remote_id IS NULL) GROUP BY bucket_date ORDER BY bucket_date DESC',
|
||||||
variables: [
|
variables: [
|
||||||
i0.Variable<int>(groupBy),
|
i0.Variable<int>(groupBy),
|
||||||
for (var $ in var2) i0.Variable<String>($)
|
for (var $ in var2) i0.Variable<String>($)
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ class RemoteAssetEntity extends Table
|
|||||||
|
|
||||||
TextColumn get id => text()();
|
TextColumn get id => text()();
|
||||||
|
|
||||||
|
TextColumn get localId => text().nullable()();
|
||||||
|
|
||||||
TextColumn get checksum => text()();
|
TextColumn get checksum => text()();
|
||||||
|
|
||||||
BoolColumn get isFavorite => boolean().withDefault(const Constant(false))();
|
BoolColumn get isFavorite => boolean().withDefault(const Constant(false))();
|
||||||
@@ -51,6 +53,6 @@ extension RemoteAssetEntityDataDomainEx on RemoteAssetEntityData {
|
|||||||
width: width,
|
width: width,
|
||||||
thumbHash: thumbHash,
|
thumbHash: thumbHash,
|
||||||
visibility: visibility,
|
visibility: visibility,
|
||||||
localId: null,
|
localId: localId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ typedef $$RemoteAssetEntityTableCreateCompanionBuilder
|
|||||||
i0.Value<int?> height,
|
i0.Value<int?> height,
|
||||||
i0.Value<int?> durationInSeconds,
|
i0.Value<int?> durationInSeconds,
|
||||||
required String id,
|
required String id,
|
||||||
|
i0.Value<String?> localId,
|
||||||
required String checksum,
|
required String checksum,
|
||||||
i0.Value<bool> isFavorite,
|
i0.Value<bool> isFavorite,
|
||||||
required String ownerId,
|
required String ownerId,
|
||||||
@@ -39,6 +40,7 @@ typedef $$RemoteAssetEntityTableUpdateCompanionBuilder
|
|||||||
i0.Value<int?> height,
|
i0.Value<int?> height,
|
||||||
i0.Value<int?> durationInSeconds,
|
i0.Value<int?> durationInSeconds,
|
||||||
i0.Value<String> id,
|
i0.Value<String> id,
|
||||||
|
i0.Value<String?> localId,
|
||||||
i0.Value<String> checksum,
|
i0.Value<String> checksum,
|
||||||
i0.Value<bool> isFavorite,
|
i0.Value<bool> isFavorite,
|
||||||
i0.Value<String> ownerId,
|
i0.Value<String> ownerId,
|
||||||
@@ -118,6 +120,9 @@ class $$RemoteAssetEntityTableFilterComposer
|
|||||||
i0.ColumnFilters<String> 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 localId => $composableBuilder(
|
||||||
|
column: $table.localId, builder: (column) => i0.ColumnFilters(column));
|
||||||
|
|
||||||
i0.ColumnFilters<String> get checksum => $composableBuilder(
|
i0.ColumnFilters<String> get checksum => $composableBuilder(
|
||||||
column: $table.checksum, builder: (column) => i0.ColumnFilters(column));
|
column: $table.checksum, builder: (column) => i0.ColumnFilters(column));
|
||||||
|
|
||||||
@@ -198,6 +203,9 @@ class $$RemoteAssetEntityTableOrderingComposer
|
|||||||
i0.ColumnOrderings<String> 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 localId => $composableBuilder(
|
||||||
|
column: $table.localId, builder: (column) => i0.ColumnOrderings(column));
|
||||||
|
|
||||||
i0.ColumnOrderings<String> get checksum => $composableBuilder(
|
i0.ColumnOrderings<String> get checksum => $composableBuilder(
|
||||||
column: $table.checksum, builder: (column) => i0.ColumnOrderings(column));
|
column: $table.checksum, builder: (column) => i0.ColumnOrderings(column));
|
||||||
|
|
||||||
@@ -277,6 +285,9 @@ class $$RemoteAssetEntityTableAnnotationComposer
|
|||||||
i0.GeneratedColumn<String> 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 localId =>
|
||||||
|
$composableBuilder(column: $table.localId, builder: (column) => column);
|
||||||
|
|
||||||
i0.GeneratedColumn<String> get checksum =>
|
i0.GeneratedColumn<String> get checksum =>
|
||||||
$composableBuilder(column: $table.checksum, builder: (column) => column);
|
$composableBuilder(column: $table.checksum, builder: (column) => column);
|
||||||
|
|
||||||
@@ -352,6 +363,7 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
|
|||||||
i0.Value<int?> height = const i0.Value.absent(),
|
i0.Value<int?> height = const i0.Value.absent(),
|
||||||
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
|
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
|
||||||
i0.Value<String> id = const i0.Value.absent(),
|
i0.Value<String> id = const i0.Value.absent(),
|
||||||
|
i0.Value<String?> localId = const i0.Value.absent(),
|
||||||
i0.Value<String> checksum = const i0.Value.absent(),
|
i0.Value<String> checksum = const i0.Value.absent(),
|
||||||
i0.Value<bool> isFavorite = const i0.Value.absent(),
|
i0.Value<bool> isFavorite = const i0.Value.absent(),
|
||||||
i0.Value<String> ownerId = const i0.Value.absent(),
|
i0.Value<String> ownerId = const i0.Value.absent(),
|
||||||
@@ -369,6 +381,7 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
|
|||||||
height: height,
|
height: height,
|
||||||
durationInSeconds: durationInSeconds,
|
durationInSeconds: durationInSeconds,
|
||||||
id: id,
|
id: id,
|
||||||
|
localId: localId,
|
||||||
checksum: checksum,
|
checksum: checksum,
|
||||||
isFavorite: isFavorite,
|
isFavorite: isFavorite,
|
||||||
ownerId: ownerId,
|
ownerId: ownerId,
|
||||||
@@ -386,6 +399,7 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
|
|||||||
i0.Value<int?> height = const i0.Value.absent(),
|
i0.Value<int?> height = const i0.Value.absent(),
|
||||||
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
|
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
|
||||||
required String id,
|
required String id,
|
||||||
|
i0.Value<String?> localId = const i0.Value.absent(),
|
||||||
required String checksum,
|
required String checksum,
|
||||||
i0.Value<bool> isFavorite = const i0.Value.absent(),
|
i0.Value<bool> isFavorite = const i0.Value.absent(),
|
||||||
required String ownerId,
|
required String ownerId,
|
||||||
@@ -403,6 +417,7 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
|
|||||||
height: height,
|
height: height,
|
||||||
durationInSeconds: durationInSeconds,
|
durationInSeconds: durationInSeconds,
|
||||||
id: id,
|
id: id,
|
||||||
|
localId: localId,
|
||||||
checksum: checksum,
|
checksum: checksum,
|
||||||
isFavorite: isFavorite,
|
isFavorite: isFavorite,
|
||||||
ownerId: ownerId,
|
ownerId: ownerId,
|
||||||
@@ -530,6 +545,12 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
|
|||||||
late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
|
late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
|
||||||
'id', aliasedName, false,
|
'id', aliasedName, false,
|
||||||
type: i0.DriftSqlType.string, requiredDuringInsert: true);
|
type: i0.DriftSqlType.string, requiredDuringInsert: true);
|
||||||
|
static const i0.VerificationMeta _localIdMeta =
|
||||||
|
const i0.VerificationMeta('localId');
|
||||||
|
@override
|
||||||
|
late final i0.GeneratedColumn<String> localId = i0.GeneratedColumn<String>(
|
||||||
|
'local_id', aliasedName, true,
|
||||||
|
type: i0.DriftSqlType.string, requiredDuringInsert: false);
|
||||||
static const i0.VerificationMeta _checksumMeta =
|
static const i0.VerificationMeta _checksumMeta =
|
||||||
const i0.VerificationMeta('checksum');
|
const i0.VerificationMeta('checksum');
|
||||||
@override
|
@override
|
||||||
@@ -589,6 +610,7 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
|
|||||||
height,
|
height,
|
||||||
durationInSeconds,
|
durationInSeconds,
|
||||||
id,
|
id,
|
||||||
|
localId,
|
||||||
checksum,
|
checksum,
|
||||||
isFavorite,
|
isFavorite,
|
||||||
ownerId,
|
ownerId,
|
||||||
@@ -641,6 +663,10 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
|
|||||||
} else if (isInserting) {
|
} else if (isInserting) {
|
||||||
context.missing(_idMeta);
|
context.missing(_idMeta);
|
||||||
}
|
}
|
||||||
|
if (data.containsKey('local_id')) {
|
||||||
|
context.handle(_localIdMeta,
|
||||||
|
localId.isAcceptableOrUnknown(data['local_id']!, _localIdMeta));
|
||||||
|
}
|
||||||
if (data.containsKey('checksum')) {
|
if (data.containsKey('checksum')) {
|
||||||
context.handle(_checksumMeta,
|
context.handle(_checksumMeta,
|
||||||
checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta));
|
checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta));
|
||||||
@@ -700,6 +726,8 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
|
|||||||
i0.DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']),
|
i0.DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']),
|
||||||
id: attachedDatabase.typeMapping
|
id: attachedDatabase.typeMapping
|
||||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!,
|
.read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!,
|
||||||
|
localId: attachedDatabase.typeMapping
|
||||||
|
.read(i0.DriftSqlType.string, data['${effectivePrefix}local_id']),
|
||||||
checksum: attachedDatabase.typeMapping
|
checksum: attachedDatabase.typeMapping
|
||||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}checksum'])!,
|
.read(i0.DriftSqlType.string, data['${effectivePrefix}checksum'])!,
|
||||||
isFavorite: attachedDatabase.typeMapping
|
isFavorite: attachedDatabase.typeMapping
|
||||||
@@ -744,6 +772,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
|||||||
final int? height;
|
final int? height;
|
||||||
final int? durationInSeconds;
|
final int? durationInSeconds;
|
||||||
final String id;
|
final String id;
|
||||||
|
final String? localId;
|
||||||
final String checksum;
|
final String checksum;
|
||||||
final bool isFavorite;
|
final bool isFavorite;
|
||||||
final String ownerId;
|
final String ownerId;
|
||||||
@@ -760,6 +789,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
|||||||
this.height,
|
this.height,
|
||||||
this.durationInSeconds,
|
this.durationInSeconds,
|
||||||
required this.id,
|
required this.id,
|
||||||
|
this.localId,
|
||||||
required this.checksum,
|
required this.checksum,
|
||||||
required this.isFavorite,
|
required this.isFavorite,
|
||||||
required this.ownerId,
|
required this.ownerId,
|
||||||
@@ -787,6 +817,9 @@ class RemoteAssetEntityData extends i0.DataClass
|
|||||||
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds);
|
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds);
|
||||||
}
|
}
|
||||||
map['id'] = i0.Variable<String>(id);
|
map['id'] = i0.Variable<String>(id);
|
||||||
|
if (!nullToAbsent || localId != null) {
|
||||||
|
map['local_id'] = i0.Variable<String>(localId);
|
||||||
|
}
|
||||||
map['checksum'] = i0.Variable<String>(checksum);
|
map['checksum'] = i0.Variable<String>(checksum);
|
||||||
map['is_favorite'] = i0.Variable<bool>(isFavorite);
|
map['is_favorite'] = i0.Variable<bool>(isFavorite);
|
||||||
map['owner_id'] = i0.Variable<String>(ownerId);
|
map['owner_id'] = i0.Variable<String>(ownerId);
|
||||||
@@ -819,6 +852,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
|||||||
height: serializer.fromJson<int?>(json['height']),
|
height: serializer.fromJson<int?>(json['height']),
|
||||||
durationInSeconds: serializer.fromJson<int?>(json['durationInSeconds']),
|
durationInSeconds: serializer.fromJson<int?>(json['durationInSeconds']),
|
||||||
id: serializer.fromJson<String>(json['id']),
|
id: serializer.fromJson<String>(json['id']),
|
||||||
|
localId: serializer.fromJson<String?>(json['localId']),
|
||||||
checksum: serializer.fromJson<String>(json['checksum']),
|
checksum: serializer.fromJson<String>(json['checksum']),
|
||||||
isFavorite: serializer.fromJson<bool>(json['isFavorite']),
|
isFavorite: serializer.fromJson<bool>(json['isFavorite']),
|
||||||
ownerId: serializer.fromJson<String>(json['ownerId']),
|
ownerId: serializer.fromJson<String>(json['ownerId']),
|
||||||
@@ -842,6 +876,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
|||||||
'height': serializer.toJson<int?>(height),
|
'height': serializer.toJson<int?>(height),
|
||||||
'durationInSeconds': serializer.toJson<int?>(durationInSeconds),
|
'durationInSeconds': serializer.toJson<int?>(durationInSeconds),
|
||||||
'id': serializer.toJson<String>(id),
|
'id': serializer.toJson<String>(id),
|
||||||
|
'localId': serializer.toJson<String?>(localId),
|
||||||
'checksum': serializer.toJson<String>(checksum),
|
'checksum': serializer.toJson<String>(checksum),
|
||||||
'isFavorite': serializer.toJson<bool>(isFavorite),
|
'isFavorite': serializer.toJson<bool>(isFavorite),
|
||||||
'ownerId': serializer.toJson<String>(ownerId),
|
'ownerId': serializer.toJson<String>(ownerId),
|
||||||
@@ -862,6 +897,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
|||||||
i0.Value<int?> height = const i0.Value.absent(),
|
i0.Value<int?> height = const i0.Value.absent(),
|
||||||
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
|
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
|
||||||
String? id,
|
String? id,
|
||||||
|
i0.Value<String?> localId = const i0.Value.absent(),
|
||||||
String? checksum,
|
String? checksum,
|
||||||
bool? isFavorite,
|
bool? isFavorite,
|
||||||
String? ownerId,
|
String? ownerId,
|
||||||
@@ -880,6 +916,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
|||||||
? durationInSeconds.value
|
? durationInSeconds.value
|
||||||
: this.durationInSeconds,
|
: this.durationInSeconds,
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
|
localId: localId.present ? localId.value : this.localId,
|
||||||
checksum: checksum ?? this.checksum,
|
checksum: checksum ?? this.checksum,
|
||||||
isFavorite: isFavorite ?? this.isFavorite,
|
isFavorite: isFavorite ?? this.isFavorite,
|
||||||
ownerId: ownerId ?? this.ownerId,
|
ownerId: ownerId ?? this.ownerId,
|
||||||
@@ -901,6 +938,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
|||||||
? data.durationInSeconds.value
|
? data.durationInSeconds.value
|
||||||
: this.durationInSeconds,
|
: this.durationInSeconds,
|
||||||
id: data.id.present ? data.id.value : this.id,
|
id: data.id.present ? data.id.value : this.id,
|
||||||
|
localId: data.localId.present ? data.localId.value : this.localId,
|
||||||
checksum: data.checksum.present ? data.checksum.value : this.checksum,
|
checksum: data.checksum.present ? data.checksum.value : this.checksum,
|
||||||
isFavorite:
|
isFavorite:
|
||||||
data.isFavorite.present ? data.isFavorite.value : this.isFavorite,
|
data.isFavorite.present ? data.isFavorite.value : this.isFavorite,
|
||||||
@@ -926,6 +964,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
|||||||
..write('height: $height, ')
|
..write('height: $height, ')
|
||||||
..write('durationInSeconds: $durationInSeconds, ')
|
..write('durationInSeconds: $durationInSeconds, ')
|
||||||
..write('id: $id, ')
|
..write('id: $id, ')
|
||||||
|
..write('localId: $localId, ')
|
||||||
..write('checksum: $checksum, ')
|
..write('checksum: $checksum, ')
|
||||||
..write('isFavorite: $isFavorite, ')
|
..write('isFavorite: $isFavorite, ')
|
||||||
..write('ownerId: $ownerId, ')
|
..write('ownerId: $ownerId, ')
|
||||||
@@ -947,6 +986,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
|||||||
height,
|
height,
|
||||||
durationInSeconds,
|
durationInSeconds,
|
||||||
id,
|
id,
|
||||||
|
localId,
|
||||||
checksum,
|
checksum,
|
||||||
isFavorite,
|
isFavorite,
|
||||||
ownerId,
|
ownerId,
|
||||||
@@ -966,6 +1006,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
|||||||
other.height == this.height &&
|
other.height == this.height &&
|
||||||
other.durationInSeconds == this.durationInSeconds &&
|
other.durationInSeconds == this.durationInSeconds &&
|
||||||
other.id == this.id &&
|
other.id == this.id &&
|
||||||
|
other.localId == this.localId &&
|
||||||
other.checksum == this.checksum &&
|
other.checksum == this.checksum &&
|
||||||
other.isFavorite == this.isFavorite &&
|
other.isFavorite == this.isFavorite &&
|
||||||
other.ownerId == this.ownerId &&
|
other.ownerId == this.ownerId &&
|
||||||
@@ -985,6 +1026,7 @@ class RemoteAssetEntityCompanion
|
|||||||
final i0.Value<int?> height;
|
final i0.Value<int?> height;
|
||||||
final i0.Value<int?> durationInSeconds;
|
final i0.Value<int?> durationInSeconds;
|
||||||
final i0.Value<String> id;
|
final i0.Value<String> id;
|
||||||
|
final i0.Value<String?> localId;
|
||||||
final i0.Value<String> checksum;
|
final i0.Value<String> checksum;
|
||||||
final i0.Value<bool> isFavorite;
|
final i0.Value<bool> isFavorite;
|
||||||
final i0.Value<String> ownerId;
|
final i0.Value<String> ownerId;
|
||||||
@@ -1001,6 +1043,7 @@ class RemoteAssetEntityCompanion
|
|||||||
this.height = const i0.Value.absent(),
|
this.height = const i0.Value.absent(),
|
||||||
this.durationInSeconds = const i0.Value.absent(),
|
this.durationInSeconds = const i0.Value.absent(),
|
||||||
this.id = const i0.Value.absent(),
|
this.id = const i0.Value.absent(),
|
||||||
|
this.localId = const i0.Value.absent(),
|
||||||
this.checksum = const i0.Value.absent(),
|
this.checksum = const i0.Value.absent(),
|
||||||
this.isFavorite = const i0.Value.absent(),
|
this.isFavorite = const i0.Value.absent(),
|
||||||
this.ownerId = const i0.Value.absent(),
|
this.ownerId = const i0.Value.absent(),
|
||||||
@@ -1018,6 +1061,7 @@ class RemoteAssetEntityCompanion
|
|||||||
this.height = const i0.Value.absent(),
|
this.height = const i0.Value.absent(),
|
||||||
this.durationInSeconds = const i0.Value.absent(),
|
this.durationInSeconds = const i0.Value.absent(),
|
||||||
required String id,
|
required String id,
|
||||||
|
this.localId = const i0.Value.absent(),
|
||||||
required String checksum,
|
required String checksum,
|
||||||
this.isFavorite = const i0.Value.absent(),
|
this.isFavorite = const i0.Value.absent(),
|
||||||
required String ownerId,
|
required String ownerId,
|
||||||
@@ -1040,6 +1084,7 @@ class RemoteAssetEntityCompanion
|
|||||||
i0.Expression<int>? height,
|
i0.Expression<int>? height,
|
||||||
i0.Expression<int>? durationInSeconds,
|
i0.Expression<int>? durationInSeconds,
|
||||||
i0.Expression<String>? id,
|
i0.Expression<String>? id,
|
||||||
|
i0.Expression<String>? localId,
|
||||||
i0.Expression<String>? checksum,
|
i0.Expression<String>? checksum,
|
||||||
i0.Expression<bool>? isFavorite,
|
i0.Expression<bool>? isFavorite,
|
||||||
i0.Expression<String>? ownerId,
|
i0.Expression<String>? ownerId,
|
||||||
@@ -1057,6 +1102,7 @@ class RemoteAssetEntityCompanion
|
|||||||
if (height != null) 'height': height,
|
if (height != null) 'height': height,
|
||||||
if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds,
|
if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds,
|
||||||
if (id != null) 'id': id,
|
if (id != null) 'id': id,
|
||||||
|
if (localId != null) 'local_id': localId,
|
||||||
if (checksum != null) 'checksum': checksum,
|
if (checksum != null) 'checksum': checksum,
|
||||||
if (isFavorite != null) 'is_favorite': isFavorite,
|
if (isFavorite != null) 'is_favorite': isFavorite,
|
||||||
if (ownerId != null) 'owner_id': ownerId,
|
if (ownerId != null) 'owner_id': ownerId,
|
||||||
@@ -1076,6 +1122,7 @@ class RemoteAssetEntityCompanion
|
|||||||
i0.Value<int?>? height,
|
i0.Value<int?>? height,
|
||||||
i0.Value<int?>? durationInSeconds,
|
i0.Value<int?>? durationInSeconds,
|
||||||
i0.Value<String>? id,
|
i0.Value<String>? id,
|
||||||
|
i0.Value<String?>? localId,
|
||||||
i0.Value<String>? checksum,
|
i0.Value<String>? checksum,
|
||||||
i0.Value<bool>? isFavorite,
|
i0.Value<bool>? isFavorite,
|
||||||
i0.Value<String>? ownerId,
|
i0.Value<String>? ownerId,
|
||||||
@@ -1092,6 +1139,7 @@ class RemoteAssetEntityCompanion
|
|||||||
height: height ?? this.height,
|
height: height ?? this.height,
|
||||||
durationInSeconds: durationInSeconds ?? this.durationInSeconds,
|
durationInSeconds: durationInSeconds ?? this.durationInSeconds,
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
|
localId: localId ?? this.localId,
|
||||||
checksum: checksum ?? this.checksum,
|
checksum: checksum ?? this.checksum,
|
||||||
isFavorite: isFavorite ?? this.isFavorite,
|
isFavorite: isFavorite ?? this.isFavorite,
|
||||||
ownerId: ownerId ?? this.ownerId,
|
ownerId: ownerId ?? this.ownerId,
|
||||||
@@ -1130,6 +1178,9 @@ class RemoteAssetEntityCompanion
|
|||||||
if (id.present) {
|
if (id.present) {
|
||||||
map['id'] = i0.Variable<String>(id.value);
|
map['id'] = i0.Variable<String>(id.value);
|
||||||
}
|
}
|
||||||
|
if (localId.present) {
|
||||||
|
map['local_id'] = i0.Variable<String>(localId.value);
|
||||||
|
}
|
||||||
if (checksum.present) {
|
if (checksum.present) {
|
||||||
map['checksum'] = i0.Variable<String>(checksum.value);
|
map['checksum'] = i0.Variable<String>(checksum.value);
|
||||||
}
|
}
|
||||||
@@ -1167,6 +1218,7 @@ class RemoteAssetEntityCompanion
|
|||||||
..write('height: $height, ')
|
..write('height: $height, ')
|
||||||
..write('durationInSeconds: $durationInSeconds, ')
|
..write('durationInSeconds: $durationInSeconds, ')
|
||||||
..write('id: $id, ')
|
..write('id: $id, ')
|
||||||
|
..write('localId: $localId, ')
|
||||||
..write('checksum: $checksum, ')
|
..write('checksum: $checksum, ')
|
||||||
..write('isFavorite: $isFavorite, ')
|
..write('isFavorite: $isFavorite, ')
|
||||||
..write('ownerId: $ownerId, ')
|
..write('ownerId: $ownerId, ')
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
||||||
|
|
||||||
|
class StackEntity extends Table with DriftDefaultsMixin {
|
||||||
|
const StackEntity();
|
||||||
|
|
||||||
|
TextColumn get id => text()();
|
||||||
|
|
||||||
|
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
||||||
|
|
||||||
|
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
|
||||||
|
|
||||||
|
TextColumn get ownerId =>
|
||||||
|
text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
|
||||||
|
|
||||||
|
TextColumn get primaryAssetId => text().references(RemoteAssetEntity, #id)();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<Column> get primaryKey => {id};
|
||||||
|
}
|
||||||
@@ -0,0 +1,706 @@
|
|||||||
|
// dart format width=80
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
import 'package:drift/drift.dart' as i0;
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart'
|
||||||
|
as i1;
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/stack.entity.dart' as i2;
|
||||||
|
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i3;
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
|
||||||
|
as i4;
|
||||||
|
import 'package:drift/internal/modular.dart' as i5;
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'
|
||||||
|
as i6;
|
||||||
|
|
||||||
|
typedef $$StackEntityTableCreateCompanionBuilder = i1.StackEntityCompanion
|
||||||
|
Function({
|
||||||
|
required String id,
|
||||||
|
i0.Value<DateTime> createdAt,
|
||||||
|
i0.Value<DateTime> updatedAt,
|
||||||
|
required String ownerId,
|
||||||
|
required String primaryAssetId,
|
||||||
|
});
|
||||||
|
typedef $$StackEntityTableUpdateCompanionBuilder = i1.StackEntityCompanion
|
||||||
|
Function({
|
||||||
|
i0.Value<String> id,
|
||||||
|
i0.Value<DateTime> createdAt,
|
||||||
|
i0.Value<DateTime> updatedAt,
|
||||||
|
i0.Value<String> ownerId,
|
||||||
|
i0.Value<String> primaryAssetId,
|
||||||
|
});
|
||||||
|
|
||||||
|
final class $$StackEntityTableReferences extends i0.BaseReferences<
|
||||||
|
i0.GeneratedDatabase, i1.$StackEntityTable, i1.StackEntityData> {
|
||||||
|
$$StackEntityTableReferences(super.$_db, super.$_table, super.$_typedResult);
|
||||||
|
|
||||||
|
static i4.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) =>
|
||||||
|
i5.ReadDatabaseContainer(db)
|
||||||
|
.resultSet<i4.$UserEntityTable>('user_entity')
|
||||||
|
.createAlias(i0.$_aliasNameGenerator(
|
||||||
|
i5.ReadDatabaseContainer(db)
|
||||||
|
.resultSet<i1.$StackEntityTable>('stack_entity')
|
||||||
|
.ownerId,
|
||||||
|
i5.ReadDatabaseContainer(db)
|
||||||
|
.resultSet<i4.$UserEntityTable>('user_entity')
|
||||||
|
.id));
|
||||||
|
|
||||||
|
i4.$$UserEntityTableProcessedTableManager get ownerId {
|
||||||
|
final $_column = $_itemColumn<String>('owner_id')!;
|
||||||
|
|
||||||
|
final manager = i4
|
||||||
|
.$$UserEntityTableTableManager(
|
||||||
|
$_db,
|
||||||
|
i5.ReadDatabaseContainer($_db)
|
||||||
|
.resultSet<i4.$UserEntityTable>('user_entity'))
|
||||||
|
.filter((f) => f.id.sqlEquals($_column));
|
||||||
|
final item = $_typedResult.readTableOrNull(_ownerIdTable($_db));
|
||||||
|
if (item == null) return manager;
|
||||||
|
return i0.ProcessedTableManager(
|
||||||
|
manager.$state.copyWith(prefetchedData: [item]));
|
||||||
|
}
|
||||||
|
|
||||||
|
static i6.$RemoteAssetEntityTable _primaryAssetIdTable(
|
||||||
|
i0.GeneratedDatabase db) =>
|
||||||
|
i5.ReadDatabaseContainer(db)
|
||||||
|
.resultSet<i6.$RemoteAssetEntityTable>('remote_asset_entity')
|
||||||
|
.createAlias(i0.$_aliasNameGenerator(
|
||||||
|
i5.ReadDatabaseContainer(db)
|
||||||
|
.resultSet<i1.$StackEntityTable>('stack_entity')
|
||||||
|
.primaryAssetId,
|
||||||
|
i5.ReadDatabaseContainer(db)
|
||||||
|
.resultSet<i6.$RemoteAssetEntityTable>('remote_asset_entity')
|
||||||
|
.id));
|
||||||
|
|
||||||
|
i6.$$RemoteAssetEntityTableProcessedTableManager get primaryAssetId {
|
||||||
|
final $_column = $_itemColumn<String>('primary_asset_id')!;
|
||||||
|
|
||||||
|
final manager = i6
|
||||||
|
.$$RemoteAssetEntityTableTableManager(
|
||||||
|
$_db,
|
||||||
|
i5.ReadDatabaseContainer($_db)
|
||||||
|
.resultSet<i6.$RemoteAssetEntityTable>('remote_asset_entity'))
|
||||||
|
.filter((f) => f.id.sqlEquals($_column));
|
||||||
|
final item = $_typedResult.readTableOrNull(_primaryAssetIdTable($_db));
|
||||||
|
if (item == null) return manager;
|
||||||
|
return i0.ProcessedTableManager(
|
||||||
|
manager.$state.copyWith(prefetchedData: [item]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class $$StackEntityTableFilterComposer
|
||||||
|
extends i0.Composer<i0.GeneratedDatabase, i1.$StackEntityTable> {
|
||||||
|
$$StackEntityTableFilterComposer({
|
||||||
|
required super.$db,
|
||||||
|
required super.$table,
|
||||||
|
super.joinBuilder,
|
||||||
|
super.$addJoinBuilderToRootComposer,
|
||||||
|
super.$removeJoinBuilderFromRootComposer,
|
||||||
|
});
|
||||||
|
i0.ColumnFilters<String> get id => $composableBuilder(
|
||||||
|
column: $table.id, builder: (column) => i0.ColumnFilters(column));
|
||||||
|
|
||||||
|
i0.ColumnFilters<DateTime> get createdAt => $composableBuilder(
|
||||||
|
column: $table.createdAt, builder: (column) => i0.ColumnFilters(column));
|
||||||
|
|
||||||
|
i0.ColumnFilters<DateTime> get updatedAt => $composableBuilder(
|
||||||
|
column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column));
|
||||||
|
|
||||||
|
i4.$$UserEntityTableFilterComposer get ownerId {
|
||||||
|
final i4.$$UserEntityTableFilterComposer composer = $composerBuilder(
|
||||||
|
composer: this,
|
||||||
|
getCurrentColumn: (t) => t.ownerId,
|
||||||
|
referencedTable: i5.ReadDatabaseContainer($db)
|
||||||
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
|
getReferencedColumn: (t) => t.id,
|
||||||
|
builder: (joinBuilder,
|
||||||
|
{$addJoinBuilderToRootComposer,
|
||||||
|
$removeJoinBuilderFromRootComposer}) =>
|
||||||
|
i4.$$UserEntityTableFilterComposer(
|
||||||
|
$db: $db,
|
||||||
|
$table: i5.ReadDatabaseContainer($db)
|
||||||
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
|
joinBuilder: joinBuilder,
|
||||||
|
$removeJoinBuilderFromRootComposer:
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
));
|
||||||
|
return composer;
|
||||||
|
}
|
||||||
|
|
||||||
|
i6.$$RemoteAssetEntityTableFilterComposer get primaryAssetId {
|
||||||
|
final i6.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder(
|
||||||
|
composer: this,
|
||||||
|
getCurrentColumn: (t) => t.primaryAssetId,
|
||||||
|
referencedTable: i5.ReadDatabaseContainer($db)
|
||||||
|
.resultSet<i6.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||||
|
getReferencedColumn: (t) => t.id,
|
||||||
|
builder: (joinBuilder,
|
||||||
|
{$addJoinBuilderToRootComposer,
|
||||||
|
$removeJoinBuilderFromRootComposer}) =>
|
||||||
|
i6.$$RemoteAssetEntityTableFilterComposer(
|
||||||
|
$db: $db,
|
||||||
|
$table: i5.ReadDatabaseContainer($db)
|
||||||
|
.resultSet<i6.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||||
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
|
joinBuilder: joinBuilder,
|
||||||
|
$removeJoinBuilderFromRootComposer:
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
));
|
||||||
|
return composer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class $$StackEntityTableOrderingComposer
|
||||||
|
extends i0.Composer<i0.GeneratedDatabase, i1.$StackEntityTable> {
|
||||||
|
$$StackEntityTableOrderingComposer({
|
||||||
|
required super.$db,
|
||||||
|
required super.$table,
|
||||||
|
super.joinBuilder,
|
||||||
|
super.$addJoinBuilderToRootComposer,
|
||||||
|
super.$removeJoinBuilderFromRootComposer,
|
||||||
|
});
|
||||||
|
i0.ColumnOrderings<String> get id => $composableBuilder(
|
||||||
|
column: $table.id, builder: (column) => i0.ColumnOrderings(column));
|
||||||
|
|
||||||
|
i0.ColumnOrderings<DateTime> get createdAt => $composableBuilder(
|
||||||
|
column: $table.createdAt,
|
||||||
|
builder: (column) => i0.ColumnOrderings(column));
|
||||||
|
|
||||||
|
i0.ColumnOrderings<DateTime> get updatedAt => $composableBuilder(
|
||||||
|
column: $table.updatedAt,
|
||||||
|
builder: (column) => i0.ColumnOrderings(column));
|
||||||
|
|
||||||
|
i4.$$UserEntityTableOrderingComposer get ownerId {
|
||||||
|
final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder(
|
||||||
|
composer: this,
|
||||||
|
getCurrentColumn: (t) => t.ownerId,
|
||||||
|
referencedTable: i5.ReadDatabaseContainer($db)
|
||||||
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
|
getReferencedColumn: (t) => t.id,
|
||||||
|
builder: (joinBuilder,
|
||||||
|
{$addJoinBuilderToRootComposer,
|
||||||
|
$removeJoinBuilderFromRootComposer}) =>
|
||||||
|
i4.$$UserEntityTableOrderingComposer(
|
||||||
|
$db: $db,
|
||||||
|
$table: i5.ReadDatabaseContainer($db)
|
||||||
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
|
joinBuilder: joinBuilder,
|
||||||
|
$removeJoinBuilderFromRootComposer:
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
));
|
||||||
|
return composer;
|
||||||
|
}
|
||||||
|
|
||||||
|
i6.$$RemoteAssetEntityTableOrderingComposer get primaryAssetId {
|
||||||
|
final i6.$$RemoteAssetEntityTableOrderingComposer composer =
|
||||||
|
$composerBuilder(
|
||||||
|
composer: this,
|
||||||
|
getCurrentColumn: (t) => t.primaryAssetId,
|
||||||
|
referencedTable: i5.ReadDatabaseContainer($db)
|
||||||
|
.resultSet<i6.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||||
|
getReferencedColumn: (t) => t.id,
|
||||||
|
builder: (joinBuilder,
|
||||||
|
{$addJoinBuilderToRootComposer,
|
||||||
|
$removeJoinBuilderFromRootComposer}) =>
|
||||||
|
i6.$$RemoteAssetEntityTableOrderingComposer(
|
||||||
|
$db: $db,
|
||||||
|
$table: i5.ReadDatabaseContainer($db)
|
||||||
|
.resultSet<i6.$RemoteAssetEntityTable>(
|
||||||
|
'remote_asset_entity'),
|
||||||
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
|
joinBuilder: joinBuilder,
|
||||||
|
$removeJoinBuilderFromRootComposer:
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
));
|
||||||
|
return composer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class $$StackEntityTableAnnotationComposer
|
||||||
|
extends i0.Composer<i0.GeneratedDatabase, i1.$StackEntityTable> {
|
||||||
|
$$StackEntityTableAnnotationComposer({
|
||||||
|
required super.$db,
|
||||||
|
required super.$table,
|
||||||
|
super.joinBuilder,
|
||||||
|
super.$addJoinBuilderToRootComposer,
|
||||||
|
super.$removeJoinBuilderFromRootComposer,
|
||||||
|
});
|
||||||
|
i0.GeneratedColumn<String> get id =>
|
||||||
|
$composableBuilder(column: $table.id, builder: (column) => column);
|
||||||
|
|
||||||
|
i0.GeneratedColumn<DateTime> get createdAt =>
|
||||||
|
$composableBuilder(column: $table.createdAt, builder: (column) => column);
|
||||||
|
|
||||||
|
i0.GeneratedColumn<DateTime> get updatedAt =>
|
||||||
|
$composableBuilder(column: $table.updatedAt, builder: (column) => column);
|
||||||
|
|
||||||
|
i4.$$UserEntityTableAnnotationComposer get ownerId {
|
||||||
|
final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
|
||||||
|
composer: this,
|
||||||
|
getCurrentColumn: (t) => t.ownerId,
|
||||||
|
referencedTable: i5.ReadDatabaseContainer($db)
|
||||||
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
|
getReferencedColumn: (t) => t.id,
|
||||||
|
builder: (joinBuilder,
|
||||||
|
{$addJoinBuilderToRootComposer,
|
||||||
|
$removeJoinBuilderFromRootComposer}) =>
|
||||||
|
i4.$$UserEntityTableAnnotationComposer(
|
||||||
|
$db: $db,
|
||||||
|
$table: i5.ReadDatabaseContainer($db)
|
||||||
|
.resultSet<i4.$UserEntityTable>('user_entity'),
|
||||||
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
|
joinBuilder: joinBuilder,
|
||||||
|
$removeJoinBuilderFromRootComposer:
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
));
|
||||||
|
return composer;
|
||||||
|
}
|
||||||
|
|
||||||
|
i6.$$RemoteAssetEntityTableAnnotationComposer get primaryAssetId {
|
||||||
|
final i6.$$RemoteAssetEntityTableAnnotationComposer composer =
|
||||||
|
$composerBuilder(
|
||||||
|
composer: this,
|
||||||
|
getCurrentColumn: (t) => t.primaryAssetId,
|
||||||
|
referencedTable: i5.ReadDatabaseContainer($db)
|
||||||
|
.resultSet<i6.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||||
|
getReferencedColumn: (t) => t.id,
|
||||||
|
builder: (joinBuilder,
|
||||||
|
{$addJoinBuilderToRootComposer,
|
||||||
|
$removeJoinBuilderFromRootComposer}) =>
|
||||||
|
i6.$$RemoteAssetEntityTableAnnotationComposer(
|
||||||
|
$db: $db,
|
||||||
|
$table: i5.ReadDatabaseContainer($db)
|
||||||
|
.resultSet<i6.$RemoteAssetEntityTable>(
|
||||||
|
'remote_asset_entity'),
|
||||||
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
|
joinBuilder: joinBuilder,
|
||||||
|
$removeJoinBuilderFromRootComposer:
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
));
|
||||||
|
return composer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class $$StackEntityTableTableManager extends i0.RootTableManager<
|
||||||
|
i0.GeneratedDatabase,
|
||||||
|
i1.$StackEntityTable,
|
||||||
|
i1.StackEntityData,
|
||||||
|
i1.$$StackEntityTableFilterComposer,
|
||||||
|
i1.$$StackEntityTableOrderingComposer,
|
||||||
|
i1.$$StackEntityTableAnnotationComposer,
|
||||||
|
$$StackEntityTableCreateCompanionBuilder,
|
||||||
|
$$StackEntityTableUpdateCompanionBuilder,
|
||||||
|
(i1.StackEntityData, i1.$$StackEntityTableReferences),
|
||||||
|
i1.StackEntityData,
|
||||||
|
i0.PrefetchHooks Function({bool ownerId, bool primaryAssetId})> {
|
||||||
|
$$StackEntityTableTableManager(
|
||||||
|
i0.GeneratedDatabase db, i1.$StackEntityTable table)
|
||||||
|
: super(i0.TableManagerState(
|
||||||
|
db: db,
|
||||||
|
table: table,
|
||||||
|
createFilteringComposer: () =>
|
||||||
|
i1.$$StackEntityTableFilterComposer($db: db, $table: table),
|
||||||
|
createOrderingComposer: () =>
|
||||||
|
i1.$$StackEntityTableOrderingComposer($db: db, $table: table),
|
||||||
|
createComputedFieldComposer: () =>
|
||||||
|
i1.$$StackEntityTableAnnotationComposer($db: db, $table: table),
|
||||||
|
updateCompanionCallback: ({
|
||||||
|
i0.Value<String> id = const i0.Value.absent(),
|
||||||
|
i0.Value<DateTime> createdAt = const i0.Value.absent(),
|
||||||
|
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
||||||
|
i0.Value<String> ownerId = const i0.Value.absent(),
|
||||||
|
i0.Value<String> primaryAssetId = const i0.Value.absent(),
|
||||||
|
}) =>
|
||||||
|
i1.StackEntityCompanion(
|
||||||
|
id: id,
|
||||||
|
createdAt: createdAt,
|
||||||
|
updatedAt: updatedAt,
|
||||||
|
ownerId: ownerId,
|
||||||
|
primaryAssetId: primaryAssetId,
|
||||||
|
),
|
||||||
|
createCompanionCallback: ({
|
||||||
|
required String id,
|
||||||
|
i0.Value<DateTime> createdAt = const i0.Value.absent(),
|
||||||
|
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
||||||
|
required String ownerId,
|
||||||
|
required String primaryAssetId,
|
||||||
|
}) =>
|
||||||
|
i1.StackEntityCompanion.insert(
|
||||||
|
id: id,
|
||||||
|
createdAt: createdAt,
|
||||||
|
updatedAt: updatedAt,
|
||||||
|
ownerId: ownerId,
|
||||||
|
primaryAssetId: primaryAssetId,
|
||||||
|
),
|
||||||
|
withReferenceMapper: (p0) => p0
|
||||||
|
.map((e) => (
|
||||||
|
e.readTable(table),
|
||||||
|
i1.$$StackEntityTableReferences(db, table, e)
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
prefetchHooksCallback: ({ownerId = false, primaryAssetId = false}) {
|
||||||
|
return i0.PrefetchHooks(
|
||||||
|
db: db,
|
||||||
|
explicitlyWatchedTables: [],
|
||||||
|
addJoins: <
|
||||||
|
T extends i0.TableManagerState<
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic>>(state) {
|
||||||
|
if (ownerId) {
|
||||||
|
state = state.withJoin(
|
||||||
|
currentTable: table,
|
||||||
|
currentColumn: table.ownerId,
|
||||||
|
referencedTable:
|
||||||
|
i1.$$StackEntityTableReferences._ownerIdTable(db),
|
||||||
|
referencedColumn:
|
||||||
|
i1.$$StackEntityTableReferences._ownerIdTable(db).id,
|
||||||
|
) as T;
|
||||||
|
}
|
||||||
|
if (primaryAssetId) {
|
||||||
|
state = state.withJoin(
|
||||||
|
currentTable: table,
|
||||||
|
currentColumn: table.primaryAssetId,
|
||||||
|
referencedTable: i1.$$StackEntityTableReferences
|
||||||
|
._primaryAssetIdTable(db),
|
||||||
|
referencedColumn: i1.$$StackEntityTableReferences
|
||||||
|
._primaryAssetIdTable(db)
|
||||||
|
.id,
|
||||||
|
) as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
},
|
||||||
|
getPrefetchedDataCallback: (items) async {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef $$StackEntityTableProcessedTableManager = i0.ProcessedTableManager<
|
||||||
|
i0.GeneratedDatabase,
|
||||||
|
i1.$StackEntityTable,
|
||||||
|
i1.StackEntityData,
|
||||||
|
i1.$$StackEntityTableFilterComposer,
|
||||||
|
i1.$$StackEntityTableOrderingComposer,
|
||||||
|
i1.$$StackEntityTableAnnotationComposer,
|
||||||
|
$$StackEntityTableCreateCompanionBuilder,
|
||||||
|
$$StackEntityTableUpdateCompanionBuilder,
|
||||||
|
(i1.StackEntityData, i1.$$StackEntityTableReferences),
|
||||||
|
i1.StackEntityData,
|
||||||
|
i0.PrefetchHooks Function({bool ownerId, bool primaryAssetId})>;
|
||||||
|
|
||||||
|
class $StackEntityTable extends i2.StackEntity
|
||||||
|
with i0.TableInfo<$StackEntityTable, i1.StackEntityData> {
|
||||||
|
@override
|
||||||
|
final i0.GeneratedDatabase attachedDatabase;
|
||||||
|
final String? _alias;
|
||||||
|
$StackEntityTable(this.attachedDatabase, [this._alias]);
|
||||||
|
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
|
||||||
|
@override
|
||||||
|
late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
|
||||||
|
'id', aliasedName, false,
|
||||||
|
type: i0.DriftSqlType.string, requiredDuringInsert: true);
|
||||||
|
static const i0.VerificationMeta _createdAtMeta =
|
||||||
|
const i0.VerificationMeta('createdAt');
|
||||||
|
@override
|
||||||
|
late final i0.GeneratedColumn<DateTime> createdAt =
|
||||||
|
i0.GeneratedColumn<DateTime>('created_at', aliasedName, false,
|
||||||
|
type: i0.DriftSqlType.dateTime,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
defaultValue: i3.currentDateAndTime);
|
||||||
|
static const i0.VerificationMeta _updatedAtMeta =
|
||||||
|
const i0.VerificationMeta('updatedAt');
|
||||||
|
@override
|
||||||
|
late final i0.GeneratedColumn<DateTime> updatedAt =
|
||||||
|
i0.GeneratedColumn<DateTime>('updated_at', aliasedName, false,
|
||||||
|
type: i0.DriftSqlType.dateTime,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
defaultValue: i3.currentDateAndTime);
|
||||||
|
static const i0.VerificationMeta _ownerIdMeta =
|
||||||
|
const i0.VerificationMeta('ownerId');
|
||||||
|
@override
|
||||||
|
late final i0.GeneratedColumn<String> ownerId = i0.GeneratedColumn<String>(
|
||||||
|
'owner_id', aliasedName, false,
|
||||||
|
type: i0.DriftSqlType.string,
|
||||||
|
requiredDuringInsert: true,
|
||||||
|
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||||
|
'REFERENCES user_entity (id) ON DELETE CASCADE'));
|
||||||
|
static const i0.VerificationMeta _primaryAssetIdMeta =
|
||||||
|
const i0.VerificationMeta('primaryAssetId');
|
||||||
|
@override
|
||||||
|
late final i0.GeneratedColumn<String> primaryAssetId =
|
||||||
|
i0.GeneratedColumn<String>(
|
||||||
|
'primary_asset_id', aliasedName, false,
|
||||||
|
type: i0.DriftSqlType.string,
|
||||||
|
requiredDuringInsert: true,
|
||||||
|
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||||
|
'REFERENCES remote_asset_entity (id)'));
|
||||||
|
@override
|
||||||
|
List<i0.GeneratedColumn> get $columns =>
|
||||||
|
[id, createdAt, updatedAt, ownerId, primaryAssetId];
|
||||||
|
@override
|
||||||
|
String get aliasedName => _alias ?? actualTableName;
|
||||||
|
@override
|
||||||
|
String get actualTableName => $name;
|
||||||
|
static const String $name = 'stack_entity';
|
||||||
|
@override
|
||||||
|
i0.VerificationContext validateIntegrity(
|
||||||
|
i0.Insertable<i1.StackEntityData> instance,
|
||||||
|
{bool isInserting = false}) {
|
||||||
|
final context = i0.VerificationContext();
|
||||||
|
final data = instance.toColumns(true);
|
||||||
|
if (data.containsKey('id')) {
|
||||||
|
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
|
||||||
|
} else if (isInserting) {
|
||||||
|
context.missing(_idMeta);
|
||||||
|
}
|
||||||
|
if (data.containsKey('created_at')) {
|
||||||
|
context.handle(_createdAtMeta,
|
||||||
|
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta));
|
||||||
|
}
|
||||||
|
if (data.containsKey('updated_at')) {
|
||||||
|
context.handle(_updatedAtMeta,
|
||||||
|
updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta));
|
||||||
|
}
|
||||||
|
if (data.containsKey('owner_id')) {
|
||||||
|
context.handle(_ownerIdMeta,
|
||||||
|
ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta));
|
||||||
|
} else if (isInserting) {
|
||||||
|
context.missing(_ownerIdMeta);
|
||||||
|
}
|
||||||
|
if (data.containsKey('primary_asset_id')) {
|
||||||
|
context.handle(
|
||||||
|
_primaryAssetIdMeta,
|
||||||
|
primaryAssetId.isAcceptableOrUnknown(
|
||||||
|
data['primary_asset_id']!, _primaryAssetIdMeta));
|
||||||
|
} else if (isInserting) {
|
||||||
|
context.missing(_primaryAssetIdMeta);
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<i0.GeneratedColumn> get $primaryKey => {id};
|
||||||
|
@override
|
||||||
|
i1.StackEntityData map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
|
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||||
|
return i1.StackEntityData(
|
||||||
|
id: attachedDatabase.typeMapping
|
||||||
|
.read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!,
|
||||||
|
createdAt: attachedDatabase.typeMapping.read(
|
||||||
|
i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
|
||||||
|
updatedAt: attachedDatabase.typeMapping.read(
|
||||||
|
i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!,
|
||||||
|
ownerId: attachedDatabase.typeMapping
|
||||||
|
.read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!,
|
||||||
|
primaryAssetId: attachedDatabase.typeMapping.read(
|
||||||
|
i0.DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
$StackEntityTable createAlias(String alias) {
|
||||||
|
return $StackEntityTable(attachedDatabase, alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get withoutRowId => true;
|
||||||
|
@override
|
||||||
|
bool get isStrict => true;
|
||||||
|
}
|
||||||
|
|
||||||
|
class StackEntityData extends i0.DataClass
|
||||||
|
implements i0.Insertable<i1.StackEntityData> {
|
||||||
|
final String id;
|
||||||
|
final DateTime createdAt;
|
||||||
|
final DateTime updatedAt;
|
||||||
|
final String ownerId;
|
||||||
|
final String primaryAssetId;
|
||||||
|
const StackEntityData(
|
||||||
|
{required this.id,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
required this.ownerId,
|
||||||
|
required this.primaryAssetId});
|
||||||
|
@override
|
||||||
|
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||||
|
final map = <String, i0.Expression>{};
|
||||||
|
map['id'] = i0.Variable<String>(id);
|
||||||
|
map['created_at'] = i0.Variable<DateTime>(createdAt);
|
||||||
|
map['updated_at'] = i0.Variable<DateTime>(updatedAt);
|
||||||
|
map['owner_id'] = i0.Variable<String>(ownerId);
|
||||||
|
map['primary_asset_id'] = i0.Variable<String>(primaryAssetId);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
factory StackEntityData.fromJson(Map<String, dynamic> json,
|
||||||
|
{i0.ValueSerializer? serializer}) {
|
||||||
|
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||||
|
return StackEntityData(
|
||||||
|
id: serializer.fromJson<String>(json['id']),
|
||||||
|
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
|
||||||
|
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
|
||||||
|
ownerId: serializer.fromJson<String>(json['ownerId']),
|
||||||
|
primaryAssetId: serializer.fromJson<String>(json['primaryAssetId']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
||||||
|
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||||
|
return <String, dynamic>{
|
||||||
|
'id': serializer.toJson<String>(id),
|
||||||
|
'createdAt': serializer.toJson<DateTime>(createdAt),
|
||||||
|
'updatedAt': serializer.toJson<DateTime>(updatedAt),
|
||||||
|
'ownerId': serializer.toJson<String>(ownerId),
|
||||||
|
'primaryAssetId': serializer.toJson<String>(primaryAssetId),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.StackEntityData copyWith(
|
||||||
|
{String? id,
|
||||||
|
DateTime? createdAt,
|
||||||
|
DateTime? updatedAt,
|
||||||
|
String? ownerId,
|
||||||
|
String? primaryAssetId}) =>
|
||||||
|
i1.StackEntityData(
|
||||||
|
id: id ?? this.id,
|
||||||
|
createdAt: createdAt ?? this.createdAt,
|
||||||
|
updatedAt: updatedAt ?? this.updatedAt,
|
||||||
|
ownerId: ownerId ?? this.ownerId,
|
||||||
|
primaryAssetId: primaryAssetId ?? this.primaryAssetId,
|
||||||
|
);
|
||||||
|
StackEntityData copyWithCompanion(i1.StackEntityCompanion data) {
|
||||||
|
return StackEntityData(
|
||||||
|
id: data.id.present ? data.id.value : this.id,
|
||||||
|
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
|
||||||
|
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
|
||||||
|
ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId,
|
||||||
|
primaryAssetId: data.primaryAssetId.present
|
||||||
|
? data.primaryAssetId.value
|
||||||
|
: this.primaryAssetId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return (StringBuffer('StackEntityData(')
|
||||||
|
..write('id: $id, ')
|
||||||
|
..write('createdAt: $createdAt, ')
|
||||||
|
..write('updatedAt: $updatedAt, ')
|
||||||
|
..write('ownerId: $ownerId, ')
|
||||||
|
..write('primaryAssetId: $primaryAssetId')
|
||||||
|
..write(')'))
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId);
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
(other is i1.StackEntityData &&
|
||||||
|
other.id == this.id &&
|
||||||
|
other.createdAt == this.createdAt &&
|
||||||
|
other.updatedAt == this.updatedAt &&
|
||||||
|
other.ownerId == this.ownerId &&
|
||||||
|
other.primaryAssetId == this.primaryAssetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
class StackEntityCompanion extends i0.UpdateCompanion<i1.StackEntityData> {
|
||||||
|
final i0.Value<String> id;
|
||||||
|
final i0.Value<DateTime> createdAt;
|
||||||
|
final i0.Value<DateTime> updatedAt;
|
||||||
|
final i0.Value<String> ownerId;
|
||||||
|
final i0.Value<String> primaryAssetId;
|
||||||
|
const StackEntityCompanion({
|
||||||
|
this.id = const i0.Value.absent(),
|
||||||
|
this.createdAt = const i0.Value.absent(),
|
||||||
|
this.updatedAt = const i0.Value.absent(),
|
||||||
|
this.ownerId = const i0.Value.absent(),
|
||||||
|
this.primaryAssetId = const i0.Value.absent(),
|
||||||
|
});
|
||||||
|
StackEntityCompanion.insert({
|
||||||
|
required String id,
|
||||||
|
this.createdAt = const i0.Value.absent(),
|
||||||
|
this.updatedAt = const i0.Value.absent(),
|
||||||
|
required String ownerId,
|
||||||
|
required String primaryAssetId,
|
||||||
|
}) : id = i0.Value(id),
|
||||||
|
ownerId = i0.Value(ownerId),
|
||||||
|
primaryAssetId = i0.Value(primaryAssetId);
|
||||||
|
static i0.Insertable<i1.StackEntityData> custom({
|
||||||
|
i0.Expression<String>? id,
|
||||||
|
i0.Expression<DateTime>? createdAt,
|
||||||
|
i0.Expression<DateTime>? updatedAt,
|
||||||
|
i0.Expression<String>? ownerId,
|
||||||
|
i0.Expression<String>? primaryAssetId,
|
||||||
|
}) {
|
||||||
|
return i0.RawValuesInsertable({
|
||||||
|
if (id != null) 'id': id,
|
||||||
|
if (createdAt != null) 'created_at': createdAt,
|
||||||
|
if (updatedAt != null) 'updated_at': updatedAt,
|
||||||
|
if (ownerId != null) 'owner_id': ownerId,
|
||||||
|
if (primaryAssetId != null) 'primary_asset_id': primaryAssetId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.StackEntityCompanion copyWith(
|
||||||
|
{i0.Value<String>? id,
|
||||||
|
i0.Value<DateTime>? createdAt,
|
||||||
|
i0.Value<DateTime>? updatedAt,
|
||||||
|
i0.Value<String>? ownerId,
|
||||||
|
i0.Value<String>? primaryAssetId}) {
|
||||||
|
return i1.StackEntityCompanion(
|
||||||
|
id: id ?? this.id,
|
||||||
|
createdAt: createdAt ?? this.createdAt,
|
||||||
|
updatedAt: updatedAt ?? this.updatedAt,
|
||||||
|
ownerId: ownerId ?? this.ownerId,
|
||||||
|
primaryAssetId: primaryAssetId ?? this.primaryAssetId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||||
|
final map = <String, i0.Expression>{};
|
||||||
|
if (id.present) {
|
||||||
|
map['id'] = i0.Variable<String>(id.value);
|
||||||
|
}
|
||||||
|
if (createdAt.present) {
|
||||||
|
map['created_at'] = i0.Variable<DateTime>(createdAt.value);
|
||||||
|
}
|
||||||
|
if (updatedAt.present) {
|
||||||
|
map['updated_at'] = i0.Variable<DateTime>(updatedAt.value);
|
||||||
|
}
|
||||||
|
if (ownerId.present) {
|
||||||
|
map['owner_id'] = i0.Variable<String>(ownerId.value);
|
||||||
|
}
|
||||||
|
if (primaryAssetId.present) {
|
||||||
|
map['primary_asset_id'] = i0.Variable<String>(primaryAssetId.value);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return (StringBuffer('StackEntityCompanion(')
|
||||||
|
..write('id: $id, ')
|
||||||
|
..write('createdAt: $createdAt, ')
|
||||||
|
..write('updatedAt: $updatedAt, ')
|
||||||
|
..write('ownerId: $ownerId, ')
|
||||||
|
..write('primaryAssetId: $primaryAssetId')
|
||||||
|
..write(')'))
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart';
|
|||||||
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/stack.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';
|
||||||
@@ -50,9 +51,11 @@ class IsarDatabaseRepository implements IDatabaseRepository {
|
|||||||
RemoteAlbumUserEntity,
|
RemoteAlbumUserEntity,
|
||||||
MemoryEntity,
|
MemoryEntity,
|
||||||
MemoryAssetEntity,
|
MemoryAssetEntity,
|
||||||
|
StackEntity,
|
||||||
],
|
],
|
||||||
include: {
|
include: {
|
||||||
'package:immich_mobile/infrastructure/entities/merged_asset.drift',
|
'package:immich_mobile/infrastructure/entities/merged_asset.drift',
|
||||||
|
'package:immich_mobile/infrastructure/entities/asset_triggers.drift',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
class Drift extends $Drift implements IDatabaseRepository {
|
class Drift extends $Drift implements IDatabaseRepository {
|
||||||
|
|||||||
+108
-52
@@ -7,29 +7,33 @@ import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.
|
|||||||
as i2;
|
as i2;
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'
|
||||||
as i3;
|
as i3;
|
||||||
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/asset_triggers.drift.dart'
|
||||||
as i4;
|
as i4;
|
||||||
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart'
|
||||||
as i5;
|
as i5;
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart'
|
||||||
as i6;
|
as i6;
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'
|
||||||
as i7;
|
as i7;
|
||||||
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart'
|
||||||
as i8;
|
as i8;
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'
|
||||||
as i9;
|
as i9;
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
|
||||||
as i10;
|
as i10;
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart'
|
||||||
as i11;
|
as i11;
|
||||||
import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart'
|
||||||
as i12;
|
as i12;
|
||||||
import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart'
|
||||||
as i13;
|
as i13;
|
||||||
import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart'
|
||||||
as i14;
|
as i14;
|
||||||
import 'package:drift/internal/modular.dart' as i15;
|
import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart'
|
||||||
|
as i15;
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart'
|
||||||
|
as i16;
|
||||||
|
import 'package:drift/internal/modular.dart' as i17;
|
||||||
|
|
||||||
abstract class $Drift extends i0.GeneratedDatabase {
|
abstract class $Drift extends i0.GeneratedDatabase {
|
||||||
$Drift(i0.QueryExecutor e) : super(e);
|
$Drift(i0.QueryExecutor e) : super(e);
|
||||||
@@ -39,27 +43,28 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
|||||||
i2.$RemoteAssetEntityTable(this);
|
i2.$RemoteAssetEntityTable(this);
|
||||||
late final i3.$LocalAssetEntityTable localAssetEntity =
|
late final i3.$LocalAssetEntityTable localAssetEntity =
|
||||||
i3.$LocalAssetEntityTable(this);
|
i3.$LocalAssetEntityTable(this);
|
||||||
late final i4.$UserMetadataEntityTable userMetadataEntity =
|
late final i5.$UserMetadataEntityTable userMetadataEntity =
|
||||||
i4.$UserMetadataEntityTable(this);
|
i5.$UserMetadataEntityTable(this);
|
||||||
late final i5.$PartnerEntityTable partnerEntity =
|
late final i6.$PartnerEntityTable partnerEntity =
|
||||||
i5.$PartnerEntityTable(this);
|
i6.$PartnerEntityTable(this);
|
||||||
late final i6.$LocalAlbumEntityTable localAlbumEntity =
|
late final i7.$LocalAlbumEntityTable localAlbumEntity =
|
||||||
i6.$LocalAlbumEntityTable(this);
|
i7.$LocalAlbumEntityTable(this);
|
||||||
late final i7.$LocalAlbumAssetEntityTable localAlbumAssetEntity =
|
late final i8.$LocalAlbumAssetEntityTable localAlbumAssetEntity =
|
||||||
i7.$LocalAlbumAssetEntityTable(this);
|
i8.$LocalAlbumAssetEntityTable(this);
|
||||||
late final i8.$RemoteExifEntityTable remoteExifEntity =
|
late final i9.$RemoteExifEntityTable remoteExifEntity =
|
||||||
i8.$RemoteExifEntityTable(this);
|
i9.$RemoteExifEntityTable(this);
|
||||||
late final i9.$RemoteAlbumEntityTable remoteAlbumEntity =
|
late final i10.$RemoteAlbumEntityTable remoteAlbumEntity =
|
||||||
i9.$RemoteAlbumEntityTable(this);
|
i10.$RemoteAlbumEntityTable(this);
|
||||||
late final i10.$RemoteAlbumAssetEntityTable remoteAlbumAssetEntity =
|
late final i11.$RemoteAlbumAssetEntityTable remoteAlbumAssetEntity =
|
||||||
i10.$RemoteAlbumAssetEntityTable(this);
|
i11.$RemoteAlbumAssetEntityTable(this);
|
||||||
late final i11.$RemoteAlbumUserEntityTable remoteAlbumUserEntity =
|
late final i12.$RemoteAlbumUserEntityTable remoteAlbumUserEntity =
|
||||||
i11.$RemoteAlbumUserEntityTable(this);
|
i12.$RemoteAlbumUserEntityTable(this);
|
||||||
late final i12.$MemoryEntityTable memoryEntity = i12.$MemoryEntityTable(this);
|
late final i13.$MemoryEntityTable memoryEntity = i13.$MemoryEntityTable(this);
|
||||||
late final i13.$MemoryAssetEntityTable memoryAssetEntity =
|
late final i14.$MemoryAssetEntityTable memoryAssetEntity =
|
||||||
i13.$MemoryAssetEntityTable(this);
|
i14.$MemoryAssetEntityTable(this);
|
||||||
i14.MergedAssetDrift get mergedAssetDrift => i15.ReadDatabaseContainer(this)
|
late final i15.$StackEntityTable stackEntity = i15.$StackEntityTable(this);
|
||||||
.accessor<i14.MergedAssetDrift>(i14.MergedAssetDrift.new);
|
i16.MergedAssetDrift get mergedAssetDrift => i17.ReadDatabaseContainer(this)
|
||||||
|
.accessor<i16.MergedAssetDrift>(i16.MergedAssetDrift.new);
|
||||||
@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?>>();
|
||||||
@@ -68,6 +73,10 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
|||||||
userEntity,
|
userEntity,
|
||||||
remoteAssetEntity,
|
remoteAssetEntity,
|
||||||
localAssetEntity,
|
localAssetEntity,
|
||||||
|
i4.trLocalAssetUpdateChecksumSetIds,
|
||||||
|
i4.trLocalAssetUpdateOldChecksumSetRemoteAssetLocalId,
|
||||||
|
i4.trLocalAssetDeleteUpdateRemoteAssetLocalId,
|
||||||
|
i4.trRemoteAssetInsertSetLocalId,
|
||||||
i3.idxLocalAssetChecksum,
|
i3.idxLocalAssetChecksum,
|
||||||
i2.uQRemoteAssetOwnerChecksum,
|
i2.uQRemoteAssetOwnerChecksum,
|
||||||
i2.idxRemoteAssetChecksum,
|
i2.idxRemoteAssetChecksum,
|
||||||
@@ -80,7 +89,8 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
|||||||
remoteAlbumAssetEntity,
|
remoteAlbumAssetEntity,
|
||||||
remoteAlbumUserEntity,
|
remoteAlbumUserEntity,
|
||||||
memoryEntity,
|
memoryEntity,
|
||||||
memoryAssetEntity
|
memoryAssetEntity,
|
||||||
|
stackEntity
|
||||||
];
|
];
|
||||||
@override
|
@override
|
||||||
i0.StreamQueryUpdateRules get streamUpdateRules =>
|
i0.StreamQueryUpdateRules get streamUpdateRules =>
|
||||||
@@ -93,6 +103,43 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
|||||||
i0.TableUpdate('remote_asset_entity', kind: i0.UpdateKind.delete),
|
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('local_asset_entity', kind: i0.UpdateKind.update),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
i0.WritePropagation(
|
||||||
|
on: i0.TableUpdateQuery.onTableName('local_asset_entity',
|
||||||
|
limitUpdateKind: i0.UpdateKind.update),
|
||||||
|
result: [
|
||||||
|
i0.TableUpdate('local_asset_entity', kind: i0.UpdateKind.update),
|
||||||
|
i0.TableUpdate('remote_asset_entity', kind: i0.UpdateKind.update),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
i0.WritePropagation(
|
||||||
|
on: i0.TableUpdateQuery.onTableName('local_asset_entity',
|
||||||
|
limitUpdateKind: i0.UpdateKind.update),
|
||||||
|
result: [
|
||||||
|
i0.TableUpdate('remote_asset_entity', kind: i0.UpdateKind.update),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
i0.WritePropagation(
|
||||||
|
on: i0.TableUpdateQuery.onTableName('local_asset_entity',
|
||||||
|
limitUpdateKind: i0.UpdateKind.delete),
|
||||||
|
result: [
|
||||||
|
i0.TableUpdate('remote_asset_entity', kind: i0.UpdateKind.update),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
i0.WritePropagation(
|
||||||
|
on: i0.TableUpdateQuery.onTableName('remote_asset_entity',
|
||||||
|
limitUpdateKind: i0.UpdateKind.insert),
|
||||||
|
result: [
|
||||||
|
i0.TableUpdate('remote_asset_entity', kind: i0.UpdateKind.update),
|
||||||
|
i0.TableUpdate('local_asset_entity', kind: i0.UpdateKind.update),
|
||||||
|
],
|
||||||
|
),
|
||||||
i0.WritePropagation(
|
i0.WritePropagation(
|
||||||
on: i0.TableUpdateQuery.onTableName('user_entity',
|
on: i0.TableUpdateQuery.onTableName('user_entity',
|
||||||
limitUpdateKind: i0.UpdateKind.delete),
|
limitUpdateKind: i0.UpdateKind.delete),
|
||||||
@@ -205,6 +252,13 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
|||||||
i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete),
|
i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
i0.WritePropagation(
|
||||||
|
on: i0.TableUpdateQuery.onTableName('user_entity',
|
||||||
|
limitUpdateKind: i0.UpdateKind.delete),
|
||||||
|
result: [
|
||||||
|
i0.TableUpdate('stack_entity', kind: i0.UpdateKind.delete),
|
||||||
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@override
|
@override
|
||||||
@@ -221,25 +275,27 @@ class $DriftManager {
|
|||||||
i2.$$RemoteAssetEntityTableTableManager(_db, _db.remoteAssetEntity);
|
i2.$$RemoteAssetEntityTableTableManager(_db, _db.remoteAssetEntity);
|
||||||
i3.$$LocalAssetEntityTableTableManager get localAssetEntity =>
|
i3.$$LocalAssetEntityTableTableManager get localAssetEntity =>
|
||||||
i3.$$LocalAssetEntityTableTableManager(_db, _db.localAssetEntity);
|
i3.$$LocalAssetEntityTableTableManager(_db, _db.localAssetEntity);
|
||||||
i4.$$UserMetadataEntityTableTableManager get userMetadataEntity =>
|
i5.$$UserMetadataEntityTableTableManager get userMetadataEntity =>
|
||||||
i4.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity);
|
i5.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity);
|
||||||
i5.$$PartnerEntityTableTableManager get partnerEntity =>
|
i6.$$PartnerEntityTableTableManager get partnerEntity =>
|
||||||
i5.$$PartnerEntityTableTableManager(_db, _db.partnerEntity);
|
i6.$$PartnerEntityTableTableManager(_db, _db.partnerEntity);
|
||||||
i6.$$LocalAlbumEntityTableTableManager get localAlbumEntity =>
|
i7.$$LocalAlbumEntityTableTableManager get localAlbumEntity =>
|
||||||
i6.$$LocalAlbumEntityTableTableManager(_db, _db.localAlbumEntity);
|
i7.$$LocalAlbumEntityTableTableManager(_db, _db.localAlbumEntity);
|
||||||
i7.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i7
|
i8.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i8
|
||||||
.$$LocalAlbumAssetEntityTableTableManager(_db, _db.localAlbumAssetEntity);
|
.$$LocalAlbumAssetEntityTableTableManager(_db, _db.localAlbumAssetEntity);
|
||||||
i8.$$RemoteExifEntityTableTableManager get remoteExifEntity =>
|
i9.$$RemoteExifEntityTableTableManager get remoteExifEntity =>
|
||||||
i8.$$RemoteExifEntityTableTableManager(_db, _db.remoteExifEntity);
|
i9.$$RemoteExifEntityTableTableManager(_db, _db.remoteExifEntity);
|
||||||
i9.$$RemoteAlbumEntityTableTableManager get remoteAlbumEntity =>
|
i10.$$RemoteAlbumEntityTableTableManager get remoteAlbumEntity =>
|
||||||
i9.$$RemoteAlbumEntityTableTableManager(_db, _db.remoteAlbumEntity);
|
i10.$$RemoteAlbumEntityTableTableManager(_db, _db.remoteAlbumEntity);
|
||||||
i10.$$RemoteAlbumAssetEntityTableTableManager get remoteAlbumAssetEntity =>
|
i11.$$RemoteAlbumAssetEntityTableTableManager get remoteAlbumAssetEntity =>
|
||||||
i10.$$RemoteAlbumAssetEntityTableTableManager(
|
i11.$$RemoteAlbumAssetEntityTableTableManager(
|
||||||
_db, _db.remoteAlbumAssetEntity);
|
_db, _db.remoteAlbumAssetEntity);
|
||||||
i11.$$RemoteAlbumUserEntityTableTableManager get remoteAlbumUserEntity => i11
|
i12.$$RemoteAlbumUserEntityTableTableManager get remoteAlbumUserEntity => i12
|
||||||
.$$RemoteAlbumUserEntityTableTableManager(_db, _db.remoteAlbumUserEntity);
|
.$$RemoteAlbumUserEntityTableTableManager(_db, _db.remoteAlbumUserEntity);
|
||||||
i12.$$MemoryEntityTableTableManager get memoryEntity =>
|
i13.$$MemoryEntityTableTableManager get memoryEntity =>
|
||||||
i12.$$MemoryEntityTableTableManager(_db, _db.memoryEntity);
|
i13.$$MemoryEntityTableTableManager(_db, _db.memoryEntity);
|
||||||
i13.$$MemoryAssetEntityTableTableManager get memoryAssetEntity =>
|
i14.$$MemoryAssetEntityTableTableManager get memoryAssetEntity =>
|
||||||
i13.$$MemoryAssetEntityTableTableManager(_db, _db.memoryAssetEntity);
|
i14.$$MemoryAssetEntityTableTableManager(_db, _db.memoryAssetEntity);
|
||||||
|
i15.$$StackEntityTableTableManager get stackEntity =>
|
||||||
|
i15.$$StackEntityTableTableManager(_db, _db.stackEntity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -282,6 +282,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
|
|||||||
durationInSeconds: Value(asset.durationInSeconds),
|
durationInSeconds: Value(asset.durationInSeconds),
|
||||||
id: asset.id,
|
id: asset.id,
|
||||||
checksum: const Value(null),
|
checksum: const Value(null),
|
||||||
|
remoteId: const Value(null),
|
||||||
);
|
);
|
||||||
batch.insert<$LocalAssetEntityTable, LocalAssetEntityData>(
|
batch.insert<$LocalAssetEntityTable, LocalAssetEntityData>(
|
||||||
_db.localAssetEntity,
|
_db.localAssetEntity,
|
||||||
|
|||||||
@@ -9,23 +9,10 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository {
|
|||||||
const DriftLocalAssetRepository(this._db) : super(_db);
|
const DriftLocalAssetRepository(this._db) : super(_db);
|
||||||
|
|
||||||
Stream<LocalAsset?> watchAsset(String id) {
|
Stream<LocalAsset?> watchAsset(String id) {
|
||||||
final query = _db.localAssetEntity
|
final query = _db.localAssetEntity.select()
|
||||||
.select()
|
..where((row) => row.id.equals(id));
|
||||||
.addColumns([_db.localAssetEntity.id]).join([
|
|
||||||
leftOuterJoin(
|
|
||||||
_db.remoteAssetEntity,
|
|
||||||
_db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum),
|
|
||||||
useColumns: false,
|
|
||||||
),
|
|
||||||
])
|
|
||||||
..where(_db.localAssetEntity.id.equals(id));
|
|
||||||
|
|
||||||
return query.map((row) {
|
return query.map((row) => row.toDto()).watchSingleOrNull();
|
||||||
final asset = row.readTable(_db.localAssetEntity).toDto();
|
|
||||||
return asset.copyWith(
|
|
||||||
remoteId: row.read(_db.remoteAssetEntity.id),
|
|
||||||
);
|
|
||||||
}).watchSingleOrNull();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateHashes(Iterable<LocalAsset> hashes) {
|
Future<void> updateHashes(Iterable<LocalAsset> hashes) {
|
||||||
|
|||||||
@@ -13,24 +13,27 @@ class RemoteAssetRepository extends DriftDatabaseRepository {
|
|||||||
final Drift _db;
|
final Drift _db;
|
||||||
const RemoteAssetRepository(this._db) : super(_db);
|
const RemoteAssetRepository(this._db) : super(_db);
|
||||||
|
|
||||||
Stream<RemoteAsset?> watchAsset(String id) {
|
/// For testing purposes
|
||||||
final query = _db.remoteAssetEntity
|
Future<List<RemoteAsset>> getSome(String userId) {
|
||||||
.select()
|
final query = _db.remoteAssetEntity.select()
|
||||||
.addColumns([_db.localAssetEntity.id]).join([
|
..where(
|
||||||
leftOuterJoin(
|
(row) =>
|
||||||
_db.localAssetEntity,
|
_db.remoteAssetEntity.ownerId.equals(userId) &
|
||||||
_db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum),
|
_db.remoteAssetEntity.deletedAt.isNull() &
|
||||||
useColumns: false,
|
_db.remoteAssetEntity.visibility
|
||||||
),
|
.equalsValue(AssetVisibility.timeline),
|
||||||
])
|
)
|
||||||
..where(_db.remoteAssetEntity.id.equals(id));
|
..orderBy([(row) => OrderingTerm.desc(row.createdAt)])
|
||||||
|
..limit(10);
|
||||||
|
|
||||||
return query.map((row) {
|
return query.map((row) => row.toDto()).get();
|
||||||
final asset = row.readTable(_db.remoteAssetEntity).toDto();
|
}
|
||||||
return asset.copyWith(
|
|
||||||
localId: row.read(_db.localAssetEntity.id),
|
Stream<RemoteAsset?> watchAsset(String id) {
|
||||||
);
|
final query = _db.remoteAssetEntity.select()
|
||||||
}).watchSingleOrNull();
|
..where((row) => row.id.equals(id));
|
||||||
|
|
||||||
|
return query.map((row) => row.toDto()).watchSingleOrNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ExifInfo?> getExif(String id) {
|
Future<ExifInfo?> getExif(String id) {
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/stack.model.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
|
|
||||||
|
class DriftStackRepository extends DriftDatabaseRepository {
|
||||||
|
final Drift _db;
|
||||||
|
const DriftStackRepository(this._db) : super(_db);
|
||||||
|
|
||||||
|
Future<List<Stack>> getAll(String userId) {
|
||||||
|
final query = _db.stackEntity.select()
|
||||||
|
..where((e) => e.ownerId.equals(userId));
|
||||||
|
|
||||||
|
return query.map((stack) {
|
||||||
|
return stack.toDto();
|
||||||
|
}).get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension on StackEntityData {
|
||||||
|
Stack toDto() {
|
||||||
|
return Stack(
|
||||||
|
id: id,
|
||||||
|
createdAt: createdAt,
|
||||||
|
updatedAt: updatedAt,
|
||||||
|
ownerId: ownerId,
|
||||||
|
primaryAssetId: primaryAssetId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -54,6 +54,8 @@ class SyncApiRepository {
|
|||||||
SyncRequestType.albumToAssetsV1,
|
SyncRequestType.albumToAssetsV1,
|
||||||
SyncRequestType.memoriesV1,
|
SyncRequestType.memoriesV1,
|
||||||
SyncRequestType.memoryToAssetsV1,
|
SyncRequestType.memoryToAssetsV1,
|
||||||
|
SyncRequestType.stacksV1,
|
||||||
|
SyncRequestType.partnerStacksV1,
|
||||||
],
|
],
|
||||||
).toJson(),
|
).toJson(),
|
||||||
);
|
);
|
||||||
@@ -163,6 +165,11 @@ const _kResponseMap = <SyncEntityType, Function(Object)>{
|
|||||||
SyncEntityType.memoryDeleteV1: SyncMemoryDeleteV1.fromJson,
|
SyncEntityType.memoryDeleteV1: SyncMemoryDeleteV1.fromJson,
|
||||||
SyncEntityType.memoryToAssetV1: SyncMemoryAssetV1.fromJson,
|
SyncEntityType.memoryToAssetV1: SyncMemoryAssetV1.fromJson,
|
||||||
SyncEntityType.memoryToAssetDeleteV1: SyncMemoryAssetDeleteV1.fromJson,
|
SyncEntityType.memoryToAssetDeleteV1: SyncMemoryAssetDeleteV1.fromJson,
|
||||||
|
SyncEntityType.stackV1: SyncStackV1.fromJson,
|
||||||
|
SyncEntityType.stackDeleteV1: SyncStackDeleteV1.fromJson,
|
||||||
|
SyncEntityType.partnerStackV1: SyncStackV1.fromJson,
|
||||||
|
SyncEntityType.partnerStackBackfillV1: SyncStackV1.fromJson,
|
||||||
|
SyncEntityType.partnerStackDeleteV1: SyncStackDeleteV1.fromJson,
|
||||||
};
|
};
|
||||||
|
|
||||||
class _SyncAckV1 {
|
class _SyncAckV1 {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.
|
|||||||
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/stack.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';
|
||||||
@@ -69,8 +70,8 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stack) {
|
||||||
_logger.severe('Error: SyncPartnerDeleteV1', error, stackTrace);
|
_logger.severe('Error: SyncPartnerDeleteV1', error, stack);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,8 +93,8 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stack) {
|
||||||
_logger.severe('Error: SyncPartnerV1', error, stackTrace);
|
_logger.severe('Error: SyncPartnerV1', error, stack);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,10 +105,10 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
await _db.remoteAssetEntity.deleteWhere(
|
await _db.remoteAssetEntity.deleteWhere(
|
||||||
(row) => row.id.isIn(data.map((error) => error.assetId)),
|
(row) => row.id.isIn(data.map((e) => e.assetId)),
|
||||||
);
|
);
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stack) {
|
||||||
_logger.severe('Error: deleteAssetsV1 - $debugLabel', error, stackTrace);
|
_logger.severe('Error: deleteAssetsV1 - $debugLabel', error, stack);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,8 +143,8 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stack) {
|
||||||
_logger.severe('Error: updateAssetsV1 - $debugLabel', error, stackTrace);
|
_logger.severe('Error: updateAssetsV1 - $debugLabel', error, stack);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,11 +187,11 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stack) {
|
||||||
_logger.severe(
|
_logger.severe(
|
||||||
'Error: updateAssetsExifV1 - $debugLabel',
|
'Error: updateAssetsExifV1 - $debugLabel',
|
||||||
error,
|
error,
|
||||||
stackTrace,
|
stack,
|
||||||
);
|
);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
@@ -201,8 +202,8 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
await _db.remoteAlbumEntity.deleteWhere(
|
await _db.remoteAlbumEntity.deleteWhere(
|
||||||
(row) => row.id.isIn(data.map((e) => e.albumId)),
|
(row) => row.id.isIn(data.map((e) => e.albumId)),
|
||||||
);
|
);
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stack) {
|
||||||
_logger.severe('Error: deleteAlbumsV1', error, stackTrace);
|
_logger.severe('Error: deleteAlbumsV1', error, stack);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -229,8 +230,8 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stack) {
|
||||||
_logger.severe('Error: updateAlbumsV1', error, stackTrace);
|
_logger.severe('Error: updateAlbumsV1', error, stack);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -248,8 +249,8 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stack) {
|
||||||
_logger.severe('Error: deleteAlbumUsersV1', error, stackTrace);
|
_logger.severe('Error: deleteAlbumUsersV1', error, stack);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -275,11 +276,11 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stack) {
|
||||||
_logger.severe(
|
_logger.severe(
|
||||||
'Error: updateAlbumUsersV1 - $debugLabel',
|
'Error: updateAlbumUsersV1 - $debugLabel',
|
||||||
error,
|
error,
|
||||||
stackTrace,
|
stack,
|
||||||
);
|
);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
@@ -300,8 +301,8 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stack) {
|
||||||
_logger.severe('Error: deleteAlbumToAssetsV1', error, stackTrace);
|
_logger.severe('Error: deleteAlbumToAssetsV1', error, stack);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -325,11 +326,11 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stack) {
|
||||||
_logger.severe(
|
_logger.severe(
|
||||||
'Error: updateAlbumToAssetsV1 - $debugLabel',
|
'Error: updateAlbumToAssetsV1 - $debugLabel',
|
||||||
error,
|
error,
|
||||||
stackTrace,
|
stack,
|
||||||
);
|
);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
@@ -359,8 +360,8 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stack) {
|
||||||
_logger.severe('Error: updateMemoriesV1', error, stackTrace);
|
_logger.severe('Error: updateMemoriesV1', error, stack);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -370,8 +371,8 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
await _db.memoryEntity.deleteWhere(
|
await _db.memoryEntity.deleteWhere(
|
||||||
(row) => row.id.isIn(data.map((e) => e.memoryId)),
|
(row) => row.id.isIn(data.map((e) => e.memoryId)),
|
||||||
);
|
);
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stack) {
|
||||||
_logger.severe('Error: deleteMemoriesV1', error, stackTrace);
|
_logger.severe('Error: deleteMemoriesV1', error, stack);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -392,8 +393,8 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stack) {
|
||||||
_logger.severe('Error: updateMemoryAssetsV1', error, stackTrace);
|
_logger.severe('Error: updateMemoryAssetsV1', error, stack);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -413,8 +414,49 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stack) {
|
||||||
_logger.severe('Error: deleteMemoryAssetsV1', error, stackTrace);
|
_logger.severe('Error: deleteMemoryAssetsV1', error, stack);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateStacksV1(
|
||||||
|
Iterable<SyncStackV1> data, {
|
||||||
|
String debugLabel = 'user',
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
await _db.batch((batch) {
|
||||||
|
for (final stack in data) {
|
||||||
|
final companion = StackEntityCompanion(
|
||||||
|
createdAt: Value(stack.createdAt),
|
||||||
|
updatedAt: Value(stack.updatedAt),
|
||||||
|
ownerId: Value(stack.ownerId),
|
||||||
|
primaryAssetId: Value(stack.primaryAssetId),
|
||||||
|
);
|
||||||
|
|
||||||
|
batch.insert(
|
||||||
|
_db.stackEntity,
|
||||||
|
companion.copyWith(id: Value(stack.id)),
|
||||||
|
onConflict: DoUpdate((_) => companion),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error, stack) {
|
||||||
|
_logger.severe('Error: updateStacksV1 - $debugLabel', error, stack);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> deleteStacksV1(
|
||||||
|
Iterable<SyncStackDeleteV1> data, {
|
||||||
|
String debugLabel = 'user',
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
await _db.stackEntity.deleteWhere(
|
||||||
|
(row) => row.id.isIn(data.map((e) => e.stackId)),
|
||||||
|
);
|
||||||
|
} catch (error, stack) {
|
||||||
|
_logger.severe('Error: deleteStacksV1 - $debugLabel', error, stack);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -467,7 +509,7 @@ extension on String {
|
|||||||
Duration? toDuration() {
|
Duration? toDuration() {
|
||||||
try {
|
try {
|
||||||
final parts = split(':')
|
final parts = split(':')
|
||||||
.map((error) => double.parse(error).toInt())
|
.map((e) => double.parse(e).toInt())
|
||||||
.toList(growable: false);
|
.toList(growable: false);
|
||||||
|
|
||||||
return Duration(hours: parts[0], minutes: parts[1], seconds: parts[2]);
|
return Duration(hours: parts[0], minutes: parts[1], seconds: parts[2]);
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||||||
).get();
|
).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<List<Bucket>> watchLocalBucket(
|
Stream<List<Bucket>> watchLocalAlbumBucket(
|
||||||
String albumId, {
|
String albumId, {
|
||||||
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||||
}) {
|
}) {
|
||||||
@@ -137,7 +137,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||||||
}).watch();
|
}).watch();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<BaseAsset>> getLocalBucketAssets(
|
Future<List<BaseAsset>> getLocalAlbumBucketAssets(
|
||||||
String albumId, {
|
String albumId, {
|
||||||
required int offset,
|
required int offset,
|
||||||
required int count,
|
required int count,
|
||||||
@@ -158,7 +158,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||||||
.get();
|
.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<List<Bucket>> watchRemoteBucket(
|
Stream<List<Bucket>> watchRemoteAlbumBucket(
|
||||||
String albumId, {
|
String albumId, {
|
||||||
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||||
}) {
|
}) {
|
||||||
@@ -192,7 +192,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||||||
}).watch();
|
}).watch();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<BaseAsset>> getRemoteBucketAssets(
|
Future<List<BaseAsset>> getRemoteAlbumBucketAssets(
|
||||||
String albumId, {
|
String albumId, {
|
||||||
required int offset,
|
required int offset,
|
||||||
required int count,
|
required int count,
|
||||||
@@ -213,6 +213,317 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||||||
.map((row) => row.readTable(_db.remoteAssetEntity).toDto())
|
.map((row) => row.readTable(_db.remoteAssetEntity).toDto())
|
||||||
.get();
|
.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stream<List<Bucket>> watchFavoriteBucket(
|
||||||
|
String userId, {
|
||||||
|
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||||
|
}) {
|
||||||
|
if (groupBy == GroupAssetsBy.none) {
|
||||||
|
return _db.remoteAssetEntity
|
||||||
|
.count(
|
||||||
|
where: (row) =>
|
||||||
|
row.isFavorite.equals(true) & row.ownerId.equals(userId),
|
||||||
|
)
|
||||||
|
.map(_generateBuckets)
|
||||||
|
.watchSingle();
|
||||||
|
}
|
||||||
|
|
||||||
|
final assetCountExp = _db.remoteAssetEntity.id.count();
|
||||||
|
final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy);
|
||||||
|
|
||||||
|
final query = _db.remoteAssetEntity.selectOnly()
|
||||||
|
..addColumns([assetCountExp, dateExp])
|
||||||
|
..where(
|
||||||
|
_db.remoteAssetEntity.ownerId.equals(userId) &
|
||||||
|
_db.remoteAssetEntity.isFavorite.equals(true),
|
||||||
|
)
|
||||||
|
..groupBy([dateExp])
|
||||||
|
..orderBy([OrderingTerm.desc(dateExp)]);
|
||||||
|
|
||||||
|
return query.map((row) {
|
||||||
|
final timeline = row.read(dateExp)!.dateFmt(groupBy);
|
||||||
|
final assetCount = row.read(assetCountExp)!;
|
||||||
|
return TimeBucket(date: timeline, assetCount: assetCount);
|
||||||
|
}).watch();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<BaseAsset>> getFavoriteBucketAssets(
|
||||||
|
String userId, {
|
||||||
|
required int offset,
|
||||||
|
required int count,
|
||||||
|
}) {
|
||||||
|
final query = _db.remoteAssetEntity.select()
|
||||||
|
..where(
|
||||||
|
(row) => row.isFavorite.equals(true) & row.ownerId.equals(userId),
|
||||||
|
)
|
||||||
|
..orderBy([(row) => OrderingTerm.desc(row.createdAt)])
|
||||||
|
..limit(count, offset: offset);
|
||||||
|
|
||||||
|
return query.map((row) => row.toDto()).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<List<Bucket>> watchTrashBucket(
|
||||||
|
String userId, {
|
||||||
|
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||||
|
}) {
|
||||||
|
if (groupBy == GroupAssetsBy.none) {
|
||||||
|
return _db.remoteAssetEntity
|
||||||
|
.count(
|
||||||
|
where: (row) =>
|
||||||
|
row.deletedAt.isNotNull() & row.ownerId.equals(userId),
|
||||||
|
)
|
||||||
|
.map(_generateBuckets)
|
||||||
|
.watchSingle();
|
||||||
|
}
|
||||||
|
|
||||||
|
final assetCountExp = _db.remoteAssetEntity.id.count();
|
||||||
|
final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy);
|
||||||
|
|
||||||
|
final query = _db.remoteAssetEntity.selectOnly()
|
||||||
|
..addColumns([assetCountExp, dateExp])
|
||||||
|
..where(
|
||||||
|
_db.remoteAssetEntity.ownerId.equals(userId) &
|
||||||
|
_db.remoteAssetEntity.deletedAt.isNotNull(),
|
||||||
|
)
|
||||||
|
..groupBy([dateExp])
|
||||||
|
..orderBy([OrderingTerm.desc(dateExp)]);
|
||||||
|
|
||||||
|
return query.map((row) {
|
||||||
|
final timeline = row.read(dateExp)!.dateFmt(groupBy);
|
||||||
|
final assetCount = row.read(assetCountExp)!;
|
||||||
|
return TimeBucket(date: timeline, assetCount: assetCount);
|
||||||
|
}).watch();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<BaseAsset>> getTrashBucketAssets(
|
||||||
|
String userId, {
|
||||||
|
required int offset,
|
||||||
|
required int count,
|
||||||
|
}) {
|
||||||
|
final query = _db.remoteAssetEntity.select()
|
||||||
|
..where(
|
||||||
|
(row) => row.deletedAt.isNotNull() & row.ownerId.equals(userId),
|
||||||
|
)
|
||||||
|
..orderBy([(row) => OrderingTerm.desc(row.createdAt)])
|
||||||
|
..limit(count, offset: offset);
|
||||||
|
|
||||||
|
return query.map((row) => row.toDto()).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<List<Bucket>> watchArchiveBucket(
|
||||||
|
String userId, {
|
||||||
|
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||||
|
}) {
|
||||||
|
if (groupBy == GroupAssetsBy.none) {
|
||||||
|
return _db.remoteAssetEntity
|
||||||
|
.count(
|
||||||
|
where: (row) =>
|
||||||
|
row.visibility.equalsValue(AssetVisibility.archive) &
|
||||||
|
row.ownerId.equals(userId),
|
||||||
|
)
|
||||||
|
.map(_generateBuckets)
|
||||||
|
.watchSingle();
|
||||||
|
}
|
||||||
|
|
||||||
|
final assetCountExp = _db.remoteAssetEntity.id.count();
|
||||||
|
final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy);
|
||||||
|
|
||||||
|
final query = _db.remoteAssetEntity.selectOnly()
|
||||||
|
..addColumns([assetCountExp, dateExp])
|
||||||
|
..where(
|
||||||
|
_db.remoteAssetEntity.ownerId.equals(userId) &
|
||||||
|
_db.remoteAssetEntity.visibility
|
||||||
|
.equalsValue(AssetVisibility.archive),
|
||||||
|
)
|
||||||
|
..groupBy([dateExp])
|
||||||
|
..orderBy([OrderingTerm.desc(dateExp)]);
|
||||||
|
|
||||||
|
return query.map((row) {
|
||||||
|
final timeline = row.read(dateExp)!.dateFmt(groupBy);
|
||||||
|
final assetCount = row.read(assetCountExp)!;
|
||||||
|
return TimeBucket(date: timeline, assetCount: assetCount);
|
||||||
|
}).watch();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<BaseAsset>> getArchiveBucketAssets(
|
||||||
|
String userId, {
|
||||||
|
required int offset,
|
||||||
|
required int count,
|
||||||
|
}) {
|
||||||
|
final query = _db.remoteAssetEntity.select()
|
||||||
|
..where(
|
||||||
|
(row) =>
|
||||||
|
row.ownerId.equals(userId) &
|
||||||
|
row.visibility.equalsValue(AssetVisibility.archive),
|
||||||
|
)
|
||||||
|
..orderBy([(row) => OrderingTerm.desc(row.createdAt)])
|
||||||
|
..limit(count, offset: offset);
|
||||||
|
|
||||||
|
return query.map((row) => row.toDto()).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<List<Bucket>> watchLockedFolderBucket(
|
||||||
|
String userId, {
|
||||||
|
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||||
|
}) {
|
||||||
|
if (groupBy == GroupAssetsBy.none) {
|
||||||
|
return _db.remoteAssetEntity
|
||||||
|
.count(
|
||||||
|
where: (row) =>
|
||||||
|
row.visibility.equalsValue(AssetVisibility.locked) &
|
||||||
|
row.ownerId.equals(userId),
|
||||||
|
)
|
||||||
|
.map(_generateBuckets)
|
||||||
|
.watchSingle();
|
||||||
|
}
|
||||||
|
|
||||||
|
final assetCountExp = _db.remoteAssetEntity.id.count();
|
||||||
|
final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy);
|
||||||
|
|
||||||
|
final query = _db.remoteAssetEntity.selectOnly()
|
||||||
|
..addColumns([assetCountExp, dateExp])
|
||||||
|
..where(
|
||||||
|
_db.remoteAssetEntity.ownerId.equals(userId) &
|
||||||
|
_db.remoteAssetEntity.visibility
|
||||||
|
.equalsValue(AssetVisibility.locked),
|
||||||
|
)
|
||||||
|
..groupBy([dateExp])
|
||||||
|
..orderBy([OrderingTerm.desc(dateExp)]);
|
||||||
|
|
||||||
|
return query.map((row) {
|
||||||
|
final timeline = row.read(dateExp)!.dateFmt(groupBy);
|
||||||
|
final assetCount = row.read(assetCountExp)!;
|
||||||
|
return TimeBucket(date: timeline, assetCount: assetCount);
|
||||||
|
}).watch();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<BaseAsset>> getLockedFolderBucketAssets(
|
||||||
|
String userId, {
|
||||||
|
required int offset,
|
||||||
|
required int count,
|
||||||
|
}) {
|
||||||
|
final query = _db.remoteAssetEntity.select()
|
||||||
|
..where(
|
||||||
|
(row) =>
|
||||||
|
row.visibility.equalsValue(AssetVisibility.locked) &
|
||||||
|
row.ownerId.equals(userId),
|
||||||
|
)
|
||||||
|
..orderBy([(row) => OrderingTerm.desc(row.createdAt)])
|
||||||
|
..limit(count, offset: offset);
|
||||||
|
|
||||||
|
return query.map((row) => row.toDto()).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<List<Bucket>> watchVideoBucket(
|
||||||
|
String userId, {
|
||||||
|
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||||
|
}) {
|
||||||
|
if (groupBy == GroupAssetsBy.none) {
|
||||||
|
return _db.remoteAssetEntity
|
||||||
|
.count(
|
||||||
|
where: (row) =>
|
||||||
|
row.type.equalsValue(AssetType.video) &
|
||||||
|
row.visibility.equalsValue(AssetVisibility.timeline) &
|
||||||
|
row.ownerId.equals(userId),
|
||||||
|
)
|
||||||
|
.map(_generateBuckets)
|
||||||
|
.watchSingle();
|
||||||
|
}
|
||||||
|
|
||||||
|
final assetCountExp = _db.remoteAssetEntity.id.count();
|
||||||
|
final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy);
|
||||||
|
|
||||||
|
final query = _db.remoteAssetEntity.selectOnly()
|
||||||
|
..addColumns([assetCountExp, dateExp])
|
||||||
|
..where(
|
||||||
|
_db.remoteAssetEntity.ownerId.equals(userId) &
|
||||||
|
_db.remoteAssetEntity.type.equalsValue(AssetType.video) &
|
||||||
|
_db.remoteAssetEntity.visibility
|
||||||
|
.equalsValue(AssetVisibility.timeline),
|
||||||
|
)
|
||||||
|
..groupBy([dateExp])
|
||||||
|
..orderBy([OrderingTerm.desc(dateExp)]);
|
||||||
|
|
||||||
|
return query.map((row) {
|
||||||
|
final timeline = row.read(dateExp)!.dateFmt(groupBy);
|
||||||
|
final assetCount = row.read(assetCountExp)!;
|
||||||
|
return TimeBucket(date: timeline, assetCount: assetCount);
|
||||||
|
}).watch();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<BaseAsset>> getVideoBucketAssets(
|
||||||
|
String userId, {
|
||||||
|
required int offset,
|
||||||
|
required int count,
|
||||||
|
}) {
|
||||||
|
final query = _db.remoteAssetEntity.select()
|
||||||
|
..where(
|
||||||
|
(row) =>
|
||||||
|
_db.remoteAssetEntity.type.equalsValue(AssetType.video) &
|
||||||
|
_db.remoteAssetEntity.visibility
|
||||||
|
.equalsValue(AssetVisibility.timeline) &
|
||||||
|
_db.remoteAssetEntity.ownerId.equals(userId),
|
||||||
|
)
|
||||||
|
..orderBy([(row) => OrderingTerm.desc(row.createdAt)])
|
||||||
|
..limit(count, offset: offset);
|
||||||
|
|
||||||
|
return query.map((row) => row.toDto()).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<List<Bucket>> watchRemoteBucket(
|
||||||
|
List<String> userIds, {
|
||||||
|
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||||
|
}) {
|
||||||
|
if (groupBy == GroupAssetsBy.none) {
|
||||||
|
return _db.remoteAssetEntity
|
||||||
|
.count(
|
||||||
|
where: (row) =>
|
||||||
|
row.deletedAt.isNull() &
|
||||||
|
row.visibility.equalsValue(AssetVisibility.timeline) &
|
||||||
|
row.ownerId.isIn(userIds),
|
||||||
|
)
|
||||||
|
.map(_generateBuckets)
|
||||||
|
.watchSingle();
|
||||||
|
}
|
||||||
|
|
||||||
|
final assetCountExp = _db.remoteAssetEntity.id.count();
|
||||||
|
final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy);
|
||||||
|
|
||||||
|
final query = _db.remoteAssetEntity.selectOnly()
|
||||||
|
..addColumns([assetCountExp, dateExp])
|
||||||
|
..where(
|
||||||
|
_db.remoteAssetEntity.deletedAt.isNull() &
|
||||||
|
_db.remoteAssetEntity.visibility
|
||||||
|
.equalsValue(AssetVisibility.timeline) &
|
||||||
|
_db.remoteAssetEntity.ownerId.isIn(userIds),
|
||||||
|
)
|
||||||
|
..groupBy([dateExp])
|
||||||
|
..orderBy([OrderingTerm.desc(dateExp)]);
|
||||||
|
|
||||||
|
return query.map((row) {
|
||||||
|
final timeline = row.read(dateExp)!.dateFmt(groupBy);
|
||||||
|
final assetCount = row.read(assetCountExp)!;
|
||||||
|
return TimeBucket(date: timeline, assetCount: assetCount);
|
||||||
|
}).watch();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<BaseAsset>> getRemoteBucketAssets(
|
||||||
|
List<String> userIds, {
|
||||||
|
required int offset,
|
||||||
|
required int count,
|
||||||
|
}) {
|
||||||
|
final query = _db.remoteAssetEntity.select()
|
||||||
|
..where(
|
||||||
|
(row) =>
|
||||||
|
row.deletedAt.isNull() &
|
||||||
|
row.visibility.equalsValue(AssetVisibility.timeline) &
|
||||||
|
row.ownerId.isIn(userIds),
|
||||||
|
)
|
||||||
|
..orderBy([(t) => OrderingTerm.desc(t.createdAt)])
|
||||||
|
..limit(count, offset: offset);
|
||||||
|
|
||||||
|
return query.map((row) => row.toDto()).get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension on Expression<DateTime> {
|
extension on Expression<DateTime> {
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ class TabShellPage extends ConsumerWidget {
|
|||||||
const MainTimelineRoute(),
|
const MainTimelineRoute(),
|
||||||
SearchRoute(),
|
SearchRoute(),
|
||||||
const DriftAlbumsRoute(),
|
const DriftAlbumsRoute(),
|
||||||
const LibraryRoute(),
|
const DriftLibraryRoute(),
|
||||||
],
|
],
|
||||||
duration: const Duration(milliseconds: 600),
|
duration: const Duration(milliseconds: 600),
|
||||||
transitionBuilder: (context, child, animation) => FadeTransition(
|
transitionBuilder: (context, child, animation) => FadeTransition(
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class DriftArchivePage extends StatelessWidget {
|
||||||
|
const DriftArchivePage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ProviderScope(
|
||||||
|
overrides: [
|
||||||
|
timelineServiceProvider.overrideWith(
|
||||||
|
(ref) {
|
||||||
|
final user = ref.watch(currentUserProvider);
|
||||||
|
if (user == null) {
|
||||||
|
throw Exception('User must be logged in to access archive');
|
||||||
|
}
|
||||||
|
|
||||||
|
final timelineService =
|
||||||
|
ref.watch(timelineFactoryProvider).archive(user.id);
|
||||||
|
ref.onDispose(timelineService.dispose);
|
||||||
|
return timelineService;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: const Timeline(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class DriftFavoritePage extends StatelessWidget {
|
||||||
|
const DriftFavoritePage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ProviderScope(
|
||||||
|
overrides: [
|
||||||
|
timelineServiceProvider.overrideWith(
|
||||||
|
(ref) {
|
||||||
|
final user = ref.watch(currentUserProvider);
|
||||||
|
if (user == null) {
|
||||||
|
throw Exception('User must be logged in to access favorite');
|
||||||
|
}
|
||||||
|
|
||||||
|
final timelineService =
|
||||||
|
ref.watch(timelineFactoryProvider).favorite(user.id);
|
||||||
|
ref.onDispose(timelineService.dispose);
|
||||||
|
return timelineService;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: const Timeline(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class DriftLockedFolderPage extends StatelessWidget {
|
||||||
|
const DriftLockedFolderPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ProviderScope(
|
||||||
|
overrides: [
|
||||||
|
timelineServiceProvider.overrideWith(
|
||||||
|
(ref) {
|
||||||
|
final user = ref.watch(currentUserProvider);
|
||||||
|
if (user == null) {
|
||||||
|
throw Exception('User must be logged in to access locked folder');
|
||||||
|
}
|
||||||
|
|
||||||
|
final timelineService =
|
||||||
|
ref.watch(timelineFactoryProvider).lockedFolder(user.id);
|
||||||
|
ref.onDispose(timelineService.dispose);
|
||||||
|
return timelineService;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: const Timeline(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class DriftTrashPage extends StatelessWidget {
|
||||||
|
const DriftTrashPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ProviderScope(
|
||||||
|
overrides: [
|
||||||
|
timelineServiceProvider.overrideWith(
|
||||||
|
(ref) {
|
||||||
|
final user = ref.watch(currentUserProvider);
|
||||||
|
if (user == null) {
|
||||||
|
throw Exception('User must be logged in to access trash');
|
||||||
|
}
|
||||||
|
|
||||||
|
final timelineService =
|
||||||
|
ref.watch(timelineFactoryProvider).trash(user.id);
|
||||||
|
ref.onDispose(timelineService.dispose);
|
||||||
|
return timelineService;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: const Timeline(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class DriftVideoPage extends StatelessWidget {
|
||||||
|
const DriftVideoPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ProviderScope(
|
||||||
|
overrides: [
|
||||||
|
timelineServiceProvider.overrideWith(
|
||||||
|
(ref) {
|
||||||
|
final user = ref.watch(currentUserProvider);
|
||||||
|
if (user == null) {
|
||||||
|
throw Exception('User must be logged in to video');
|
||||||
|
}
|
||||||
|
|
||||||
|
final timelineService =
|
||||||
|
ref.watch(timelineFactoryProvider).video(user.id);
|
||||||
|
ref.onDispose(timelineService.dispose);
|
||||||
|
return timelineService;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: const Timeline(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,15 +5,43 @@ import 'package:drift/drift.dart' hide Column;
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart';
|
import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart';
|
||||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/asset.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/user.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
|
|
||||||
final _features = [
|
final _features = [
|
||||||
|
_Feature(
|
||||||
|
name: 'Selection Mode Timeline',
|
||||||
|
icon: Icons.developer_mode_rounded,
|
||||||
|
onTap: (ctx, ref) async {
|
||||||
|
final user = ref.watch(currentUserProvider);
|
||||||
|
if (user == null) {
|
||||||
|
return Future.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
final assets =
|
||||||
|
await ref.read(remoteAssetRepositoryProvider).getSome(user.id);
|
||||||
|
|
||||||
|
final selectedAssets = await ctx.pushRoute<Set<BaseAsset>>(
|
||||||
|
DriftAssetSelectionTimelineRoute(
|
||||||
|
lockedSelectionAssets: assets.toSet(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
DLog.log(
|
||||||
|
"Selected ${selectedAssets?.length ?? 0} assets",
|
||||||
|
);
|
||||||
|
|
||||||
|
return Future.value();
|
||||||
|
},
|
||||||
|
),
|
||||||
_Feature(
|
_Feature(
|
||||||
name: 'Sync Local',
|
name: 'Sync Local',
|
||||||
icon: Icons.photo_album_rounded,
|
icon: Icons.photo_album_rounded,
|
||||||
@@ -66,6 +94,9 @@ final _features = [
|
|||||||
await db.remoteAlbumEntity.deleteAll();
|
await db.remoteAlbumEntity.deleteAll();
|
||||||
await db.remoteAlbumUserEntity.deleteAll();
|
await db.remoteAlbumUserEntity.deleteAll();
|
||||||
await db.remoteAlbumAssetEntity.deleteAll();
|
await db.remoteAlbumAssetEntity.deleteAll();
|
||||||
|
await db.memoryEntity.deleteAll();
|
||||||
|
await db.memoryAssetEntity.deleteAll();
|
||||||
|
await db.stackEntity.deleteAll();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
_Feature(
|
_Feature(
|
||||||
@@ -96,6 +127,11 @@ final _features = [
|
|||||||
icon: Icons.timeline_rounded,
|
icon: Icons.timeline_rounded,
|
||||||
onTap: (ctx, _) => ctx.pushRoute(const TabShellRoute()),
|
onTap: (ctx, _) => ctx.pushRoute(const TabShellRoute()),
|
||||||
),
|
),
|
||||||
|
_Feature(
|
||||||
|
name: 'Video',
|
||||||
|
icon: Icons.video_collection_outlined,
|
||||||
|
onTap: (ctx, _) => ctx.pushRoute(const DriftVideoRoute()),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
|
|||||||
@@ -162,6 +162,10 @@ final _remoteStats = [
|
|||||||
name: 'Memories Assets',
|
name: 'Memories Assets',
|
||||||
load: (db) => db.managers.memoryAssetEntity.count(),
|
load: (db) => db.managers.memoryAssetEntity.count(),
|
||||||
),
|
),
|
||||||
|
_Stat(
|
||||||
|
name: 'Stacks',
|
||||||
|
load: (db) => db.managers.stackEntity.count(),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class DriftAssetSelectionTimelinePage extends ConsumerWidget {
|
||||||
|
final Set<BaseAsset> lockedSelectionAssets;
|
||||||
|
const DriftAssetSelectionTimelinePage({
|
||||||
|
super.key,
|
||||||
|
this.lockedSelectionAssets = const {},
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
return ProviderScope(
|
||||||
|
overrides: [
|
||||||
|
multiSelectProvider.overrideWith(
|
||||||
|
() => MultiSelectNotifier(
|
||||||
|
MultiSelectState(
|
||||||
|
selectedAssets: {},
|
||||||
|
lockedSelectionAssets: lockedSelectionAssets,
|
||||||
|
forceEnable: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
timelineServiceProvider.overrideWith(
|
||||||
|
(ref) {
|
||||||
|
final timelineUsers =
|
||||||
|
ref.watch(timelineUsersProvider).valueOrNull ?? [];
|
||||||
|
final timelineService =
|
||||||
|
ref.watch(timelineFactoryProvider).remoteAssets(timelineUsers);
|
||||||
|
ref.onDispose(timelineService.dispose);
|
||||||
|
return timelineService;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: const Timeline(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,501 @@
|
|||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||||
|
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
||||||
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
|
import 'package:immich_mobile/providers/album/album.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/partner.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/search/people.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/server_info.provider.dart';
|
||||||
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
|
import 'package:immich_mobile/services/api.service.dart';
|
||||||
|
import 'package:immich_mobile/utils/image_url_builder.dart';
|
||||||
|
import 'package:immich_mobile/widgets/album/album_thumbnail_card.dart';
|
||||||
|
import 'package:immich_mobile/widgets/common/immich_sliver_app_bar.dart';
|
||||||
|
import 'package:immich_mobile/widgets/common/user_avatar.dart';
|
||||||
|
import 'package:immich_mobile/widgets/map/map_thumbnail.dart';
|
||||||
|
import 'package:maplibre_gl/maplibre_gl.dart';
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class DriftLibraryPage extends ConsumerWidget {
|
||||||
|
const DriftLibraryPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
return const Scaffold(
|
||||||
|
body: CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
ImmichSliverAppBar(),
|
||||||
|
_ActionButtonGrid(),
|
||||||
|
_CollectionCards(),
|
||||||
|
_QuickAccessButtonList(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ActionButtonGrid extends ConsumerWidget {
|
||||||
|
const _ActionButtonGrid();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final isTrashEnable = ref.watch(
|
||||||
|
serverInfoProvider.select((state) => state.serverFeatures.trash),
|
||||||
|
);
|
||||||
|
|
||||||
|
return SliverPadding(
|
||||||
|
padding: const EdgeInsets.only(left: 16, top: 16, right: 16, bottom: 12),
|
||||||
|
sliver: SliverToBoxAdapter(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
_ActionButton(
|
||||||
|
icon: Icons.favorite_outline_rounded,
|
||||||
|
onTap: () => context.pushRoute(const DriftFavoriteRoute()),
|
||||||
|
label: 'favorites'.t(context: context),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
_ActionButton(
|
||||||
|
icon: Icons.archive_outlined,
|
||||||
|
onTap: () => context.pushRoute(const DriftArchiveRoute()),
|
||||||
|
label: 'archived'.t(context: context),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
_ActionButton(
|
||||||
|
icon: Icons.link_outlined,
|
||||||
|
onTap: () => context.pushRoute(const SharedLinkRoute()),
|
||||||
|
label: 'shared_links'.t(context: context),
|
||||||
|
),
|
||||||
|
isTrashEnable
|
||||||
|
? const SizedBox(width: 8)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
isTrashEnable
|
||||||
|
? _ActionButton(
|
||||||
|
icon: Icons.delete_outline_rounded,
|
||||||
|
onTap: () => context.pushRoute(const DriftTrashRoute()),
|
||||||
|
label: 'trash'.t(context: context),
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ActionButton extends StatelessWidget {
|
||||||
|
const _ActionButton({
|
||||||
|
required this.icon,
|
||||||
|
required this.onTap,
|
||||||
|
required this.label,
|
||||||
|
});
|
||||||
|
|
||||||
|
final IconData icon;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
final String label;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Expanded(
|
||||||
|
child: FilledButton.icon(
|
||||||
|
onPressed: onTap,
|
||||||
|
label: Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 4.0),
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
style: TextStyle(
|
||||||
|
color: context.colorScheme.onSurface,
|
||||||
|
fontSize: 15,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
style: FilledButton.styleFrom(
|
||||||
|
elevation: 0,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
|
||||||
|
backgroundColor: context.colorScheme.surfaceContainerLow,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(25)),
|
||||||
|
side: BorderSide(
|
||||||
|
color: context.colorScheme.onSurface.withAlpha(10),
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
icon: Icon(
|
||||||
|
icon,
|
||||||
|
color: context.primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CollectionCards extends StatelessWidget {
|
||||||
|
const _CollectionCards();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return const SliverPadding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
sliver: SliverToBoxAdapter(
|
||||||
|
child: Wrap(
|
||||||
|
spacing: 8,
|
||||||
|
runSpacing: 8,
|
||||||
|
children: [
|
||||||
|
_PeopleCollectionCard(),
|
||||||
|
_PlacesCollectionCard(),
|
||||||
|
_LocalAlbumsCollectionCard(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PeopleCollectionCard extends ConsumerWidget {
|
||||||
|
const _PeopleCollectionCard();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final people = ref.watch(getAllPeopleProvider);
|
||||||
|
|
||||||
|
return LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
final isTablet = constraints.maxWidth > 600;
|
||||||
|
final widthFactor = isTablet ? 0.25 : 0.5;
|
||||||
|
final size = context.width * widthFactor - 20.0;
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () => context.pushRoute(const PeopleCollectionRoute()),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: size,
|
||||||
|
width: size,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(
|
||||||
|
Radius.circular(20),
|
||||||
|
),
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: [
|
||||||
|
context.colorScheme.primary.withAlpha(30),
|
||||||
|
context.colorScheme.primary.withAlpha(25),
|
||||||
|
],
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: people.widgetWhen(
|
||||||
|
onLoading: () => const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
onData: (people) {
|
||||||
|
return GridView.count(
|
||||||
|
crossAxisCount: 2,
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
crossAxisSpacing: 8,
|
||||||
|
mainAxisSpacing: 8,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
children: people.take(4).map((person) {
|
||||||
|
return CircleAvatar(
|
||||||
|
backgroundImage: NetworkImage(
|
||||||
|
getFaceThumbnailUrl(person.id),
|
||||||
|
headers: ApiService.getRequestHeaders(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Text(
|
||||||
|
'people'.t(context: context),
|
||||||
|
style: context.textTheme.titleSmall?.copyWith(
|
||||||
|
color: context.colorScheme.onSurface,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PlacesCollectionCard extends StatelessWidget {
|
||||||
|
const _PlacesCollectionCard();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
final isTablet = constraints.maxWidth > 600;
|
||||||
|
final widthFactor = isTablet ? 0.25 : 0.5;
|
||||||
|
final size = context.width * widthFactor - 20.0;
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () => context.pushRoute(
|
||||||
|
PlacesCollectionRoute(
|
||||||
|
currentLocation: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: size,
|
||||||
|
width: size,
|
||||||
|
child: DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||||
|
color:
|
||||||
|
context.colorScheme.secondaryContainer.withAlpha(100),
|
||||||
|
),
|
||||||
|
child: IgnorePointer(
|
||||||
|
child: MapThumbnail(
|
||||||
|
zoom: 8,
|
||||||
|
centre: const LatLng(
|
||||||
|
21.44950,
|
||||||
|
-157.91959,
|
||||||
|
),
|
||||||
|
showAttribution: false,
|
||||||
|
themeMode: context.isDarkTheme
|
||||||
|
? ThemeMode.dark
|
||||||
|
: ThemeMode.light,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Text(
|
||||||
|
'places'.t(),
|
||||||
|
style: context.textTheme.titleSmall?.copyWith(
|
||||||
|
color: context.colorScheme.onSurface,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LocalAlbumsCollectionCard extends ConsumerWidget {
|
||||||
|
const _LocalAlbumsCollectionCard();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
// TODO: Migrate to the drift after local album page
|
||||||
|
final albums = ref.watch(localAlbumsProvider);
|
||||||
|
|
||||||
|
return LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
final isTablet = constraints.maxWidth > 600;
|
||||||
|
final widthFactor = isTablet ? 0.25 : 0.5;
|
||||||
|
final size = context.width * widthFactor - 20.0;
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () => context.pushRoute(
|
||||||
|
const LocalAlbumsRoute(),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: size,
|
||||||
|
width: size,
|
||||||
|
child: DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: [
|
||||||
|
context.colorScheme.primary.withAlpha(30),
|
||||||
|
context.colorScheme.primary.withAlpha(25),
|
||||||
|
],
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: GridView.count(
|
||||||
|
crossAxisCount: 2,
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
crossAxisSpacing: 8,
|
||||||
|
mainAxisSpacing: 8,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
children: albums.take(4).map((album) {
|
||||||
|
return AlbumThumbnailCard(
|
||||||
|
album: album,
|
||||||
|
showTitle: false,
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Text(
|
||||||
|
'on_this_device'.t(context: context),
|
||||||
|
style: context.textTheme.titleSmall?.copyWith(
|
||||||
|
color: context.colorScheme.onSurface,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _QuickAccessButtonList extends ConsumerWidget {
|
||||||
|
const _QuickAccessButtonList();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final partners = ref.watch(partnerSharedWithProvider);
|
||||||
|
|
||||||
|
return SliverPadding(
|
||||||
|
padding: const EdgeInsets.only(left: 16, top: 12, right: 16, bottom: 32),
|
||||||
|
sliver: SliverToBoxAdapter(
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: context.colorScheme.onSurface.withAlpha(10),
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
borderRadius: const BorderRadius.all(
|
||||||
|
Radius.circular(20),
|
||||||
|
),
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: [
|
||||||
|
context.colorScheme.primary.withAlpha(10),
|
||||||
|
context.colorScheme.primary.withAlpha(15),
|
||||||
|
context.colorScheme.primary.withAlpha(20),
|
||||||
|
],
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: ListView(
|
||||||
|
shrinkWrap: true,
|
||||||
|
padding: const EdgeInsets.all(0),
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: const Radius.circular(20),
|
||||||
|
topRight: const Radius.circular(20),
|
||||||
|
bottomLeft: Radius.circular(partners.isEmpty ? 20 : 0),
|
||||||
|
bottomRight: Radius.circular(partners.isEmpty ? 20 : 0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
leading: const Icon(
|
||||||
|
Icons.folder_outlined,
|
||||||
|
size: 26,
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
'folders'.t(context: context),
|
||||||
|
style: context.textTheme.titleSmall?.copyWith(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: () => context.pushRoute(FolderRoute()),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(
|
||||||
|
Icons.lock_outline_rounded,
|
||||||
|
size: 26,
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
'locked_folder'.t(context: context),
|
||||||
|
style: context.textTheme.titleSmall?.copyWith(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// TODO: PIN code is needed
|
||||||
|
onTap: () => context.pushRoute(const DriftLockedFolderRoute()),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(
|
||||||
|
Icons.group_outlined,
|
||||||
|
size: 26,
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
'partners'.t(context: context),
|
||||||
|
style: context.textTheme.titleSmall?.copyWith(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: () => context.pushRoute(const PartnerRoute()),
|
||||||
|
),
|
||||||
|
_PartnerList(partners: partners),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PartnerList extends StatelessWidget {
|
||||||
|
const _PartnerList({required this.partners});
|
||||||
|
|
||||||
|
final List<UserDto> partners;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ListView.builder(
|
||||||
|
padding: const EdgeInsets.all(0),
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemCount: partners.length,
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final partner = partners[index];
|
||||||
|
final isLastItem = index == partners.length - 1;
|
||||||
|
return ListTile(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
bottomLeft: Radius.circular(isLastItem ? 20 : 0),
|
||||||
|
bottomRight: Radius.circular(isLastItem ? 20 : 0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
contentPadding: const EdgeInsets.only(
|
||||||
|
left: 12.0,
|
||||||
|
right: 18.0,
|
||||||
|
),
|
||||||
|
leading: userAvatar(context, partner, radius: 16),
|
||||||
|
title: const Text(
|
||||||
|
"partner_list_user_photos",
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
).t(context: context, args: {'user': partner.name}),
|
||||||
|
onTap: () => context.pushRoute(PartnerDetailRoute(partner: partner)),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
import 'dart:convert' hide Codec;
|
|
||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/rendering.dart';
|
|
||||||
import 'package:thumbhash/thumbhash.dart';
|
|
||||||
|
|
||||||
class ThumbHashProvider extends ImageProvider<ThumbHashProvider> {
|
|
||||||
final String thumbHash;
|
|
||||||
|
|
||||||
const ThumbHashProvider({
|
|
||||||
required this.thumbHash,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<ThumbHashProvider> obtainKey(ImageConfiguration configuration) {
|
|
||||||
return SynchronousFuture(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
ImageStreamCompleter loadImage(
|
|
||||||
ThumbHashProvider key,
|
|
||||||
ImageDecoderCallback decode,
|
|
||||||
) {
|
|
||||||
return MultiFrameImageStreamCompleter(
|
|
||||||
codec: _loadCodec(key, decode),
|
|
||||||
scale: 1.0,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Codec> _loadCodec(
|
|
||||||
ThumbHashProvider key,
|
|
||||||
ImageDecoderCallback decode,
|
|
||||||
) async {
|
|
||||||
final image = thumbHashToRGBA(base64Decode(key.thumbHash));
|
|
||||||
return decode(await ImmutableBuffer.fromUint8List(rgbaToBmp(image)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if (identical(this, other)) return true;
|
|
||||||
if (other is ThumbHashProvider) {
|
|
||||||
return thumbHash == other.thumbHash;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => thumbHash.hashCode;
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
|
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/images/thumb_hash_provider.dart';
|
import 'package:immich_mobile/widgets/common/thumbhash.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_grid/thumbnail_placeholder.dart';
|
|
||||||
import 'package:immich_mobile/widgets/common/fade_in_placeholder_image.dart';
|
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:octo_image/octo_image.dart';
|
import 'package:octo_image/octo_image.dart';
|
||||||
|
|
||||||
@@ -56,13 +54,7 @@ OctoPlaceholderBuilder _blurHashPlaceholderBuilder(
|
|||||||
String? thumbHash, {
|
String? thumbHash, {
|
||||||
BoxFit? fit,
|
BoxFit? fit,
|
||||||
}) {
|
}) {
|
||||||
return (context) => thumbHash == null
|
return (context) => Thumbhash(blurhash: thumbHash, fit: fit ?? BoxFit.cover);
|
||||||
? const ThumbnailPlaceholder()
|
|
||||||
: FadeInPlaceholderImage(
|
|
||||||
placeholder: const ThumbnailPlaceholder(),
|
|
||||||
image: ThumbHashProvider(thumbHash: thumbHash),
|
|
||||||
fit: fit ?? BoxFit.cover,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OctoErrorBuilder _blurHashErrorBuilder(
|
OctoErrorBuilder _blurHashErrorBuilder(
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class ThumbnailTile extends ConsumerWidget {
|
|||||||
this.size = const Size.square(256),
|
this.size = const Size.square(256),
|
||||||
this.fit = BoxFit.cover,
|
this.fit = BoxFit.cover,
|
||||||
this.showStorageIndicator = true,
|
this.showStorageIndicator = true,
|
||||||
this.canDeselect = true,
|
this.lockSelection = false,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -20,15 +20,13 @@ class ThumbnailTile extends ConsumerWidget {
|
|||||||
final Size size;
|
final Size size;
|
||||||
final BoxFit fit;
|
final BoxFit fit;
|
||||||
final bool showStorageIndicator;
|
final bool showStorageIndicator;
|
||||||
|
final bool lockSelection;
|
||||||
/// If we are allowed to deselect this image
|
|
||||||
final bool canDeselect;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final assetContainerColor = context.isDarkTheme
|
final assetContainerColor = context.isDarkTheme
|
||||||
? context.primaryColor.darken(amount: 0.6)
|
? context.primaryColor.darken(amount: 0.4)
|
||||||
: context.primaryColor.lighten(amount: 0.8);
|
: context.primaryColor.lighten(amount: 0.75);
|
||||||
|
|
||||||
final isSelected = ref.watch(
|
final isSelected = ref.watch(
|
||||||
multiSelectProvider.select(
|
multiSelectProvider.select(
|
||||||
@@ -36,24 +34,29 @@ class ThumbnailTile extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final borderStyle = lockSelection
|
||||||
|
? BoxDecoration(
|
||||||
|
color: context.colorScheme.surfaceContainerHighest,
|
||||||
|
border: Border.all(
|
||||||
|
color: context.colorScheme.surfaceContainerHighest,
|
||||||
|
width: 6,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: isSelected
|
||||||
|
? BoxDecoration(
|
||||||
|
color: assetContainerColor,
|
||||||
|
border: Border.all(color: assetContainerColor, width: 6),
|
||||||
|
)
|
||||||
|
: const BoxDecoration();
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
AnimatedContainer(
|
AnimatedContainer(
|
||||||
duration: Durations.short4,
|
duration: Durations.short4,
|
||||||
curve: Curves.decelerate,
|
curve: Curves.decelerate,
|
||||||
decoration: BoxDecoration(
|
decoration: borderStyle,
|
||||||
color: isSelected
|
|
||||||
? (canDeselect ? assetContainerColor : Colors.grey)
|
|
||||||
: null,
|
|
||||||
border: isSelected
|
|
||||||
? Border.all(
|
|
||||||
color: canDeselect ? assetContainerColor : Colors.grey,
|
|
||||||
width: 8,
|
|
||||||
)
|
|
||||||
: const Border(),
|
|
||||||
),
|
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: isSelected
|
borderRadius: isSelected || lockSelection
|
||||||
? const BorderRadius.all(Radius.circular(15.0))
|
? const BorderRadius.all(Radius.circular(15.0))
|
||||||
: BorderRadius.zero,
|
: BorderRadius.zero,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
@@ -102,14 +105,17 @@ class ThumbnailTile extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (isSelected)
|
if (isSelected || lockSelection)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(3.0),
|
padding: const EdgeInsets.all(3.0),
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.topLeft,
|
alignment: Alignment.topLeft,
|
||||||
child: _SelectionIndicator(
|
child: _SelectionIndicator(
|
||||||
isSelected: isSelected,
|
isSelected: isSelected,
|
||||||
color: assetContainerColor,
|
isLocked: lockSelection,
|
||||||
|
color: lockSelection
|
||||||
|
? context.colorScheme.surfaceContainerHighest
|
||||||
|
: assetContainerColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -120,15 +126,29 @@ class ThumbnailTile extends ConsumerWidget {
|
|||||||
|
|
||||||
class _SelectionIndicator extends StatelessWidget {
|
class _SelectionIndicator extends StatelessWidget {
|
||||||
final bool isSelected;
|
final bool isSelected;
|
||||||
|
final bool isLocked;
|
||||||
final Color? color;
|
final Color? color;
|
||||||
|
|
||||||
const _SelectionIndicator({
|
const _SelectionIndicator({
|
||||||
required this.isSelected,
|
required this.isSelected,
|
||||||
|
required this.isLocked,
|
||||||
this.color,
|
this.color,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (isSelected) {
|
if (isLocked) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: color,
|
||||||
|
),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.check_circle_rounded,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (isSelected) {
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
|||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/images/full_image.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/images/full_image.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
|
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
|
||||||
import 'package:immich_mobile/utils/hooks/blurhash_hook.dart';
|
import 'package:immich_mobile/widgets/common/thumbhash.dart';
|
||||||
|
|
||||||
class DriftMemoryCard extends StatelessWidget {
|
class DriftMemoryCard extends StatelessWidget {
|
||||||
final RemoteAsset asset;
|
final RemoteAsset asset;
|
||||||
@@ -117,43 +117,29 @@ class _BlurredBackdrop extends HookWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final blurhash = useDriftBlurHashRef(asset).value;
|
final blurhash = asset.thumbHash;
|
||||||
if (blurhash != null) {
|
if (blurhash != null) {
|
||||||
// Use a nice cheap blur hash image decoration
|
// Use a nice cheap blur hash image decoration
|
||||||
return Container(
|
return Thumbhash(blurhash: blurhash, fit: BoxFit.cover);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to using a more expensive image filtered
|
||||||
|
// Since the ImmichImage is already precached, we can
|
||||||
|
// safely use that as the image provider
|
||||||
|
return ImageFiltered(
|
||||||
|
imageFilter: ImageFilter.blur(sigmaX: 30, sigmaY: 30),
|
||||||
|
child: DecoratedBox(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
image: DecorationImage(
|
image: DecorationImage(
|
||||||
image: MemoryImage(
|
image: getFullImageProvider(
|
||||||
blurhash,
|
asset,
|
||||||
|
size: Size(context.width, context.height),
|
||||||
),
|
),
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Container(
|
child: const ColoredBox(color: Color.fromRGBO(0, 0, 0, 0.2)),
|
||||||
color: Colors.black.withValues(alpha: 0.2),
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Fall back to using a more expensive image filtered
|
|
||||||
// Since the ImmichImage is already precached, we can
|
|
||||||
// safely use that as the image provider
|
|
||||||
return ImageFiltered(
|
|
||||||
imageFilter: ImageFilter.blur(sigmaX: 30, sigmaY: 30),
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
image: DecorationImage(
|
|
||||||
image: getFullImageProvider(
|
|
||||||
asset,
|
|
||||||
size: Size(context.width, context.height),
|
|
||||||
),
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Container(
|
|
||||||
color: Colors.black.withValues(alpha: 0.2),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,22 +166,22 @@ class _AssetTileWidget extends ConsumerWidget {
|
|||||||
BaseAsset asset,
|
BaseAsset asset,
|
||||||
) {
|
) {
|
||||||
final multiSelectState = ref.read(multiSelectProvider);
|
final multiSelectState = ref.read(multiSelectProvider);
|
||||||
if (!multiSelectState.isEnabled) {
|
|
||||||
|
if (multiSelectState.forceEnable || multiSelectState.isEnabled) {
|
||||||
|
ref.read(multiSelectProvider.notifier).toggleAssetSelection(asset);
|
||||||
|
} else {
|
||||||
ctx.pushRoute(
|
ctx.pushRoute(
|
||||||
AssetViewerRoute(
|
AssetViewerRoute(
|
||||||
initialIndex: assetIndex,
|
initialIndex: assetIndex,
|
||||||
timelineService: ref.read(timelineServiceProvider),
|
timelineService: ref.read(timelineServiceProvider),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ref.read(multiSelectProvider.notifier).toggleAssetSelection(asset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleOnLongPress(WidgetRef ref, BaseAsset asset) {
|
void _handleOnLongPress(WidgetRef ref, BaseAsset asset) {
|
||||||
final multiSelectState = ref.read(multiSelectProvider);
|
final multiSelectState = ref.read(multiSelectProvider);
|
||||||
if (multiSelectState.isEnabled) {
|
if (multiSelectState.isEnabled || multiSelectState.forceEnable) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,13 +189,35 @@ class _AssetTileWidget extends ConsumerWidget {
|
|||||||
ref.read(multiSelectProvider.notifier).toggleAssetSelection(asset);
|
ref.read(multiSelectProvider.notifier).toggleAssetSelection(asset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _getLockSelectionStatus(WidgetRef ref) {
|
||||||
|
final lockSelectionAssets = ref.read(
|
||||||
|
multiSelectProvider.select(
|
||||||
|
(state) => state.lockedSelectionAssets,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (lockSelectionAssets.isEmpty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lockSelectionAssets.contains(asset);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final lockSelection = _getLockSelectionStatus(ref);
|
||||||
|
|
||||||
return RepaintBoundary(
|
return RepaintBoundary(
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () => _handleOnTap(context, ref, assetIndex, asset),
|
onTap: () => lockSelection
|
||||||
onLongPress: () => _handleOnLongPress(ref, asset),
|
? null
|
||||||
child: ThumbnailTile(asset),
|
: _handleOnTap(context, ref, assetIndex, asset),
|
||||||
|
onLongPress: () =>
|
||||||
|
lockSelection ? null : _handleOnLongPress(ref, asset),
|
||||||
|
child: ThumbnailTile(
|
||||||
|
asset,
|
||||||
|
lockSelection: lockSelection,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -354,22 +354,24 @@ class ScrubberState extends ConsumerState<Scrubber>
|
|||||||
isDragging: _isDragging,
|
isDragging: _isDragging,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PositionedDirectional(
|
if (_scrollController.hasClients &&
|
||||||
top: _thumbTopOffset + widget.topPadding,
|
_scrollController.position.maxScrollExtent > 0)
|
||||||
end: 0,
|
PositionedDirectional(
|
||||||
child: RepaintBoundary(
|
top: _thumbTopOffset + widget.topPadding,
|
||||||
child: GestureDetector(
|
end: 0,
|
||||||
onVerticalDragStart: _onDragStart,
|
child: RepaintBoundary(
|
||||||
onVerticalDragUpdate: _onDragUpdate,
|
child: GestureDetector(
|
||||||
onVerticalDragEnd: _onDragEnd,
|
onVerticalDragStart: _onDragStart,
|
||||||
child: _Scrubber(
|
onVerticalDragUpdate: _onDragUpdate,
|
||||||
thumbAnimation: _thumbAnimation,
|
onVerticalDragEnd: _onDragEnd,
|
||||||
labelAnimation: _labelAnimation,
|
child: _Scrubber(
|
||||||
label: label,
|
thumbAnimation: _thumbAnimation,
|
||||||
|
labelAnimation: _labelAnimation,
|
||||||
|
label: label,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -18,9 +18,14 @@ import 'package:immich_mobile/providers/infrastructure/setting.provider.dart';
|
|||||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||||
import 'package:immich_mobile/widgets/common/immich_sliver_app_bar.dart';
|
import 'package:immich_mobile/widgets/common/immich_sliver_app_bar.dart';
|
||||||
|
import 'package:immich_mobile/widgets/common/selection_sliver_app_bar.dart';
|
||||||
|
|
||||||
class Timeline extends StatelessWidget {
|
class Timeline extends StatelessWidget {
|
||||||
const Timeline({super.key, this.topSliverWidget, this.topSliverWidgetHeight});
|
const Timeline({
|
||||||
|
super.key,
|
||||||
|
this.topSliverWidget,
|
||||||
|
this.topSliverWidgetHeight,
|
||||||
|
});
|
||||||
|
|
||||||
final Widget? topSliverWidget;
|
final Widget? topSliverWidget;
|
||||||
final double? topSliverWidgetHeight;
|
final double? topSliverWidgetHeight;
|
||||||
@@ -52,7 +57,10 @@ class Timeline extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _SliverTimeline extends ConsumerStatefulWidget {
|
class _SliverTimeline extends ConsumerStatefulWidget {
|
||||||
const _SliverTimeline({this.topSliverWidget, this.topSliverWidgetHeight});
|
const _SliverTimeline({
|
||||||
|
this.topSliverWidget,
|
||||||
|
this.topSliverWidgetHeight,
|
||||||
|
});
|
||||||
|
|
||||||
final Widget? topSliverWidget;
|
final Widget? topSliverWidget;
|
||||||
final double? topSliverWidgetHeight;
|
final double? topSliverWidgetHeight;
|
||||||
@@ -84,6 +92,10 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
|||||||
final asyncSegments = ref.watch(timelineSegmentProvider);
|
final asyncSegments = ref.watch(timelineSegmentProvider);
|
||||||
final maxHeight =
|
final maxHeight =
|
||||||
ref.watch(timelineArgsProvider.select((args) => args.maxHeight));
|
ref.watch(timelineArgsProvider.select((args) => args.maxHeight));
|
||||||
|
final isSelectionMode = ref.watch(
|
||||||
|
multiSelectProvider.select((s) => s.forceEnable),
|
||||||
|
);
|
||||||
|
|
||||||
return asyncSegments.widgetWhen(
|
return asyncSegments.widgetWhen(
|
||||||
onData: (segments) {
|
onData: (segments) {
|
||||||
final childCount = (segments.lastOrNull?.lastIndex ?? -1) + 1;
|
final childCount = (segments.lastOrNull?.lastIndex ?? -1) + 1;
|
||||||
@@ -105,11 +117,14 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
|||||||
primary: true,
|
primary: true,
|
||||||
cacheExtent: maxHeight * 2,
|
cacheExtent: maxHeight * 2,
|
||||||
slivers: [
|
slivers: [
|
||||||
const ImmichSliverAppBar(
|
if (isSelectionMode)
|
||||||
floating: true,
|
const SelectionSliverAppBar()
|
||||||
pinned: false,
|
else
|
||||||
snap: false,
|
const ImmichSliverAppBar(
|
||||||
),
|
floating: true,
|
||||||
|
pinned: false,
|
||||||
|
snap: false,
|
||||||
|
),
|
||||||
if (widget.topSliverWidget != null) widget.topSliverWidget!,
|
if (widget.topSliverWidget != null) widget.topSliverWidget!,
|
||||||
_SliverSegmentedList(
|
_SliverSegmentedList(
|
||||||
segments: segments,
|
segments: segments,
|
||||||
@@ -134,40 +149,42 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Consumer(
|
if (!isSelectionMode) ...[
|
||||||
builder: (_, consumerRef, child) {
|
Consumer(
|
||||||
final isMultiSelectEnabled = consumerRef.watch(
|
builder: (_, consumerRef, child) {
|
||||||
multiSelectProvider.select(
|
final isMultiSelectEnabled = consumerRef.watch(
|
||||||
(s) => s.isEnabled,
|
multiSelectProvider.select(
|
||||||
),
|
(s) => s.isEnabled,
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
|
||||||
if (isMultiSelectEnabled) {
|
if (isMultiSelectEnabled) {
|
||||||
return child!;
|
return child!;
|
||||||
}
|
}
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
},
|
},
|
||||||
child: const Positioned(
|
child: const Positioned(
|
||||||
top: 60,
|
top: 60,
|
||||||
left: 25,
|
left: 25,
|
||||||
child: _MultiSelectStatusButton(),
|
child: _MultiSelectStatusButton(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
Consumer(
|
||||||
Consumer(
|
builder: (_, consumerRef, child) {
|
||||||
builder: (_, consumerRef, child) {
|
final isMultiSelectEnabled = consumerRef.watch(
|
||||||
final isMultiSelectEnabled = consumerRef.watch(
|
multiSelectProvider.select(
|
||||||
multiSelectProvider.select(
|
(s) => s.isEnabled,
|
||||||
(s) => s.isEnabled,
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
if (isMultiSelectEnabled) {
|
if (isMultiSelectEnabled) {
|
||||||
return child!;
|
return child!;
|
||||||
}
|
}
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
},
|
},
|
||||||
child: const HomeBottomAppBar(),
|
child: const HomeBottomAppBar(),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/stack.repository.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||||
|
|
||||||
|
final driftStackProvider = Provider<DriftStackRepository>(
|
||||||
|
(ref) => DriftStackRepository(ref.watch(driftProvider)),
|
||||||
|
);
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/domain/services/timeline.service.dart';
|
import 'package:immich_mobile/domain/services/timeline.service.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
@@ -12,8 +13,14 @@ final multiSelectProvider =
|
|||||||
|
|
||||||
class MultiSelectState {
|
class MultiSelectState {
|
||||||
final Set<BaseAsset> selectedAssets;
|
final Set<BaseAsset> selectedAssets;
|
||||||
|
final Set<BaseAsset> lockedSelectionAssets;
|
||||||
|
final bool forceEnable;
|
||||||
|
|
||||||
const MultiSelectState({required this.selectedAssets});
|
const MultiSelectState({
|
||||||
|
required this.selectedAssets,
|
||||||
|
required this.lockedSelectionAssets,
|
||||||
|
this.forceEnable = false,
|
||||||
|
});
|
||||||
|
|
||||||
bool get isEnabled => selectedAssets.isNotEmpty;
|
bool get isEnabled => selectedAssets.isNotEmpty;
|
||||||
bool get hasRemote => selectedAssets.any(
|
bool get hasRemote => selectedAssets.any(
|
||||||
@@ -25,33 +32,54 @@ class MultiSelectState {
|
|||||||
(asset) => asset.storage == AssetState.local,
|
(asset) => asset.storage == AssetState.local,
|
||||||
);
|
);
|
||||||
|
|
||||||
MultiSelectState copyWith({Set<BaseAsset>? selectedAssets}) {
|
MultiSelectState copyWith({
|
||||||
|
Set<BaseAsset>? selectedAssets,
|
||||||
|
Set<BaseAsset>? lockedSelectionAssets,
|
||||||
|
bool? forceEnable,
|
||||||
|
}) {
|
||||||
return MultiSelectState(
|
return MultiSelectState(
|
||||||
selectedAssets: selectedAssets ?? this.selectedAssets,
|
selectedAssets: selectedAssets ?? this.selectedAssets,
|
||||||
|
lockedSelectionAssets:
|
||||||
|
lockedSelectionAssets ?? this.lockedSelectionAssets,
|
||||||
|
forceEnable: forceEnable ?? this.forceEnable,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'MultiSelectState(selectedAssets: $selectedAssets)';
|
String toString() =>
|
||||||
|
'MultiSelectState(selectedAssets: $selectedAssets, lockedSelectionAssets: $lockedSelectionAssets, forceEnable: $forceEnable)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant MultiSelectState other) {
|
bool operator ==(covariant MultiSelectState other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) return true;
|
||||||
final listEquals = const DeepCollectionEquality().equals;
|
final setEquals = const DeepCollectionEquality().equals;
|
||||||
|
|
||||||
return listEquals(other.selectedAssets, selectedAssets);
|
return setEquals(other.selectedAssets, selectedAssets) &&
|
||||||
|
setEquals(other.lockedSelectionAssets, lockedSelectionAssets) &&
|
||||||
|
other.forceEnable == forceEnable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => selectedAssets.hashCode;
|
int get hashCode =>
|
||||||
|
selectedAssets.hashCode ^
|
||||||
|
lockedSelectionAssets.hashCode ^
|
||||||
|
forceEnable.hashCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MultiSelectNotifier extends Notifier<MultiSelectState> {
|
class MultiSelectNotifier extends Notifier<MultiSelectState> {
|
||||||
|
MultiSelectNotifier([this._defaultState]);
|
||||||
|
final MultiSelectState? _defaultState;
|
||||||
|
|
||||||
TimelineService get _timelineService => ref.read(timelineServiceProvider);
|
TimelineService get _timelineService => ref.read(timelineServiceProvider);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MultiSelectState build() {
|
MultiSelectState build() {
|
||||||
return const MultiSelectState(selectedAssets: {});
|
return _defaultState ??
|
||||||
|
const MultiSelectState(
|
||||||
|
selectedAssets: {},
|
||||||
|
lockedSelectionAssets: {},
|
||||||
|
forceEnable: false,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void selectAsset(BaseAsset asset) {
|
void selectAsset(BaseAsset asset) {
|
||||||
@@ -83,7 +111,11 @@ class MultiSelectNotifier extends Notifier<MultiSelectState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
void reset() {
|
||||||
state = const MultiSelectState(selectedAssets: {});
|
state = const MultiSelectState(
|
||||||
|
selectedAssets: {},
|
||||||
|
lockedSelectionAssets: {},
|
||||||
|
forceEnable: false,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Bucket bulk operations
|
/// Bucket bulk operations
|
||||||
@@ -131,6 +163,12 @@ class MultiSelectNotifier extends Notifier<MultiSelectState> {
|
|||||||
|
|
||||||
state = state.copyWith(selectedAssets: selectedAssets);
|
state = state.copyWith(selectedAssets: selectedAssets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setLockedSelectionAssets(Set<BaseAsset> assets) {
|
||||||
|
state = state.copyWith(
|
||||||
|
lockedSelectionAssets: assets,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final bucketSelectionProvider = Provider.family<bool, List<BaseAsset>>(
|
final bucketSelectionProvider = Provider.family<bool, List<BaseAsset>>(
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ class AuthRepository extends DatabaseRepository {
|
|||||||
_drift.remoteAlbumEntity.deleteAll(),
|
_drift.remoteAlbumEntity.deleteAll(),
|
||||||
_drift.remoteAlbumAssetEntity.deleteAll(),
|
_drift.remoteAlbumAssetEntity.deleteAll(),
|
||||||
_drift.remoteAlbumUserEntity.deleteAll(),
|
_drift.remoteAlbumUserEntity.deleteAll(),
|
||||||
|
_drift.memoryEntity.deleteAll(),
|
||||||
|
_drift.memoryAssetEntity.deleteAll(),
|
||||||
|
_drift.stackEntity.deleteAll(),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/log.model.dart';
|
import 'package:immich_mobile/domain/models/log.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/memory.model.dart';
|
import 'package:immich_mobile/domain/models/memory.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||||
@@ -66,12 +67,19 @@ import 'package:immich_mobile/pages/search/person_result.page.dart';
|
|||||||
import 'package:immich_mobile/pages/search/recently_taken.page.dart';
|
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/drift_favorite.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/dev/drift_video.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/dev/drift_trash.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/dev/drift_archive.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/dev/drift_locked_folder.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_timeline.page.dart';
|
import 'package:immich_mobile/presentation/pages/dev/local_timeline.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/dev/main_timeline.page.dart';
|
import 'package:immich_mobile/presentation/pages/dev/main_timeline.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/dev/media_stat.page.dart';
|
import 'package:immich_mobile/presentation/pages/dev/media_stat.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/dev/remote_timeline.page.dart';
|
import 'package:immich_mobile/presentation/pages/dev/remote_timeline.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_album.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_album.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/drift_library.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/drift_asset_selection_timeline.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_memory.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_memory.page.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.page.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.page.dart';
|
||||||
import 'package:immich_mobile/providers/api.provider.dart';
|
import 'package:immich_mobile/providers/api.provider.dart';
|
||||||
@@ -174,7 +182,7 @@ class AppRouter extends RootStackRouter {
|
|||||||
maintainState: false,
|
maintainState: false,
|
||||||
),
|
),
|
||||||
AutoRoute(
|
AutoRoute(
|
||||||
page: LibraryRoute.page,
|
page: DriftLibraryRoute.page,
|
||||||
guards: [_authGuard, _duplicateGuard],
|
guards: [_authGuard, _duplicateGuard],
|
||||||
),
|
),
|
||||||
AutoRoute(
|
AutoRoute(
|
||||||
@@ -392,6 +400,34 @@ class AppRouter extends RootStackRouter {
|
|||||||
page: DriftMemoryRoute.page,
|
page: DriftMemoryRoute.page,
|
||||||
guards: [_authGuard, _duplicateGuard],
|
guards: [_authGuard, _duplicateGuard],
|
||||||
),
|
),
|
||||||
|
AutoRoute(
|
||||||
|
page: DriftFavoriteRoute.page,
|
||||||
|
guards: [_authGuard, _duplicateGuard],
|
||||||
|
),
|
||||||
|
AutoRoute(
|
||||||
|
page: DriftTrashRoute.page,
|
||||||
|
guards: [_authGuard, _duplicateGuard],
|
||||||
|
),
|
||||||
|
AutoRoute(
|
||||||
|
page: DriftArchiveRoute.page,
|
||||||
|
guards: [_authGuard, _duplicateGuard],
|
||||||
|
),
|
||||||
|
AutoRoute(
|
||||||
|
page: DriftLockedFolderRoute.page,
|
||||||
|
guards: [_authGuard, _duplicateGuard],
|
||||||
|
),
|
||||||
|
AutoRoute(
|
||||||
|
page: DriftVideoRoute.page,
|
||||||
|
guards: [_authGuard, _duplicateGuard],
|
||||||
|
),
|
||||||
|
AutoRoute(
|
||||||
|
page: DriftLibraryRoute.page,
|
||||||
|
guards: [_authGuard, _duplicateGuard],
|
||||||
|
),
|
||||||
|
AutoRoute(
|
||||||
|
page: DriftAssetSelectionTimelineRoute.page,
|
||||||
|
guards: [_authGuard, _duplicateGuard],
|
||||||
|
),
|
||||||
|
|
||||||
// required to handle all deeplinks in deep_link.service.dart
|
// required to handle all deeplinks in deep_link.service.dart
|
||||||
// auto_route_library#1722
|
// auto_route_library#1722
|
||||||
|
|||||||
@@ -618,6 +618,119 @@ class DriftAlbumsRoute extends PageRouteInfo<void> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [DriftArchivePage]
|
||||||
|
class DriftArchiveRoute extends PageRouteInfo<void> {
|
||||||
|
const DriftArchiveRoute({List<PageRouteInfo>? children})
|
||||||
|
: super(DriftArchiveRoute.name, initialChildren: children);
|
||||||
|
|
||||||
|
static const String name = 'DriftArchiveRoute';
|
||||||
|
|
||||||
|
static PageInfo page = PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
return const DriftArchivePage();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [DriftAssetSelectionTimelinePage]
|
||||||
|
class DriftAssetSelectionTimelineRoute
|
||||||
|
extends PageRouteInfo<DriftAssetSelectionTimelineRouteArgs> {
|
||||||
|
DriftAssetSelectionTimelineRoute({
|
||||||
|
Key? key,
|
||||||
|
Set<BaseAsset> lockedSelectionAssets = const {},
|
||||||
|
List<PageRouteInfo>? children,
|
||||||
|
}) : super(
|
||||||
|
DriftAssetSelectionTimelineRoute.name,
|
||||||
|
args: DriftAssetSelectionTimelineRouteArgs(
|
||||||
|
key: key,
|
||||||
|
lockedSelectionAssets: lockedSelectionAssets,
|
||||||
|
),
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const String name = 'DriftAssetSelectionTimelineRoute';
|
||||||
|
|
||||||
|
static PageInfo page = PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
final args = data.argsAs<DriftAssetSelectionTimelineRouteArgs>(
|
||||||
|
orElse: () => const DriftAssetSelectionTimelineRouteArgs(),
|
||||||
|
);
|
||||||
|
return DriftAssetSelectionTimelinePage(
|
||||||
|
key: args.key,
|
||||||
|
lockedSelectionAssets: args.lockedSelectionAssets,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class DriftAssetSelectionTimelineRouteArgs {
|
||||||
|
const DriftAssetSelectionTimelineRouteArgs({
|
||||||
|
this.key,
|
||||||
|
this.lockedSelectionAssets = const {},
|
||||||
|
});
|
||||||
|
|
||||||
|
final Key? key;
|
||||||
|
|
||||||
|
final Set<BaseAsset> lockedSelectionAssets;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'DriftAssetSelectionTimelineRouteArgs{key: $key, lockedSelectionAssets: $lockedSelectionAssets}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [DriftFavoritePage]
|
||||||
|
class DriftFavoriteRoute extends PageRouteInfo<void> {
|
||||||
|
const DriftFavoriteRoute({List<PageRouteInfo>? children})
|
||||||
|
: super(DriftFavoriteRoute.name, initialChildren: children);
|
||||||
|
|
||||||
|
static const String name = 'DriftFavoriteRoute';
|
||||||
|
|
||||||
|
static PageInfo page = PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
return const DriftFavoritePage();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [DriftLibraryPage]
|
||||||
|
class DriftLibraryRoute extends PageRouteInfo<void> {
|
||||||
|
const DriftLibraryRoute({List<PageRouteInfo>? children})
|
||||||
|
: super(DriftLibraryRoute.name, initialChildren: children);
|
||||||
|
|
||||||
|
static const String name = 'DriftLibraryRoute';
|
||||||
|
|
||||||
|
static PageInfo page = PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
return const DriftLibraryPage();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [DriftLockedFolderPage]
|
||||||
|
class DriftLockedFolderRoute extends PageRouteInfo<void> {
|
||||||
|
const DriftLockedFolderRoute({List<PageRouteInfo>? children})
|
||||||
|
: super(DriftLockedFolderRoute.name, initialChildren: children);
|
||||||
|
|
||||||
|
static const String name = 'DriftLockedFolderRoute';
|
||||||
|
|
||||||
|
static PageInfo page = PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
return const DriftLockedFolderPage();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [DriftMemoryPage]
|
/// [DriftMemoryPage]
|
||||||
class DriftMemoryRoute extends PageRouteInfo<DriftMemoryRouteArgs> {
|
class DriftMemoryRoute extends PageRouteInfo<DriftMemoryRouteArgs> {
|
||||||
@@ -670,6 +783,38 @@ class DriftMemoryRouteArgs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [DriftTrashPage]
|
||||||
|
class DriftTrashRoute extends PageRouteInfo<void> {
|
||||||
|
const DriftTrashRoute({List<PageRouteInfo>? children})
|
||||||
|
: super(DriftTrashRoute.name, initialChildren: children);
|
||||||
|
|
||||||
|
static const String name = 'DriftTrashRoute';
|
||||||
|
|
||||||
|
static PageInfo page = PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
return const DriftTrashPage();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [DriftVideoPage]
|
||||||
|
class DriftVideoRoute extends PageRouteInfo<void> {
|
||||||
|
const DriftVideoRoute({List<PageRouteInfo>? children})
|
||||||
|
: super(DriftVideoRoute.name, initialChildren: children);
|
||||||
|
|
||||||
|
static const String name = 'DriftVideoRoute';
|
||||||
|
|
||||||
|
static PageInfo page = PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
return const DriftVideoPage();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [EditImagePage]
|
/// [EditImagePage]
|
||||||
class EditImageRoute extends PageRouteInfo<EditImageRouteArgs> {
|
class EditImageRoute extends PageRouteInfo<EditImageRouteArgs> {
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
import 'dart:convert';
|
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
|
||||||
import 'package:thumbhash/thumbhash.dart' as thumbhash;
|
|
||||||
|
|
||||||
ObjectRef<Uint8List?> useBlurHashRef(Asset? asset) {
|
|
||||||
if (asset?.thumbhash == null) {
|
|
||||||
return useRef(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
final rbga = thumbhash.thumbHashToRGBA(
|
|
||||||
base64Decode(asset!.thumbhash!),
|
|
||||||
);
|
|
||||||
|
|
||||||
return useRef(thumbhash.rgbaToBmp(rbga));
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectRef<Uint8List?> useDriftBlurHashRef(RemoteAsset? asset) {
|
|
||||||
if (asset?.thumbHash == null) {
|
|
||||||
return useRef(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
final rbga = thumbhash.thumbHashToRGBA(
|
|
||||||
base64Decode(asset!.thumbHash!),
|
|
||||||
);
|
|
||||||
|
|
||||||
return useRef(thumbhash.rgbaToBmp(rbga));
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:immich_mobile/widgets/common/transparent_image.dart';
|
|
||||||
|
|
||||||
class FadeInPlaceholderImage extends StatelessWidget {
|
|
||||||
final Widget placeholder;
|
|
||||||
final ImageProvider image;
|
|
||||||
final Duration duration;
|
|
||||||
final BoxFit fit;
|
|
||||||
|
|
||||||
const FadeInPlaceholderImage({
|
|
||||||
super.key,
|
|
||||||
required this.placeholder,
|
|
||||||
required this.image,
|
|
||||||
this.duration = const Duration(milliseconds: 100),
|
|
||||||
this.fit = BoxFit.cover,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return SizedBox.expand(
|
|
||||||
child: Stack(
|
|
||||||
fit: StackFit.expand,
|
|
||||||
children: [
|
|
||||||
placeholder,
|
|
||||||
FadeInImage(
|
|
||||||
fadeInDuration: duration,
|
|
||||||
image: image,
|
|
||||||
fit: fit,
|
|
||||||
placeholder: MemoryImage(kTransparentImage),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
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/providers/image/immich_local_thumbnail_provider.dart';
|
import 'package:immich_mobile/providers/image/immich_local_thumbnail_provider.dart';
|
||||||
import 'package:immich_mobile/providers/image/immich_remote_thumbnail_provider.dart';
|
import 'package:immich_mobile/providers/image/immich_remote_thumbnail_provider.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/utils/hooks/blurhash_hook.dart';
|
|
||||||
import 'package:immich_mobile/utils/thumbnail_utils.dart';
|
import 'package:immich_mobile/utils/thumbnail_utils.dart';
|
||||||
import 'package:immich_mobile/widgets/common/immich_image.dart';
|
import 'package:immich_mobile/widgets/common/immich_image.dart';
|
||||||
import 'package:immich_mobile/widgets/common/thumbhash_placeholder.dart';
|
import 'package:immich_mobile/widgets/common/thumbhash_placeholder.dart';
|
||||||
@@ -64,7 +61,6 @@ class ImmichThumbnail extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
Uint8List? blurhash = useBlurHashRef(asset).value;
|
|
||||||
final userId = ref.watch(currentUserProvider)?.id;
|
final userId = ref.watch(currentUserProvider)?.id;
|
||||||
|
|
||||||
if (asset == null) {
|
if (asset == null) {
|
||||||
@@ -82,7 +78,7 @@ class ImmichThumbnail extends HookConsumerWidget {
|
|||||||
asset!.exifInfo,
|
asset!.exifInfo,
|
||||||
asset!.fileCreatedAt,
|
asset!.fileCreatedAt,
|
||||||
asset!.type,
|
asset!.type,
|
||||||
[],
|
const [],
|
||||||
);
|
);
|
||||||
|
|
||||||
final thumbnailProviderInstance = ImmichThumbnail.imageProvider(
|
final thumbnailProviderInstance = ImmichThumbnail.imageProvider(
|
||||||
@@ -94,7 +90,7 @@ class ImmichThumbnail extends HookConsumerWidget {
|
|||||||
thumbnailProviderInstance.evict();
|
thumbnailProviderInstance.evict();
|
||||||
|
|
||||||
final originalErrorWidgetBuilder =
|
final originalErrorWidgetBuilder =
|
||||||
blurHashErrorBuilder(blurhash, fit: fit);
|
blurHashErrorBuilder(asset?.thumbhash, fit: fit);
|
||||||
return originalErrorWidgetBuilder(ctx, error, stackTrace);
|
return originalErrorWidgetBuilder(ctx, error, stackTrace);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +101,8 @@ class ImmichThumbnail extends HookConsumerWidget {
|
|||||||
fadeInDuration: Duration.zero,
|
fadeInDuration: Duration.zero,
|
||||||
fadeOutDuration: const Duration(milliseconds: 100),
|
fadeOutDuration: const Duration(milliseconds: 100),
|
||||||
octoSet: OctoSet(
|
octoSet: OctoSet(
|
||||||
placeholderBuilder: blurHashPlaceholderBuilder(blurhash, fit: fit),
|
placeholderBuilder:
|
||||||
|
blurHashPlaceholderBuilder(asset?.thumbhash, fit: fit),
|
||||||
errorBuilder: customErrorBuilder,
|
errorBuilder: customErrorBuilder,
|
||||||
),
|
),
|
||||||
image: thumbnailProviderInstance,
|
image: thumbnailProviderInstance,
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
|
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||||
|
|
||||||
|
class SelectionSliverAppBar extends ConsumerStatefulWidget {
|
||||||
|
const SelectionSliverAppBar({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConsumerState<SelectionSliverAppBar> createState() =>
|
||||||
|
_SelectionSliverAppBarState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SelectionSliverAppBarState extends ConsumerState<SelectionSliverAppBar> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final selection = ref.watch(
|
||||||
|
multiSelectProvider.select((s) => s.selectedAssets),
|
||||||
|
);
|
||||||
|
|
||||||
|
final toExclude = ref.watch(
|
||||||
|
multiSelectProvider.select((s) => s.lockedSelectionAssets),
|
||||||
|
);
|
||||||
|
|
||||||
|
final filteredAssets = selection.where((asset) {
|
||||||
|
return !toExclude.contains(asset);
|
||||||
|
}).toSet();
|
||||||
|
|
||||||
|
onDone(Set<BaseAsset> selected) {
|
||||||
|
ref.read(multiSelectProvider.notifier).reset();
|
||||||
|
context.maybePop<Set<BaseAsset>>(selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SliverAppBar(
|
||||||
|
floating: true,
|
||||||
|
pinned: true,
|
||||||
|
snap: false,
|
||||||
|
backgroundColor: context.colorScheme.surfaceContainer,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(5)),
|
||||||
|
),
|
||||||
|
automaticallyImplyLeading: false,
|
||||||
|
leading: IconButton(
|
||||||
|
icon: const Icon(Icons.close_rounded),
|
||||||
|
onPressed: () {
|
||||||
|
ref.read(multiSelectProvider.notifier).reset();
|
||||||
|
context.pop<Set<BaseAsset>>(null);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
centerTitle: true,
|
||||||
|
title: Text(
|
||||||
|
"Select {count}".t(
|
||||||
|
context: context,
|
||||||
|
args: {
|
||||||
|
'count': filteredAssets.length.toString(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => onDone(filteredAssets),
|
||||||
|
child: Text(
|
||||||
|
'done'.t(context: context),
|
||||||
|
style: context.textTheme.titleSmall?.copyWith(
|
||||||
|
color: context.colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:ui' as ui;
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:thumbhash/thumbhash.dart' as thumbhash;
|
||||||
|
|
||||||
|
class ThumbhashImage extends RenderBox {
|
||||||
|
Color _placeholderColor;
|
||||||
|
ui.Image? _image;
|
||||||
|
BoxFit _fit;
|
||||||
|
|
||||||
|
ThumbhashImage({
|
||||||
|
required ui.Image? image,
|
||||||
|
required BoxFit fit,
|
||||||
|
required Color placeholderColor,
|
||||||
|
}) : _image = image,
|
||||||
|
_fit = fit,
|
||||||
|
_placeholderColor = placeholderColor;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(PaintingContext context, Offset offset) {
|
||||||
|
final image = _image;
|
||||||
|
final rect = offset & size;
|
||||||
|
if (image == null) {
|
||||||
|
final paint = Paint();
|
||||||
|
paint.color = _placeholderColor;
|
||||||
|
context.canvas.drawRect(rect, paint);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
paintImage(
|
||||||
|
canvas: context.canvas,
|
||||||
|
rect: rect,
|
||||||
|
image: image,
|
||||||
|
fit: _fit,
|
||||||
|
filterQuality: FilterQuality.low,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void performLayout() {
|
||||||
|
size = constraints.biggest;
|
||||||
|
}
|
||||||
|
|
||||||
|
set image(ui.Image? value) {
|
||||||
|
if (_image != value) {
|
||||||
|
_image = value;
|
||||||
|
markNeedsPaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set fit(BoxFit value) {
|
||||||
|
if (_fit != value) {
|
||||||
|
_fit = value;
|
||||||
|
markNeedsPaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set placeholderColor(Color value) {
|
||||||
|
if (_placeholderColor != value) {
|
||||||
|
_placeholderColor = value;
|
||||||
|
markNeedsPaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ThumbhashLeaf extends LeafRenderObjectWidget {
|
||||||
|
final ui.Image? image;
|
||||||
|
final BoxFit fit;
|
||||||
|
final Color placeholderColor;
|
||||||
|
|
||||||
|
const ThumbhashLeaf({
|
||||||
|
super.key,
|
||||||
|
required this.image,
|
||||||
|
required this.fit,
|
||||||
|
required this.placeholderColor,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
RenderObject createRenderObject(BuildContext context) {
|
||||||
|
return ThumbhashImage(
|
||||||
|
image: image,
|
||||||
|
fit: fit,
|
||||||
|
placeholderColor: placeholderColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void updateRenderObject(BuildContext context, ThumbhashImage renderObject) {
|
||||||
|
renderObject.fit = fit;
|
||||||
|
renderObject.image = image;
|
||||||
|
renderObject.placeholderColor = placeholderColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Thumbhash extends StatefulWidget {
|
||||||
|
final String? blurhash;
|
||||||
|
final BoxFit fit;
|
||||||
|
final Color placeholderColor;
|
||||||
|
|
||||||
|
const Thumbhash({
|
||||||
|
required this.blurhash,
|
||||||
|
this.fit = BoxFit.cover,
|
||||||
|
this.placeholderColor = const Color.fromRGBO(0, 0, 0, 0.2),
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<Thumbhash> createState() => _ThumbhashState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ThumbhashState extends State<Thumbhash> {
|
||||||
|
String? blurhash;
|
||||||
|
BoxFit? fit;
|
||||||
|
ui.Image? _image;
|
||||||
|
Color? placeholderColor;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
final blurhash_ = blurhash = widget.blurhash;
|
||||||
|
fit = widget.fit;
|
||||||
|
placeholderColor = widget.placeholderColor;
|
||||||
|
if (blurhash_ == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final image = thumbhash.thumbHashToRGBA(base64.decode(blurhash_));
|
||||||
|
_decode(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _decode(thumbhash.Image image) async {
|
||||||
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final buffer = await ImmutableBuffer.fromUint8List(image.rgba);
|
||||||
|
if (!mounted) {
|
||||||
|
buffer.dispose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final descriptor = ImageDescriptor.raw(
|
||||||
|
buffer,
|
||||||
|
width: image.width,
|
||||||
|
height: image.height,
|
||||||
|
pixelFormat: PixelFormat.rgba8888,
|
||||||
|
);
|
||||||
|
if (!mounted) {
|
||||||
|
buffer.dispose();
|
||||||
|
descriptor.dispose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final codec = await descriptor.instantiateCodec(
|
||||||
|
targetWidth: image.width,
|
||||||
|
targetHeight: image.height,
|
||||||
|
);
|
||||||
|
if (!mounted) {
|
||||||
|
buffer.dispose();
|
||||||
|
descriptor.dispose();
|
||||||
|
codec.dispose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final frame = (await codec.getNextFrame()).image;
|
||||||
|
buffer.dispose();
|
||||||
|
descriptor.dispose();
|
||||||
|
codec.dispose();
|
||||||
|
if (!mounted) {
|
||||||
|
frame.dispose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_image = frame;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ThumbhashLeaf(
|
||||||
|
image: _image,
|
||||||
|
fit: fit!,
|
||||||
|
placeholderColor: placeholderColor!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_image?.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,12 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_grid/thumbnail_placeholder.dart';
|
import 'package:immich_mobile/widgets/common/thumbhash.dart';
|
||||||
import 'package:immich_mobile/widgets/common/fade_in_placeholder_image.dart';
|
|
||||||
import 'package:octo_image/octo_image.dart';
|
import 'package:octo_image/octo_image.dart';
|
||||||
|
|
||||||
/// Simple set to show [OctoPlaceholder.circularProgressIndicator] as
|
/// Simple set to show [OctoPlaceholder.circularProgressIndicator] as
|
||||||
/// placeholder and [OctoError.icon] as error.
|
/// placeholder and [OctoError.icon] as error.
|
||||||
OctoSet blurHashOrPlaceholder(
|
OctoSet blurHashOrPlaceholder(
|
||||||
Uint8List? blurhash, {
|
String? blurhash, {
|
||||||
BoxFit? fit,
|
BoxFit fit = BoxFit.cover,
|
||||||
Text? errorMessage,
|
Text? errorMessage,
|
||||||
}) {
|
}) {
|
||||||
return OctoSet(
|
return OctoSet(
|
||||||
@@ -19,21 +17,15 @@ OctoSet blurHashOrPlaceholder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
OctoPlaceholderBuilder blurHashPlaceholderBuilder(
|
OctoPlaceholderBuilder blurHashPlaceholderBuilder(
|
||||||
Uint8List? blurhash, {
|
String? blurhash, {
|
||||||
BoxFit? fit,
|
required BoxFit fit,
|
||||||
}) {
|
}) {
|
||||||
return (context) => blurhash == null
|
return (context) => Thumbhash(blurhash: blurhash, fit: fit);
|
||||||
? const ThumbnailPlaceholder()
|
|
||||||
: FadeInPlaceholderImage(
|
|
||||||
placeholder: const ThumbnailPlaceholder(),
|
|
||||||
image: MemoryImage(blurhash),
|
|
||||||
fit: fit ?? BoxFit.cover,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OctoErrorBuilder blurHashErrorBuilder(
|
OctoErrorBuilder blurHashErrorBuilder(
|
||||||
Uint8List? blurhash, {
|
String? blurhash, {
|
||||||
BoxFit? fit,
|
BoxFit fit = BoxFit.cover,
|
||||||
Text? message,
|
Text? message,
|
||||||
IconData? icon,
|
IconData? icon,
|
||||||
Color? iconColor,
|
Color? iconColor,
|
||||||
|
|||||||
@@ -5,9 +5,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/user_metadata.model.dart';
|
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
|
||||||
import 'package:immich_mobile/services/api.service.dart';
|
import 'package:immich_mobile/services/api.service.dart';
|
||||||
import 'package:immich_mobile/widgets/common/transparent_image.dart';
|
import 'package:immich_mobile/widgets/common/transparent_image.dart';
|
||||||
|
|
||||||
@@ -26,7 +24,7 @@ class UserCircleAvatar extends ConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
bool isDarkTheme = context.themeData.brightness == Brightness.dark;
|
final userAvatarColor = user.avatarColor.toColor();
|
||||||
final profileImageUrl =
|
final profileImageUrl =
|
||||||
'${Store.get(StoreKey.serverEndpoint)}/users/${user.id}/profile-image?d=${Random().nextInt(1024)}';
|
'${Store.get(StoreKey.serverEndpoint)}/users/${user.id}/profile-image?d=${Random().nextInt(1024)}';
|
||||||
|
|
||||||
@@ -34,14 +32,14 @@ class UserCircleAvatar extends ConsumerWidget {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: isDarkTheme && user.avatarColor == AvatarColor.primary
|
color: userAvatarColor.computeLuminance() > 0.5
|
||||||
? Colors.black
|
? Colors.black
|
||||||
: Colors.white,
|
: Colors.white,
|
||||||
),
|
),
|
||||||
child: Text(user.name[0].toUpperCase()),
|
child: Text(user.name[0].toUpperCase()),
|
||||||
);
|
);
|
||||||
return CircleAvatar(
|
return CircleAvatar(
|
||||||
backgroundColor: user.avatarColor.toColor(),
|
backgroundColor: userAvatarColor,
|
||||||
radius: radius,
|
radius: radius,
|
||||||
child: user.profileImagePath == null
|
child: user.profileImagePath == null
|
||||||
? textIcon
|
? textIcon
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/pages/common/native_video_viewer.page.dart';
|
import 'package:immich_mobile/pages/common/native_video_viewer.page.dart';
|
||||||
import 'package:immich_mobile/utils/hooks/blurhash_hook.dart';
|
|
||||||
import 'package:immich_mobile/widgets/common/immich_image.dart';
|
import 'package:immich_mobile/widgets/common/immich_image.dart';
|
||||||
|
import 'package:immich_mobile/widgets/common/thumbhash.dart';
|
||||||
|
|
||||||
class MemoryCard extends StatelessWidget {
|
class MemoryCard extends StatelessWidget {
|
||||||
final Asset asset;
|
final Asset asset;
|
||||||
@@ -113,44 +113,35 @@ class _BlurredBackdrop extends HookWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final blurhash = useBlurHashRef(asset).value;
|
final blurhash = asset.thumbhash;
|
||||||
if (blurhash != null) {
|
if (blurhash != null) {
|
||||||
// Use a nice cheap blur hash image decoration
|
// Use a nice cheap blur hash image decoration
|
||||||
return Container(
|
return Stack(
|
||||||
|
children: [
|
||||||
|
const ColoredBox(color: Color.fromRGBO(0, 0, 0, 0.2)),
|
||||||
|
Thumbhash(blurhash: blurhash, fit: BoxFit.cover),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to using a more expensive image filtered
|
||||||
|
// Since the ImmichImage is already precached, we can
|
||||||
|
// safely use that as the image provider
|
||||||
|
return ImageFiltered(
|
||||||
|
imageFilter: ImageFilter.blur(sigmaX: 30, sigmaY: 30),
|
||||||
|
child: DecoratedBox(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
image: DecorationImage(
|
image: DecorationImage(
|
||||||
image: MemoryImage(
|
image: ImmichImage.imageProvider(
|
||||||
blurhash,
|
asset: asset,
|
||||||
|
height: context.height,
|
||||||
|
width: context.width,
|
||||||
),
|
),
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Container(
|
child: const ColoredBox(color: Color.fromRGBO(0, 0, 0, 0.2)),
|
||||||
color: Colors.black.withValues(alpha: 0.2),
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Fall back to using a more expensive image filtered
|
|
||||||
// Since the ImmichImage is already precached, we can
|
|
||||||
// safely use that as the image provider
|
|
||||||
return ImageFiltered(
|
|
||||||
imageFilter: ImageFilter.blur(sigmaX: 30, sigmaY: 30),
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
image: DecorationImage(
|
|
||||||
image: ImmichImage.imageProvider(
|
|
||||||
asset: asset,
|
|
||||||
height: context.height,
|
|
||||||
width: context.width,
|
|
||||||
),
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Container(
|
|
||||||
color: Colors.black.withValues(alpha: 0.2),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-1
@@ -24,6 +24,7 @@ class SystemConfigOAuthDto {
|
|||||||
required this.mobileOverrideEnabled,
|
required this.mobileOverrideEnabled,
|
||||||
required this.mobileRedirectUri,
|
required this.mobileRedirectUri,
|
||||||
required this.profileSigningAlgorithm,
|
required this.profileSigningAlgorithm,
|
||||||
|
required this.roleClaim,
|
||||||
required this.scope,
|
required this.scope,
|
||||||
required this.signingAlgorithm,
|
required this.signingAlgorithm,
|
||||||
required this.storageLabelClaim,
|
required this.storageLabelClaim,
|
||||||
@@ -55,6 +56,8 @@ class SystemConfigOAuthDto {
|
|||||||
|
|
||||||
String profileSigningAlgorithm;
|
String profileSigningAlgorithm;
|
||||||
|
|
||||||
|
String roleClaim;
|
||||||
|
|
||||||
String scope;
|
String scope;
|
||||||
|
|
||||||
String signingAlgorithm;
|
String signingAlgorithm;
|
||||||
@@ -81,6 +84,7 @@ class SystemConfigOAuthDto {
|
|||||||
other.mobileOverrideEnabled == mobileOverrideEnabled &&
|
other.mobileOverrideEnabled == mobileOverrideEnabled &&
|
||||||
other.mobileRedirectUri == mobileRedirectUri &&
|
other.mobileRedirectUri == mobileRedirectUri &&
|
||||||
other.profileSigningAlgorithm == profileSigningAlgorithm &&
|
other.profileSigningAlgorithm == profileSigningAlgorithm &&
|
||||||
|
other.roleClaim == roleClaim &&
|
||||||
other.scope == scope &&
|
other.scope == scope &&
|
||||||
other.signingAlgorithm == signingAlgorithm &&
|
other.signingAlgorithm == signingAlgorithm &&
|
||||||
other.storageLabelClaim == storageLabelClaim &&
|
other.storageLabelClaim == storageLabelClaim &&
|
||||||
@@ -102,6 +106,7 @@ class SystemConfigOAuthDto {
|
|||||||
(mobileOverrideEnabled.hashCode) +
|
(mobileOverrideEnabled.hashCode) +
|
||||||
(mobileRedirectUri.hashCode) +
|
(mobileRedirectUri.hashCode) +
|
||||||
(profileSigningAlgorithm.hashCode) +
|
(profileSigningAlgorithm.hashCode) +
|
||||||
|
(roleClaim.hashCode) +
|
||||||
(scope.hashCode) +
|
(scope.hashCode) +
|
||||||
(signingAlgorithm.hashCode) +
|
(signingAlgorithm.hashCode) +
|
||||||
(storageLabelClaim.hashCode) +
|
(storageLabelClaim.hashCode) +
|
||||||
@@ -110,7 +115,7 @@ class SystemConfigOAuthDto {
|
|||||||
(tokenEndpointAuthMethod.hashCode);
|
(tokenEndpointAuthMethod.hashCode);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'SystemConfigOAuthDto[autoLaunch=$autoLaunch, autoRegister=$autoRegister, buttonText=$buttonText, clientId=$clientId, clientSecret=$clientSecret, defaultStorageQuota=$defaultStorageQuota, enabled=$enabled, issuerUrl=$issuerUrl, mobileOverrideEnabled=$mobileOverrideEnabled, mobileRedirectUri=$mobileRedirectUri, profileSigningAlgorithm=$profileSigningAlgorithm, scope=$scope, signingAlgorithm=$signingAlgorithm, storageLabelClaim=$storageLabelClaim, storageQuotaClaim=$storageQuotaClaim, timeout=$timeout, tokenEndpointAuthMethod=$tokenEndpointAuthMethod]';
|
String toString() => 'SystemConfigOAuthDto[autoLaunch=$autoLaunch, autoRegister=$autoRegister, buttonText=$buttonText, clientId=$clientId, clientSecret=$clientSecret, defaultStorageQuota=$defaultStorageQuota, enabled=$enabled, issuerUrl=$issuerUrl, mobileOverrideEnabled=$mobileOverrideEnabled, mobileRedirectUri=$mobileRedirectUri, profileSigningAlgorithm=$profileSigningAlgorithm, roleClaim=$roleClaim, scope=$scope, signingAlgorithm=$signingAlgorithm, storageLabelClaim=$storageLabelClaim, storageQuotaClaim=$storageQuotaClaim, timeout=$timeout, tokenEndpointAuthMethod=$tokenEndpointAuthMethod]';
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final json = <String, dynamic>{};
|
final json = <String, dynamic>{};
|
||||||
@@ -129,6 +134,7 @@ class SystemConfigOAuthDto {
|
|||||||
json[r'mobileOverrideEnabled'] = this.mobileOverrideEnabled;
|
json[r'mobileOverrideEnabled'] = this.mobileOverrideEnabled;
|
||||||
json[r'mobileRedirectUri'] = this.mobileRedirectUri;
|
json[r'mobileRedirectUri'] = this.mobileRedirectUri;
|
||||||
json[r'profileSigningAlgorithm'] = this.profileSigningAlgorithm;
|
json[r'profileSigningAlgorithm'] = this.profileSigningAlgorithm;
|
||||||
|
json[r'roleClaim'] = this.roleClaim;
|
||||||
json[r'scope'] = this.scope;
|
json[r'scope'] = this.scope;
|
||||||
json[r'signingAlgorithm'] = this.signingAlgorithm;
|
json[r'signingAlgorithm'] = this.signingAlgorithm;
|
||||||
json[r'storageLabelClaim'] = this.storageLabelClaim;
|
json[r'storageLabelClaim'] = this.storageLabelClaim;
|
||||||
@@ -158,6 +164,7 @@ class SystemConfigOAuthDto {
|
|||||||
mobileOverrideEnabled: mapValueOfType<bool>(json, r'mobileOverrideEnabled')!,
|
mobileOverrideEnabled: mapValueOfType<bool>(json, r'mobileOverrideEnabled')!,
|
||||||
mobileRedirectUri: mapValueOfType<String>(json, r'mobileRedirectUri')!,
|
mobileRedirectUri: mapValueOfType<String>(json, r'mobileRedirectUri')!,
|
||||||
profileSigningAlgorithm: mapValueOfType<String>(json, r'profileSigningAlgorithm')!,
|
profileSigningAlgorithm: mapValueOfType<String>(json, r'profileSigningAlgorithm')!,
|
||||||
|
roleClaim: mapValueOfType<String>(json, r'roleClaim')!,
|
||||||
scope: mapValueOfType<String>(json, r'scope')!,
|
scope: mapValueOfType<String>(json, r'scope')!,
|
||||||
signingAlgorithm: mapValueOfType<String>(json, r'signingAlgorithm')!,
|
signingAlgorithm: mapValueOfType<String>(json, r'signingAlgorithm')!,
|
||||||
storageLabelClaim: mapValueOfType<String>(json, r'storageLabelClaim')!,
|
storageLabelClaim: mapValueOfType<String>(json, r'storageLabelClaim')!,
|
||||||
@@ -222,6 +229,7 @@ class SystemConfigOAuthDto {
|
|||||||
'mobileOverrideEnabled',
|
'mobileOverrideEnabled',
|
||||||
'mobileRedirectUri',
|
'mobileRedirectUri',
|
||||||
'profileSigningAlgorithm',
|
'profileSigningAlgorithm',
|
||||||
|
'roleClaim',
|
||||||
'scope',
|
'scope',
|
||||||
'signingAlgorithm',
|
'signingAlgorithm',
|
||||||
'storageLabelClaim',
|
'storageLabelClaim',
|
||||||
|
|||||||
@@ -89,6 +89,18 @@ void main() {
|
|||||||
.thenAnswer(successHandler);
|
.thenAnswer(successHandler);
|
||||||
when(() => mockSyncStreamRepo.deleteMemoryAssetsV1(any()))
|
when(() => mockSyncStreamRepo.deleteMemoryAssetsV1(any()))
|
||||||
.thenAnswer(successHandler);
|
.thenAnswer(successHandler);
|
||||||
|
when(
|
||||||
|
() => mockSyncStreamRepo.updateStacksV1(
|
||||||
|
any(),
|
||||||
|
debugLabel: any(named: 'debugLabel'),
|
||||||
|
),
|
||||||
|
).thenAnswer(successHandler);
|
||||||
|
when(
|
||||||
|
() => mockSyncStreamRepo.deleteStacksV1(
|
||||||
|
any(),
|
||||||
|
debugLabel: any(named: 'debugLabel'),
|
||||||
|
),
|
||||||
|
).thenAnswer(successHandler);
|
||||||
|
|
||||||
sut = SyncStreamService(
|
sut = SyncStreamService(
|
||||||
syncApiRepository: mockSyncApiRepo,
|
syncApiRepository: mockSyncApiRepo,
|
||||||
|
|||||||
@@ -28,11 +28,11 @@ function dart {
|
|||||||
|
|
||||||
function typescript {
|
function typescript {
|
||||||
npx --yes oazapfts --optimistic --argumentStyle=object --useEnumType immich-openapi-specs.json typescript-sdk/src/fetch-client.ts
|
npx --yes oazapfts --optimistic --argumentStyle=object --useEnumType immich-openapi-specs.json typescript-sdk/src/fetch-client.ts
|
||||||
pnpm --filter @immich/sdk install --frozen-lockfile && pnpm --filter @immich/sdk build
|
npm --prefix typescript-sdk ci && npm --prefix typescript-sdk run build
|
||||||
}
|
}
|
||||||
|
|
||||||
# requires server to be built
|
# requires server to be built
|
||||||
(cd .. && pnpm --filter immich install && pnpm --filter immich build && pnpm --filter immich sync:open-api)
|
npm run sync:open-api --prefix=../server
|
||||||
|
|
||||||
if [[ $1 == 'dart' ]]; then
|
if [[ $1 == 'dart' ]]; then
|
||||||
dart
|
dart
|
||||||
|
|||||||
@@ -14654,6 +14654,9 @@
|
|||||||
"profileSigningAlgorithm": {
|
"profileSigningAlgorithm": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"roleClaim": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"scope": {
|
"scope": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -14690,6 +14693,7 @@
|
|||||||
"mobileOverrideEnabled",
|
"mobileOverrideEnabled",
|
||||||
"mobileRedirectUri",
|
"mobileRedirectUri",
|
||||||
"profileSigningAlgorithm",
|
"profileSigningAlgorithm",
|
||||||
|
"roleClaim",
|
||||||
"scope",
|
"scope",
|
||||||
"signingAlgorithm",
|
"signingAlgorithm",
|
||||||
"storageLabelClaim",
|
"storageLabelClaim",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ A TypeScript SDK for interfacing with the [Immich](https://immich.app/) API.
|
|||||||
## Install
|
## Install
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm i --save @immich/sdk
|
npm i --save @immich/sdk
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
"name": "@immich/sdk",
|
||||||
|
"version": "1.135.3",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "@immich/sdk",
|
||||||
|
"version": "1.135.3",
|
||||||
|
"license": "GNU Affero General Public License version 3",
|
||||||
|
"dependencies": {
|
||||||
|
"@oazapfts/runtime": "^1.0.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^22.15.33",
|
||||||
|
"typescript": "^5.3.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@oazapfts/runtime": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@oazapfts/runtime/-/runtime-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-7t6C2shug/6tZhQgkCa532oTYBLEnbASV/i1SG1rH2GB4h3aQQujYciYSPT92hvN4IwTe8S2hPkN/6iiOyTlCg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "22.15.34",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.34.tgz",
|
||||||
|
"integrity": "sha512-8Y6E5WUupYy1Dd0II32BsWAx5MWdcnRd8L84Oys3veg1YrYtNtzgO4CFhiBg6MDSjk7Ay36HYOnU7/tuOzIzcw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~6.21.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "5.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||||
|
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "6.21.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||||
|
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,5 @@
|
|||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "22.17.0"
|
"node": "22.17.0"
|
||||||
},
|
}
|
||||||
"packageManager": "pnpm@10.12.3+sha512.467df2c586056165580ad6dfb54ceaad94c5a30f80893ebdec5a44c5aa73c205ae4a5bb9d5ed6bb84ea7c249ece786642bbb49d06a307df218d03da41c317417"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1398,6 +1398,7 @@ export type SystemConfigOAuthDto = {
|
|||||||
mobileOverrideEnabled: boolean;
|
mobileOverrideEnabled: boolean;
|
||||||
mobileRedirectUri: string;
|
mobileRedirectUri: string;
|
||||||
profileSigningAlgorithm: string;
|
profileSigningAlgorithm: string;
|
||||||
|
roleClaim: string;
|
||||||
scope: string;
|
scope: string;
|
||||||
signingAlgorithm: string;
|
signingAlgorithm: string;
|
||||||
storageLabelClaim: string;
|
storageLabelClaim: string;
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "immich-monorepo",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"description": "monorepo for immich and friends",
|
|
||||||
"private": true,
|
|
||||||
"packageManager": "pnpm@10.12.4",
|
|
||||||
"engines": {
|
|
||||||
"pnpm": ">=10.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Generated
-25306
File diff suppressed because it is too large
Load Diff
@@ -1,79 +0,0 @@
|
|||||||
packages:
|
|
||||||
- cli
|
|
||||||
- docs
|
|
||||||
- e2e
|
|
||||||
- open-api/typescript-sdk
|
|
||||||
- server
|
|
||||||
- web
|
|
||||||
- .github
|
|
||||||
|
|
||||||
|
|
||||||
ignoredBuiltDependencies:
|
|
||||||
- '@nestjs/core'
|
|
||||||
- '@scarf/scarf'
|
|
||||||
- '@swc/core'
|
|
||||||
- bcrypt
|
|
||||||
- canvas
|
|
||||||
- core-js
|
|
||||||
- core-js-pure
|
|
||||||
- cpu-features
|
|
||||||
- es5-ext
|
|
||||||
- esbuild
|
|
||||||
- msgpackr-extract
|
|
||||||
- postman-code-generators
|
|
||||||
- protobufjs
|
|
||||||
- ssh2
|
|
||||||
- utimes
|
|
||||||
|
|
||||||
onlyBuiltDependencies:
|
|
||||||
- sharp
|
|
||||||
- '@tailwindcss/oxide'
|
|
||||||
|
|
||||||
overrides:
|
|
||||||
canvas: 2.11.2
|
|
||||||
sharp: ^0.34.2
|
|
||||||
'@img/sharp-darwin-arm64': '-'
|
|
||||||
'@img/sharp-darwin-x64': '-'
|
|
||||||
'@img/sharp-libvips-darwin-arm64': '-'
|
|
||||||
'@img/sharp-libvips-darwin-x64': '-'
|
|
||||||
'@img/sharp-libvips-linux-arm': '-'
|
|
||||||
'@img/sharp-libvips-linux-ppc64': '-'
|
|
||||||
'@img/sharp-libvips-linux-s390x': '-'
|
|
||||||
'@img/sharp-libvips-linuxmusl-arm64': '-'
|
|
||||||
'@img/sharp-linux-arm64': '-'
|
|
||||||
'@img/sharp-linux-s390x': '-'
|
|
||||||
'@img/sharp-linuxmusl-arm64': '-'
|
|
||||||
'@img/sharp-wasm32': '-'
|
|
||||||
'@img/sharp-win32-arm64': '-'
|
|
||||||
'@img/sharp-win32-ia32': '-'
|
|
||||||
'@img/sharp-win32-x64': '-'
|
|
||||||
|
|
||||||
packageExtensions:
|
|
||||||
nestjs-kysely:
|
|
||||||
dependencies:
|
|
||||||
tslib: '*'
|
|
||||||
nestjs-otel:
|
|
||||||
dependencies:
|
|
||||||
tslib: '*'
|
|
||||||
'@photo-sphere-viewer/equirectangular-video-adapter':
|
|
||||||
dependencies:
|
|
||||||
three: '*'
|
|
||||||
'@photo-sphere-viewer/video-plugin':
|
|
||||||
dependencies:
|
|
||||||
three: '*'
|
|
||||||
sharp:
|
|
||||||
dependencies:
|
|
||||||
node-addon-api: '*'
|
|
||||||
node-gyp: '*'
|
|
||||||
'@immich/ui':
|
|
||||||
dependencies:
|
|
||||||
tailwindcss: ^4.1.11
|
|
||||||
tailwind-variants:
|
|
||||||
dependencies:
|
|
||||||
tailwindcss: ^4.1.11
|
|
||||||
|
|
||||||
dedupePeerDependents: false
|
|
||||||
packageImportMethod: hardlink
|
|
||||||
preferWorkspacePackages: true
|
|
||||||
shamefullyHoist: false
|
|
||||||
injectWorkspacePackages: true
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
src
|
|
||||||
tsconfig*
|
|
||||||
eslint*
|
|
||||||
pnpm*
|
|
||||||
coverage
|
|
||||||
+75
-99
@@ -1,68 +1,52 @@
|
|||||||
# dev build
|
# dev build
|
||||||
FROM ghcr.io/immich-app/base-server-dev:commit-95312641aeba3ecaab9d73d05ca5d9746ab633b6 AS dev
|
FROM ghcr.io/immich-app/base-server-dev:202505131114@sha256:cf4507bbbf307e9b6d8ee9418993321f2b85867da8ce14d0a20ccaf9574cb995 AS dev
|
||||||
|
|
||||||
ENV COREPACK_ENABLE_AUTO_PIN=0 \
|
|
||||||
COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
|
|
||||||
CI=1
|
|
||||||
|
|
||||||
RUN echo "umask 000" >> /etc/profile && \
|
|
||||||
echo "umask 000" >> /etc/bash.bashrc && \
|
|
||||||
umask 000 && \
|
|
||||||
corepack enable && \
|
|
||||||
corepack install -g pnpm && \
|
|
||||||
apt-get update && \
|
|
||||||
apt-get install --no-install-recommends -yqq tini make && \
|
|
||||||
rm -rf /var/lib/apt/lists && \
|
|
||||||
rm -rf /usr/src/app && \
|
|
||||||
mkdir -p /usr/src/app
|
|
||||||
|
|
||||||
|
RUN apt-get install --no-install-recommends -yqq tini
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
COPY ./server ./server/
|
COPY server/package.json server/package-lock.json ./
|
||||||
COPY Makefile ./package* ./pnpm* ./
|
RUN npm ci && \
|
||||||
|
# exiftool-vendored.pl, sharp-linux-x64 and sharp-linux-arm64 are the only ones we need
|
||||||
RUN umask 000 && mkdir -p /buildcache/pnpm-store && \
|
# they're marked as optional dependencies, so we need to copy them manually after pruning
|
||||||
pnpm config set store-dir /buildcache/pnpm-store && \
|
rm -rf node_modules/@img/sharp-libvips* && \
|
||||||
SHARP_IGNORE_GLOBAL_LIBVIPS=true make setup-server-dev
|
rm -rf node_modules/@img/sharp-linuxmusl-x64
|
||||||
|
|
||||||
ENV PATH="${PATH}:/usr/src/app/bin" \
|
ENV PATH="${PATH}:/usr/src/app/bin" \
|
||||||
IMMICH_ENV=development \
|
IMMICH_ENV=development \
|
||||||
NVIDIA_DRIVER_CAPABILITIES=all
|
NVIDIA_DRIVER_CAPABILITIES=all \
|
||||||
|
NVIDIA_VISIBLE_DEVICES=all
|
||||||
ENTRYPOINT ["tini", "--", "/bin/sh"]
|
ENTRYPOINT ["tini", "--", "/bin/sh"]
|
||||||
|
|
||||||
FROM dev AS dev-container-server
|
FROM dev AS dev-container-server
|
||||||
|
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install sudo inetutils-ping openjdk-11-jre-headless \
|
|
||||||
vim nano -y --no-install-recommends --fix-missing && \
|
|
||||||
rm -rf /var/lib/apt/lists
|
|
||||||
RUN usermod -aG sudo node && \
|
|
||||||
echo "node ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers && \
|
|
||||||
# create workspaces dirs
|
|
||||||
mkdir -p /workspaces/immich && \
|
|
||||||
mkdir /immich-devcontainer;
|
|
||||||
|
|
||||||
WORKDIR /workspaces/immich
|
|
||||||
# Remove app dir from dev container
|
|
||||||
RUN rm -rf /usr/src/app
|
RUN rm -rf /usr/src/app
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install sudo inetutils-ping openjdk-11-jre-headless \
|
||||||
|
vim nano \
|
||||||
|
-y --no-install-recommends --fix-missing
|
||||||
|
|
||||||
COPY --chmod=777 ../.devcontainer/server/*.sh /immich-devcontainer/
|
RUN usermod -aG sudo node
|
||||||
|
RUN echo "node ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
||||||
|
RUN mkdir -p /workspaces/immich
|
||||||
|
RUN chown node -R /workspaces
|
||||||
|
COPY --chown=node:node --chmod=777 ../.devcontainer/server/*.sh /immich-devcontainer/
|
||||||
|
|
||||||
|
USER node
|
||||||
|
COPY --chown=node:node .. /tmp/create-dep-cache/
|
||||||
|
WORKDIR /tmp/create-dep-cache
|
||||||
|
RUN make ci-all && rm -rf /tmp/create-dep-cache
|
||||||
|
|
||||||
WORKDIR /workspaces/immich
|
|
||||||
|
|
||||||
FROM dev-container-server AS dev-container-mobile
|
FROM dev-container-server AS dev-container-mobile
|
||||||
|
|
||||||
USER root
|
USER root
|
||||||
# Enable multiarch for arm64 if necessary
|
# Enable multiarch for arm64 if necessary
|
||||||
RUN if [ "$(dpkg --print-architecture)" = "arm64" ]; then \
|
RUN if [ "$(dpkg --print-architecture)" = "arm64" ]; then \
|
||||||
sudo dpkg --add-architecture amd64 && \
|
dpkg --add-architecture amd64 && \
|
||||||
sudo apt-get install -y --no-install-recommends \
|
apt-get update && \
|
||||||
qemu-user-static \
|
apt-get install -y --no-install-recommends \
|
||||||
libc6:amd64 \
|
qemu-user-static \
|
||||||
libstdc++6:amd64 \
|
libc6:amd64 \
|
||||||
libgcc1:amd64 && \
|
libstdc++6:amd64 \
|
||||||
rm -rf /var/lib/apt/lists; \
|
libgcc1:amd64; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Flutter SDK
|
# Flutter SDK
|
||||||
# https://flutter.dev/docs/development/tools/sdk/releases?tab=linux
|
# https://flutter.dev/docs/development/tools/sdk/releases?tab=linux
|
||||||
@@ -78,68 +62,60 @@ RUN mkdir -p ${FLUTTER_HOME} \
|
|||||||
&& rm flutter.tar.xz \
|
&& rm flutter.tar.xz \
|
||||||
&& chown -R node ${FLUTTER_HOME}
|
&& chown -R node ${FLUTTER_HOME}
|
||||||
|
|
||||||
RUN wget -qO- https://dcm.dev/pgp-key.public | gpg --dearmor -o /usr/share/keyrings/dcm.gpg \
|
USER node
|
||||||
&& echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | tee /etc/apt/sources.list.d/dart_stable.list \
|
RUN sudo apt-get update \
|
||||||
&& apt-get update \
|
&& wget -qO- https://dcm.dev/pgp-key.public | sudo gpg --dearmor -o /usr/share/keyrings/dcm.gpg \
|
||||||
&& apt-get install dcm -y && \
|
&& echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list \
|
||||||
&& rm -rf /var/lib/apt/lists
|
&& sudo apt-get update \
|
||||||
|
&& sudo apt-get install dcm -y
|
||||||
|
|
||||||
COPY --chmod=777 ../.devcontainer/mobile/container-mobile-post-create.sh /immich-devcontainer/container-mobile-post-create.sh
|
COPY --chmod=777 ../.devcontainer/mobile/container-mobile-post-create.sh /immich-devcontainer/container-mobile-post-create.sh
|
||||||
|
|
||||||
RUN dart --disable-analytics
|
RUN dart --disable-analytics
|
||||||
USER node
|
|
||||||
|
|
||||||
# server production build
|
FROM dev AS prod
|
||||||
FROM dev AS server-prod
|
|
||||||
|
|
||||||
RUN pnpm --filter immich install --frozen-lockfile && \
|
COPY server .
|
||||||
pnpm --filter immich build && \
|
RUN npm run build
|
||||||
pnpm --filter immich --prod --no-optional deploy /output/server-pruned
|
RUN npm prune --omit=dev --omit=optional
|
||||||
|
COPY --from=dev /usr/src/app/node_modules/@img ./node_modules/@img
|
||||||
|
COPY --from=dev /usr/src/app/node_modules/exiftool-vendored.pl ./node_modules/exiftool-vendored.pl
|
||||||
|
|
||||||
# web production build
|
# web build
|
||||||
FROM dev AS web-prod
|
FROM node:22.16.0-alpine3.20@sha256:2289fb1fba0f4633b08ec47b94a89c7e20b829fc5679f9b7b298eaa2f1ed8b7e AS web
|
||||||
|
|
||||||
COPY ./open-api/typescript-sdk ./open-api/typescript-sdk/
|
WORKDIR /usr/src/open-api/typescript-sdk
|
||||||
COPY ./web ./web/
|
COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./
|
||||||
COPY ./i18n ./i18n/
|
RUN npm ci
|
||||||
RUN pnpm install --filter @immich/sdk --filter immich-web --frozen-lockfile && \
|
COPY open-api/typescript-sdk/ ./
|
||||||
pnpm --filter @immich/sdk build && \
|
RUN npm run build
|
||||||
pnpm --filter immich-web build
|
|
||||||
|
|
||||||
FROM dev AS cli-prod
|
|
||||||
|
|
||||||
COPY ./cli ./cli/
|
|
||||||
COPY ./open-api/typescript-sdk ./open-api/typescript-sdk/
|
|
||||||
RUN pnpm install --filter @immich/sdk --filter @immich/cli --frozen-lockfile && \
|
|
||||||
pnpm --filter @immich/sdk build && \
|
|
||||||
pnpm --filter @immich/cli build && \
|
|
||||||
pnpm --filter @immich/cli --prod --no-optional deploy /output/cli-pruned
|
|
||||||
|
|
||||||
# prod build
|
|
||||||
FROM ghcr.io/immich-app/base-server-prod:commit-95312641aeba3ecaab9d73d05ca5d9746ab633b6
|
|
||||||
|
|
||||||
ENV NODE_ENV=production \
|
|
||||||
NVIDIA_DRIVER_CAPABILITIES=all \
|
|
||||||
NVIDIA_VISIBLE_DEVICES=all \
|
|
||||||
COREPACK_ENABLE_DOWNLOAD_PROMPT=0
|
|
||||||
|
|
||||||
RUN corepack enable && \
|
|
||||||
corepack install -g pnpm && \
|
|
||||||
mkdir -p /usr/src/app/upload
|
|
||||||
|
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
COPY --from=server-prod /output/server-pruned/dist ./dist
|
COPY web/package*.json web/svelte.config.js ./
|
||||||
COPY --from=server-prod /output/server-pruned/bin ./bin
|
RUN npm ci
|
||||||
COPY --from=server-prod /output/server-pruned/package.json ./
|
COPY web ./
|
||||||
COPY --from=server-prod /output/server-pruned/node_modules/ ./node_modules
|
COPY i18n ../i18n
|
||||||
COPY --from=web-prod /usr/src/app/web/build /build/www
|
RUN npm run build
|
||||||
COPY --from=cli-prod /output/cli-pruned ./cli
|
|
||||||
RUN ln -S ./cli/bin/immich /usr/src/app/bin/immich
|
|
||||||
COPY server/resources ./resources/
|
# prod build
|
||||||
COPY server/start*.sh docker/scripts/get-cpus.sh ./
|
FROM ghcr.io/immich-app/base-server-prod:202505061115@sha256:9971d3a089787f0bd01f4682141d3665bcf5efb3e101a88e394ffd25bee4eedb
|
||||||
|
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
ENV NODE_ENV=production \
|
||||||
|
NVIDIA_DRIVER_CAPABILITIES=all \
|
||||||
|
NVIDIA_VISIBLE_DEVICES=all
|
||||||
|
COPY --from=prod /usr/src/app/node_modules ./node_modules
|
||||||
|
COPY --from=prod /usr/src/app/dist ./dist
|
||||||
|
COPY --from=prod /usr/src/app/bin ./bin
|
||||||
|
COPY --from=web /usr/src/app/build /build/www
|
||||||
|
COPY server/resources resources
|
||||||
|
COPY server/package.json server/package-lock.json ./
|
||||||
|
COPY server/start*.sh ./
|
||||||
|
COPY "docker/scripts/get-cpus.sh" ./
|
||||||
|
RUN npm install -g @immich/cli && npm cache clean --force
|
||||||
COPY LICENSE /licenses/LICENSE.txt
|
COPY LICENSE /licenses/LICENSE.txt
|
||||||
COPY LICENSE /LICENSE
|
COPY LICENSE /LICENSE
|
||||||
|
|
||||||
ENV PATH="${PATH}:/usr/src/app/bin"
|
ENV PATH="${PATH}:/usr/src/app/bin"
|
||||||
|
|
||||||
ARG BUILD_ID
|
ARG BUILD_ID
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
cd /usr/src/app || exit
|
node /usr/src/app/node_modules/.bin/nest start --debug "0.0.0.0:9230" --watch -- "$@"
|
||||||
FROZEN=1 make install-server
|
|
||||||
cd /usr/src/app/server || exit
|
|
||||||
pnpm exec nest start --debug "0.0.0.0:9230" --watch -- "$@"
|
|
||||||
|
|||||||
Generated
+18496
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user