Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 053dd490b4 | |||
| f4f81341da | |||
| 3e66913cf8 | |||
| 504309eff5 | |||
| b44abf5b4b | |||
| c76e8da173 | |||
| 9cc2189ef7 | |||
| 6b87efe7a3 | |||
| 7b75da1f10 | |||
| a7559f0691 | |||
| 6f2f295cf3 | |||
| 523fe5bef7 | |||
| 77a362f0c0 | |||
| 5f5308631e | |||
| 004c2f2496 | |||
| e2dfbd66c3 | |||
| de756d9497 | |||
| 103b83d2d6 | |||
| f54cfa7a5a | |||
| ed5b260eeb | |||
| 8923d5b0a3 | |||
| 2f3d4e15d2 | |||
| c9bcae813b | |||
| bddb43e1d4 | |||
| 176656b5f4 | |||
| b3d080f6e8 |
@@ -1,2 +0,0 @@
|
|||||||
.env
|
|
||||||
library
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
ARG BASEIMAGE=mcr.microsoft.com/devcontainers/typescript-node:22@sha256:7c2e711a4f7b02f32d2da16192d5e05aa7c95279be4ce889cff5df316f251c1d
|
|
||||||
FROM ${BASEIMAGE}
|
|
||||||
|
|
||||||
# Flutter SDK
|
|
||||||
# https://flutter.dev/docs/development/tools/sdk/releases?tab=linux
|
|
||||||
ENV FLUTTER_CHANNEL="stable"
|
|
||||||
ENV FLUTTER_VERSION="3.29.3"
|
|
||||||
ENV FLUTTER_HOME=/flutter
|
|
||||||
ENV PATH=${PATH}:${FLUTTER_HOME}/bin
|
|
||||||
|
|
||||||
# Flutter SDK
|
|
||||||
RUN mkdir -p ${FLUTTER_HOME} \
|
|
||||||
&& curl -C - --output flutter.tar.xz https://storage.googleapis.com/flutter_infra_release/releases/${FLUTTER_CHANNEL}/linux/flutter_linux_${FLUTTER_VERSION}-${FLUTTER_CHANNEL}.tar.xz \
|
|
||||||
&& tar -xf flutter.tar.xz --strip-components=1 -C ${FLUTTER_HOME} \
|
|
||||||
&& rm flutter.tar.xz \
|
|
||||||
&& chown -R 1000:1000 ${FLUTTER_HOME}
|
|
||||||
@@ -1,26 +1,67 @@
|
|||||||
{
|
{
|
||||||
"name": "Immich",
|
"name": "Immich - Backend, Frontend and ML",
|
||||||
"service": "immich-devcontainer",
|
"service": "immich-server",
|
||||||
|
"runServices": [
|
||||||
|
"immich-server",
|
||||||
|
"redis",
|
||||||
|
"database",
|
||||||
|
"immich-machine-learning"
|
||||||
|
],
|
||||||
"dockerComposeFile": [
|
"dockerComposeFile": [
|
||||||
"docker-compose.yml",
|
"../docker/docker-compose.dev.yml",
|
||||||
"../docker/docker-compose.dev.yml"
|
"./server/container-compose-overrides.yml"
|
||||||
],
|
],
|
||||||
"customizations": {
|
"customizations": {
|
||||||
"vscode": {
|
"vscode": {
|
||||||
"extensions": [
|
"extensions": [
|
||||||
"Dart-Code.dart-code",
|
|
||||||
"Dart-Code.flutter",
|
|
||||||
"dbaeumer.vscode-eslint",
|
"dbaeumer.vscode-eslint",
|
||||||
"dcmdev.dcm-vscode-extension",
|
|
||||||
"esbenp.prettier-vscode",
|
"esbenp.prettier-vscode",
|
||||||
"svelte.svelte-vscode"
|
"svelte.svelte-vscode",
|
||||||
|
"ms-vscode-remote.remote-containers",
|
||||||
|
"foxundermoon.shell-format",
|
||||||
|
"timonwong.shellcheck",
|
||||||
|
"rvest.vs-code-prettier-eslint",
|
||||||
|
"bluebrown.yamlfmt",
|
||||||
|
"vkrishna04.cspell-sync",
|
||||||
|
"vitest.explorer",
|
||||||
|
"ms-playwright.playwright",
|
||||||
|
"ms-azuretools.vscode-docker"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"forwardPorts": [],
|
"forwardPorts": [3000, 9231, 9230, 2283],
|
||||||
"initializeCommand": "bash .devcontainer/scripts/initializeCommand.sh",
|
"portsAttributes": {
|
||||||
"onCreateCommand": "bash .devcontainer/scripts/onCreateCommand.sh",
|
"3000": {
|
||||||
|
"label": "Immich - Frontend HTTP",
|
||||||
|
"description": "The frontend of the Immich project",
|
||||||
|
"onAutoForward": "openBrowserOnce"
|
||||||
|
},
|
||||||
|
"2283": {
|
||||||
|
"label": "Immich - API Server - HTTP",
|
||||||
|
"description": "The API server of the Immich project"
|
||||||
|
},
|
||||||
|
"9231": {
|
||||||
|
"label": "Immich - API Server - DEBUG",
|
||||||
|
"description": "The API server of the Immich project"
|
||||||
|
},
|
||||||
|
"9230": {
|
||||||
|
"label": "Immich - Workers - DEBUG",
|
||||||
|
"description": "The workers of the Immich project"
|
||||||
|
}
|
||||||
|
},
|
||||||
"overrideCommand": true,
|
"overrideCommand": true,
|
||||||
"workspaceFolder": "/immich",
|
"workspaceFolder": "/workspaces/immich",
|
||||||
"remoteUser": "node"
|
"remoteUser": "node",
|
||||||
|
"userEnvProbe": "loginInteractiveShell",
|
||||||
|
"remoteEnv": {
|
||||||
|
// The location where your uploaded files are stored
|
||||||
|
"UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./Library}",
|
||||||
|
// Connection secret for postgres. You should change it to a random password
|
||||||
|
// Please use only the characters `A-Za-z0-9`, without special characters or spaces
|
||||||
|
"DB_PASSWORD": "${localEnv:DB_PASSWORD:postgres}",
|
||||||
|
// The database username
|
||||||
|
"DB_USERNAME": "${localEnv:DB_USERNAME:postgres}",
|
||||||
|
// The database name
|
||||||
|
"DB_DATABASE_NAME": "${localEnv:DB_DATABASE_NAME:immich}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
services:
|
|
||||||
immich-devcontainer:
|
|
||||||
build:
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
extra_hosts:
|
|
||||||
- 'host.docker.internal:host-gateway'
|
|
||||||
volumes:
|
|
||||||
- ..:/immich:cached
|
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
services:
|
||||||
|
immich-server:
|
||||||
|
build:
|
||||||
|
target: dev-container-mobile
|
||||||
|
environment:
|
||||||
|
- IMMICH_SERVER_URL=http://127.0.0.1:2283/
|
||||||
|
volumes: !override # bind mount host to /workspaces/immich
|
||||||
|
- ..:/workspaces/immich
|
||||||
|
- cli_node_modules:/workspaces/immich/cli/node_modules
|
||||||
|
- e2e_node_modules:/workspaces/immich/e2e/node_modules
|
||||||
|
- open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules
|
||||||
|
- server_node_modules:/workspaces/immich/server/node_modules
|
||||||
|
- web_node_modules:/workspaces/immich/web/node_modules
|
||||||
|
- ${UPLOAD_LOCATION}/photos:/workspaces/immich/server/upload
|
||||||
|
- ${UPLOAD_LOCATION}/photos/upload:/workspaces/immich/server/upload/upload
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
|
||||||
|
database:
|
||||||
|
volumes:
|
||||||
|
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
# Node modules for each service to avoid conflicts and ensure consistent dependencies
|
||||||
|
cli_node_modules:
|
||||||
|
e2e_node_modules:
|
||||||
|
open_api_node_modules:
|
||||||
|
server_node_modules:
|
||||||
|
web_node_modules:
|
||||||
|
|
||||||
|
# UPLOAD_LOCATION must be set to a absolute path or vol-upload
|
||||||
|
vol-upload:
|
||||||
|
|
||||||
|
# DB_DATA_LOCATION must be set to a absolute path or vol-database
|
||||||
|
vol-database:
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"name": "Immich - Mobile",
|
||||||
|
"service": "immich-server",
|
||||||
|
"runServices": [
|
||||||
|
"immich-server",
|
||||||
|
"redis",
|
||||||
|
"database",
|
||||||
|
"immich-machine-learning"
|
||||||
|
],
|
||||||
|
"dockerComposeFile": [
|
||||||
|
"../../docker/docker-compose.dev.yml",
|
||||||
|
"./container-compose-overrides.yml"
|
||||||
|
],
|
||||||
|
"customizations": {
|
||||||
|
"vscode": {
|
||||||
|
"extensions": [
|
||||||
|
"Dart-Code.dart-code",
|
||||||
|
"Dart-Code.flutter",
|
||||||
|
"dcmdev.dcm-vscode-extension",
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"svelte.svelte-vscode",
|
||||||
|
"ms-vscode-remote.remote-containers",
|
||||||
|
"foxundermoon.shell-format",
|
||||||
|
"timonwong.shellcheck",
|
||||||
|
"rvest.vs-code-prettier-eslint",
|
||||||
|
"bluebrown.yamlfmt",
|
||||||
|
"vkrishna04.cspell-sync",
|
||||||
|
"vitest.explorer",
|
||||||
|
"ms-playwright.playwright",
|
||||||
|
"ms-azuretools.vscode-docker"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"forwardPorts": [],
|
||||||
|
"overrideCommand": true,
|
||||||
|
"workspaceFolder": "/workspaces/immich",
|
||||||
|
"remoteUser": "node",
|
||||||
|
"userEnvProbe": "loginInteractiveShell",
|
||||||
|
"remoteEnv": {
|
||||||
|
// The location where your uploaded files are stored
|
||||||
|
"UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./Library}",
|
||||||
|
// Connection secret for postgres. You should change it to a random password
|
||||||
|
// Please use only the characters `A-Za-z0-9`, without special characters or spaces
|
||||||
|
"DB_PASSWORD": "${localEnv:DB_PASSWORD:postgres}",
|
||||||
|
// The database username
|
||||||
|
"DB_USERNAME": "${localEnv:DB_USERNAME:postgres}",
|
||||||
|
// The database name
|
||||||
|
"DB_DATABASE_NAME": "${localEnv:DB_DATABASE_NAME:immich}"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# If .env file does not exist, create it by copying example.env from the docker folder
|
|
||||||
if [ ! -f ".devcontainer/.env" ]; then
|
|
||||||
cp docker/example.env .devcontainer/.env
|
|
||||||
fi
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Enable multiarch for arm64 if necessary
|
|
||||||
if [ "$(dpkg --print-architecture)" = "arm64" ]; then
|
|
||||||
sudo dpkg --add-architecture amd64 && \
|
|
||||||
sudo apt-get update && \
|
|
||||||
sudo apt-get install -y --no-install-recommends \
|
|
||||||
qemu-user-static \
|
|
||||||
libc6:amd64 \
|
|
||||||
libstdc++6:amd64 \
|
|
||||||
libgcc1:amd64
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install DCM
|
|
||||||
wget -qO- https://dcm.dev/pgp-key.public | sudo gpg --dearmor -o /usr/share/keyrings/dcm.gpg
|
|
||||||
sudo echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list
|
|
||||||
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install dcm
|
|
||||||
|
|
||||||
dart --disable-analytics
|
|
||||||
|
|
||||||
# Install immich
|
|
||||||
cd /immich || exit
|
|
||||||
make install-all
|
|
||||||
Executable
+57
@@ -0,0 +1,57 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
export IMMICH_PORT="${DEV_SERVER_PORT:-2283}"
|
||||||
|
export DEV_PORT="${DEV_PORT:-3000}"
|
||||||
|
|
||||||
|
# search for immich directory inside workspace.
|
||||||
|
# /workspaces/immich is the bind mount, but other directories can be mounted if runing
|
||||||
|
# Devcontainer: Clone [repository|pull request] in container volumne
|
||||||
|
WORKSPACES_DIR="/workspaces"
|
||||||
|
IMMICH_DIR="$WORKSPACES_DIR/immich"
|
||||||
|
|
||||||
|
# Find directories excluding /workspaces/immich
|
||||||
|
mapfile -t other_dirs < <(find "$WORKSPACES_DIR" -mindepth 1 -maxdepth 1 -type d ! -path "$IMMICH_DIR" ! -name ".*")
|
||||||
|
|
||||||
|
if [ ${#other_dirs[@]} -gt 1 ]; then
|
||||||
|
echo "Error: More than one directory found in $WORKSPACES_DIR other than $IMMICH_DIR."
|
||||||
|
exit 1
|
||||||
|
elif [ ${#other_dirs[@]} -eq 1 ]; then
|
||||||
|
export IMMICH_WORKSPACE="${other_dirs[0]}"
|
||||||
|
else
|
||||||
|
export IMMICH_WORKSPACE="$IMMICH_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Found immich workspace in $IMMICH_WORKSPACE"
|
||||||
|
|
||||||
|
run_cmd() {
|
||||||
|
echo "$@"
|
||||||
|
"$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
fix_permissions() {
|
||||||
|
|
||||||
|
echo "Fixing permissions for ${IMMICH_WORKSPACE}"
|
||||||
|
|
||||||
|
run_cmd sudo find "${IMMICH_WORKSPACE}/server/upload" -not -path "${IMMICH_WORKSPACE}/server/upload/postgres/*" -not -path "${IMMICH_WORKSPACE}/server/upload/postgres" -exec chown node {} +
|
||||||
|
|
||||||
|
run_cmd sudo chown node -R "${IMMICH_WORKSPACE}/.vscode" \
|
||||||
|
"${IMMICH_WORKSPACE}/cli/node_modules" \
|
||||||
|
"${IMMICH_WORKSPACE}/e2e/node_modules" \
|
||||||
|
"${IMMICH_WORKSPACE}/open-api/typescript-sdk/node_modules" \
|
||||||
|
"${IMMICH_WORKSPACE}/server/node_modules" \
|
||||||
|
"${IMMICH_WORKSPACE}/server/dist" \
|
||||||
|
"${IMMICH_WORKSPACE}/web/node_modules" \
|
||||||
|
"${IMMICH_WORKSPACE}/web/dist"
|
||||||
|
}
|
||||||
|
|
||||||
|
install_dependencies() {
|
||||||
|
|
||||||
|
echo "Installing dependencies"
|
||||||
|
|
||||||
|
(
|
||||||
|
cd "${IMMICH_WORKSPACE}" || exit 1
|
||||||
|
run_cmd make install-server
|
||||||
|
run_cmd make install-open-api
|
||||||
|
run_cmd make build-open-api
|
||||||
|
run_cmd make install-web
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
services:
|
||||||
|
immich-server:
|
||||||
|
build:
|
||||||
|
target: dev-container-server
|
||||||
|
env_file: !reset []
|
||||||
|
environment:
|
||||||
|
- IMMICH_SERVER_URL=http://127.0.0.1:2283/
|
||||||
|
volumes: !override
|
||||||
|
- ..:/workspaces/immich
|
||||||
|
- cli_node_modules:/workspaces/immich/cli/node_modules
|
||||||
|
- e2e_node_modules:/workspaces/immich/e2e/node_modules
|
||||||
|
- open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules
|
||||||
|
- server_node_modules:/workspaces/immich/server/node_modules
|
||||||
|
- web_node_modules:/workspaces/immich/web/node_modules
|
||||||
|
- ${UPLOAD_LOCATION-./Library}/photos:/workspaces/immich/server/upload
|
||||||
|
- ${UPLOAD_LOCATION-./Library}/photos/upload:/workspaces/immich/server/upload/upload
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
|
||||||
|
immich-web:
|
||||||
|
env_file: !reset []
|
||||||
|
|
||||||
|
immich-machine-learning:
|
||||||
|
env_file: !reset []
|
||||||
|
|
||||||
|
database:
|
||||||
|
env_file: !reset []
|
||||||
|
environment: !override
|
||||||
|
POSTGRES_PASSWORD: ${DB_PASSWORD-postgres}
|
||||||
|
POSTGRES_USER: ${DB_USERNAME-postgres}
|
||||||
|
POSTGRES_DB: ${DB_DATABASE_NAME-immich}
|
||||||
|
POSTGRES_INITDB_ARGS: '--data-checksums'
|
||||||
|
volumes:
|
||||||
|
- ${UPLOAD_LOCATION-./Library}/postgres:/var/lib/postgresql/data
|
||||||
|
|
||||||
|
redis:
|
||||||
|
env_file: !reset []
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
# Node modules for each service to avoid conflicts and ensure consistent dependencies
|
||||||
|
cli_node_modules:
|
||||||
|
e2e_node_modules:
|
||||||
|
open_api_node_modules:
|
||||||
|
server_node_modules:
|
||||||
|
web_node_modules:
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# shellcheck source=common.sh
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
source /immich-devcontainer/container-common.sh
|
||||||
|
|
||||||
|
echo "Starting Nest API Server"
|
||||||
|
|
||||||
|
cd "${IMMICH_WORKSPACE}/server" || (
|
||||||
|
echo workspace not found
|
||||||
|
exit 1
|
||||||
|
)
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
node ./node_modules/.bin/nest start --debug "0.0.0.0:9230" --watch
|
||||||
|
echo " Nest API Server crashed with exit code $?. Respawning in 3s ..."
|
||||||
|
sleep 3
|
||||||
|
done
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# shellcheck source=common.sh
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
source /immich-devcontainer/container-common.sh
|
||||||
|
|
||||||
|
echo "Starting Immich Web Frontend"
|
||||||
|
|
||||||
|
cd "${IMMICH_WORKSPACE}/web" || (
|
||||||
|
echo Workspace not found
|
||||||
|
exit 1
|
||||||
|
)
|
||||||
|
|
||||||
|
until curl --output /dev/null --silent --head --fail "http://127.0.0.1:${IMMICH_PORT}/api/server/config"; do
|
||||||
|
echo 'waiting for api server...'
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
node ./node_modules/.bin/vite dev --host 0.0.0.0 --port "${DEV_PORT}"
|
||||||
|
echo "Web crashed with exit code $?. Respawning in 3s ..."
|
||||||
|
sleep 3
|
||||||
|
done
|
||||||
Executable
+7
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# shellcheck source=common.sh
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
source /immich-devcontainer/container-common.sh
|
||||||
|
|
||||||
|
fix_permissions
|
||||||
|
install_dependencies
|
||||||
Vendored
+72
@@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "Fix Permissions, Install Dependencies",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "[ -f /immich-devcontainer/container-start.sh ] && /immich-devcontainer/container-start.sh || exit 0",
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "dedicated",
|
||||||
|
"showReuseMessage": true,
|
||||||
|
"clear": false,
|
||||||
|
"group": "Devcontainer tasks",
|
||||||
|
"close": true
|
||||||
|
},
|
||||||
|
"runOptions": {
|
||||||
|
"runOn": "default"
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Immich API Server (Nest)",
|
||||||
|
"dependsOn": ["Fix Permissions, Install Dependencies"],
|
||||||
|
"type": "shell",
|
||||||
|
"command": "[ -f /immich-devcontainer/container-start-backend.sh ] && /immich-devcontainer/container-start-backend.sh || exit 0",
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "dedicated",
|
||||||
|
"showReuseMessage": true,
|
||||||
|
"clear": false,
|
||||||
|
"group": "Devcontainer tasks",
|
||||||
|
"close": true
|
||||||
|
},
|
||||||
|
"runOptions": {
|
||||||
|
"runOn": "default"
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Immich Web Server (Vite)",
|
||||||
|
"dependsOn": ["Fix Permissions, Install Dependencies"],
|
||||||
|
"type": "shell",
|
||||||
|
"command": "[ -f /immich-devcontainer/container-start-frontend.sh ] && /immich-devcontainer/container-start-frontend.sh || exit 0",
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "dedicated",
|
||||||
|
"showReuseMessage": true,
|
||||||
|
"clear": false,
|
||||||
|
"group": "Devcontainer tasks",
|
||||||
|
"close": true
|
||||||
|
},
|
||||||
|
"runOptions": {
|
||||||
|
"runOn": "default"
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Immich Server and Web",
|
||||||
|
"dependsOn": ["Immich Web Server (Vite)", "Immich API Server (Nest)"],
|
||||||
|
"runOptions": {
|
||||||
|
"runOn": "folderOpen"
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -70,7 +70,10 @@ const uploadBatch = async (files: string[], options: UploadOptionsDto) => {
|
|||||||
console.log(JSON.stringify({ newFiles, duplicates, newAssets }, undefined, 4));
|
console.log(JSON.stringify({ newFiles, duplicates, newAssets }, undefined, 4));
|
||||||
}
|
}
|
||||||
await updateAlbums([...newAssets, ...duplicates], options);
|
await updateAlbums([...newAssets, ...duplicates], options);
|
||||||
await deleteFiles(newFiles, options);
|
await deleteFiles(
|
||||||
|
newAssets.map(({ filepath }) => filepath),
|
||||||
|
options,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const startWatch = async (
|
export const startWatch = async (
|
||||||
|
|||||||
@@ -0,0 +1,481 @@
|
|||||||
|
---
|
||||||
|
title: Devcontainers
|
||||||
|
sidebar_position: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
# Development with Dev Containers
|
||||||
|
|
||||||
|
Dev Containers provide a consistent, reproducible development environment using Docker containers. With a single click, you can get started with an Immich development environment on Mac, Linux, Windows, or in the cloud using GitHub Codespaces.
|
||||||
|
|
||||||
|
[](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/immich-app/immich/)
|
||||||
|
|
||||||
|
[](https://codespaces.new/immich-app/immich/)
|
||||||
|
|
||||||
|
[Learn more about Dev Containers](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containers)
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before getting started, ensure you have:
|
||||||
|
|
||||||
|
- **Docker Desktop** (latest version)
|
||||||
|
- [Mac](https://docs.docker.com/desktop/install/mac-install/)
|
||||||
|
- [Windows](https://docs.docker.com/desktop/install/windows-install/) (with WSL2 backend recommended)
|
||||||
|
- [Linux](https://docs.docker.com/desktop/install/linux-install/)
|
||||||
|
- **Visual Studio Code** with the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
|
||||||
|
- **Git** for cloning the repository
|
||||||
|
- At least **8GB of RAM** (16GB recommended)
|
||||||
|
- **20GB of free disk space**
|
||||||
|
|
||||||
|
:::tip Alternative Development Environments
|
||||||
|
While this guide focuses on VS Code, you have many options for Dev Container development:
|
||||||
|
|
||||||
|
**Local Editors:**
|
||||||
|
|
||||||
|
- [IntelliJ IDEA](https://www.jetbrains.com/help/idea/connect-to-devcontainer.html) - Full JetBrains IDE support
|
||||||
|
- [neovim](https://github.com/jamestthompson3/nvim-remote-containers) - Lightweight terminal-based editor
|
||||||
|
- [Emacs](https://github.com/emacs-lsp/lsp-docker) - Extensible text editor
|
||||||
|
- [DevContainer CLI](https://github.com/devcontainers/cli) - Command-line interface
|
||||||
|
|
||||||
|
**Cloud-Based Solutions:**
|
||||||
|
|
||||||
|
- [GitHub Codespaces](https://github.com/features/codespaces) - Fully integrated with GitHub, excellent devcontainer.json support
|
||||||
|
- [GitPod](https://www.gitpod.io) - SaaS platform with recent Dev Container support (historically used gitpod.yml)
|
||||||
|
|
||||||
|
**Self-Hostable Options:**
|
||||||
|
|
||||||
|
- [Coder](https://coder.com) - Enterprise-focused, requires Terraform knowledge, self-managed
|
||||||
|
- [DevPod](https://devpod.sh) - Client-only tool with excellent devcontainer.json support, works with any provider (local, cloud, or on-premise)
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Dev Container Services
|
||||||
|
|
||||||
|
The Dev Container environment consists of the following services:
|
||||||
|
|
||||||
|
| Service | Container Name | Description | Ports |
|
||||||
|
| ---------------- | ------------------------- | --------------------------------------------------------- | ----------------------------------------------------------------------- |
|
||||||
|
| Server & Web | `immich-server` | Runs both API server and web frontend in development mode | 2283 (API)<br/>3000 (Web)<br/>9230 (Workers Debug)<br/>9231 (API Debug) |
|
||||||
|
| Database | `database` | PostgreSQL database | 5432 |
|
||||||
|
| Cache | `redis` | Valkey cache server | 6379 |
|
||||||
|
| Machine Learning | `immich-machine-learning` | Immich ML model inference server | 3003 |
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
### Step 1: Clone the Repository
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/immich-app/immich.git
|
||||||
|
cd immich
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Configure Environment Variables
|
||||||
|
|
||||||
|
The immich dev containers read environment variables from your shell environment, not from `.env` files. This allows them to work in cloud environments without pre-configuration.
|
||||||
|
|
||||||
|
:::important Required Configuration
|
||||||
|
When running locally, and if you want to create (or use an existing) DB and/or photo storage folder, you must set the `UPLOAD_LOCATION` variable in your shell environment before launching the Dev Container. This determines where uploaded files are stored and also where the DB stores it data.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set temporarily for current session
|
||||||
|
export UPLOAD_LOCATION=/opt/dev_upload_folder
|
||||||
|
|
||||||
|
# Or add to your shell profile for persistence
|
||||||
|
# (~/.bashrc, ~/.zshrc, ~/.bash_profile, etc.)
|
||||||
|
echo 'export UPLOAD_LOCATION=/opt/dev_upload_folder' >> ~/.bashrc
|
||||||
|
source ~/.bashrc
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Step 3: Launch the Dev Container
|
||||||
|
|
||||||
|
#### Using VS Code UI:
|
||||||
|
|
||||||
|
1. Open the cloned repository in VS Code
|
||||||
|
2. Press `F1` or `Ctrl/Cmd+Shift+P` to open the command palette
|
||||||
|
3. Type and select "Dev Containers: Rebuild and Reopen in Container"
|
||||||
|
4. Select "Immich - Backend, Frontend and ML" from the list
|
||||||
|
5. Wait for the container to build and start (this may take several minutes on first run)
|
||||||
|
|
||||||
|
#### Using VS Code Quick Actions:
|
||||||
|
|
||||||
|
1. Open the repository in VS Code
|
||||||
|
2. You should see a popup asking if you want to reopen in a container
|
||||||
|
3. Click "Reopen in Container"
|
||||||
|
|
||||||
|
#### Using Command Line:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Using the DevContainer CLI
|
||||||
|
devcontainer up --workspace-folder .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment Variable Details
|
||||||
|
|
||||||
|
### How Dev Containers Handle Environment Variables
|
||||||
|
|
||||||
|
Unlike the Immich developer setup based on Docker Compose which uses `.env` files, Immich Dev Containers read environment variables from your shell environment. This is configured in `.devcontainer/devcontainer.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"remoteEnv": {
|
||||||
|
"UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./Library}",
|
||||||
|
"DB_PASSWORD": "${localEnv:DB_PASSWORD:postgres}",
|
||||||
|
"DB_USERNAME": "${localEnv:DB_USERNAME:postgres}",
|
||||||
|
"DB_DATABASE_NAME": "${localEnv:DB_DATABASE_NAME:immich}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `${localEnv:VARIABLE:default}` syntax reads from your shell environment with optional defaults.
|
||||||
|
|
||||||
|
### Upload Location Path Resolution
|
||||||
|
|
||||||
|
The `UPLOAD_LOCATION` environment variable controls where files are stored:
|
||||||
|
|
||||||
|
**Default:** `./Library` (relative to the `docker` directory)
|
||||||
|
**Resolved to:** `<immich-root>/docker/Library`
|
||||||
|
|
||||||
|
**Bind Mounts Created:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# From .devcontainer/server/container-compose-overrides.yml
|
||||||
|
- ${UPLOAD_LOCATION-./Library}/photos:/workspaces/immich/server/upload
|
||||||
|
- ${UPLOAD_LOCATION-./Library}/postgres:/var/lib/postgresql/data
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Configuration
|
||||||
|
|
||||||
|
These variables have sensible defaults (for development) but can be customized:
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
| ------------------ | ---------- | ------------------- |
|
||||||
|
| `DB_PASSWORD` | `postgres` | PostgreSQL password |
|
||||||
|
| `DB_USERNAME` | `postgres` | PostgreSQL username |
|
||||||
|
| `DB_DATABASE_NAME` | `immich` | Database name |
|
||||||
|
|
||||||
|
### Setting Environment Variables
|
||||||
|
|
||||||
|
Add these to your shell profile (`~/.bashrc`, `~/.zshrc`, `~/.bash_profile`, etc.):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Required
|
||||||
|
export UPLOAD_LOCATION=./Library # or absolute path
|
||||||
|
|
||||||
|
# Optional (only if using non-default values)
|
||||||
|
export DB_PASSWORD=your_password
|
||||||
|
export DB_USERNAME=your_username
|
||||||
|
export DB_DATABASE_NAME=your_database
|
||||||
|
```
|
||||||
|
|
||||||
|
Remember to reload your shell configuration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source ~/.bashrc # or ~/.zshrc, etc.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Git Configuration
|
||||||
|
|
||||||
|
### SSH Keys and Authentication
|
||||||
|
|
||||||
|
To use your SSH keys for GitHub access inside the Dev Container:
|
||||||
|
|
||||||
|
1. **Start SSH Agent** on your host machine:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
eval "$(ssh-agent -s)"
|
||||||
|
ssh-add ~/.ssh/id_rsa # or your key path
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **VS Code automatically forwards your SSH agent** to the container
|
||||||
|
|
||||||
|
For detailed instructions, see the [VS Code guide on sharing Git credentials](https://code.visualstudio.com/remote/advancedcontainers/sharing-git-credentials).
|
||||||
|
|
||||||
|
### Commit Signing
|
||||||
|
|
||||||
|
To use your SSH key for commit signing, see the [GitHub guide on SSH commit signing](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key#telling-git-about-your-ssh-key).
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
### Automatic Setup
|
||||||
|
|
||||||
|
When the Dev Container starts, it automatically:
|
||||||
|
|
||||||
|
1. **Runs post-create script** (`container-server-post-create.sh`):
|
||||||
|
|
||||||
|
- Adjusts file permissions for the `node` user
|
||||||
|
- Installs dependencies: `npm install` in all packages
|
||||||
|
- Builds TypeScript SDK: `npm run build` in `open-api/typescript-sdk`
|
||||||
|
|
||||||
|
2. **Starts development servers** via VS Code tasks:
|
||||||
|
|
||||||
|
- `Immich API Server (Nest)` - API server with hot-reloading on port 2283
|
||||||
|
- `Immich Web Server (Vite)` - Web frontend with hot-reloading on port 3000
|
||||||
|
- Both servers watch for file changes and recompile automatically
|
||||||
|
|
||||||
|
3. **Configures port forwarding**:
|
||||||
|
- Web UI: http://localhost:3000 (opens automatically)
|
||||||
|
- API: http://localhost:2283
|
||||||
|
- Debug ports: 9230 (workers), 9231 (API)
|
||||||
|
|
||||||
|
:::info
|
||||||
|
The Dev Container setup replaces the `make dev` command from the traditional setup. All services start automatically when you open the container.
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Accessing Services
|
||||||
|
|
||||||
|
Once running, you can access:
|
||||||
|
|
||||||
|
| Service | URL | Description |
|
||||||
|
| -------- | --------------------- | ---------------------------------------------------------------------------------------------- |
|
||||||
|
| Web UI | http://localhost:3000 | Main web interface |
|
||||||
|
| API | http://localhost:2283 | REST API endpoints (Not used directly, web UI will expose this over http://localhost:3000/api) |
|
||||||
|
| Database | localhost:5432 | PostgreSQL (username: `postgres`) (Not used directly) |
|
||||||
|
|
||||||
|
### Connecting Mobile Apps
|
||||||
|
|
||||||
|
To connect the mobile app to your Dev Container:
|
||||||
|
|
||||||
|
1. Find your machine's IP address
|
||||||
|
2. In the mobile app, use: `http://YOUR_IP:3000/api`
|
||||||
|
3. Ensure your firewall allows connections on port 2283
|
||||||
|
|
||||||
|
### Making Code Changes
|
||||||
|
|
||||||
|
- **Server code** (`/server`): Changes trigger automatic restart
|
||||||
|
- **Web code** (`/web`): Changes trigger hot module replacement
|
||||||
|
- **Database migrations**: Run `npm run sync:sql` in the server directory
|
||||||
|
- **API changes**: Regenerate TypeScript SDK with `make open-api`
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
### Running Tests
|
||||||
|
|
||||||
|
The Dev Container supports multiple ways to run tests:
|
||||||
|
|
||||||
|
#### Using Make Commands (Recommended)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run tests for specific components
|
||||||
|
make test-server # Server unit tests
|
||||||
|
make test-web # Web unit tests
|
||||||
|
make test-e2e # End-to-end tests
|
||||||
|
make test-cli # CLI tests
|
||||||
|
|
||||||
|
# Run all tests
|
||||||
|
make test-all # Runs tests for all components
|
||||||
|
|
||||||
|
# Medium tests (integration tests)
|
||||||
|
make test-medium-dev # End-to-end tests
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Using NPM Directly
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Server tests
|
||||||
|
cd /workspaces/immich/server
|
||||||
|
npm test # Run all tests
|
||||||
|
npm run test:watch # Watch mode
|
||||||
|
npm run test:cov # Coverage report
|
||||||
|
|
||||||
|
# Web tests
|
||||||
|
cd /workspaces/immich/web
|
||||||
|
npm test # Run all tests
|
||||||
|
npm run test:watch # Watch mode
|
||||||
|
|
||||||
|
# E2E tests
|
||||||
|
cd /workspaces/immich/e2e
|
||||||
|
npm run test # Run API tests
|
||||||
|
npm run test:web # Run web UI tests
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Quality Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Linting
|
||||||
|
make lint-server # Lint server code
|
||||||
|
make lint-web # Lint web code
|
||||||
|
make lint-all # Lint all components
|
||||||
|
|
||||||
|
# Formatting
|
||||||
|
make format-server # Format server code
|
||||||
|
make format-web # Format web code
|
||||||
|
make format-all # Format all code
|
||||||
|
|
||||||
|
# Type checking
|
||||||
|
make check-server # Type check server
|
||||||
|
make check-web # Type check web
|
||||||
|
make check-all # Check all components
|
||||||
|
|
||||||
|
# Complete hygiene check
|
||||||
|
make hygiene-all # Runs lint, format, check, SQL sync, and audit
|
||||||
|
```
|
||||||
|
|
||||||
|
### Additional Make Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build commands
|
||||||
|
make build-server # Build server
|
||||||
|
make build-web # Build web app
|
||||||
|
make build-all # Build everything
|
||||||
|
|
||||||
|
# API generation
|
||||||
|
make open-api # Generate OpenAPI specs
|
||||||
|
make open-api-typescript # Generate TypeScript SDK
|
||||||
|
make open-api-dart # Generate Dart SDK
|
||||||
|
|
||||||
|
# Database
|
||||||
|
make sql # Sync database schema
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
make install-server # Install server dependencies
|
||||||
|
make install-web # Install web dependencies
|
||||||
|
make install-all # Install all dependencies
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debugging
|
||||||
|
|
||||||
|
The Dev Container is pre-configured for debugging:
|
||||||
|
|
||||||
|
1. **API Server Debugging**:
|
||||||
|
|
||||||
|
- Set breakpoints in VS Code
|
||||||
|
- Press `F5` or use "Run and Debug" panel
|
||||||
|
- Select "Attach to Server" configuration
|
||||||
|
- Debug port: 9231
|
||||||
|
|
||||||
|
2. **Worker Debugging**:
|
||||||
|
|
||||||
|
- Use "Attach to Workers" configuration
|
||||||
|
- Debug port: 9230
|
||||||
|
|
||||||
|
3. **Web Debugging**:
|
||||||
|
- Use browser DevTools
|
||||||
|
- VS Code debugger for Chrome/Edge extensions supported
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
#### Permission Errors
|
||||||
|
|
||||||
|
**Problem**: `EACCES` or permission denied errors
|
||||||
|
**Solution**:
|
||||||
|
|
||||||
|
- The Dev Container runs as the `node` user (UID 1000)
|
||||||
|
- If your host UID differs, you may see permission issues
|
||||||
|
- Try rebuilding the container: "Dev Containers: Rebuild Container"
|
||||||
|
|
||||||
|
#### Container Won't Start
|
||||||
|
|
||||||
|
**Problem**: Dev Container fails to start or build
|
||||||
|
**Solution**:
|
||||||
|
|
||||||
|
1. Check Docker is running: `docker ps`
|
||||||
|
2. Clean Docker resources: `docker system prune -a`
|
||||||
|
3. Check available disk space
|
||||||
|
4. Review Docker Desktop resource limits
|
||||||
|
|
||||||
|
#### Port Already in Use
|
||||||
|
|
||||||
|
**Problem**: "Port 3000/2283 is already in use"
|
||||||
|
**Solution**:
|
||||||
|
|
||||||
|
1. Check for conflicting services: `lsof -i :3000` (macOS/Linux)
|
||||||
|
2. Stop conflicting services or change port mappings
|
||||||
|
3. Restart Docker Desktop
|
||||||
|
|
||||||
|
#### Upload Location Not Set
|
||||||
|
|
||||||
|
**Problem**: Errors about missing UPLOAD_LOCATION
|
||||||
|
**Solution**:
|
||||||
|
|
||||||
|
1. Set the environment variable: `export UPLOAD_LOCATION=./Library`
|
||||||
|
2. Add to your shell profile for persistence
|
||||||
|
3. Restart your terminal and VS Code
|
||||||
|
|
||||||
|
#### Database Connection Failed
|
||||||
|
|
||||||
|
**Problem**: Cannot connect to PostgreSQL
|
||||||
|
**Solution**:
|
||||||
|
|
||||||
|
1. Ensure all containers are running: `docker ps`
|
||||||
|
2. Check logs: "Dev Containers: Show Container Log"
|
||||||
|
3. Verify database credentials match environment variables
|
||||||
|
|
||||||
|
### Getting Help
|
||||||
|
|
||||||
|
If you encounter issues:
|
||||||
|
|
||||||
|
1. Check container logs: View → Output → Select "Dev Containers"
|
||||||
|
2. Rebuild without cache: "Dev Containers: Rebuild Container Without Cache"
|
||||||
|
3. Review [common Docker issues](https://docs.docker.com/desktop/troubleshoot/)
|
||||||
|
4. Ask in [Discord](https://discord.immich.app) `#help-desk-support` channel
|
||||||
|
|
||||||
|
## Mobile Development
|
||||||
|
|
||||||
|
While the Dev Container focuses on server and web development, you can connect mobile apps for testing:
|
||||||
|
|
||||||
|
### Connecting iOS/Android Apps
|
||||||
|
|
||||||
|
1. **Ensure API is accessible**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find your machine's IP
|
||||||
|
# macOS
|
||||||
|
ipconfig getifaddr en0
|
||||||
|
# Linux
|
||||||
|
hostname -I
|
||||||
|
# Windows (in WSL2)
|
||||||
|
ip addr show eth0
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Configure mobile app**:
|
||||||
|
|
||||||
|
- Server URL: `http://YOUR_IP:2283/api`
|
||||||
|
- Ensure firewall allows port 2283
|
||||||
|
|
||||||
|
3. **For full mobile development**, see the [mobile development guide](/docs/developer/setup) which covers:
|
||||||
|
- Flutter setup
|
||||||
|
- Running on simulators/devices
|
||||||
|
- Mobile-specific debugging
|
||||||
|
|
||||||
|
## Advanced Configuration
|
||||||
|
|
||||||
|
### Custom VS Code Extensions
|
||||||
|
|
||||||
|
Add extensions to `.devcontainer/devcontainer.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"customizations": {
|
||||||
|
"vscode": {
|
||||||
|
"extensions": [
|
||||||
|
"your.extension-id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Additional Services
|
||||||
|
|
||||||
|
To add services (e.g., Redis Commander), modify:
|
||||||
|
|
||||||
|
1. `/docker/docker-compose.dev.yml` - Add service definition
|
||||||
|
2. `/.devcontainer/server/container-compose-overrides.yml` - Add overrides if needed
|
||||||
|
|
||||||
|
### Resource Limits
|
||||||
|
|
||||||
|
Adjust Docker Desktop resources:
|
||||||
|
|
||||||
|
- **macOS/Windows**: Docker Desktop → Settings → Resources
|
||||||
|
- **Linux**: Modify Docker daemon configuration
|
||||||
|
|
||||||
|
Recommended minimums:
|
||||||
|
|
||||||
|
- CPU: 4 cores
|
||||||
|
- Memory: 8GB
|
||||||
|
- Disk: 20GB
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
- Read the [architecture overview](/docs/developer/architecture)
|
||||||
|
- Learn about [database migrations](/docs/developer/database-migrations)
|
||||||
|
- Explore [API documentation](/docs/api)
|
||||||
|
- Join `#immich` on [Discord](https://discord.immich.app)
|
||||||
@@ -123,7 +123,7 @@ The default configuration looks like this:
|
|||||||
"buttonText": "Login with OAuth",
|
"buttonText": "Login with OAuth",
|
||||||
"clientId": "",
|
"clientId": "",
|
||||||
"clientSecret": "",
|
"clientSecret": "",
|
||||||
"defaultStorageQuota": 0,
|
"defaultStorageQuota": null,
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"issuerUrl": "",
|
"issuerUrl": "",
|
||||||
"mobileOverrideEnabled": false,
|
"mobileOverrideEnabled": false,
|
||||||
|
|||||||
@@ -11,11 +11,24 @@ describe('/people', () => {
|
|||||||
let hiddenPerson: PersonResponseDto;
|
let hiddenPerson: PersonResponseDto;
|
||||||
let multipleAssetsPerson: PersonResponseDto;
|
let multipleAssetsPerson: PersonResponseDto;
|
||||||
|
|
||||||
|
let nameAlicePerson: PersonResponseDto;
|
||||||
|
let nameBobPerson: PersonResponseDto;
|
||||||
|
let nameCharliePerson: PersonResponseDto;
|
||||||
|
let nameNullPerson: PersonResponseDto;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await utils.resetDatabase();
|
await utils.resetDatabase();
|
||||||
admin = await utils.adminSetup();
|
admin = await utils.adminSetup();
|
||||||
|
|
||||||
[visiblePerson, hiddenPerson, multipleAssetsPerson] = await Promise.all([
|
[
|
||||||
|
visiblePerson,
|
||||||
|
hiddenPerson,
|
||||||
|
multipleAssetsPerson,
|
||||||
|
nameCharliePerson,
|
||||||
|
nameBobPerson,
|
||||||
|
nameAlicePerson,
|
||||||
|
nameNullPerson,
|
||||||
|
] = await Promise.all([
|
||||||
utils.createPerson(admin.accessToken, {
|
utils.createPerson(admin.accessToken, {
|
||||||
name: 'visible_person',
|
name: 'visible_person',
|
||||||
}),
|
}),
|
||||||
@@ -26,10 +39,24 @@ describe('/people', () => {
|
|||||||
utils.createPerson(admin.accessToken, {
|
utils.createPerson(admin.accessToken, {
|
||||||
name: 'multiple_assets_person',
|
name: 'multiple_assets_person',
|
||||||
}),
|
}),
|
||||||
|
// --- Setup for the specific sorting test ---
|
||||||
|
utils.createPerson(admin.accessToken, {
|
||||||
|
name: 'Charlie',
|
||||||
|
}),
|
||||||
|
utils.createPerson(admin.accessToken, {
|
||||||
|
name: 'Bob',
|
||||||
|
}),
|
||||||
|
utils.createPerson(admin.accessToken, {
|
||||||
|
name: 'Alice',
|
||||||
|
}),
|
||||||
|
utils.createPerson(admin.accessToken, {
|
||||||
|
name: '',
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const asset1 = await utils.createAsset(admin.accessToken);
|
const asset1 = await utils.createAsset(admin.accessToken);
|
||||||
const asset2 = await utils.createAsset(admin.accessToken);
|
const asset2 = await utils.createAsset(admin.accessToken);
|
||||||
|
const asset3 = await utils.createAsset(admin.accessToken);
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
utils.createFace({ assetId: asset1.id, personId: visiblePerson.id }),
|
utils.createFace({ assetId: asset1.id, personId: visiblePerson.id }),
|
||||||
@@ -37,6 +64,15 @@ describe('/people', () => {
|
|||||||
utils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }),
|
utils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }),
|
||||||
utils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }),
|
utils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }),
|
||||||
utils.createFace({ assetId: asset2.id, personId: multipleAssetsPerson.id }),
|
utils.createFace({ assetId: asset2.id, personId: multipleAssetsPerson.id }),
|
||||||
|
utils.createFace({ assetId: asset3.id, personId: multipleAssetsPerson.id }),
|
||||||
|
// Named persons
|
||||||
|
utils.createFace({ assetId: asset1.id, personId: nameCharliePerson.id }), // 1 asset
|
||||||
|
utils.createFace({ assetId: asset1.id, personId: nameBobPerson.id }),
|
||||||
|
utils.createFace({ assetId: asset2.id, personId: nameBobPerson.id }), // 2 assets
|
||||||
|
utils.createFace({ assetId: asset1.id, personId: nameAlicePerson.id }), // 1 asset
|
||||||
|
// Null-named person
|
||||||
|
utils.createFace({ assetId: asset1.id, personId: nameNullPerson.id }),
|
||||||
|
utils.createFace({ assetId: asset2.id, personId: nameNullPerson.id }), // 2 assets
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -51,26 +87,53 @@ describe('/people', () => {
|
|||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual({
|
expect(body).toEqual({
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
total: 3,
|
total: 7,
|
||||||
hidden: 1,
|
hidden: 1,
|
||||||
people: [
|
people: [
|
||||||
expect.objectContaining({ name: 'multiple_assets_person' }),
|
expect.objectContaining({ name: 'multiple_assets_person' }),
|
||||||
|
expect.objectContaining({ name: 'Bob' }),
|
||||||
|
expect.objectContaining({ name: 'Alice' }),
|
||||||
|
expect.objectContaining({ name: 'Charlie' }),
|
||||||
expect.objectContaining({ name: 'visible_person' }),
|
expect.objectContaining({ name: 'visible_person' }),
|
||||||
expect.objectContaining({ name: 'hidden_person' }),
|
expect.objectContaining({ name: 'hidden_person' }),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should sort visible people by asset count (desc), then by name (asc, nulls last)', async () => {
|
||||||
|
const { status, body } = await request(app).get('/people').set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body.hasNextPage).toBe(false);
|
||||||
|
expect(body.total).toBe(7); // All persons
|
||||||
|
expect(body.hidden).toBe(1); // 'hidden_person'
|
||||||
|
|
||||||
|
const people = body.people as PersonResponseDto[];
|
||||||
|
|
||||||
|
expect(people.map((p) => p.id)).toEqual([
|
||||||
|
multipleAssetsPerson.id, // name: 'multiple_assets_person', count: 3
|
||||||
|
nameBobPerson.id, // name: 'Bob', count: 2
|
||||||
|
nameAlicePerson.id, // name: 'Alice', count: 1
|
||||||
|
nameCharliePerson.id, // name: 'Charlie', count: 1
|
||||||
|
visiblePerson.id, // name: 'visible_person', count: 1
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(people.some((p) => p.id === hiddenPerson.id)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return only visible people', async () => {
|
it('should return only visible people', async () => {
|
||||||
const { status, body } = await request(app).get('/people').set('Authorization', `Bearer ${admin.accessToken}`);
|
const { status, body } = await request(app).get('/people').set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual({
|
expect(body).toEqual({
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
total: 3,
|
total: 7,
|
||||||
hidden: 1,
|
hidden: 1,
|
||||||
people: [
|
people: [
|
||||||
expect.objectContaining({ name: 'multiple_assets_person' }),
|
expect.objectContaining({ name: 'multiple_assets_person' }),
|
||||||
|
expect.objectContaining({ name: 'Bob' }),
|
||||||
|
expect.objectContaining({ name: 'Alice' }),
|
||||||
|
expect.objectContaining({ name: 'Charlie' }),
|
||||||
expect.objectContaining({ name: 'visible_person' }),
|
expect.objectContaining({ name: 'visible_person' }),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@@ -80,12 +143,12 @@ describe('/people', () => {
|
|||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/people')
|
.get('/people')
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||||
.query({ withHidden: true, page: 2, size: 1 });
|
.query({ withHidden: true, page: 5, size: 1 });
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual({
|
expect(body).toEqual({
|
||||||
hasNextPage: true,
|
hasNextPage: true,
|
||||||
total: 3,
|
total: 7,
|
||||||
hidden: 1,
|
hidden: 1,
|
||||||
people: [expect.objectContaining({ name: 'visible_person' })],
|
people: [expect.objectContaining({ name: 'visible_person' })],
|
||||||
});
|
});
|
||||||
@@ -128,7 +191,7 @@ describe('/people', () => {
|
|||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual(expect.objectContaining({ assets: 2 }));
|
expect(body).toEqual(expect.objectContaining({ assets: 3 }));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ describe('/timeline', () => {
|
|||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/timeline/bucket')
|
.get('/timeline/bucket')
|
||||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
||||||
.query({ timeBucket: '1970-02-01T00:00:00.000Z', isTrashed: true });
|
.query({ timeBucket: '1970-02-01', isTrashed: true });
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -216,7 +216,11 @@ export const utils = {
|
|||||||
websocket
|
websocket
|
||||||
.on('connect', () => resolve(websocket))
|
.on('connect', () => resolve(websocket))
|
||||||
.on('on_upload_success', (data: AssetResponseDto) => onEvent({ event: 'assetUpload', id: data.id }))
|
.on('on_upload_success', (data: AssetResponseDto) => onEvent({ event: 'assetUpload', id: data.id }))
|
||||||
.on('on_asset_update', (data: AssetResponseDto) => onEvent({ event: 'assetUpdate', id: data.id }))
|
.on('on_asset_update', (assetId: string[]) => {
|
||||||
|
for (const id of assetId) {
|
||||||
|
onEvent({ event: 'assetUpdate', id });
|
||||||
|
}
|
||||||
|
})
|
||||||
.on('on_asset_hidden', (assetId: string) => onEvent({ event: 'assetHidden', id: assetId }))
|
.on('on_asset_hidden', (assetId: string) => onEvent({ event: 'assetHidden', id: assetId }))
|
||||||
.on('on_asset_delete', (assetId: string) => onEvent({ event: 'assetDelete', id: assetId }))
|
.on('on_asset_delete', (assetId: string) => onEvent({ event: 'assetDelete', id: assetId }))
|
||||||
.on('on_user_delete', (userId: string) => onEvent({ event: 'userDelete', id: userId }))
|
.on('on_user_delete', (userId: string) => onEvent({ event: 'userDelete', id: userId }))
|
||||||
|
|||||||
+4
-1
@@ -204,7 +204,7 @@
|
|||||||
"oauth_storage_quota_claim": "Storage quota claim",
|
"oauth_storage_quota_claim": "Storage quota claim",
|
||||||
"oauth_storage_quota_claim_description": "Automatically set the user's storage quota to the value of this claim.",
|
"oauth_storage_quota_claim_description": "Automatically set the user's storage quota to the value of this claim.",
|
||||||
"oauth_storage_quota_default": "Default storage quota (GiB)",
|
"oauth_storage_quota_default": "Default storage quota (GiB)",
|
||||||
"oauth_storage_quota_default_description": "Quota in GiB to be used when no claim is provided (Enter 0 for unlimited quota).",
|
"oauth_storage_quota_default_description": "Quota in GiB to be used when no claim is provided.",
|
||||||
"oauth_timeout": "Request Timeout",
|
"oauth_timeout": "Request Timeout",
|
||||||
"oauth_timeout_description": "Timeout for requests in milliseconds",
|
"oauth_timeout_description": "Timeout for requests in milliseconds",
|
||||||
"password_enable_description": "Login with email and password",
|
"password_enable_description": "Login with email and password",
|
||||||
@@ -1150,6 +1150,7 @@
|
|||||||
"locked_folder": "Locked Folder",
|
"locked_folder": "Locked Folder",
|
||||||
"log_out": "Log out",
|
"log_out": "Log out",
|
||||||
"log_out_all_devices": "Log Out All Devices",
|
"log_out_all_devices": "Log Out All Devices",
|
||||||
|
"logged_in_as": "Logged in as {user}",
|
||||||
"logged_out_all_devices": "Logged out all devices",
|
"logged_out_all_devices": "Logged out all devices",
|
||||||
"logged_out_device": "Logged out device",
|
"logged_out_device": "Logged out device",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
@@ -1607,6 +1608,7 @@
|
|||||||
"select_album_cover": "Select album cover",
|
"select_album_cover": "Select album cover",
|
||||||
"select_all": "Select all",
|
"select_all": "Select all",
|
||||||
"select_all_duplicates": "Select all duplicates",
|
"select_all_duplicates": "Select all duplicates",
|
||||||
|
"select_all_in": "Select all in {group}",
|
||||||
"select_avatar_color": "Select avatar color",
|
"select_avatar_color": "Select avatar color",
|
||||||
"select_face": "Select face",
|
"select_face": "Select face",
|
||||||
"select_featured_photo": "Select featured photo",
|
"select_featured_photo": "Select featured photo",
|
||||||
@@ -1871,6 +1873,7 @@
|
|||||||
"unsaved_change": "Unsaved change",
|
"unsaved_change": "Unsaved change",
|
||||||
"unselect_all": "Unselect all",
|
"unselect_all": "Unselect all",
|
||||||
"unselect_all_duplicates": "Unselect all duplicates",
|
"unselect_all_duplicates": "Unselect all duplicates",
|
||||||
|
"unselect_all_in": "Unselect all in {group}",
|
||||||
"unstack": "Un-stack",
|
"unstack": "Un-stack",
|
||||||
"unstacked_assets_count": "Un-stacked {count, plural, one {# asset} other {# assets}}",
|
"unstacked_assets_count": "Un-stacked {count, plural, one {# asset} other {# assets}}",
|
||||||
"up_next": "Up next",
|
"up_next": "Up next",
|
||||||
|
|||||||
@@ -15,13 +15,4 @@ abstract interface class ISyncStreamRepository implements IDatabaseRepository {
|
|||||||
Future<void> updatePartnerAssetsV1(Iterable<SyncAssetV1> data);
|
Future<void> updatePartnerAssetsV1(Iterable<SyncAssetV1> data);
|
||||||
Future<void> deletePartnerAssetsV1(Iterable<SyncAssetDeleteV1> data);
|
Future<void> deletePartnerAssetsV1(Iterable<SyncAssetDeleteV1> data);
|
||||||
Future<void> updatePartnerAssetsExifV1(Iterable<SyncAssetExifV1> data);
|
Future<void> updatePartnerAssetsExifV1(Iterable<SyncAssetExifV1> data);
|
||||||
|
|
||||||
Future<void> updateAlbumsV1(Iterable<SyncAlbumV1> data);
|
|
||||||
Future<void> deleteAlbumsV1(Iterable<SyncAlbumDeleteV1> data);
|
|
||||||
|
|
||||||
// Future<void> updateAlbumAssetsV1(Iterable<SyncAlbumAssetV1> data);
|
|
||||||
// Future<void> deleteAlbumAssetsV1(Iterable<SyncAlbumAssetV1> data);
|
|
||||||
|
|
||||||
Future<void> updateAlbumUsersV1(Iterable<SyncAlbumUserV1> data);
|
|
||||||
Future<void> deleteAlbumUsersV1(Iterable<SyncAlbumUserDeleteV1> data);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +0,0 @@
|
|||||||
enum AssetOrder {
|
|
||||||
// do not change this order!
|
|
||||||
asc,
|
|
||||||
desc,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Model for an album stored in the server
|
|
||||||
class Album {
|
|
||||||
final String id;
|
|
||||||
final String name;
|
|
||||||
final String description;
|
|
||||||
final DateTime createdAt;
|
|
||||||
final DateTime updatedAt;
|
|
||||||
final String? thumbnailAssetId;
|
|
||||||
final bool isActivityEnabled;
|
|
||||||
final AssetOrder order;
|
|
||||||
|
|
||||||
const Album({
|
|
||||||
required this.id,
|
|
||||||
required this.name,
|
|
||||||
required this.description,
|
|
||||||
required this.createdAt,
|
|
||||||
required this.updatedAt,
|
|
||||||
this.thumbnailAssetId,
|
|
||||||
required this.isActivityEnabled,
|
|
||||||
required this.order,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return '''Album {
|
|
||||||
id: $id,
|
|
||||||
name: $name,
|
|
||||||
description: $description,
|
|
||||||
createdAt: $createdAt,
|
|
||||||
updatedAt: $updatedAt,
|
|
||||||
isActivityEnabled: $isActivityEnabled,
|
|
||||||
order: $order,
|
|
||||||
thumbnailAssetId: ${thumbnailAssetId ?? "<NA>"}
|
|
||||||
}''';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if (other is! Album) return false;
|
|
||||||
if (identical(this, other)) return true;
|
|
||||||
return id == other.id &&
|
|
||||||
name == other.name &&
|
|
||||||
description == other.description &&
|
|
||||||
createdAt == other.createdAt &&
|
|
||||||
updatedAt == other.updatedAt &&
|
|
||||||
thumbnailAssetId == other.thumbnailAssetId &&
|
|
||||||
isActivityEnabled == other.isActivityEnabled &&
|
|
||||||
order == other.order;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode {
|
|
||||||
return id.hashCode ^
|
|
||||||
name.hashCode ^
|
|
||||||
description.hashCode ^
|
|
||||||
createdAt.hashCode ^
|
|
||||||
updatedAt.hashCode ^
|
|
||||||
thumbnailAssetId.hashCode ^
|
|
||||||
isActivityEnabled.hashCode ^
|
|
||||||
order.hashCode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
enum AlbumUserRole {
|
|
||||||
// do not change this order!
|
|
||||||
editor,
|
|
||||||
viewer,
|
|
||||||
}
|
|
||||||
@@ -81,18 +81,6 @@ class SyncStreamService {
|
|||||||
return _syncStreamRepository.deletePartnerAssetsV1(data.cast());
|
return _syncStreamRepository.deletePartnerAssetsV1(data.cast());
|
||||||
case SyncEntityType.partnerAssetExifV1:
|
case SyncEntityType.partnerAssetExifV1:
|
||||||
return _syncStreamRepository.updatePartnerAssetsExifV1(data.cast());
|
return _syncStreamRepository.updatePartnerAssetsExifV1(data.cast());
|
||||||
case SyncEntityType.albumV1:
|
|
||||||
return _syncStreamRepository.updateAlbumsV1(data.cast());
|
|
||||||
case SyncEntityType.albumDeleteV1:
|
|
||||||
return _syncStreamRepository.deleteAlbumsV1(data.cast());
|
|
||||||
// case SyncEntityType.albumAssetV1:
|
|
||||||
// return _syncStreamRepository.updateAlbumAssetsV1(data.cast());
|
|
||||||
// case SyncEntityType.albumAssetDeleteV1:
|
|
||||||
// return _syncStreamRepository.deleteAlbumAssetsV1(data.cast());
|
|
||||||
case SyncEntityType.albumUserV1:
|
|
||||||
return _syncStreamRepository.updateAlbumUsersV1(data.cast());
|
|
||||||
case SyncEntityType.albumUserDeleteV1:
|
|
||||||
return _syncStreamRepository.deleteAlbumUsersV1(data.cast());
|
|
||||||
default:
|
default:
|
||||||
_logger.warning("Unknown sync data type: $type");
|
_logger.warning("Unknown sync data type: $type");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
import 'package:drift/drift.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/album_user.model.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
|
||||||
|
|
||||||
class AlbumUserEntity extends Table with DriftDefaultsMixin {
|
|
||||||
const AlbumUserEntity();
|
|
||||||
|
|
||||||
TextColumn get albumId =>
|
|
||||||
text().references(RemoteAlbumEntity, #id, onDelete: KeyAction.cascade)();
|
|
||||||
|
|
||||||
TextColumn get userId =>
|
|
||||||
text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
|
|
||||||
|
|
||||||
IntColumn get role => intEnum<AlbumUserRole>()();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Set<Column> get primaryKey => {albumId, userId};
|
|
||||||
}
|
|
||||||
@@ -1,602 +0,0 @@
|
|||||||
// dart format width=80
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
import 'package:drift/drift.dart' as i0;
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/album_user.entity.drift.dart'
|
|
||||||
as i1;
|
|
||||||
import 'package:immich_mobile/domain/models/album_user.model.dart' as i2;
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/album_user.entity.dart'
|
|
||||||
as i3;
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
|
|
||||||
as i4;
|
|
||||||
import 'package:drift/internal/modular.dart' as i5;
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
|
|
||||||
as i6;
|
|
||||||
|
|
||||||
typedef $$AlbumUserEntityTableCreateCompanionBuilder
|
|
||||||
= i1.AlbumUserEntityCompanion Function({
|
|
||||||
required String albumId,
|
|
||||||
required String userId,
|
|
||||||
required i2.AlbumUserRole role,
|
|
||||||
});
|
|
||||||
typedef $$AlbumUserEntityTableUpdateCompanionBuilder
|
|
||||||
= i1.AlbumUserEntityCompanion Function({
|
|
||||||
i0.Value<String> albumId,
|
|
||||||
i0.Value<String> userId,
|
|
||||||
i0.Value<i2.AlbumUserRole> role,
|
|
||||||
});
|
|
||||||
|
|
||||||
final class $$AlbumUserEntityTableReferences extends i0.BaseReferences<
|
|
||||||
i0.GeneratedDatabase, i1.$AlbumUserEntityTable, i1.AlbumUserEntityData> {
|
|
||||||
$$AlbumUserEntityTableReferences(
|
|
||||||
super.$_db, super.$_table, super.$_typedResult);
|
|
||||||
|
|
||||||
static i4.$RemoteAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) =>
|
|
||||||
i5.ReadDatabaseContainer(db)
|
|
||||||
.resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity')
|
|
||||||
.createAlias(i0.$_aliasNameGenerator(
|
|
||||||
i5.ReadDatabaseContainer(db)
|
|
||||||
.resultSet<i1.$AlbumUserEntityTable>('album_user_entity')
|
|
||||||
.albumId,
|
|
||||||
i5.ReadDatabaseContainer(db)
|
|
||||||
.resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity')
|
|
||||||
.id));
|
|
||||||
|
|
||||||
i4.$$RemoteAlbumEntityTableProcessedTableManager get albumId {
|
|
||||||
final $_column = $_itemColumn<String>('album_id')!;
|
|
||||||
|
|
||||||
final manager = i4
|
|
||||||
.$$RemoteAlbumEntityTableTableManager(
|
|
||||||
$_db,
|
|
||||||
i5.ReadDatabaseContainer($_db)
|
|
||||||
.resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'))
|
|
||||||
.filter((f) => f.id.sqlEquals($_column));
|
|
||||||
final item = $_typedResult.readTableOrNull(_albumIdTable($_db));
|
|
||||||
if (item == null) return manager;
|
|
||||||
return i0.ProcessedTableManager(
|
|
||||||
manager.$state.copyWith(prefetchedData: [item]));
|
|
||||||
}
|
|
||||||
|
|
||||||
static i6.$UserEntityTable _userIdTable(i0.GeneratedDatabase db) =>
|
|
||||||
i5.ReadDatabaseContainer(db)
|
|
||||||
.resultSet<i6.$UserEntityTable>('user_entity')
|
|
||||||
.createAlias(i0.$_aliasNameGenerator(
|
|
||||||
i5.ReadDatabaseContainer(db)
|
|
||||||
.resultSet<i1.$AlbumUserEntityTable>('album_user_entity')
|
|
||||||
.userId,
|
|
||||||
i5.ReadDatabaseContainer(db)
|
|
||||||
.resultSet<i6.$UserEntityTable>('user_entity')
|
|
||||||
.id));
|
|
||||||
|
|
||||||
i6.$$UserEntityTableProcessedTableManager get userId {
|
|
||||||
final $_column = $_itemColumn<String>('user_id')!;
|
|
||||||
|
|
||||||
final manager = i6
|
|
||||||
.$$UserEntityTableTableManager(
|
|
||||||
$_db,
|
|
||||||
i5.ReadDatabaseContainer($_db)
|
|
||||||
.resultSet<i6.$UserEntityTable>('user_entity'))
|
|
||||||
.filter((f) => f.id.sqlEquals($_column));
|
|
||||||
final item = $_typedResult.readTableOrNull(_userIdTable($_db));
|
|
||||||
if (item == null) return manager;
|
|
||||||
return i0.ProcessedTableManager(
|
|
||||||
manager.$state.copyWith(prefetchedData: [item]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class $$AlbumUserEntityTableFilterComposer
|
|
||||||
extends i0.Composer<i0.GeneratedDatabase, i1.$AlbumUserEntityTable> {
|
|
||||||
$$AlbumUserEntityTableFilterComposer({
|
|
||||||
required super.$db,
|
|
||||||
required super.$table,
|
|
||||||
super.joinBuilder,
|
|
||||||
super.$addJoinBuilderToRootComposer,
|
|
||||||
super.$removeJoinBuilderFromRootComposer,
|
|
||||||
});
|
|
||||||
i0.ColumnWithTypeConverterFilters<i2.AlbumUserRole, i2.AlbumUserRole, int>
|
|
||||||
get role => $composableBuilder(
|
|
||||||
column: $table.role,
|
|
||||||
builder: (column) => i0.ColumnWithTypeConverterFilters(column));
|
|
||||||
|
|
||||||
i4.$$RemoteAlbumEntityTableFilterComposer get albumId {
|
|
||||||
final i4.$$RemoteAlbumEntityTableFilterComposer composer = $composerBuilder(
|
|
||||||
composer: this,
|
|
||||||
getCurrentColumn: (t) => t.albumId,
|
|
||||||
referencedTable: i5.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'),
|
|
||||||
getReferencedColumn: (t) => t.id,
|
|
||||||
builder: (joinBuilder,
|
|
||||||
{$addJoinBuilderToRootComposer,
|
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
|
||||||
i4.$$RemoteAlbumEntityTableFilterComposer(
|
|
||||||
$db: $db,
|
|
||||||
$table: i5.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'),
|
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
|
||||||
joinBuilder: joinBuilder,
|
|
||||||
$removeJoinBuilderFromRootComposer:
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
));
|
|
||||||
return composer;
|
|
||||||
}
|
|
||||||
|
|
||||||
i6.$$UserEntityTableFilterComposer get userId {
|
|
||||||
final i6.$$UserEntityTableFilterComposer composer = $composerBuilder(
|
|
||||||
composer: this,
|
|
||||||
getCurrentColumn: (t) => t.userId,
|
|
||||||
referencedTable: i5.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i6.$UserEntityTable>('user_entity'),
|
|
||||||
getReferencedColumn: (t) => t.id,
|
|
||||||
builder: (joinBuilder,
|
|
||||||
{$addJoinBuilderToRootComposer,
|
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
|
||||||
i6.$$UserEntityTableFilterComposer(
|
|
||||||
$db: $db,
|
|
||||||
$table: i5.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i6.$UserEntityTable>('user_entity'),
|
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
|
||||||
joinBuilder: joinBuilder,
|
|
||||||
$removeJoinBuilderFromRootComposer:
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
));
|
|
||||||
return composer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class $$AlbumUserEntityTableOrderingComposer
|
|
||||||
extends i0.Composer<i0.GeneratedDatabase, i1.$AlbumUserEntityTable> {
|
|
||||||
$$AlbumUserEntityTableOrderingComposer({
|
|
||||||
required super.$db,
|
|
||||||
required super.$table,
|
|
||||||
super.joinBuilder,
|
|
||||||
super.$addJoinBuilderToRootComposer,
|
|
||||||
super.$removeJoinBuilderFromRootComposer,
|
|
||||||
});
|
|
||||||
i0.ColumnOrderings<int> get role => $composableBuilder(
|
|
||||||
column: $table.role, builder: (column) => i0.ColumnOrderings(column));
|
|
||||||
|
|
||||||
i4.$$RemoteAlbumEntityTableOrderingComposer get albumId {
|
|
||||||
final i4.$$RemoteAlbumEntityTableOrderingComposer composer =
|
|
||||||
$composerBuilder(
|
|
||||||
composer: this,
|
|
||||||
getCurrentColumn: (t) => t.albumId,
|
|
||||||
referencedTable: i5.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'),
|
|
||||||
getReferencedColumn: (t) => t.id,
|
|
||||||
builder: (joinBuilder,
|
|
||||||
{$addJoinBuilderToRootComposer,
|
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
|
||||||
i4.$$RemoteAlbumEntityTableOrderingComposer(
|
|
||||||
$db: $db,
|
|
||||||
$table: i5.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i4.$RemoteAlbumEntityTable>(
|
|
||||||
'remote_album_entity'),
|
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
|
||||||
joinBuilder: joinBuilder,
|
|
||||||
$removeJoinBuilderFromRootComposer:
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
));
|
|
||||||
return composer;
|
|
||||||
}
|
|
||||||
|
|
||||||
i6.$$UserEntityTableOrderingComposer get userId {
|
|
||||||
final i6.$$UserEntityTableOrderingComposer composer = $composerBuilder(
|
|
||||||
composer: this,
|
|
||||||
getCurrentColumn: (t) => t.userId,
|
|
||||||
referencedTable: i5.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i6.$UserEntityTable>('user_entity'),
|
|
||||||
getReferencedColumn: (t) => t.id,
|
|
||||||
builder: (joinBuilder,
|
|
||||||
{$addJoinBuilderToRootComposer,
|
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
|
||||||
i6.$$UserEntityTableOrderingComposer(
|
|
||||||
$db: $db,
|
|
||||||
$table: i5.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i6.$UserEntityTable>('user_entity'),
|
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
|
||||||
joinBuilder: joinBuilder,
|
|
||||||
$removeJoinBuilderFromRootComposer:
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
));
|
|
||||||
return composer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class $$AlbumUserEntityTableAnnotationComposer
|
|
||||||
extends i0.Composer<i0.GeneratedDatabase, i1.$AlbumUserEntityTable> {
|
|
||||||
$$AlbumUserEntityTableAnnotationComposer({
|
|
||||||
required super.$db,
|
|
||||||
required super.$table,
|
|
||||||
super.joinBuilder,
|
|
||||||
super.$addJoinBuilderToRootComposer,
|
|
||||||
super.$removeJoinBuilderFromRootComposer,
|
|
||||||
});
|
|
||||||
i0.GeneratedColumnWithTypeConverter<i2.AlbumUserRole, int> get role =>
|
|
||||||
$composableBuilder(column: $table.role, builder: (column) => column);
|
|
||||||
|
|
||||||
i4.$$RemoteAlbumEntityTableAnnotationComposer get albumId {
|
|
||||||
final i4.$$RemoteAlbumEntityTableAnnotationComposer composer =
|
|
||||||
$composerBuilder(
|
|
||||||
composer: this,
|
|
||||||
getCurrentColumn: (t) => t.albumId,
|
|
||||||
referencedTable: i5.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'),
|
|
||||||
getReferencedColumn: (t) => t.id,
|
|
||||||
builder: (joinBuilder,
|
|
||||||
{$addJoinBuilderToRootComposer,
|
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
|
||||||
i4.$$RemoteAlbumEntityTableAnnotationComposer(
|
|
||||||
$db: $db,
|
|
||||||
$table: i5.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i4.$RemoteAlbumEntityTable>(
|
|
||||||
'remote_album_entity'),
|
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
|
||||||
joinBuilder: joinBuilder,
|
|
||||||
$removeJoinBuilderFromRootComposer:
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
));
|
|
||||||
return composer;
|
|
||||||
}
|
|
||||||
|
|
||||||
i6.$$UserEntityTableAnnotationComposer get userId {
|
|
||||||
final i6.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
|
|
||||||
composer: this,
|
|
||||||
getCurrentColumn: (t) => t.userId,
|
|
||||||
referencedTable: i5.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i6.$UserEntityTable>('user_entity'),
|
|
||||||
getReferencedColumn: (t) => t.id,
|
|
||||||
builder: (joinBuilder,
|
|
||||||
{$addJoinBuilderToRootComposer,
|
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
|
||||||
i6.$$UserEntityTableAnnotationComposer(
|
|
||||||
$db: $db,
|
|
||||||
$table: i5.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i6.$UserEntityTable>('user_entity'),
|
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
|
||||||
joinBuilder: joinBuilder,
|
|
||||||
$removeJoinBuilderFromRootComposer:
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
));
|
|
||||||
return composer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class $$AlbumUserEntityTableTableManager extends i0.RootTableManager<
|
|
||||||
i0.GeneratedDatabase,
|
|
||||||
i1.$AlbumUserEntityTable,
|
|
||||||
i1.AlbumUserEntityData,
|
|
||||||
i1.$$AlbumUserEntityTableFilterComposer,
|
|
||||||
i1.$$AlbumUserEntityTableOrderingComposer,
|
|
||||||
i1.$$AlbumUserEntityTableAnnotationComposer,
|
|
||||||
$$AlbumUserEntityTableCreateCompanionBuilder,
|
|
||||||
$$AlbumUserEntityTableUpdateCompanionBuilder,
|
|
||||||
(i1.AlbumUserEntityData, i1.$$AlbumUserEntityTableReferences),
|
|
||||||
i1.AlbumUserEntityData,
|
|
||||||
i0.PrefetchHooks Function({bool albumId, bool userId})> {
|
|
||||||
$$AlbumUserEntityTableTableManager(
|
|
||||||
i0.GeneratedDatabase db, i1.$AlbumUserEntityTable table)
|
|
||||||
: super(i0.TableManagerState(
|
|
||||||
db: db,
|
|
||||||
table: table,
|
|
||||||
createFilteringComposer: () =>
|
|
||||||
i1.$$AlbumUserEntityTableFilterComposer($db: db, $table: table),
|
|
||||||
createOrderingComposer: () =>
|
|
||||||
i1.$$AlbumUserEntityTableOrderingComposer($db: db, $table: table),
|
|
||||||
createComputedFieldComposer: () => i1
|
|
||||||
.$$AlbumUserEntityTableAnnotationComposer($db: db, $table: table),
|
|
||||||
updateCompanionCallback: ({
|
|
||||||
i0.Value<String> albumId = const i0.Value.absent(),
|
|
||||||
i0.Value<String> userId = const i0.Value.absent(),
|
|
||||||
i0.Value<i2.AlbumUserRole> role = const i0.Value.absent(),
|
|
||||||
}) =>
|
|
||||||
i1.AlbumUserEntityCompanion(
|
|
||||||
albumId: albumId,
|
|
||||||
userId: userId,
|
|
||||||
role: role,
|
|
||||||
),
|
|
||||||
createCompanionCallback: ({
|
|
||||||
required String albumId,
|
|
||||||
required String userId,
|
|
||||||
required i2.AlbumUserRole role,
|
|
||||||
}) =>
|
|
||||||
i1.AlbumUserEntityCompanion.insert(
|
|
||||||
albumId: albumId,
|
|
||||||
userId: userId,
|
|
||||||
role: role,
|
|
||||||
),
|
|
||||||
withReferenceMapper: (p0) => p0
|
|
||||||
.map((e) => (
|
|
||||||
e.readTable(table),
|
|
||||||
i1.$$AlbumUserEntityTableReferences(db, table, e)
|
|
||||||
))
|
|
||||||
.toList(),
|
|
||||||
prefetchHooksCallback: ({albumId = false, userId = false}) {
|
|
||||||
return i0.PrefetchHooks(
|
|
||||||
db: db,
|
|
||||||
explicitlyWatchedTables: [],
|
|
||||||
addJoins: <
|
|
||||||
T extends i0.TableManagerState<
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic>>(state) {
|
|
||||||
if (albumId) {
|
|
||||||
state = state.withJoin(
|
|
||||||
currentTable: table,
|
|
||||||
currentColumn: table.albumId,
|
|
||||||
referencedTable:
|
|
||||||
i1.$$AlbumUserEntityTableReferences._albumIdTable(db),
|
|
||||||
referencedColumn: i1.$$AlbumUserEntityTableReferences
|
|
||||||
._albumIdTable(db)
|
|
||||||
.id,
|
|
||||||
) as T;
|
|
||||||
}
|
|
||||||
if (userId) {
|
|
||||||
state = state.withJoin(
|
|
||||||
currentTable: table,
|
|
||||||
currentColumn: table.userId,
|
|
||||||
referencedTable:
|
|
||||||
i1.$$AlbumUserEntityTableReferences._userIdTable(db),
|
|
||||||
referencedColumn:
|
|
||||||
i1.$$AlbumUserEntityTableReferences._userIdTable(db).id,
|
|
||||||
) as T;
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
|
||||||
},
|
|
||||||
getPrefetchedDataCallback: (items) async {
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef $$AlbumUserEntityTableProcessedTableManager = i0.ProcessedTableManager<
|
|
||||||
i0.GeneratedDatabase,
|
|
||||||
i1.$AlbumUserEntityTable,
|
|
||||||
i1.AlbumUserEntityData,
|
|
||||||
i1.$$AlbumUserEntityTableFilterComposer,
|
|
||||||
i1.$$AlbumUserEntityTableOrderingComposer,
|
|
||||||
i1.$$AlbumUserEntityTableAnnotationComposer,
|
|
||||||
$$AlbumUserEntityTableCreateCompanionBuilder,
|
|
||||||
$$AlbumUserEntityTableUpdateCompanionBuilder,
|
|
||||||
(i1.AlbumUserEntityData, i1.$$AlbumUserEntityTableReferences),
|
|
||||||
i1.AlbumUserEntityData,
|
|
||||||
i0.PrefetchHooks Function({bool albumId, bool userId})>;
|
|
||||||
|
|
||||||
class $AlbumUserEntityTable extends i3.AlbumUserEntity
|
|
||||||
with i0.TableInfo<$AlbumUserEntityTable, i1.AlbumUserEntityData> {
|
|
||||||
@override
|
|
||||||
final i0.GeneratedDatabase attachedDatabase;
|
|
||||||
final String? _alias;
|
|
||||||
$AlbumUserEntityTable(this.attachedDatabase, [this._alias]);
|
|
||||||
static const i0.VerificationMeta _albumIdMeta =
|
|
||||||
const i0.VerificationMeta('albumId');
|
|
||||||
@override
|
|
||||||
late final i0.GeneratedColumn<String> albumId = i0.GeneratedColumn<String>(
|
|
||||||
'album_id', aliasedName, false,
|
|
||||||
type: i0.DriftSqlType.string,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
|
||||||
'REFERENCES remote_album_entity (id) ON DELETE CASCADE'));
|
|
||||||
static const i0.VerificationMeta _userIdMeta =
|
|
||||||
const i0.VerificationMeta('userId');
|
|
||||||
@override
|
|
||||||
late final i0.GeneratedColumn<String> userId = i0.GeneratedColumn<String>(
|
|
||||||
'user_id', aliasedName, false,
|
|
||||||
type: i0.DriftSqlType.string,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
|
||||||
'REFERENCES user_entity (id) ON DELETE CASCADE'));
|
|
||||||
@override
|
|
||||||
late final i0.GeneratedColumnWithTypeConverter<i2.AlbumUserRole, int> role =
|
|
||||||
i0.GeneratedColumn<int>('role', aliasedName, false,
|
|
||||||
type: i0.DriftSqlType.int, requiredDuringInsert: true)
|
|
||||||
.withConverter<i2.AlbumUserRole>(
|
|
||||||
i1.$AlbumUserEntityTable.$converterrole);
|
|
||||||
@override
|
|
||||||
List<i0.GeneratedColumn> get $columns => [albumId, userId, role];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? actualTableName;
|
|
||||||
@override
|
|
||||||
String get actualTableName => $name;
|
|
||||||
static const String $name = 'album_user_entity';
|
|
||||||
@override
|
|
||||||
i0.VerificationContext validateIntegrity(
|
|
||||||
i0.Insertable<i1.AlbumUserEntityData> instance,
|
|
||||||
{bool isInserting = false}) {
|
|
||||||
final context = i0.VerificationContext();
|
|
||||||
final data = instance.toColumns(true);
|
|
||||||
if (data.containsKey('album_id')) {
|
|
||||||
context.handle(_albumIdMeta,
|
|
||||||
albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta));
|
|
||||||
} else if (isInserting) {
|
|
||||||
context.missing(_albumIdMeta);
|
|
||||||
}
|
|
||||||
if (data.containsKey('user_id')) {
|
|
||||||
context.handle(_userIdMeta,
|
|
||||||
userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta));
|
|
||||||
} else if (isInserting) {
|
|
||||||
context.missing(_userIdMeta);
|
|
||||||
}
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Set<i0.GeneratedColumn> get $primaryKey => {albumId, userId};
|
|
||||||
@override
|
|
||||||
i1.AlbumUserEntityData map(Map<String, dynamic> data, {String? tablePrefix}) {
|
|
||||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
|
||||||
return i1.AlbumUserEntityData(
|
|
||||||
albumId: attachedDatabase.typeMapping
|
|
||||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}album_id'])!,
|
|
||||||
userId: attachedDatabase.typeMapping
|
|
||||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}user_id'])!,
|
|
||||||
role: i1.$AlbumUserEntityTable.$converterrole.fromSql(attachedDatabase
|
|
||||||
.typeMapping
|
|
||||||
.read(i0.DriftSqlType.int, data['${effectivePrefix}role'])!),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
$AlbumUserEntityTable createAlias(String alias) {
|
|
||||||
return $AlbumUserEntityTable(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
static i0.JsonTypeConverter2<i2.AlbumUserRole, int, int> $converterrole =
|
|
||||||
const i0.EnumIndexConverter<i2.AlbumUserRole>(i2.AlbumUserRole.values);
|
|
||||||
@override
|
|
||||||
bool get withoutRowId => true;
|
|
||||||
@override
|
|
||||||
bool get isStrict => true;
|
|
||||||
}
|
|
||||||
|
|
||||||
class AlbumUserEntityData extends i0.DataClass
|
|
||||||
implements i0.Insertable<i1.AlbumUserEntityData> {
|
|
||||||
final String albumId;
|
|
||||||
final String userId;
|
|
||||||
final i2.AlbumUserRole role;
|
|
||||||
const AlbumUserEntityData(
|
|
||||||
{required this.albumId, required this.userId, required this.role});
|
|
||||||
@override
|
|
||||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
|
||||||
final map = <String, i0.Expression>{};
|
|
||||||
map['album_id'] = i0.Variable<String>(albumId);
|
|
||||||
map['user_id'] = i0.Variable<String>(userId);
|
|
||||||
{
|
|
||||||
map['role'] =
|
|
||||||
i0.Variable<int>(i1.$AlbumUserEntityTable.$converterrole.toSql(role));
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
factory AlbumUserEntityData.fromJson(Map<String, dynamic> json,
|
|
||||||
{i0.ValueSerializer? serializer}) {
|
|
||||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
|
||||||
return AlbumUserEntityData(
|
|
||||||
albumId: serializer.fromJson<String>(json['albumId']),
|
|
||||||
userId: serializer.fromJson<String>(json['userId']),
|
|
||||||
role: i1.$AlbumUserEntityTable.$converterrole
|
|
||||||
.fromJson(serializer.fromJson<int>(json['role'])),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
|
||||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
|
||||||
return <String, dynamic>{
|
|
||||||
'albumId': serializer.toJson<String>(albumId),
|
|
||||||
'userId': serializer.toJson<String>(userId),
|
|
||||||
'role': serializer
|
|
||||||
.toJson<int>(i1.$AlbumUserEntityTable.$converterrole.toJson(role)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
i1.AlbumUserEntityData copyWith(
|
|
||||||
{String? albumId, String? userId, i2.AlbumUserRole? role}) =>
|
|
||||||
i1.AlbumUserEntityData(
|
|
||||||
albumId: albumId ?? this.albumId,
|
|
||||||
userId: userId ?? this.userId,
|
|
||||||
role: role ?? this.role,
|
|
||||||
);
|
|
||||||
AlbumUserEntityData copyWithCompanion(i1.AlbumUserEntityCompanion data) {
|
|
||||||
return AlbumUserEntityData(
|
|
||||||
albumId: data.albumId.present ? data.albumId.value : this.albumId,
|
|
||||||
userId: data.userId.present ? data.userId.value : this.userId,
|
|
||||||
role: data.role.present ? data.role.value : this.role,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return (StringBuffer('AlbumUserEntityData(')
|
|
||||||
..write('albumId: $albumId, ')
|
|
||||||
..write('userId: $userId, ')
|
|
||||||
..write('role: $role')
|
|
||||||
..write(')'))
|
|
||||||
.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(albumId, userId, role);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) =>
|
|
||||||
identical(this, other) ||
|
|
||||||
(other is i1.AlbumUserEntityData &&
|
|
||||||
other.albumId == this.albumId &&
|
|
||||||
other.userId == this.userId &&
|
|
||||||
other.role == this.role);
|
|
||||||
}
|
|
||||||
|
|
||||||
class AlbumUserEntityCompanion
|
|
||||||
extends i0.UpdateCompanion<i1.AlbumUserEntityData> {
|
|
||||||
final i0.Value<String> albumId;
|
|
||||||
final i0.Value<String> userId;
|
|
||||||
final i0.Value<i2.AlbumUserRole> role;
|
|
||||||
const AlbumUserEntityCompanion({
|
|
||||||
this.albumId = const i0.Value.absent(),
|
|
||||||
this.userId = const i0.Value.absent(),
|
|
||||||
this.role = const i0.Value.absent(),
|
|
||||||
});
|
|
||||||
AlbumUserEntityCompanion.insert({
|
|
||||||
required String albumId,
|
|
||||||
required String userId,
|
|
||||||
required i2.AlbumUserRole role,
|
|
||||||
}) : albumId = i0.Value(albumId),
|
|
||||||
userId = i0.Value(userId),
|
|
||||||
role = i0.Value(role);
|
|
||||||
static i0.Insertable<i1.AlbumUserEntityData> custom({
|
|
||||||
i0.Expression<String>? albumId,
|
|
||||||
i0.Expression<String>? userId,
|
|
||||||
i0.Expression<int>? role,
|
|
||||||
}) {
|
|
||||||
return i0.RawValuesInsertable({
|
|
||||||
if (albumId != null) 'album_id': albumId,
|
|
||||||
if (userId != null) 'user_id': userId,
|
|
||||||
if (role != null) 'role': role,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
i1.AlbumUserEntityCompanion copyWith(
|
|
||||||
{i0.Value<String>? albumId,
|
|
||||||
i0.Value<String>? userId,
|
|
||||||
i0.Value<i2.AlbumUserRole>? role}) {
|
|
||||||
return i1.AlbumUserEntityCompanion(
|
|
||||||
albumId: albumId ?? this.albumId,
|
|
||||||
userId: userId ?? this.userId,
|
|
||||||
role: role ?? this.role,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
|
||||||
final map = <String, i0.Expression>{};
|
|
||||||
if (albumId.present) {
|
|
||||||
map['album_id'] = i0.Variable<String>(albumId.value);
|
|
||||||
}
|
|
||||||
if (userId.present) {
|
|
||||||
map['user_id'] = i0.Variable<String>(userId.value);
|
|
||||||
}
|
|
||||||
if (role.present) {
|
|
||||||
map['role'] = i0.Variable<int>(
|
|
||||||
i1.$AlbumUserEntityTable.$converterrole.toSql(role.value));
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return (StringBuffer('AlbumUserEntityCompanion(')
|
|
||||||
..write('albumId: $albumId, ')
|
|
||||||
..write('userId: $userId, ')
|
|
||||||
..write('role: $role')
|
|
||||||
..write(')'))
|
|
||||||
.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import 'package:drift/drift.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
|
||||||
|
|
||||||
class RemoteAlbumEntity extends Table with DriftDefaultsMixin {
|
|
||||||
const RemoteAlbumEntity();
|
|
||||||
|
|
||||||
TextColumn get id => text()();
|
|
||||||
|
|
||||||
TextColumn get name => text()();
|
|
||||||
|
|
||||||
TextColumn get description => text()();
|
|
||||||
|
|
||||||
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
|
||||||
|
|
||||||
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
|
|
||||||
|
|
||||||
TextColumn get ownerId =>
|
|
||||||
text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
|
|
||||||
|
|
||||||
TextColumn get thumbnailAssetId => text()
|
|
||||||
.references(RemoteAssetEntity, #id, onDelete: KeyAction.setNull)
|
|
||||||
.nullable()();
|
|
||||||
|
|
||||||
BoolColumn get isActivityEnabled =>
|
|
||||||
boolean().withDefault(const Constant(true))();
|
|
||||||
|
|
||||||
IntColumn get order => intEnum<AssetOrder>()();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Set<Column> get primaryKey => {id};
|
|
||||||
}
|
|
||||||
@@ -1,946 +0,0 @@
|
|||||||
// dart format width=80
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
import 'package:drift/drift.dart' as i0;
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
|
|
||||||
as i1;
|
|
||||||
import 'package:immich_mobile/domain/models/album/album.model.dart' as i2;
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart'
|
|
||||||
as i3;
|
|
||||||
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4;
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
|
|
||||||
as i5;
|
|
||||||
import 'package:drift/internal/modular.dart' as i6;
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'
|
|
||||||
as i7;
|
|
||||||
|
|
||||||
typedef $$RemoteAlbumEntityTableCreateCompanionBuilder
|
|
||||||
= i1.RemoteAlbumEntityCompanion Function({
|
|
||||||
required String id,
|
|
||||||
required String name,
|
|
||||||
required String description,
|
|
||||||
i0.Value<DateTime> createdAt,
|
|
||||||
i0.Value<DateTime> updatedAt,
|
|
||||||
required String ownerId,
|
|
||||||
i0.Value<String?> thumbnailAssetId,
|
|
||||||
i0.Value<bool> isActivityEnabled,
|
|
||||||
required i2.AssetOrder order,
|
|
||||||
});
|
|
||||||
typedef $$RemoteAlbumEntityTableUpdateCompanionBuilder
|
|
||||||
= i1.RemoteAlbumEntityCompanion Function({
|
|
||||||
i0.Value<String> id,
|
|
||||||
i0.Value<String> name,
|
|
||||||
i0.Value<String> description,
|
|
||||||
i0.Value<DateTime> createdAt,
|
|
||||||
i0.Value<DateTime> updatedAt,
|
|
||||||
i0.Value<String> ownerId,
|
|
||||||
i0.Value<String?> thumbnailAssetId,
|
|
||||||
i0.Value<bool> isActivityEnabled,
|
|
||||||
i0.Value<i2.AssetOrder> order,
|
|
||||||
});
|
|
||||||
|
|
||||||
final class $$RemoteAlbumEntityTableReferences extends i0.BaseReferences<
|
|
||||||
i0.GeneratedDatabase,
|
|
||||||
i1.$RemoteAlbumEntityTable,
|
|
||||||
i1.RemoteAlbumEntityData> {
|
|
||||||
$$RemoteAlbumEntityTableReferences(
|
|
||||||
super.$_db, super.$_table, super.$_typedResult);
|
|
||||||
|
|
||||||
static i5.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) =>
|
|
||||||
i6.ReadDatabaseContainer(db)
|
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity')
|
|
||||||
.createAlias(i0.$_aliasNameGenerator(
|
|
||||||
i6.ReadDatabaseContainer(db)
|
|
||||||
.resultSet<i1.$RemoteAlbumEntityTable>('remote_album_entity')
|
|
||||||
.ownerId,
|
|
||||||
i6.ReadDatabaseContainer(db)
|
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity')
|
|
||||||
.id));
|
|
||||||
|
|
||||||
i5.$$UserEntityTableProcessedTableManager get ownerId {
|
|
||||||
final $_column = $_itemColumn<String>('owner_id')!;
|
|
||||||
|
|
||||||
final manager = i5
|
|
||||||
.$$UserEntityTableTableManager(
|
|
||||||
$_db,
|
|
||||||
i6.ReadDatabaseContainer($_db)
|
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'))
|
|
||||||
.filter((f) => f.id.sqlEquals($_column));
|
|
||||||
final item = $_typedResult.readTableOrNull(_ownerIdTable($_db));
|
|
||||||
if (item == null) return manager;
|
|
||||||
return i0.ProcessedTableManager(
|
|
||||||
manager.$state.copyWith(prefetchedData: [item]));
|
|
||||||
}
|
|
||||||
|
|
||||||
static i7.$RemoteAssetEntityTable _thumbnailAssetIdTable(
|
|
||||||
i0.GeneratedDatabase db) =>
|
|
||||||
i6.ReadDatabaseContainer(db)
|
|
||||||
.resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity')
|
|
||||||
.createAlias(i0.$_aliasNameGenerator(
|
|
||||||
i6.ReadDatabaseContainer(db)
|
|
||||||
.resultSet<i1.$RemoteAlbumEntityTable>('remote_album_entity')
|
|
||||||
.thumbnailAssetId,
|
|
||||||
i6.ReadDatabaseContainer(db)
|
|
||||||
.resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity')
|
|
||||||
.id));
|
|
||||||
|
|
||||||
i7.$$RemoteAssetEntityTableProcessedTableManager? get thumbnailAssetId {
|
|
||||||
final $_column = $_itemColumn<String>('thumbnail_asset_id');
|
|
||||||
if ($_column == null) return null;
|
|
||||||
final manager = i7
|
|
||||||
.$$RemoteAssetEntityTableTableManager(
|
|
||||||
$_db,
|
|
||||||
i6.ReadDatabaseContainer($_db)
|
|
||||||
.resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'))
|
|
||||||
.filter((f) => f.id.sqlEquals($_column));
|
|
||||||
final item = $_typedResult.readTableOrNull(_thumbnailAssetIdTable($_db));
|
|
||||||
if (item == null) return manager;
|
|
||||||
return i0.ProcessedTableManager(
|
|
||||||
manager.$state.copyWith(prefetchedData: [item]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class $$RemoteAlbumEntityTableFilterComposer
|
|
||||||
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumEntityTable> {
|
|
||||||
$$RemoteAlbumEntityTableFilterComposer({
|
|
||||||
required super.$db,
|
|
||||||
required super.$table,
|
|
||||||
super.joinBuilder,
|
|
||||||
super.$addJoinBuilderToRootComposer,
|
|
||||||
super.$removeJoinBuilderFromRootComposer,
|
|
||||||
});
|
|
||||||
i0.ColumnFilters<String> get id => $composableBuilder(
|
|
||||||
column: $table.id, builder: (column) => i0.ColumnFilters(column));
|
|
||||||
|
|
||||||
i0.ColumnFilters<String> get name => $composableBuilder(
|
|
||||||
column: $table.name, builder: (column) => i0.ColumnFilters(column));
|
|
||||||
|
|
||||||
i0.ColumnFilters<String> get description => $composableBuilder(
|
|
||||||
column: $table.description,
|
|
||||||
builder: (column) => i0.ColumnFilters(column));
|
|
||||||
|
|
||||||
i0.ColumnFilters<DateTime> get createdAt => $composableBuilder(
|
|
||||||
column: $table.createdAt, builder: (column) => i0.ColumnFilters(column));
|
|
||||||
|
|
||||||
i0.ColumnFilters<DateTime> get updatedAt => $composableBuilder(
|
|
||||||
column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column));
|
|
||||||
|
|
||||||
i0.ColumnFilters<bool> get isActivityEnabled => $composableBuilder(
|
|
||||||
column: $table.isActivityEnabled,
|
|
||||||
builder: (column) => i0.ColumnFilters(column));
|
|
||||||
|
|
||||||
i0.ColumnWithTypeConverterFilters<i2.AssetOrder, i2.AssetOrder, int>
|
|
||||||
get order => $composableBuilder(
|
|
||||||
column: $table.order,
|
|
||||||
builder: (column) => i0.ColumnWithTypeConverterFilters(column));
|
|
||||||
|
|
||||||
i5.$$UserEntityTableFilterComposer get ownerId {
|
|
||||||
final i5.$$UserEntityTableFilterComposer composer = $composerBuilder(
|
|
||||||
composer: this,
|
|
||||||
getCurrentColumn: (t) => t.ownerId,
|
|
||||||
referencedTable: i6.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
|
||||||
getReferencedColumn: (t) => t.id,
|
|
||||||
builder: (joinBuilder,
|
|
||||||
{$addJoinBuilderToRootComposer,
|
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
|
||||||
i5.$$UserEntityTableFilterComposer(
|
|
||||||
$db: $db,
|
|
||||||
$table: i6.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
|
||||||
joinBuilder: joinBuilder,
|
|
||||||
$removeJoinBuilderFromRootComposer:
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
));
|
|
||||||
return composer;
|
|
||||||
}
|
|
||||||
|
|
||||||
i7.$$RemoteAssetEntityTableFilterComposer get thumbnailAssetId {
|
|
||||||
final i7.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder(
|
|
||||||
composer: this,
|
|
||||||
getCurrentColumn: (t) => t.thumbnailAssetId,
|
|
||||||
referencedTable: i6.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
|
|
||||||
getReferencedColumn: (t) => t.id,
|
|
||||||
builder: (joinBuilder,
|
|
||||||
{$addJoinBuilderToRootComposer,
|
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
|
||||||
i7.$$RemoteAssetEntityTableFilterComposer(
|
|
||||||
$db: $db,
|
|
||||||
$table: i6.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
|
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
|
||||||
joinBuilder: joinBuilder,
|
|
||||||
$removeJoinBuilderFromRootComposer:
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
));
|
|
||||||
return composer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class $$RemoteAlbumEntityTableOrderingComposer
|
|
||||||
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumEntityTable> {
|
|
||||||
$$RemoteAlbumEntityTableOrderingComposer({
|
|
||||||
required super.$db,
|
|
||||||
required super.$table,
|
|
||||||
super.joinBuilder,
|
|
||||||
super.$addJoinBuilderToRootComposer,
|
|
||||||
super.$removeJoinBuilderFromRootComposer,
|
|
||||||
});
|
|
||||||
i0.ColumnOrderings<String> get id => $composableBuilder(
|
|
||||||
column: $table.id, builder: (column) => i0.ColumnOrderings(column));
|
|
||||||
|
|
||||||
i0.ColumnOrderings<String> get name => $composableBuilder(
|
|
||||||
column: $table.name, builder: (column) => i0.ColumnOrderings(column));
|
|
||||||
|
|
||||||
i0.ColumnOrderings<String> get description => $composableBuilder(
|
|
||||||
column: $table.description,
|
|
||||||
builder: (column) => i0.ColumnOrderings(column));
|
|
||||||
|
|
||||||
i0.ColumnOrderings<DateTime> get createdAt => $composableBuilder(
|
|
||||||
column: $table.createdAt,
|
|
||||||
builder: (column) => i0.ColumnOrderings(column));
|
|
||||||
|
|
||||||
i0.ColumnOrderings<DateTime> get updatedAt => $composableBuilder(
|
|
||||||
column: $table.updatedAt,
|
|
||||||
builder: (column) => i0.ColumnOrderings(column));
|
|
||||||
|
|
||||||
i0.ColumnOrderings<bool> get isActivityEnabled => $composableBuilder(
|
|
||||||
column: $table.isActivityEnabled,
|
|
||||||
builder: (column) => i0.ColumnOrderings(column));
|
|
||||||
|
|
||||||
i0.ColumnOrderings<int> get order => $composableBuilder(
|
|
||||||
column: $table.order, builder: (column) => i0.ColumnOrderings(column));
|
|
||||||
|
|
||||||
i5.$$UserEntityTableOrderingComposer get ownerId {
|
|
||||||
final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder(
|
|
||||||
composer: this,
|
|
||||||
getCurrentColumn: (t) => t.ownerId,
|
|
||||||
referencedTable: i6.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
|
||||||
getReferencedColumn: (t) => t.id,
|
|
||||||
builder: (joinBuilder,
|
|
||||||
{$addJoinBuilderToRootComposer,
|
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
|
||||||
i5.$$UserEntityTableOrderingComposer(
|
|
||||||
$db: $db,
|
|
||||||
$table: i6.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
|
||||||
joinBuilder: joinBuilder,
|
|
||||||
$removeJoinBuilderFromRootComposer:
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
));
|
|
||||||
return composer;
|
|
||||||
}
|
|
||||||
|
|
||||||
i7.$$RemoteAssetEntityTableOrderingComposer get thumbnailAssetId {
|
|
||||||
final i7.$$RemoteAssetEntityTableOrderingComposer composer =
|
|
||||||
$composerBuilder(
|
|
||||||
composer: this,
|
|
||||||
getCurrentColumn: (t) => t.thumbnailAssetId,
|
|
||||||
referencedTable: i6.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
|
|
||||||
getReferencedColumn: (t) => t.id,
|
|
||||||
builder: (joinBuilder,
|
|
||||||
{$addJoinBuilderToRootComposer,
|
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
|
||||||
i7.$$RemoteAssetEntityTableOrderingComposer(
|
|
||||||
$db: $db,
|
|
||||||
$table: i6.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i7.$RemoteAssetEntityTable>(
|
|
||||||
'remote_asset_entity'),
|
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
|
||||||
joinBuilder: joinBuilder,
|
|
||||||
$removeJoinBuilderFromRootComposer:
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
));
|
|
||||||
return composer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class $$RemoteAlbumEntityTableAnnotationComposer
|
|
||||||
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumEntityTable> {
|
|
||||||
$$RemoteAlbumEntityTableAnnotationComposer({
|
|
||||||
required super.$db,
|
|
||||||
required super.$table,
|
|
||||||
super.joinBuilder,
|
|
||||||
super.$addJoinBuilderToRootComposer,
|
|
||||||
super.$removeJoinBuilderFromRootComposer,
|
|
||||||
});
|
|
||||||
i0.GeneratedColumn<String> get id =>
|
|
||||||
$composableBuilder(column: $table.id, builder: (column) => column);
|
|
||||||
|
|
||||||
i0.GeneratedColumn<String> get name =>
|
|
||||||
$composableBuilder(column: $table.name, builder: (column) => column);
|
|
||||||
|
|
||||||
i0.GeneratedColumn<String> get description => $composableBuilder(
|
|
||||||
column: $table.description, builder: (column) => column);
|
|
||||||
|
|
||||||
i0.GeneratedColumn<DateTime> get createdAt =>
|
|
||||||
$composableBuilder(column: $table.createdAt, builder: (column) => column);
|
|
||||||
|
|
||||||
i0.GeneratedColumn<DateTime> get updatedAt =>
|
|
||||||
$composableBuilder(column: $table.updatedAt, builder: (column) => column);
|
|
||||||
|
|
||||||
i0.GeneratedColumn<bool> get isActivityEnabled => $composableBuilder(
|
|
||||||
column: $table.isActivityEnabled, builder: (column) => column);
|
|
||||||
|
|
||||||
i0.GeneratedColumnWithTypeConverter<i2.AssetOrder, int> get order =>
|
|
||||||
$composableBuilder(column: $table.order, builder: (column) => column);
|
|
||||||
|
|
||||||
i5.$$UserEntityTableAnnotationComposer get ownerId {
|
|
||||||
final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
|
|
||||||
composer: this,
|
|
||||||
getCurrentColumn: (t) => t.ownerId,
|
|
||||||
referencedTable: i6.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
|
||||||
getReferencedColumn: (t) => t.id,
|
|
||||||
builder: (joinBuilder,
|
|
||||||
{$addJoinBuilderToRootComposer,
|
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
|
||||||
i5.$$UserEntityTableAnnotationComposer(
|
|
||||||
$db: $db,
|
|
||||||
$table: i6.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i5.$UserEntityTable>('user_entity'),
|
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
|
||||||
joinBuilder: joinBuilder,
|
|
||||||
$removeJoinBuilderFromRootComposer:
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
));
|
|
||||||
return composer;
|
|
||||||
}
|
|
||||||
|
|
||||||
i7.$$RemoteAssetEntityTableAnnotationComposer get thumbnailAssetId {
|
|
||||||
final i7.$$RemoteAssetEntityTableAnnotationComposer composer =
|
|
||||||
$composerBuilder(
|
|
||||||
composer: this,
|
|
||||||
getCurrentColumn: (t) => t.thumbnailAssetId,
|
|
||||||
referencedTable: i6.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
|
|
||||||
getReferencedColumn: (t) => t.id,
|
|
||||||
builder: (joinBuilder,
|
|
||||||
{$addJoinBuilderToRootComposer,
|
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
|
||||||
i7.$$RemoteAssetEntityTableAnnotationComposer(
|
|
||||||
$db: $db,
|
|
||||||
$table: i6.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i7.$RemoteAssetEntityTable>(
|
|
||||||
'remote_asset_entity'),
|
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
|
||||||
joinBuilder: joinBuilder,
|
|
||||||
$removeJoinBuilderFromRootComposer:
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
));
|
|
||||||
return composer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class $$RemoteAlbumEntityTableTableManager extends i0.RootTableManager<
|
|
||||||
i0.GeneratedDatabase,
|
|
||||||
i1.$RemoteAlbumEntityTable,
|
|
||||||
i1.RemoteAlbumEntityData,
|
|
||||||
i1.$$RemoteAlbumEntityTableFilterComposer,
|
|
||||||
i1.$$RemoteAlbumEntityTableOrderingComposer,
|
|
||||||
i1.$$RemoteAlbumEntityTableAnnotationComposer,
|
|
||||||
$$RemoteAlbumEntityTableCreateCompanionBuilder,
|
|
||||||
$$RemoteAlbumEntityTableUpdateCompanionBuilder,
|
|
||||||
(i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences),
|
|
||||||
i1.RemoteAlbumEntityData,
|
|
||||||
i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId})> {
|
|
||||||
$$RemoteAlbumEntityTableTableManager(
|
|
||||||
i0.GeneratedDatabase db, i1.$RemoteAlbumEntityTable table)
|
|
||||||
: super(i0.TableManagerState(
|
|
||||||
db: db,
|
|
||||||
table: table,
|
|
||||||
createFilteringComposer: () =>
|
|
||||||
i1.$$RemoteAlbumEntityTableFilterComposer($db: db, $table: table),
|
|
||||||
createOrderingComposer: () => i1
|
|
||||||
.$$RemoteAlbumEntityTableOrderingComposer($db: db, $table: table),
|
|
||||||
createComputedFieldComposer: () =>
|
|
||||||
i1.$$RemoteAlbumEntityTableAnnotationComposer(
|
|
||||||
$db: db, $table: table),
|
|
||||||
updateCompanionCallback: ({
|
|
||||||
i0.Value<String> id = const i0.Value.absent(),
|
|
||||||
i0.Value<String> name = const i0.Value.absent(),
|
|
||||||
i0.Value<String> description = const i0.Value.absent(),
|
|
||||||
i0.Value<DateTime> createdAt = const i0.Value.absent(),
|
|
||||||
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
|
||||||
i0.Value<String> ownerId = const i0.Value.absent(),
|
|
||||||
i0.Value<String?> thumbnailAssetId = const i0.Value.absent(),
|
|
||||||
i0.Value<bool> isActivityEnabled = const i0.Value.absent(),
|
|
||||||
i0.Value<i2.AssetOrder> order = const i0.Value.absent(),
|
|
||||||
}) =>
|
|
||||||
i1.RemoteAlbumEntityCompanion(
|
|
||||||
id: id,
|
|
||||||
name: name,
|
|
||||||
description: description,
|
|
||||||
createdAt: createdAt,
|
|
||||||
updatedAt: updatedAt,
|
|
||||||
ownerId: ownerId,
|
|
||||||
thumbnailAssetId: thumbnailAssetId,
|
|
||||||
isActivityEnabled: isActivityEnabled,
|
|
||||||
order: order,
|
|
||||||
),
|
|
||||||
createCompanionCallback: ({
|
|
||||||
required String id,
|
|
||||||
required String name,
|
|
||||||
required String description,
|
|
||||||
i0.Value<DateTime> createdAt = const i0.Value.absent(),
|
|
||||||
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
|
||||||
required String ownerId,
|
|
||||||
i0.Value<String?> thumbnailAssetId = const i0.Value.absent(),
|
|
||||||
i0.Value<bool> isActivityEnabled = const i0.Value.absent(),
|
|
||||||
required i2.AssetOrder order,
|
|
||||||
}) =>
|
|
||||||
i1.RemoteAlbumEntityCompanion.insert(
|
|
||||||
id: id,
|
|
||||||
name: name,
|
|
||||||
description: description,
|
|
||||||
createdAt: createdAt,
|
|
||||||
updatedAt: updatedAt,
|
|
||||||
ownerId: ownerId,
|
|
||||||
thumbnailAssetId: thumbnailAssetId,
|
|
||||||
isActivityEnabled: isActivityEnabled,
|
|
||||||
order: order,
|
|
||||||
),
|
|
||||||
withReferenceMapper: (p0) => p0
|
|
||||||
.map((e) => (
|
|
||||||
e.readTable(table),
|
|
||||||
i1.$$RemoteAlbumEntityTableReferences(db, table, e)
|
|
||||||
))
|
|
||||||
.toList(),
|
|
||||||
prefetchHooksCallback: ({ownerId = false, thumbnailAssetId = false}) {
|
|
||||||
return i0.PrefetchHooks(
|
|
||||||
db: db,
|
|
||||||
explicitlyWatchedTables: [],
|
|
||||||
addJoins: <
|
|
||||||
T extends i0.TableManagerState<
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic>>(state) {
|
|
||||||
if (ownerId) {
|
|
||||||
state = state.withJoin(
|
|
||||||
currentTable: table,
|
|
||||||
currentColumn: table.ownerId,
|
|
||||||
referencedTable:
|
|
||||||
i1.$$RemoteAlbumEntityTableReferences._ownerIdTable(db),
|
|
||||||
referencedColumn: i1.$$RemoteAlbumEntityTableReferences
|
|
||||||
._ownerIdTable(db)
|
|
||||||
.id,
|
|
||||||
) as T;
|
|
||||||
}
|
|
||||||
if (thumbnailAssetId) {
|
|
||||||
state = state.withJoin(
|
|
||||||
currentTable: table,
|
|
||||||
currentColumn: table.thumbnailAssetId,
|
|
||||||
referencedTable: i1.$$RemoteAlbumEntityTableReferences
|
|
||||||
._thumbnailAssetIdTable(db),
|
|
||||||
referencedColumn: i1.$$RemoteAlbumEntityTableReferences
|
|
||||||
._thumbnailAssetIdTable(db)
|
|
||||||
.id,
|
|
||||||
) as T;
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
|
||||||
},
|
|
||||||
getPrefetchedDataCallback: (items) async {
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef $$RemoteAlbumEntityTableProcessedTableManager
|
|
||||||
= i0.ProcessedTableManager<
|
|
||||||
i0.GeneratedDatabase,
|
|
||||||
i1.$RemoteAlbumEntityTable,
|
|
||||||
i1.RemoteAlbumEntityData,
|
|
||||||
i1.$$RemoteAlbumEntityTableFilterComposer,
|
|
||||||
i1.$$RemoteAlbumEntityTableOrderingComposer,
|
|
||||||
i1.$$RemoteAlbumEntityTableAnnotationComposer,
|
|
||||||
$$RemoteAlbumEntityTableCreateCompanionBuilder,
|
|
||||||
$$RemoteAlbumEntityTableUpdateCompanionBuilder,
|
|
||||||
(i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences),
|
|
||||||
i1.RemoteAlbumEntityData,
|
|
||||||
i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId})>;
|
|
||||||
|
|
||||||
class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity
|
|
||||||
with i0.TableInfo<$RemoteAlbumEntityTable, i1.RemoteAlbumEntityData> {
|
|
||||||
@override
|
|
||||||
final i0.GeneratedDatabase attachedDatabase;
|
|
||||||
final String? _alias;
|
|
||||||
$RemoteAlbumEntityTable(this.attachedDatabase, [this._alias]);
|
|
||||||
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
|
|
||||||
@override
|
|
||||||
late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
|
|
||||||
'id', aliasedName, false,
|
|
||||||
type: i0.DriftSqlType.string, requiredDuringInsert: true);
|
|
||||||
static const i0.VerificationMeta _nameMeta =
|
|
||||||
const i0.VerificationMeta('name');
|
|
||||||
@override
|
|
||||||
late final i0.GeneratedColumn<String> name = i0.GeneratedColumn<String>(
|
|
||||||
'name', aliasedName, false,
|
|
||||||
type: i0.DriftSqlType.string, requiredDuringInsert: true);
|
|
||||||
static const i0.VerificationMeta _descriptionMeta =
|
|
||||||
const i0.VerificationMeta('description');
|
|
||||||
@override
|
|
||||||
late final i0.GeneratedColumn<String> description =
|
|
||||||
i0.GeneratedColumn<String>('description', aliasedName, false,
|
|
||||||
type: i0.DriftSqlType.string, requiredDuringInsert: true);
|
|
||||||
static const i0.VerificationMeta _createdAtMeta =
|
|
||||||
const i0.VerificationMeta('createdAt');
|
|
||||||
@override
|
|
||||||
late final i0.GeneratedColumn<DateTime> createdAt =
|
|
||||||
i0.GeneratedColumn<DateTime>('created_at', aliasedName, false,
|
|
||||||
type: i0.DriftSqlType.dateTime,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultValue: i4.currentDateAndTime);
|
|
||||||
static const i0.VerificationMeta _updatedAtMeta =
|
|
||||||
const i0.VerificationMeta('updatedAt');
|
|
||||||
@override
|
|
||||||
late final i0.GeneratedColumn<DateTime> updatedAt =
|
|
||||||
i0.GeneratedColumn<DateTime>('updated_at', aliasedName, false,
|
|
||||||
type: i0.DriftSqlType.dateTime,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultValue: i4.currentDateAndTime);
|
|
||||||
static const i0.VerificationMeta _ownerIdMeta =
|
|
||||||
const i0.VerificationMeta('ownerId');
|
|
||||||
@override
|
|
||||||
late final i0.GeneratedColumn<String> ownerId = i0.GeneratedColumn<String>(
|
|
||||||
'owner_id', aliasedName, false,
|
|
||||||
type: i0.DriftSqlType.string,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
|
||||||
'REFERENCES user_entity (id) ON DELETE CASCADE'));
|
|
||||||
static const i0.VerificationMeta _thumbnailAssetIdMeta =
|
|
||||||
const i0.VerificationMeta('thumbnailAssetId');
|
|
||||||
@override
|
|
||||||
late final i0.GeneratedColumn<String> thumbnailAssetId =
|
|
||||||
i0.GeneratedColumn<String>('thumbnail_asset_id', aliasedName, true,
|
|
||||||
type: i0.DriftSqlType.string,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
|
||||||
'REFERENCES remote_asset_entity (id) ON DELETE SET NULL'));
|
|
||||||
static const i0.VerificationMeta _isActivityEnabledMeta =
|
|
||||||
const i0.VerificationMeta('isActivityEnabled');
|
|
||||||
@override
|
|
||||||
late final i0.GeneratedColumn<bool> isActivityEnabled =
|
|
||||||
i0.GeneratedColumn<bool>('is_activity_enabled', aliasedName, false,
|
|
||||||
type: i0.DriftSqlType.bool,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
|
||||||
'CHECK ("is_activity_enabled" IN (0, 1))'),
|
|
||||||
defaultValue: const i4.Constant(true));
|
|
||||||
@override
|
|
||||||
late final i0.GeneratedColumnWithTypeConverter<i2.AssetOrder, int> order =
|
|
||||||
i0.GeneratedColumn<int>('order', aliasedName, false,
|
|
||||||
type: i0.DriftSqlType.int, requiredDuringInsert: true)
|
|
||||||
.withConverter<i2.AssetOrder>(
|
|
||||||
i1.$RemoteAlbumEntityTable.$converterorder);
|
|
||||||
@override
|
|
||||||
List<i0.GeneratedColumn> get $columns => [
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
createdAt,
|
|
||||||
updatedAt,
|
|
||||||
ownerId,
|
|
||||||
thumbnailAssetId,
|
|
||||||
isActivityEnabled,
|
|
||||||
order
|
|
||||||
];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? actualTableName;
|
|
||||||
@override
|
|
||||||
String get actualTableName => $name;
|
|
||||||
static const String $name = 'remote_album_entity';
|
|
||||||
@override
|
|
||||||
i0.VerificationContext validateIntegrity(
|
|
||||||
i0.Insertable<i1.RemoteAlbumEntityData> instance,
|
|
||||||
{bool isInserting = false}) {
|
|
||||||
final context = i0.VerificationContext();
|
|
||||||
final data = instance.toColumns(true);
|
|
||||||
if (data.containsKey('id')) {
|
|
||||||
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
|
|
||||||
} else if (isInserting) {
|
|
||||||
context.missing(_idMeta);
|
|
||||||
}
|
|
||||||
if (data.containsKey('name')) {
|
|
||||||
context.handle(
|
|
||||||
_nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta));
|
|
||||||
} else if (isInserting) {
|
|
||||||
context.missing(_nameMeta);
|
|
||||||
}
|
|
||||||
if (data.containsKey('description')) {
|
|
||||||
context.handle(
|
|
||||||
_descriptionMeta,
|
|
||||||
description.isAcceptableOrUnknown(
|
|
||||||
data['description']!, _descriptionMeta));
|
|
||||||
} else if (isInserting) {
|
|
||||||
context.missing(_descriptionMeta);
|
|
||||||
}
|
|
||||||
if (data.containsKey('created_at')) {
|
|
||||||
context.handle(_createdAtMeta,
|
|
||||||
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta));
|
|
||||||
}
|
|
||||||
if (data.containsKey('updated_at')) {
|
|
||||||
context.handle(_updatedAtMeta,
|
|
||||||
updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta));
|
|
||||||
}
|
|
||||||
if (data.containsKey('owner_id')) {
|
|
||||||
context.handle(_ownerIdMeta,
|
|
||||||
ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta));
|
|
||||||
} else if (isInserting) {
|
|
||||||
context.missing(_ownerIdMeta);
|
|
||||||
}
|
|
||||||
if (data.containsKey('thumbnail_asset_id')) {
|
|
||||||
context.handle(
|
|
||||||
_thumbnailAssetIdMeta,
|
|
||||||
thumbnailAssetId.isAcceptableOrUnknown(
|
|
||||||
data['thumbnail_asset_id']!, _thumbnailAssetIdMeta));
|
|
||||||
}
|
|
||||||
if (data.containsKey('is_activity_enabled')) {
|
|
||||||
context.handle(
|
|
||||||
_isActivityEnabledMeta,
|
|
||||||
isActivityEnabled.isAcceptableOrUnknown(
|
|
||||||
data['is_activity_enabled']!, _isActivityEnabledMeta));
|
|
||||||
}
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Set<i0.GeneratedColumn> get $primaryKey => {id};
|
|
||||||
@override
|
|
||||||
i1.RemoteAlbumEntityData map(Map<String, dynamic> data,
|
|
||||||
{String? tablePrefix}) {
|
|
||||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
|
||||||
return i1.RemoteAlbumEntityData(
|
|
||||||
id: attachedDatabase.typeMapping
|
|
||||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!,
|
|
||||||
name: attachedDatabase.typeMapping
|
|
||||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!,
|
|
||||||
description: attachedDatabase.typeMapping
|
|
||||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}description'])!,
|
|
||||||
createdAt: attachedDatabase.typeMapping.read(
|
|
||||||
i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
|
|
||||||
updatedAt: attachedDatabase.typeMapping.read(
|
|
||||||
i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!,
|
|
||||||
ownerId: attachedDatabase.typeMapping
|
|
||||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!,
|
|
||||||
thumbnailAssetId: attachedDatabase.typeMapping.read(
|
|
||||||
i0.DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']),
|
|
||||||
isActivityEnabled: attachedDatabase.typeMapping.read(
|
|
||||||
i0.DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!,
|
|
||||||
order: i1.$RemoteAlbumEntityTable.$converterorder.fromSql(attachedDatabase
|
|
||||||
.typeMapping
|
|
||||||
.read(i0.DriftSqlType.int, data['${effectivePrefix}order'])!),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
$RemoteAlbumEntityTable createAlias(String alias) {
|
|
||||||
return $RemoteAlbumEntityTable(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
static i0.JsonTypeConverter2<i2.AssetOrder, int, int> $converterorder =
|
|
||||||
const i0.EnumIndexConverter<i2.AssetOrder>(i2.AssetOrder.values);
|
|
||||||
@override
|
|
||||||
bool get withoutRowId => true;
|
|
||||||
@override
|
|
||||||
bool get isStrict => true;
|
|
||||||
}
|
|
||||||
|
|
||||||
class RemoteAlbumEntityData extends i0.DataClass
|
|
||||||
implements i0.Insertable<i1.RemoteAlbumEntityData> {
|
|
||||||
final String id;
|
|
||||||
final String name;
|
|
||||||
final String description;
|
|
||||||
final DateTime createdAt;
|
|
||||||
final DateTime updatedAt;
|
|
||||||
final String ownerId;
|
|
||||||
final String? thumbnailAssetId;
|
|
||||||
final bool isActivityEnabled;
|
|
||||||
final i2.AssetOrder order;
|
|
||||||
const RemoteAlbumEntityData(
|
|
||||||
{required this.id,
|
|
||||||
required this.name,
|
|
||||||
required this.description,
|
|
||||||
required this.createdAt,
|
|
||||||
required this.updatedAt,
|
|
||||||
required this.ownerId,
|
|
||||||
this.thumbnailAssetId,
|
|
||||||
required this.isActivityEnabled,
|
|
||||||
required this.order});
|
|
||||||
@override
|
|
||||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
|
||||||
final map = <String, i0.Expression>{};
|
|
||||||
map['id'] = i0.Variable<String>(id);
|
|
||||||
map['name'] = i0.Variable<String>(name);
|
|
||||||
map['description'] = i0.Variable<String>(description);
|
|
||||||
map['created_at'] = i0.Variable<DateTime>(createdAt);
|
|
||||||
map['updated_at'] = i0.Variable<DateTime>(updatedAt);
|
|
||||||
map['owner_id'] = i0.Variable<String>(ownerId);
|
|
||||||
if (!nullToAbsent || thumbnailAssetId != null) {
|
|
||||||
map['thumbnail_asset_id'] = i0.Variable<String>(thumbnailAssetId);
|
|
||||||
}
|
|
||||||
map['is_activity_enabled'] = i0.Variable<bool>(isActivityEnabled);
|
|
||||||
{
|
|
||||||
map['order'] = i0.Variable<int>(
|
|
||||||
i1.$RemoteAlbumEntityTable.$converterorder.toSql(order));
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
factory RemoteAlbumEntityData.fromJson(Map<String, dynamic> json,
|
|
||||||
{i0.ValueSerializer? serializer}) {
|
|
||||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
|
||||||
return RemoteAlbumEntityData(
|
|
||||||
id: serializer.fromJson<String>(json['id']),
|
|
||||||
name: serializer.fromJson<String>(json['name']),
|
|
||||||
description: serializer.fromJson<String>(json['description']),
|
|
||||||
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
|
|
||||||
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
|
|
||||||
ownerId: serializer.fromJson<String>(json['ownerId']),
|
|
||||||
thumbnailAssetId: serializer.fromJson<String?>(json['thumbnailAssetId']),
|
|
||||||
isActivityEnabled: serializer.fromJson<bool>(json['isActivityEnabled']),
|
|
||||||
order: i1.$RemoteAlbumEntityTable.$converterorder
|
|
||||||
.fromJson(serializer.fromJson<int>(json['order'])),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
|
||||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
|
||||||
return <String, dynamic>{
|
|
||||||
'id': serializer.toJson<String>(id),
|
|
||||||
'name': serializer.toJson<String>(name),
|
|
||||||
'description': serializer.toJson<String>(description),
|
|
||||||
'createdAt': serializer.toJson<DateTime>(createdAt),
|
|
||||||
'updatedAt': serializer.toJson<DateTime>(updatedAt),
|
|
||||||
'ownerId': serializer.toJson<String>(ownerId),
|
|
||||||
'thumbnailAssetId': serializer.toJson<String?>(thumbnailAssetId),
|
|
||||||
'isActivityEnabled': serializer.toJson<bool>(isActivityEnabled),
|
|
||||||
'order': serializer.toJson<int>(
|
|
||||||
i1.$RemoteAlbumEntityTable.$converterorder.toJson(order)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
i1.RemoteAlbumEntityData copyWith(
|
|
||||||
{String? id,
|
|
||||||
String? name,
|
|
||||||
String? description,
|
|
||||||
DateTime? createdAt,
|
|
||||||
DateTime? updatedAt,
|
|
||||||
String? ownerId,
|
|
||||||
i0.Value<String?> thumbnailAssetId = const i0.Value.absent(),
|
|
||||||
bool? isActivityEnabled,
|
|
||||||
i2.AssetOrder? order}) =>
|
|
||||||
i1.RemoteAlbumEntityData(
|
|
||||||
id: id ?? this.id,
|
|
||||||
name: name ?? this.name,
|
|
||||||
description: description ?? this.description,
|
|
||||||
createdAt: createdAt ?? this.createdAt,
|
|
||||||
updatedAt: updatedAt ?? this.updatedAt,
|
|
||||||
ownerId: ownerId ?? this.ownerId,
|
|
||||||
thumbnailAssetId: thumbnailAssetId.present
|
|
||||||
? thumbnailAssetId.value
|
|
||||||
: this.thumbnailAssetId,
|
|
||||||
isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled,
|
|
||||||
order: order ?? this.order,
|
|
||||||
);
|
|
||||||
RemoteAlbumEntityData copyWithCompanion(i1.RemoteAlbumEntityCompanion data) {
|
|
||||||
return RemoteAlbumEntityData(
|
|
||||||
id: data.id.present ? data.id.value : this.id,
|
|
||||||
name: data.name.present ? data.name.value : this.name,
|
|
||||||
description:
|
|
||||||
data.description.present ? data.description.value : this.description,
|
|
||||||
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
|
|
||||||
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
|
|
||||||
ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId,
|
|
||||||
thumbnailAssetId: data.thumbnailAssetId.present
|
|
||||||
? data.thumbnailAssetId.value
|
|
||||||
: this.thumbnailAssetId,
|
|
||||||
isActivityEnabled: data.isActivityEnabled.present
|
|
||||||
? data.isActivityEnabled.value
|
|
||||||
: this.isActivityEnabled,
|
|
||||||
order: data.order.present ? data.order.value : this.order,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return (StringBuffer('RemoteAlbumEntityData(')
|
|
||||||
..write('id: $id, ')
|
|
||||||
..write('name: $name, ')
|
|
||||||
..write('description: $description, ')
|
|
||||||
..write('createdAt: $createdAt, ')
|
|
||||||
..write('updatedAt: $updatedAt, ')
|
|
||||||
..write('ownerId: $ownerId, ')
|
|
||||||
..write('thumbnailAssetId: $thumbnailAssetId, ')
|
|
||||||
..write('isActivityEnabled: $isActivityEnabled, ')
|
|
||||||
..write('order: $order')
|
|
||||||
..write(')'))
|
|
||||||
.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(id, name, description, createdAt, updatedAt,
|
|
||||||
ownerId, thumbnailAssetId, isActivityEnabled, order);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) =>
|
|
||||||
identical(this, other) ||
|
|
||||||
(other is i1.RemoteAlbumEntityData &&
|
|
||||||
other.id == this.id &&
|
|
||||||
other.name == this.name &&
|
|
||||||
other.description == this.description &&
|
|
||||||
other.createdAt == this.createdAt &&
|
|
||||||
other.updatedAt == this.updatedAt &&
|
|
||||||
other.ownerId == this.ownerId &&
|
|
||||||
other.thumbnailAssetId == this.thumbnailAssetId &&
|
|
||||||
other.isActivityEnabled == this.isActivityEnabled &&
|
|
||||||
other.order == this.order);
|
|
||||||
}
|
|
||||||
|
|
||||||
class RemoteAlbumEntityCompanion
|
|
||||||
extends i0.UpdateCompanion<i1.RemoteAlbumEntityData> {
|
|
||||||
final i0.Value<String> id;
|
|
||||||
final i0.Value<String> name;
|
|
||||||
final i0.Value<String> description;
|
|
||||||
final i0.Value<DateTime> createdAt;
|
|
||||||
final i0.Value<DateTime> updatedAt;
|
|
||||||
final i0.Value<String> ownerId;
|
|
||||||
final i0.Value<String?> thumbnailAssetId;
|
|
||||||
final i0.Value<bool> isActivityEnabled;
|
|
||||||
final i0.Value<i2.AssetOrder> order;
|
|
||||||
const RemoteAlbumEntityCompanion({
|
|
||||||
this.id = const i0.Value.absent(),
|
|
||||||
this.name = const i0.Value.absent(),
|
|
||||||
this.description = const i0.Value.absent(),
|
|
||||||
this.createdAt = const i0.Value.absent(),
|
|
||||||
this.updatedAt = const i0.Value.absent(),
|
|
||||||
this.ownerId = const i0.Value.absent(),
|
|
||||||
this.thumbnailAssetId = const i0.Value.absent(),
|
|
||||||
this.isActivityEnabled = const i0.Value.absent(),
|
|
||||||
this.order = const i0.Value.absent(),
|
|
||||||
});
|
|
||||||
RemoteAlbumEntityCompanion.insert({
|
|
||||||
required String id,
|
|
||||||
required String name,
|
|
||||||
required String description,
|
|
||||||
this.createdAt = const i0.Value.absent(),
|
|
||||||
this.updatedAt = const i0.Value.absent(),
|
|
||||||
required String ownerId,
|
|
||||||
this.thumbnailAssetId = const i0.Value.absent(),
|
|
||||||
this.isActivityEnabled = const i0.Value.absent(),
|
|
||||||
required i2.AssetOrder order,
|
|
||||||
}) : id = i0.Value(id),
|
|
||||||
name = i0.Value(name),
|
|
||||||
description = i0.Value(description),
|
|
||||||
ownerId = i0.Value(ownerId),
|
|
||||||
order = i0.Value(order);
|
|
||||||
static i0.Insertable<i1.RemoteAlbumEntityData> custom({
|
|
||||||
i0.Expression<String>? id,
|
|
||||||
i0.Expression<String>? name,
|
|
||||||
i0.Expression<String>? description,
|
|
||||||
i0.Expression<DateTime>? createdAt,
|
|
||||||
i0.Expression<DateTime>? updatedAt,
|
|
||||||
i0.Expression<String>? ownerId,
|
|
||||||
i0.Expression<String>? thumbnailAssetId,
|
|
||||||
i0.Expression<bool>? isActivityEnabled,
|
|
||||||
i0.Expression<int>? order,
|
|
||||||
}) {
|
|
||||||
return i0.RawValuesInsertable({
|
|
||||||
if (id != null) 'id': id,
|
|
||||||
if (name != null) 'name': name,
|
|
||||||
if (description != null) 'description': description,
|
|
||||||
if (createdAt != null) 'created_at': createdAt,
|
|
||||||
if (updatedAt != null) 'updated_at': updatedAt,
|
|
||||||
if (ownerId != null) 'owner_id': ownerId,
|
|
||||||
if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId,
|
|
||||||
if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled,
|
|
||||||
if (order != null) 'order': order,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
i1.RemoteAlbumEntityCompanion copyWith(
|
|
||||||
{i0.Value<String>? id,
|
|
||||||
i0.Value<String>? name,
|
|
||||||
i0.Value<String>? description,
|
|
||||||
i0.Value<DateTime>? createdAt,
|
|
||||||
i0.Value<DateTime>? updatedAt,
|
|
||||||
i0.Value<String>? ownerId,
|
|
||||||
i0.Value<String?>? thumbnailAssetId,
|
|
||||||
i0.Value<bool>? isActivityEnabled,
|
|
||||||
i0.Value<i2.AssetOrder>? order}) {
|
|
||||||
return i1.RemoteAlbumEntityCompanion(
|
|
||||||
id: id ?? this.id,
|
|
||||||
name: name ?? this.name,
|
|
||||||
description: description ?? this.description,
|
|
||||||
createdAt: createdAt ?? this.createdAt,
|
|
||||||
updatedAt: updatedAt ?? this.updatedAt,
|
|
||||||
ownerId: ownerId ?? this.ownerId,
|
|
||||||
thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId,
|
|
||||||
isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled,
|
|
||||||
order: order ?? this.order,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
|
||||||
final map = <String, i0.Expression>{};
|
|
||||||
if (id.present) {
|
|
||||||
map['id'] = i0.Variable<String>(id.value);
|
|
||||||
}
|
|
||||||
if (name.present) {
|
|
||||||
map['name'] = i0.Variable<String>(name.value);
|
|
||||||
}
|
|
||||||
if (description.present) {
|
|
||||||
map['description'] = i0.Variable<String>(description.value);
|
|
||||||
}
|
|
||||||
if (createdAt.present) {
|
|
||||||
map['created_at'] = i0.Variable<DateTime>(createdAt.value);
|
|
||||||
}
|
|
||||||
if (updatedAt.present) {
|
|
||||||
map['updated_at'] = i0.Variable<DateTime>(updatedAt.value);
|
|
||||||
}
|
|
||||||
if (ownerId.present) {
|
|
||||||
map['owner_id'] = i0.Variable<String>(ownerId.value);
|
|
||||||
}
|
|
||||||
if (thumbnailAssetId.present) {
|
|
||||||
map['thumbnail_asset_id'] = i0.Variable<String>(thumbnailAssetId.value);
|
|
||||||
}
|
|
||||||
if (isActivityEnabled.present) {
|
|
||||||
map['is_activity_enabled'] = i0.Variable<bool>(isActivityEnabled.value);
|
|
||||||
}
|
|
||||||
if (order.present) {
|
|
||||||
map['order'] = i0.Variable<int>(
|
|
||||||
i1.$RemoteAlbumEntityTable.$converterorder.toSql(order.value));
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return (StringBuffer('RemoteAlbumEntityCompanion(')
|
|
||||||
..write('id: $id, ')
|
|
||||||
..write('name: $name, ')
|
|
||||||
..write('description: $description, ')
|
|
||||||
..write('createdAt: $createdAt, ')
|
|
||||||
..write('updatedAt: $updatedAt, ')
|
|
||||||
..write('ownerId: $ownerId, ')
|
|
||||||
..write('thumbnailAssetId: $thumbnailAssetId, ')
|
|
||||||
..write('isActivityEnabled: $isActivityEnabled, ')
|
|
||||||
..write('order: $order')
|
|
||||||
..write(')'))
|
|
||||||
.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import 'package:drift/drift.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
|
||||||
|
|
||||||
class RemoteAlbumAssetEntity extends Table with DriftDefaultsMixin {
|
|
||||||
const RemoteAlbumAssetEntity();
|
|
||||||
|
|
||||||
TextColumn get assetId =>
|
|
||||||
text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)();
|
|
||||||
|
|
||||||
TextColumn get albumId =>
|
|
||||||
text().references(RemoteAlbumEntity, #id, onDelete: KeyAction.cascade)();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Set<Column> get primaryKey => {assetId, albumId};
|
|
||||||
}
|
|
||||||
@@ -1,565 +0,0 @@
|
|||||||
// dart format width=80
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
import 'package:drift/drift.dart' as i0;
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart'
|
|
||||||
as i1;
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.dart'
|
|
||||||
as i2;
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'
|
|
||||||
as i3;
|
|
||||||
import 'package:drift/internal/modular.dart' as i4;
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
|
|
||||||
as i5;
|
|
||||||
|
|
||||||
typedef $$RemoteAlbumAssetEntityTableCreateCompanionBuilder
|
|
||||||
= i1.RemoteAlbumAssetEntityCompanion Function({
|
|
||||||
required String assetId,
|
|
||||||
required String albumId,
|
|
||||||
});
|
|
||||||
typedef $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder
|
|
||||||
= i1.RemoteAlbumAssetEntityCompanion Function({
|
|
||||||
i0.Value<String> assetId,
|
|
||||||
i0.Value<String> albumId,
|
|
||||||
});
|
|
||||||
|
|
||||||
final class $$RemoteAlbumAssetEntityTableReferences extends i0.BaseReferences<
|
|
||||||
i0.GeneratedDatabase,
|
|
||||||
i1.$RemoteAlbumAssetEntityTable,
|
|
||||||
i1.RemoteAlbumAssetEntityData> {
|
|
||||||
$$RemoteAlbumAssetEntityTableReferences(
|
|
||||||
super.$_db, super.$_table, super.$_typedResult);
|
|
||||||
|
|
||||||
static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) =>
|
|
||||||
i4.ReadDatabaseContainer(db)
|
|
||||||
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity')
|
|
||||||
.createAlias(i0.$_aliasNameGenerator(
|
|
||||||
i4.ReadDatabaseContainer(db)
|
|
||||||
.resultSet<i1.$RemoteAlbumAssetEntityTable>(
|
|
||||||
'remote_album_asset_entity')
|
|
||||||
.assetId,
|
|
||||||
i4.ReadDatabaseContainer(db)
|
|
||||||
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity')
|
|
||||||
.id));
|
|
||||||
|
|
||||||
i3.$$RemoteAssetEntityTableProcessedTableManager get assetId {
|
|
||||||
final $_column = $_itemColumn<String>('asset_id')!;
|
|
||||||
|
|
||||||
final manager = i3
|
|
||||||
.$$RemoteAssetEntityTableTableManager(
|
|
||||||
$_db,
|
|
||||||
i4.ReadDatabaseContainer($_db)
|
|
||||||
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'))
|
|
||||||
.filter((f) => f.id.sqlEquals($_column));
|
|
||||||
final item = $_typedResult.readTableOrNull(_assetIdTable($_db));
|
|
||||||
if (item == null) return manager;
|
|
||||||
return i0.ProcessedTableManager(
|
|
||||||
manager.$state.copyWith(prefetchedData: [item]));
|
|
||||||
}
|
|
||||||
|
|
||||||
static i5.$RemoteAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) =>
|
|
||||||
i4.ReadDatabaseContainer(db)
|
|
||||||
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity')
|
|
||||||
.createAlias(i0.$_aliasNameGenerator(
|
|
||||||
i4.ReadDatabaseContainer(db)
|
|
||||||
.resultSet<i1.$RemoteAlbumAssetEntityTable>(
|
|
||||||
'remote_album_asset_entity')
|
|
||||||
.albumId,
|
|
||||||
i4.ReadDatabaseContainer(db)
|
|
||||||
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity')
|
|
||||||
.id));
|
|
||||||
|
|
||||||
i5.$$RemoteAlbumEntityTableProcessedTableManager get albumId {
|
|
||||||
final $_column = $_itemColumn<String>('album_id')!;
|
|
||||||
|
|
||||||
final manager = i5
|
|
||||||
.$$RemoteAlbumEntityTableTableManager(
|
|
||||||
$_db,
|
|
||||||
i4.ReadDatabaseContainer($_db)
|
|
||||||
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'))
|
|
||||||
.filter((f) => f.id.sqlEquals($_column));
|
|
||||||
final item = $_typedResult.readTableOrNull(_albumIdTable($_db));
|
|
||||||
if (item == null) return manager;
|
|
||||||
return i0.ProcessedTableManager(
|
|
||||||
manager.$state.copyWith(prefetchedData: [item]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class $$RemoteAlbumAssetEntityTableFilterComposer
|
|
||||||
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumAssetEntityTable> {
|
|
||||||
$$RemoteAlbumAssetEntityTableFilterComposer({
|
|
||||||
required super.$db,
|
|
||||||
required super.$table,
|
|
||||||
super.joinBuilder,
|
|
||||||
super.$addJoinBuilderToRootComposer,
|
|
||||||
super.$removeJoinBuilderFromRootComposer,
|
|
||||||
});
|
|
||||||
i3.$$RemoteAssetEntityTableFilterComposer get assetId {
|
|
||||||
final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder(
|
|
||||||
composer: this,
|
|
||||||
getCurrentColumn: (t) => t.assetId,
|
|
||||||
referencedTable: i4.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
|
|
||||||
getReferencedColumn: (t) => t.id,
|
|
||||||
builder: (joinBuilder,
|
|
||||||
{$addJoinBuilderToRootComposer,
|
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
|
||||||
i3.$$RemoteAssetEntityTableFilterComposer(
|
|
||||||
$db: $db,
|
|
||||||
$table: i4.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
|
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
|
||||||
joinBuilder: joinBuilder,
|
|
||||||
$removeJoinBuilderFromRootComposer:
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
));
|
|
||||||
return composer;
|
|
||||||
}
|
|
||||||
|
|
||||||
i5.$$RemoteAlbumEntityTableFilterComposer get albumId {
|
|
||||||
final i5.$$RemoteAlbumEntityTableFilterComposer composer = $composerBuilder(
|
|
||||||
composer: this,
|
|
||||||
getCurrentColumn: (t) => t.albumId,
|
|
||||||
referencedTable: i4.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
|
|
||||||
getReferencedColumn: (t) => t.id,
|
|
||||||
builder: (joinBuilder,
|
|
||||||
{$addJoinBuilderToRootComposer,
|
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
|
||||||
i5.$$RemoteAlbumEntityTableFilterComposer(
|
|
||||||
$db: $db,
|
|
||||||
$table: i4.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
|
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
|
||||||
joinBuilder: joinBuilder,
|
|
||||||
$removeJoinBuilderFromRootComposer:
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
));
|
|
||||||
return composer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class $$RemoteAlbumAssetEntityTableOrderingComposer
|
|
||||||
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumAssetEntityTable> {
|
|
||||||
$$RemoteAlbumAssetEntityTableOrderingComposer({
|
|
||||||
required super.$db,
|
|
||||||
required super.$table,
|
|
||||||
super.joinBuilder,
|
|
||||||
super.$addJoinBuilderToRootComposer,
|
|
||||||
super.$removeJoinBuilderFromRootComposer,
|
|
||||||
});
|
|
||||||
i3.$$RemoteAssetEntityTableOrderingComposer get assetId {
|
|
||||||
final i3.$$RemoteAssetEntityTableOrderingComposer composer =
|
|
||||||
$composerBuilder(
|
|
||||||
composer: this,
|
|
||||||
getCurrentColumn: (t) => t.assetId,
|
|
||||||
referencedTable: i4.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
|
|
||||||
getReferencedColumn: (t) => t.id,
|
|
||||||
builder: (joinBuilder,
|
|
||||||
{$addJoinBuilderToRootComposer,
|
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
|
||||||
i3.$$RemoteAssetEntityTableOrderingComposer(
|
|
||||||
$db: $db,
|
|
||||||
$table: i4.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i3.$RemoteAssetEntityTable>(
|
|
||||||
'remote_asset_entity'),
|
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
|
||||||
joinBuilder: joinBuilder,
|
|
||||||
$removeJoinBuilderFromRootComposer:
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
));
|
|
||||||
return composer;
|
|
||||||
}
|
|
||||||
|
|
||||||
i5.$$RemoteAlbumEntityTableOrderingComposer get albumId {
|
|
||||||
final i5.$$RemoteAlbumEntityTableOrderingComposer composer =
|
|
||||||
$composerBuilder(
|
|
||||||
composer: this,
|
|
||||||
getCurrentColumn: (t) => t.albumId,
|
|
||||||
referencedTable: i4.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
|
|
||||||
getReferencedColumn: (t) => t.id,
|
|
||||||
builder: (joinBuilder,
|
|
||||||
{$addJoinBuilderToRootComposer,
|
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
|
||||||
i5.$$RemoteAlbumEntityTableOrderingComposer(
|
|
||||||
$db: $db,
|
|
||||||
$table: i4.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i5.$RemoteAlbumEntityTable>(
|
|
||||||
'remote_album_entity'),
|
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
|
||||||
joinBuilder: joinBuilder,
|
|
||||||
$removeJoinBuilderFromRootComposer:
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
));
|
|
||||||
return composer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class $$RemoteAlbumAssetEntityTableAnnotationComposer
|
|
||||||
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumAssetEntityTable> {
|
|
||||||
$$RemoteAlbumAssetEntityTableAnnotationComposer({
|
|
||||||
required super.$db,
|
|
||||||
required super.$table,
|
|
||||||
super.joinBuilder,
|
|
||||||
super.$addJoinBuilderToRootComposer,
|
|
||||||
super.$removeJoinBuilderFromRootComposer,
|
|
||||||
});
|
|
||||||
i3.$$RemoteAssetEntityTableAnnotationComposer get assetId {
|
|
||||||
final i3.$$RemoteAssetEntityTableAnnotationComposer composer =
|
|
||||||
$composerBuilder(
|
|
||||||
composer: this,
|
|
||||||
getCurrentColumn: (t) => t.assetId,
|
|
||||||
referencedTable: i4.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
|
|
||||||
getReferencedColumn: (t) => t.id,
|
|
||||||
builder: (joinBuilder,
|
|
||||||
{$addJoinBuilderToRootComposer,
|
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
|
||||||
i3.$$RemoteAssetEntityTableAnnotationComposer(
|
|
||||||
$db: $db,
|
|
||||||
$table: i4.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i3.$RemoteAssetEntityTable>(
|
|
||||||
'remote_asset_entity'),
|
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
|
||||||
joinBuilder: joinBuilder,
|
|
||||||
$removeJoinBuilderFromRootComposer:
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
));
|
|
||||||
return composer;
|
|
||||||
}
|
|
||||||
|
|
||||||
i5.$$RemoteAlbumEntityTableAnnotationComposer get albumId {
|
|
||||||
final i5.$$RemoteAlbumEntityTableAnnotationComposer composer =
|
|
||||||
$composerBuilder(
|
|
||||||
composer: this,
|
|
||||||
getCurrentColumn: (t) => t.albumId,
|
|
||||||
referencedTable: i4.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
|
|
||||||
getReferencedColumn: (t) => t.id,
|
|
||||||
builder: (joinBuilder,
|
|
||||||
{$addJoinBuilderToRootComposer,
|
|
||||||
$removeJoinBuilderFromRootComposer}) =>
|
|
||||||
i5.$$RemoteAlbumEntityTableAnnotationComposer(
|
|
||||||
$db: $db,
|
|
||||||
$table: i4.ReadDatabaseContainer($db)
|
|
||||||
.resultSet<i5.$RemoteAlbumEntityTable>(
|
|
||||||
'remote_album_entity'),
|
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
|
||||||
joinBuilder: joinBuilder,
|
|
||||||
$removeJoinBuilderFromRootComposer:
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
));
|
|
||||||
return composer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class $$RemoteAlbumAssetEntityTableTableManager extends i0.RootTableManager<
|
|
||||||
i0.GeneratedDatabase,
|
|
||||||
i1.$RemoteAlbumAssetEntityTable,
|
|
||||||
i1.RemoteAlbumAssetEntityData,
|
|
||||||
i1.$$RemoteAlbumAssetEntityTableFilterComposer,
|
|
||||||
i1.$$RemoteAlbumAssetEntityTableOrderingComposer,
|
|
||||||
i1.$$RemoteAlbumAssetEntityTableAnnotationComposer,
|
|
||||||
$$RemoteAlbumAssetEntityTableCreateCompanionBuilder,
|
|
||||||
$$RemoteAlbumAssetEntityTableUpdateCompanionBuilder,
|
|
||||||
(i1.RemoteAlbumAssetEntityData, i1.$$RemoteAlbumAssetEntityTableReferences),
|
|
||||||
i1.RemoteAlbumAssetEntityData,
|
|
||||||
i0.PrefetchHooks Function({bool assetId, bool albumId})> {
|
|
||||||
$$RemoteAlbumAssetEntityTableTableManager(
|
|
||||||
i0.GeneratedDatabase db, i1.$RemoteAlbumAssetEntityTable table)
|
|
||||||
: super(i0.TableManagerState(
|
|
||||||
db: db,
|
|
||||||
table: table,
|
|
||||||
createFilteringComposer: () =>
|
|
||||||
i1.$$RemoteAlbumAssetEntityTableFilterComposer(
|
|
||||||
$db: db, $table: table),
|
|
||||||
createOrderingComposer: () =>
|
|
||||||
i1.$$RemoteAlbumAssetEntityTableOrderingComposer(
|
|
||||||
$db: db, $table: table),
|
|
||||||
createComputedFieldComposer: () =>
|
|
||||||
i1.$$RemoteAlbumAssetEntityTableAnnotationComposer(
|
|
||||||
$db: db, $table: table),
|
|
||||||
updateCompanionCallback: ({
|
|
||||||
i0.Value<String> assetId = const i0.Value.absent(),
|
|
||||||
i0.Value<String> albumId = const i0.Value.absent(),
|
|
||||||
}) =>
|
|
||||||
i1.RemoteAlbumAssetEntityCompanion(
|
|
||||||
assetId: assetId,
|
|
||||||
albumId: albumId,
|
|
||||||
),
|
|
||||||
createCompanionCallback: ({
|
|
||||||
required String assetId,
|
|
||||||
required String albumId,
|
|
||||||
}) =>
|
|
||||||
i1.RemoteAlbumAssetEntityCompanion.insert(
|
|
||||||
assetId: assetId,
|
|
||||||
albumId: albumId,
|
|
||||||
),
|
|
||||||
withReferenceMapper: (p0) => p0
|
|
||||||
.map((e) => (
|
|
||||||
e.readTable(table),
|
|
||||||
i1.$$RemoteAlbumAssetEntityTableReferences(db, table, e)
|
|
||||||
))
|
|
||||||
.toList(),
|
|
||||||
prefetchHooksCallback: ({assetId = false, albumId = false}) {
|
|
||||||
return i0.PrefetchHooks(
|
|
||||||
db: db,
|
|
||||||
explicitlyWatchedTables: [],
|
|
||||||
addJoins: <
|
|
||||||
T extends i0.TableManagerState<
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic>>(state) {
|
|
||||||
if (assetId) {
|
|
||||||
state = state.withJoin(
|
|
||||||
currentTable: table,
|
|
||||||
currentColumn: table.assetId,
|
|
||||||
referencedTable: i1.$$RemoteAlbumAssetEntityTableReferences
|
|
||||||
._assetIdTable(db),
|
|
||||||
referencedColumn: i1.$$RemoteAlbumAssetEntityTableReferences
|
|
||||||
._assetIdTable(db)
|
|
||||||
.id,
|
|
||||||
) as T;
|
|
||||||
}
|
|
||||||
if (albumId) {
|
|
||||||
state = state.withJoin(
|
|
||||||
currentTable: table,
|
|
||||||
currentColumn: table.albumId,
|
|
||||||
referencedTable: i1.$$RemoteAlbumAssetEntityTableReferences
|
|
||||||
._albumIdTable(db),
|
|
||||||
referencedColumn: i1.$$RemoteAlbumAssetEntityTableReferences
|
|
||||||
._albumIdTable(db)
|
|
||||||
.id,
|
|
||||||
) as T;
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
|
||||||
},
|
|
||||||
getPrefetchedDataCallback: (items) async {
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef $$RemoteAlbumAssetEntityTableProcessedTableManager
|
|
||||||
= i0.ProcessedTableManager<
|
|
||||||
i0.GeneratedDatabase,
|
|
||||||
i1.$RemoteAlbumAssetEntityTable,
|
|
||||||
i1.RemoteAlbumAssetEntityData,
|
|
||||||
i1.$$RemoteAlbumAssetEntityTableFilterComposer,
|
|
||||||
i1.$$RemoteAlbumAssetEntityTableOrderingComposer,
|
|
||||||
i1.$$RemoteAlbumAssetEntityTableAnnotationComposer,
|
|
||||||
$$RemoteAlbumAssetEntityTableCreateCompanionBuilder,
|
|
||||||
$$RemoteAlbumAssetEntityTableUpdateCompanionBuilder,
|
|
||||||
(
|
|
||||||
i1.RemoteAlbumAssetEntityData,
|
|
||||||
i1.$$RemoteAlbumAssetEntityTableReferences
|
|
||||||
),
|
|
||||||
i1.RemoteAlbumAssetEntityData,
|
|
||||||
i0.PrefetchHooks Function({bool assetId, bool albumId})>;
|
|
||||||
|
|
||||||
class $RemoteAlbumAssetEntityTable extends i2.RemoteAlbumAssetEntity
|
|
||||||
with
|
|
||||||
i0.TableInfo<$RemoteAlbumAssetEntityTable,
|
|
||||||
i1.RemoteAlbumAssetEntityData> {
|
|
||||||
@override
|
|
||||||
final i0.GeneratedDatabase attachedDatabase;
|
|
||||||
final String? _alias;
|
|
||||||
$RemoteAlbumAssetEntityTable(this.attachedDatabase, [this._alias]);
|
|
||||||
static const i0.VerificationMeta _assetIdMeta =
|
|
||||||
const i0.VerificationMeta('assetId');
|
|
||||||
@override
|
|
||||||
late final i0.GeneratedColumn<String> assetId = i0.GeneratedColumn<String>(
|
|
||||||
'asset_id', aliasedName, false,
|
|
||||||
type: i0.DriftSqlType.string,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
|
||||||
'REFERENCES remote_asset_entity (id) ON DELETE CASCADE'));
|
|
||||||
static const i0.VerificationMeta _albumIdMeta =
|
|
||||||
const i0.VerificationMeta('albumId');
|
|
||||||
@override
|
|
||||||
late final i0.GeneratedColumn<String> albumId = i0.GeneratedColumn<String>(
|
|
||||||
'album_id', aliasedName, false,
|
|
||||||
type: i0.DriftSqlType.string,
|
|
||||||
requiredDuringInsert: true,
|
|
||||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
|
||||||
'REFERENCES remote_album_entity (id) ON DELETE CASCADE'));
|
|
||||||
@override
|
|
||||||
List<i0.GeneratedColumn> get $columns => [assetId, albumId];
|
|
||||||
@override
|
|
||||||
String get aliasedName => _alias ?? actualTableName;
|
|
||||||
@override
|
|
||||||
String get actualTableName => $name;
|
|
||||||
static const String $name = 'remote_album_asset_entity';
|
|
||||||
@override
|
|
||||||
i0.VerificationContext validateIntegrity(
|
|
||||||
i0.Insertable<i1.RemoteAlbumAssetEntityData> instance,
|
|
||||||
{bool isInserting = false}) {
|
|
||||||
final context = i0.VerificationContext();
|
|
||||||
final data = instance.toColumns(true);
|
|
||||||
if (data.containsKey('asset_id')) {
|
|
||||||
context.handle(_assetIdMeta,
|
|
||||||
assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta));
|
|
||||||
} else if (isInserting) {
|
|
||||||
context.missing(_assetIdMeta);
|
|
||||||
}
|
|
||||||
if (data.containsKey('album_id')) {
|
|
||||||
context.handle(_albumIdMeta,
|
|
||||||
albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta));
|
|
||||||
} else if (isInserting) {
|
|
||||||
context.missing(_albumIdMeta);
|
|
||||||
}
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Set<i0.GeneratedColumn> get $primaryKey => {assetId, albumId};
|
|
||||||
@override
|
|
||||||
i1.RemoteAlbumAssetEntityData map(Map<String, dynamic> data,
|
|
||||||
{String? tablePrefix}) {
|
|
||||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
|
||||||
return i1.RemoteAlbumAssetEntityData(
|
|
||||||
assetId: attachedDatabase.typeMapping
|
|
||||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!,
|
|
||||||
albumId: attachedDatabase.typeMapping
|
|
||||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}album_id'])!,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
$RemoteAlbumAssetEntityTable createAlias(String alias) {
|
|
||||||
return $RemoteAlbumAssetEntityTable(attachedDatabase, alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool get withoutRowId => true;
|
|
||||||
@override
|
|
||||||
bool get isStrict => true;
|
|
||||||
}
|
|
||||||
|
|
||||||
class RemoteAlbumAssetEntityData extends i0.DataClass
|
|
||||||
implements i0.Insertable<i1.RemoteAlbumAssetEntityData> {
|
|
||||||
final String assetId;
|
|
||||||
final String albumId;
|
|
||||||
const RemoteAlbumAssetEntityData(
|
|
||||||
{required this.assetId, required this.albumId});
|
|
||||||
@override
|
|
||||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
|
||||||
final map = <String, i0.Expression>{};
|
|
||||||
map['asset_id'] = i0.Variable<String>(assetId);
|
|
||||||
map['album_id'] = i0.Variable<String>(albumId);
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
factory RemoteAlbumAssetEntityData.fromJson(Map<String, dynamic> json,
|
|
||||||
{i0.ValueSerializer? serializer}) {
|
|
||||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
|
||||||
return RemoteAlbumAssetEntityData(
|
|
||||||
assetId: serializer.fromJson<String>(json['assetId']),
|
|
||||||
albumId: serializer.fromJson<String>(json['albumId']),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
|
||||||
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
|
||||||
return <String, dynamic>{
|
|
||||||
'assetId': serializer.toJson<String>(assetId),
|
|
||||||
'albumId': serializer.toJson<String>(albumId),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
i1.RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) =>
|
|
||||||
i1.RemoteAlbumAssetEntityData(
|
|
||||||
assetId: assetId ?? this.assetId,
|
|
||||||
albumId: albumId ?? this.albumId,
|
|
||||||
);
|
|
||||||
RemoteAlbumAssetEntityData copyWithCompanion(
|
|
||||||
i1.RemoteAlbumAssetEntityCompanion data) {
|
|
||||||
return RemoteAlbumAssetEntityData(
|
|
||||||
assetId: data.assetId.present ? data.assetId.value : this.assetId,
|
|
||||||
albumId: data.albumId.present ? data.albumId.value : this.albumId,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return (StringBuffer('RemoteAlbumAssetEntityData(')
|
|
||||||
..write('assetId: $assetId, ')
|
|
||||||
..write('albumId: $albumId')
|
|
||||||
..write(')'))
|
|
||||||
.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(assetId, albumId);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) =>
|
|
||||||
identical(this, other) ||
|
|
||||||
(other is i1.RemoteAlbumAssetEntityData &&
|
|
||||||
other.assetId == this.assetId &&
|
|
||||||
other.albumId == this.albumId);
|
|
||||||
}
|
|
||||||
|
|
||||||
class RemoteAlbumAssetEntityCompanion
|
|
||||||
extends i0.UpdateCompanion<i1.RemoteAlbumAssetEntityData> {
|
|
||||||
final i0.Value<String> assetId;
|
|
||||||
final i0.Value<String> albumId;
|
|
||||||
const RemoteAlbumAssetEntityCompanion({
|
|
||||||
this.assetId = const i0.Value.absent(),
|
|
||||||
this.albumId = const i0.Value.absent(),
|
|
||||||
});
|
|
||||||
RemoteAlbumAssetEntityCompanion.insert({
|
|
||||||
required String assetId,
|
|
||||||
required String albumId,
|
|
||||||
}) : assetId = i0.Value(assetId),
|
|
||||||
albumId = i0.Value(albumId);
|
|
||||||
static i0.Insertable<i1.RemoteAlbumAssetEntityData> custom({
|
|
||||||
i0.Expression<String>? assetId,
|
|
||||||
i0.Expression<String>? albumId,
|
|
||||||
}) {
|
|
||||||
return i0.RawValuesInsertable({
|
|
||||||
if (assetId != null) 'asset_id': assetId,
|
|
||||||
if (albumId != null) 'album_id': albumId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
i1.RemoteAlbumAssetEntityCompanion copyWith(
|
|
||||||
{i0.Value<String>? assetId, i0.Value<String>? albumId}) {
|
|
||||||
return i1.RemoteAlbumAssetEntityCompanion(
|
|
||||||
assetId: assetId ?? this.assetId,
|
|
||||||
albumId: albumId ?? this.albumId,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
|
||||||
final map = <String, i0.Expression>{};
|
|
||||||
if (assetId.present) {
|
|
||||||
map['asset_id'] = i0.Variable<String>(assetId.value);
|
|
||||||
}
|
|
||||||
if (albumId.present) {
|
|
||||||
map['album_id'] = i0.Variable<String>(albumId.value);
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return (StringBuffer('RemoteAlbumAssetEntityCompanion(')
|
|
||||||
..write('assetId: $assetId, ')
|
|
||||||
..write('albumId: $albumId')
|
|
||||||
..write(')'))
|
|
||||||
.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,15 +3,12 @@ import 'dart:async';
|
|||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:drift_flutter/drift_flutter.dart';
|
import 'package:drift_flutter/drift_flutter.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/album_user.entity.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/partner.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/partner.entity.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
@@ -41,11 +38,8 @@ class IsarDatabaseRepository implements IDatabaseRepository {
|
|||||||
LocalAlbumEntity,
|
LocalAlbumEntity,
|
||||||
LocalAssetEntity,
|
LocalAssetEntity,
|
||||||
LocalAlbumAssetEntity,
|
LocalAlbumAssetEntity,
|
||||||
RemoteExifEntity,
|
|
||||||
RemoteAssetEntity,
|
RemoteAssetEntity,
|
||||||
RemoteAlbumEntity,
|
RemoteExifEntity,
|
||||||
RemoteAlbumAssetEntity,
|
|
||||||
AlbumUserEntity,
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
class Drift extends $Drift implements IDatabaseRepository {
|
class Drift extends $Drift implements IDatabaseRepository {
|
||||||
|
|||||||
@@ -17,12 +17,6 @@ import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.
|
|||||||
as i7;
|
as i7;
|
||||||
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'
|
||||||
as i8;
|
as i8;
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
|
|
||||||
as i9;
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart'
|
|
||||||
as i10;
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/album_user.entity.drift.dart'
|
|
||||||
as i11;
|
|
||||||
|
|
||||||
abstract class $Drift extends i0.GeneratedDatabase {
|
abstract class $Drift extends i0.GeneratedDatabase {
|
||||||
$Drift(i0.QueryExecutor e) : super(e);
|
$Drift(i0.QueryExecutor e) : super(e);
|
||||||
@@ -42,12 +36,6 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
|||||||
i7.$RemoteAssetEntityTable(this);
|
i7.$RemoteAssetEntityTable(this);
|
||||||
late final i8.$RemoteExifEntityTable remoteExifEntity =
|
late final i8.$RemoteExifEntityTable remoteExifEntity =
|
||||||
i8.$RemoteExifEntityTable(this);
|
i8.$RemoteExifEntityTable(this);
|
||||||
late final i9.$RemoteAlbumEntityTable remoteAlbumEntity =
|
|
||||||
i9.$RemoteAlbumEntityTable(this);
|
|
||||||
late final i10.$RemoteAlbumAssetEntityTable remoteAlbumAssetEntity =
|
|
||||||
i10.$RemoteAlbumAssetEntityTable(this);
|
|
||||||
late final i11.$AlbumUserEntityTable albumUserEntity =
|
|
||||||
i11.$AlbumUserEntityTable(this);
|
|
||||||
@override
|
@override
|
||||||
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
|
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
|
||||||
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
|
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
|
||||||
@@ -61,9 +49,6 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
|||||||
localAlbumAssetEntity,
|
localAlbumAssetEntity,
|
||||||
remoteAssetEntity,
|
remoteAssetEntity,
|
||||||
remoteExifEntity,
|
remoteExifEntity,
|
||||||
remoteAlbumEntity,
|
|
||||||
remoteAlbumAssetEntity,
|
|
||||||
albumUserEntity,
|
|
||||||
i5.idxLocalAssetChecksum,
|
i5.idxLocalAssetChecksum,
|
||||||
i7.uQRemoteAssetOwnerChecksum
|
i7.uQRemoteAssetOwnerChecksum
|
||||||
];
|
];
|
||||||
@@ -123,50 +108,6 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
|||||||
i0.TableUpdate('remote_exif_entity', kind: i0.UpdateKind.delete),
|
i0.TableUpdate('remote_exif_entity', kind: i0.UpdateKind.delete),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
i0.WritePropagation(
|
|
||||||
on: i0.TableUpdateQuery.onTableName('user_entity',
|
|
||||||
limitUpdateKind: i0.UpdateKind.delete),
|
|
||||||
result: [
|
|
||||||
i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.delete),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
i0.WritePropagation(
|
|
||||||
on: i0.TableUpdateQuery.onTableName('remote_asset_entity',
|
|
||||||
limitUpdateKind: i0.UpdateKind.delete),
|
|
||||||
result: [
|
|
||||||
i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.update),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
i0.WritePropagation(
|
|
||||||
on: i0.TableUpdateQuery.onTableName('remote_asset_entity',
|
|
||||||
limitUpdateKind: i0.UpdateKind.delete),
|
|
||||||
result: [
|
|
||||||
i0.TableUpdate('remote_album_asset_entity',
|
|
||||||
kind: i0.UpdateKind.delete),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
i0.WritePropagation(
|
|
||||||
on: i0.TableUpdateQuery.onTableName('remote_album_entity',
|
|
||||||
limitUpdateKind: i0.UpdateKind.delete),
|
|
||||||
result: [
|
|
||||||
i0.TableUpdate('remote_album_asset_entity',
|
|
||||||
kind: i0.UpdateKind.delete),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
i0.WritePropagation(
|
|
||||||
on: i0.TableUpdateQuery.onTableName('remote_album_entity',
|
|
||||||
limitUpdateKind: i0.UpdateKind.delete),
|
|
||||||
result: [
|
|
||||||
i0.TableUpdate('album_user_entity', kind: i0.UpdateKind.delete),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
i0.WritePropagation(
|
|
||||||
on: i0.TableUpdateQuery.onTableName('user_entity',
|
|
||||||
limitUpdateKind: i0.UpdateKind.delete),
|
|
||||||
result: [
|
|
||||||
i0.TableUpdate('album_user_entity', kind: i0.UpdateKind.delete),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@override
|
@override
|
||||||
@@ -193,11 +134,4 @@ class $DriftManager {
|
|||||||
i7.$$RemoteAssetEntityTableTableManager(_db, _db.remoteAssetEntity);
|
i7.$$RemoteAssetEntityTableTableManager(_db, _db.remoteAssetEntity);
|
||||||
i8.$$RemoteExifEntityTableTableManager get remoteExifEntity =>
|
i8.$$RemoteExifEntityTableTableManager get remoteExifEntity =>
|
||||||
i8.$$RemoteExifEntityTableTableManager(_db, _db.remoteExifEntity);
|
i8.$$RemoteExifEntityTableTableManager(_db, _db.remoteExifEntity);
|
||||||
i9.$$RemoteAlbumEntityTableTableManager get remoteAlbumEntity =>
|
|
||||||
i9.$$RemoteAlbumEntityTableTableManager(_db, _db.remoteAlbumEntity);
|
|
||||||
i10.$$RemoteAlbumAssetEntityTableTableManager get remoteAlbumAssetEntity =>
|
|
||||||
i10.$$RemoteAlbumAssetEntityTableTableManager(
|
|
||||||
_db, _db.remoteAlbumAssetEntity);
|
|
||||||
i11.$$AlbumUserEntityTableTableManager get albumUserEntity =>
|
|
||||||
i11.$$AlbumUserEntityTableTableManager(_db, _db.albumUserEntity);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,9 +50,6 @@ class SyncApiRepository implements ISyncApiRepository {
|
|||||||
SyncRequestType.partnerAssetsV1,
|
SyncRequestType.partnerAssetsV1,
|
||||||
SyncRequestType.assetExifsV1,
|
SyncRequestType.assetExifsV1,
|
||||||
SyncRequestType.partnerAssetExifsV1,
|
SyncRequestType.partnerAssetExifsV1,
|
||||||
SyncRequestType.albumsV1,
|
|
||||||
// SyncRequestType.albumAssetsV1,
|
|
||||||
SyncRequestType.albumUsersV1,
|
|
||||||
],
|
],
|
||||||
).toJson(),
|
).toJson(),
|
||||||
);
|
);
|
||||||
@@ -143,10 +140,4 @@ const _kResponseMap = <SyncEntityType, Function(Object)>{
|
|||||||
SyncEntityType.partnerAssetV1: SyncAssetV1.fromJson,
|
SyncEntityType.partnerAssetV1: SyncAssetV1.fromJson,
|
||||||
SyncEntityType.partnerAssetDeleteV1: SyncAssetDeleteV1.fromJson,
|
SyncEntityType.partnerAssetDeleteV1: SyncAssetDeleteV1.fromJson,
|
||||||
SyncEntityType.partnerAssetExifV1: SyncAssetExifV1.fromJson,
|
SyncEntityType.partnerAssetExifV1: SyncAssetExifV1.fromJson,
|
||||||
SyncEntityType.albumV1: SyncAlbumV1.fromJson,
|
|
||||||
SyncEntityType.albumDeleteV1: SyncAlbumDeleteV1.fromJson,
|
|
||||||
// SyncEntityType.albumAssetV1: SyncAlbumAssetV1.fromJson,
|
|
||||||
// SyncEntityType.albumAssetDeleteV1: SyncAlbumAssetDeleteV1.fromJson,
|
|
||||||
SyncEntityType.albumUserV1: SyncAlbumUserV1.fromJson,
|
|
||||||
SyncEntityType.albumUserDeleteV1: SyncAlbumUserDeleteV1.fromJson,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,19 +3,12 @@ import 'package:immich_mobile/domain/interfaces/sync_stream.interface.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/exif.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart';
|
||||||
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/album_user.model.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/album_user.entity.drift.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart';
|
|
||||||
// import 'package:immich_mobile/infrastructure/entities/remote_album_asset.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/user.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:openapi/api.dart' as api
|
import 'package:openapi/api.dart' as api show AssetVisibility;
|
||||||
show AssetVisibility, AssetOrder, AlbumUserRole;
|
import 'package:openapi/api.dart' hide AssetVisibility;
|
||||||
import 'package:openapi/api.dart'
|
|
||||||
hide AssetVisibility, AssetOrder, AlbumUserRole;
|
|
||||||
|
|
||||||
class DriftSyncStreamRepository extends DriftDatabaseRepository
|
class DriftSyncStreamRepository extends DriftDatabaseRepository
|
||||||
implements ISyncStreamRepository {
|
implements ISyncStreamRepository {
|
||||||
@@ -168,135 +161,6 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> updateAlbumsV1(Iterable<SyncAlbumV1> data) async {
|
|
||||||
try {
|
|
||||||
await _db.batch((batch) {
|
|
||||||
for (final album in data) {
|
|
||||||
final companion = RemoteAlbumEntityCompanion(
|
|
||||||
name: Value(album.name),
|
|
||||||
description: Value(album.description),
|
|
||||||
ownerId: Value(album.ownerId),
|
|
||||||
thumbnailAssetId: Value(album.thumbnailAssetId),
|
|
||||||
createdAt: Value(album.createdAt),
|
|
||||||
updatedAt: Value(album.updatedAt),
|
|
||||||
isActivityEnabled: Value(album.isActivityEnabled),
|
|
||||||
order: Value(album.order.toAssetOrder()),
|
|
||||||
);
|
|
||||||
|
|
||||||
batch.insert(
|
|
||||||
_db.remoteAlbumEntity,
|
|
||||||
companion.copyWith(id: Value(album.id)),
|
|
||||||
onConflict: DoUpdate((_) => companion),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (e, s) {
|
|
||||||
_logger.severe('Error while processing updateAlbumsV1', e, s);
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> deleteAlbumsV1(Iterable<SyncAlbumDeleteV1> data) async {
|
|
||||||
try {
|
|
||||||
_db.batch((batch) {
|
|
||||||
for (final album in data) {
|
|
||||||
batch.delete(
|
|
||||||
_db.remoteAlbumEntity,
|
|
||||||
RemoteAlbumEntityCompanion(id: Value(album.albumId)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (e, s) {
|
|
||||||
_logger.severe('Error while processing deleteAlbumsV1', e, s);
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// @override
|
|
||||||
// Future<void> updateAlbumAssetsV1(Iterable<SyncAlbumAssetV1> data) async {
|
|
||||||
// try {
|
|
||||||
// await _db.remoteAlbumAssetEntity.insertAll(
|
|
||||||
// data.map(
|
|
||||||
// (albumAsset) => RemoteAlbumAssetEntityCompanion.insert(
|
|
||||||
// albumId: albumAsset.albumId,
|
|
||||||
// assetId: albumAsset.assetId,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// mode: InsertMode.insertOrIgnore,
|
|
||||||
// );
|
|
||||||
// } catch (e, s) {
|
|
||||||
// _logger.severe('Error while processing updateAlbumAssetsV1', e, s);
|
|
||||||
// rethrow;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @override
|
|
||||||
// Future<void> deleteAlbumAssetsV1(Iterable<SyncAlbumAssetDeleteV1> data) async {
|
|
||||||
// try {
|
|
||||||
// await _db.batch((batch) {
|
|
||||||
// for (final albumAsset in data) {
|
|
||||||
// batch.delete(
|
|
||||||
// _db.remoteAlbumAssetEntity,
|
|
||||||
// RemoteAlbumAssetEntityCompanion(
|
|
||||||
// albumId: Value(albumAsset.albumId),
|
|
||||||
// assetId: Value(albumAsset.assetId),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// } catch (e, s) {
|
|
||||||
// _logger.severe('Error while processing deleteAlbumAssetsV1', e, s);
|
|
||||||
// rethrow;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> updateAlbumUsersV1(Iterable<SyncAlbumUserV1> data) async {
|
|
||||||
try {
|
|
||||||
await _db.batch((batch) {
|
|
||||||
for (final albumUser in data) {
|
|
||||||
final companion = AlbumUserEntityCompanion(
|
|
||||||
role: Value(albumUser.role.toAlbumUserRole()),
|
|
||||||
);
|
|
||||||
|
|
||||||
batch.insert(
|
|
||||||
_db.albumUserEntity,
|
|
||||||
companion.copyWith(
|
|
||||||
albumId: Value(albumUser.albumId),
|
|
||||||
userId: Value(albumUser.userId),
|
|
||||||
),
|
|
||||||
onConflict: DoUpdate((_) => companion),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (e, s) {
|
|
||||||
_logger.severe('Error while processing updateAlbumUsersV1', e, s);
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> deleteAlbumUsersV1(Iterable<SyncAlbumUserDeleteV1> data) async {
|
|
||||||
try {
|
|
||||||
await _db.batch((batch) {
|
|
||||||
for (final albumUser in data) {
|
|
||||||
batch.delete(
|
|
||||||
_db.albumUserEntity,
|
|
||||||
AlbumUserEntityCompanion(
|
|
||||||
albumId: Value(albumUser.albumId),
|
|
||||||
userId: Value(albumUser.userId),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (e, s) {
|
|
||||||
_logger.severe('Error while processing deleteAlbumUsersV1', e, s);
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _updateAssetsV1(Iterable<SyncAssetV1> data) =>
|
Future<void> _updateAssetsV1(Iterable<SyncAssetV1> data) =>
|
||||||
_db.batch((batch) {
|
_db.batch((batch) {
|
||||||
for (final asset in data) {
|
for (final asset in data) {
|
||||||
@@ -387,19 +251,3 @@ extension on api.AssetVisibility {
|
|||||||
_ => throw Exception('Unknown AssetVisibility value: $this'),
|
_ => throw Exception('Unknown AssetVisibility value: $this'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
extension on api.AssetOrder {
|
|
||||||
AssetOrder toAssetOrder() => switch (this) {
|
|
||||||
api.AssetOrder.asc => AssetOrder.asc,
|
|
||||||
api.AssetOrder.desc => AssetOrder.desc,
|
|
||||||
_ => throw Exception('Unknown AssetOrder value: $this'),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
extension on api.AlbumUserRole {
|
|
||||||
AlbumUserRole toAlbumUserRole() => switch (this) {
|
|
||||||
api.AlbumUserRole.editor => AlbumUserRole.editor,
|
|
||||||
api.AlbumUserRole.viewer => AlbumUserRole.viewer,
|
|
||||||
_ => throw Exception('Unknown AlbumUserRole value: $this'),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -61,10 +61,8 @@ final _features = [
|
|||||||
icon: Icons.delete_sweep_rounded,
|
icon: Icons.delete_sweep_rounded,
|
||||||
onTap: (_, ref) async {
|
onTap: (_, ref) async {
|
||||||
final db = ref.read(driftProvider);
|
final db = ref.read(driftProvider);
|
||||||
await db.remoteExifEntity.deleteAll();
|
|
||||||
await db.remoteAssetEntity.deleteAll();
|
await db.remoteAssetEntity.deleteAll();
|
||||||
await db.remoteAlbumEntity.deleteAll();
|
await db.remoteExifEntity.deleteAll();
|
||||||
await db.remoteAlbumAssetEntity.deleteAll();
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
_Feature(
|
_Feature(
|
||||||
|
|||||||
@@ -130,10 +130,6 @@ final _remoteStats = [
|
|||||||
name: 'Exif Entities',
|
name: 'Exif Entities',
|
||||||
load: (db) => db.managers.remoteExifEntity.count(),
|
load: (db) => db.managers.remoteExifEntity.count(),
|
||||||
),
|
),
|
||||||
_Stat(
|
|
||||||
name: 'Remote Albums',
|
|
||||||
load: (db) => db.managers.remoteAlbumEntity.count(),
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
|
|||||||
@@ -35,10 +35,8 @@ class AuthRepository extends DatabaseRepository implements IAuthRepository {
|
|||||||
db.albums.clear(),
|
db.albums.clear(),
|
||||||
db.eTags.clear(),
|
db.eTags.clear(),
|
||||||
db.users.clear(),
|
db.users.clear(),
|
||||||
_drift.remoteExifEntity.deleteAll(),
|
|
||||||
_drift.remoteAssetEntity.deleteAll(),
|
_drift.remoteAssetEntity.deleteAll(),
|
||||||
_drift.remoteAlbumEntity.deleteAll(),
|
_drift.remoteExifEntity.deleteAll(),
|
||||||
_drift.remoteAlbumAssetEntity.deleteAll(),
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/exif.model.dart';
|
||||||
|
import 'package:immich_mobile/utils/translation.dart';
|
||||||
|
|
||||||
|
String getAltText(
|
||||||
|
ExifInfo? exifInfo,
|
||||||
|
DateTime fileCreatedAt,
|
||||||
|
AssetType type,
|
||||||
|
List<String> peopleNames,
|
||||||
|
) {
|
||||||
|
if (exifInfo?.description != null && exifInfo!.description!.isNotEmpty) {
|
||||||
|
return exifInfo.description!;
|
||||||
|
}
|
||||||
|
final (template, args) =
|
||||||
|
getAltTextTemplate(exifInfo, fileCreatedAt, type, peopleNames);
|
||||||
|
return t(template, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
(String, Map<String, String>) getAltTextTemplate(
|
||||||
|
ExifInfo? exifInfo,
|
||||||
|
DateTime fileCreatedAt,
|
||||||
|
AssetType type,
|
||||||
|
List<String> peopleNames,
|
||||||
|
) {
|
||||||
|
final isVideo = type == AssetType.video;
|
||||||
|
final hasLocation = exifInfo?.city != null && exifInfo?.country != null;
|
||||||
|
final date = DateFormat.yMMMMd().format(fileCreatedAt);
|
||||||
|
final args = {
|
||||||
|
"isVideo": isVideo.toString(),
|
||||||
|
"date": date,
|
||||||
|
"city": exifInfo?.city ?? "",
|
||||||
|
"country": exifInfo?.country ?? "",
|
||||||
|
"person1": peopleNames.elementAtOrNull(0) ?? "",
|
||||||
|
"person2": peopleNames.elementAtOrNull(1) ?? "",
|
||||||
|
"person3": peopleNames.elementAtOrNull(2) ?? "",
|
||||||
|
"additionalCount": (peopleNames.length - 3).toString(),
|
||||||
|
};
|
||||||
|
final template = hasLocation
|
||||||
|
? (switch (peopleNames.length) {
|
||||||
|
0 => "image_alt_text_date_place",
|
||||||
|
1 => "image_alt_text_date_place_1_person",
|
||||||
|
2 => "image_alt_text_date_place_2_people",
|
||||||
|
3 => "image_alt_text_date_place_3_people",
|
||||||
|
_ => "image_alt_text_date_place_4_or_more_people"
|
||||||
|
})
|
||||||
|
: (switch (peopleNames.length) {
|
||||||
|
0 => "image_alt_text_date",
|
||||||
|
1 => "image_alt_text_date_1_person",
|
||||||
|
2 => "image_alt_text_date_2_people",
|
||||||
|
3 => "image_alt_text_date_3_people",
|
||||||
|
_ => "image_alt_text_date_4_or_more_people"
|
||||||
|
});
|
||||||
|
return (template, args);
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
@@ -74,10 +75,14 @@ class GroupDividerTitle extends HookConsumerWidget {
|
|||||||
? Icon(
|
? Icon(
|
||||||
Icons.check_circle_rounded,
|
Icons.check_circle_rounded,
|
||||||
color: context.primaryColor,
|
color: context.primaryColor,
|
||||||
|
semanticLabel:
|
||||||
|
"unselect_all_in".tr(namedArgs: {"group": text}),
|
||||||
)
|
)
|
||||||
: Icon(
|
: Icon(
|
||||||
Icons.check_circle_outline_rounded,
|
Icons.check_circle_outline_rounded,
|
||||||
color: context.colorScheme.onSurfaceSecondary,
|
color: context.colorScheme.onSurfaceSecondary,
|
||||||
|
semanticLabel:
|
||||||
|
"select_all_in".tr(namedArgs: {"group": text}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -66,10 +66,13 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||||||
Icons.face_outlined,
|
Icons.face_outlined,
|
||||||
size: widgetSize,
|
size: widgetSize,
|
||||||
)
|
)
|
||||||
: UserCircleAvatar(
|
: Semantics(
|
||||||
radius: 17,
|
label: "logged_in_as".tr(namedArgs: {"user": user.name}),
|
||||||
size: 31,
|
child: UserCircleAvatar(
|
||||||
user: user,
|
radius: 17,
|
||||||
|
size: 31,
|
||||||
|
user: user,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:immich_mobile/providers/image/immich_local_thumbnail_provider.da
|
|||||||
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/hooks/blurhash_hook.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';
|
||||||
import 'package:octo_image/octo_image.dart';
|
import 'package:octo_image/octo_image.dart';
|
||||||
@@ -77,6 +78,13 @@ class ImmichThumbnail extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final assetAltText = getAltText(
|
||||||
|
asset!.exifInfo,
|
||||||
|
asset!.fileCreatedAt,
|
||||||
|
asset!.type,
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
final thumbnailProviderInstance = ImmichThumbnail.imageProvider(
|
final thumbnailProviderInstance = ImmichThumbnail.imageProvider(
|
||||||
asset: asset,
|
asset: asset,
|
||||||
userId: userId,
|
userId: userId,
|
||||||
@@ -90,18 +98,21 @@ class ImmichThumbnail extends HookConsumerWidget {
|
|||||||
return originalErrorWidgetBuilder(ctx, error, stackTrace);
|
return originalErrorWidgetBuilder(ctx, error, stackTrace);
|
||||||
}
|
}
|
||||||
|
|
||||||
return OctoImage.fromSet(
|
return Semantics(
|
||||||
placeholderFadeInDuration: Duration.zero,
|
label: assetAltText,
|
||||||
fadeInDuration: Duration.zero,
|
child: OctoImage.fromSet(
|
||||||
fadeOutDuration: const Duration(milliseconds: 100),
|
placeholderFadeInDuration: Duration.zero,
|
||||||
octoSet: OctoSet(
|
fadeInDuration: Duration.zero,
|
||||||
placeholderBuilder: blurHashPlaceholderBuilder(blurhash, fit: fit),
|
fadeOutDuration: const Duration(milliseconds: 100),
|
||||||
errorBuilder: customErrorBuilder,
|
octoSet: OctoSet(
|
||||||
|
placeholderBuilder: blurHashPlaceholderBuilder(blurhash, fit: fit),
|
||||||
|
errorBuilder: customErrorBuilder,
|
||||||
|
),
|
||||||
|
image: thumbnailProviderInstance,
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
fit: fit,
|
||||||
),
|
),
|
||||||
image: thumbnailProviderInstance,
|
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
fit: fit,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+2
@@ -122,6 +122,8 @@ Class | Method | HTTP request | Description
|
|||||||
*DeprecatedApi* | [**getRandom**](doc//DeprecatedApi.md#getrandom) | **GET** /assets/random |
|
*DeprecatedApi* | [**getRandom**](doc//DeprecatedApi.md#getrandom) | **GET** /assets/random |
|
||||||
*DownloadApi* | [**downloadArchive**](doc//DownloadApi.md#downloadarchive) | **POST** /download/archive |
|
*DownloadApi* | [**downloadArchive**](doc//DownloadApi.md#downloadarchive) | **POST** /download/archive |
|
||||||
*DownloadApi* | [**getDownloadInfo**](doc//DownloadApi.md#getdownloadinfo) | **POST** /download/info |
|
*DownloadApi* | [**getDownloadInfo**](doc//DownloadApi.md#getdownloadinfo) | **POST** /download/info |
|
||||||
|
*DuplicatesApi* | [**deleteDuplicate**](doc//DuplicatesApi.md#deleteduplicate) | **DELETE** /duplicates/{id} |
|
||||||
|
*DuplicatesApi* | [**deleteDuplicates**](doc//DuplicatesApi.md#deleteduplicates) | **DELETE** /duplicates |
|
||||||
*DuplicatesApi* | [**getAssetDuplicates**](doc//DuplicatesApi.md#getassetduplicates) | **GET** /duplicates |
|
*DuplicatesApi* | [**getAssetDuplicates**](doc//DuplicatesApi.md#getassetduplicates) | **GET** /duplicates |
|
||||||
*FacesApi* | [**createFace**](doc//FacesApi.md#createface) | **POST** /faces |
|
*FacesApi* | [**createFace**](doc//FacesApi.md#createface) | **POST** /faces |
|
||||||
*FacesApi* | [**deleteFace**](doc//FacesApi.md#deleteface) | **DELETE** /faces/{id} |
|
*FacesApi* | [**deleteFace**](doc//FacesApi.md#deleteface) | **DELETE** /faces/{id} |
|
||||||
|
|||||||
Generated
+22
-6
@@ -542,7 +542,9 @@ class AssetsApi {
|
|||||||
/// * [String] key:
|
/// * [String] key:
|
||||||
///
|
///
|
||||||
/// * [String] duration:
|
/// * [String] duration:
|
||||||
Future<Response> replaceAssetWithHttpInfo(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? duration, }) async {
|
///
|
||||||
|
/// * [String] filename:
|
||||||
|
Future<Response> replaceAssetWithHttpInfo(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? duration, String? filename, }) async {
|
||||||
// ignore: prefer_const_declarations
|
// ignore: prefer_const_declarations
|
||||||
final apiPath = r'/assets/{id}/original'
|
final apiPath = r'/assets/{id}/original'
|
||||||
.replaceAll('{id}', id);
|
.replaceAll('{id}', id);
|
||||||
@@ -587,6 +589,10 @@ class AssetsApi {
|
|||||||
hasFields = true;
|
hasFields = true;
|
||||||
mp.fields[r'fileModifiedAt'] = parameterToString(fileModifiedAt);
|
mp.fields[r'fileModifiedAt'] = parameterToString(fileModifiedAt);
|
||||||
}
|
}
|
||||||
|
if (filename != null) {
|
||||||
|
hasFields = true;
|
||||||
|
mp.fields[r'filename'] = parameterToString(filename);
|
||||||
|
}
|
||||||
if (hasFields) {
|
if (hasFields) {
|
||||||
postBody = mp;
|
postBody = mp;
|
||||||
}
|
}
|
||||||
@@ -623,8 +629,10 @@ class AssetsApi {
|
|||||||
/// * [String] key:
|
/// * [String] key:
|
||||||
///
|
///
|
||||||
/// * [String] duration:
|
/// * [String] duration:
|
||||||
Future<AssetMediaResponseDto?> replaceAsset(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? duration, }) async {
|
///
|
||||||
final response = await replaceAssetWithHttpInfo(id, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, duration: duration, );
|
/// * [String] filename:
|
||||||
|
Future<AssetMediaResponseDto?> replaceAsset(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? duration, String? filename, }) async {
|
||||||
|
final response = await replaceAssetWithHttpInfo(id, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, duration: duration, filename: filename, );
|
||||||
if (response.statusCode >= HttpStatus.badRequest) {
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
}
|
}
|
||||||
@@ -788,6 +796,8 @@ class AssetsApi {
|
|||||||
///
|
///
|
||||||
/// * [String] duration:
|
/// * [String] duration:
|
||||||
///
|
///
|
||||||
|
/// * [String] filename:
|
||||||
|
///
|
||||||
/// * [bool] isFavorite:
|
/// * [bool] isFavorite:
|
||||||
///
|
///
|
||||||
/// * [String] livePhotoVideoId:
|
/// * [String] livePhotoVideoId:
|
||||||
@@ -795,7 +805,7 @@ class AssetsApi {
|
|||||||
/// * [MultipartFile] sidecarData:
|
/// * [MultipartFile] sidecarData:
|
||||||
///
|
///
|
||||||
/// * [AssetVisibility] visibility:
|
/// * [AssetVisibility] visibility:
|
||||||
Future<Response> uploadAssetWithHttpInfo(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? xImmichChecksum, String? duration, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async {
|
Future<Response> uploadAssetWithHttpInfo(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async {
|
||||||
// ignore: prefer_const_declarations
|
// ignore: prefer_const_declarations
|
||||||
final apiPath = r'/assets';
|
final apiPath = r'/assets';
|
||||||
|
|
||||||
@@ -843,6 +853,10 @@ class AssetsApi {
|
|||||||
hasFields = true;
|
hasFields = true;
|
||||||
mp.fields[r'fileModifiedAt'] = parameterToString(fileModifiedAt);
|
mp.fields[r'fileModifiedAt'] = parameterToString(fileModifiedAt);
|
||||||
}
|
}
|
||||||
|
if (filename != null) {
|
||||||
|
hasFields = true;
|
||||||
|
mp.fields[r'filename'] = parameterToString(filename);
|
||||||
|
}
|
||||||
if (isFavorite != null) {
|
if (isFavorite != null) {
|
||||||
hasFields = true;
|
hasFields = true;
|
||||||
mp.fields[r'isFavorite'] = parameterToString(isFavorite);
|
mp.fields[r'isFavorite'] = parameterToString(isFavorite);
|
||||||
@@ -894,6 +908,8 @@ class AssetsApi {
|
|||||||
///
|
///
|
||||||
/// * [String] duration:
|
/// * [String] duration:
|
||||||
///
|
///
|
||||||
|
/// * [String] filename:
|
||||||
|
///
|
||||||
/// * [bool] isFavorite:
|
/// * [bool] isFavorite:
|
||||||
///
|
///
|
||||||
/// * [String] livePhotoVideoId:
|
/// * [String] livePhotoVideoId:
|
||||||
@@ -901,8 +917,8 @@ class AssetsApi {
|
|||||||
/// * [MultipartFile] sidecarData:
|
/// * [MultipartFile] sidecarData:
|
||||||
///
|
///
|
||||||
/// * [AssetVisibility] visibility:
|
/// * [AssetVisibility] visibility:
|
||||||
Future<AssetMediaResponseDto?> uploadAsset(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? xImmichChecksum, String? duration, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async {
|
Future<AssetMediaResponseDto?> uploadAsset(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async {
|
||||||
final response = await uploadAssetWithHttpInfo(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, xImmichChecksum: xImmichChecksum, duration: duration, isFavorite: isFavorite, livePhotoVideoId: livePhotoVideoId, sidecarData: sidecarData, visibility: visibility, );
|
final response = await uploadAssetWithHttpInfo(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, xImmichChecksum: xImmichChecksum, duration: duration, filename: filename, isFavorite: isFavorite, livePhotoVideoId: livePhotoVideoId, sidecarData: sidecarData, visibility: visibility, );
|
||||||
if (response.statusCode >= HttpStatus.badRequest) {
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
}
|
}
|
||||||
|
|||||||
+79
@@ -16,6 +16,85 @@ class DuplicatesApi {
|
|||||||
|
|
||||||
final ApiClient apiClient;
|
final ApiClient apiClient;
|
||||||
|
|
||||||
|
/// Performs an HTTP 'DELETE /duplicates/{id}' operation and returns the [Response].
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [String] id (required):
|
||||||
|
Future<Response> deleteDuplicateWithHttpInfo(String id,) async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final apiPath = r'/duplicates/{id}'
|
||||||
|
.replaceAll('{id}', id);
|
||||||
|
|
||||||
|
// ignore: prefer_final_locals
|
||||||
|
Object? postBody;
|
||||||
|
|
||||||
|
final queryParams = <QueryParam>[];
|
||||||
|
final headerParams = <String, String>{};
|
||||||
|
final formParams = <String, String>{};
|
||||||
|
|
||||||
|
const contentTypes = <String>[];
|
||||||
|
|
||||||
|
|
||||||
|
return apiClient.invokeAPI(
|
||||||
|
apiPath,
|
||||||
|
'DELETE',
|
||||||
|
queryParams,
|
||||||
|
postBody,
|
||||||
|
headerParams,
|
||||||
|
formParams,
|
||||||
|
contentTypes.isEmpty ? null : contentTypes.first,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [String] id (required):
|
||||||
|
Future<void> deleteDuplicate(String id,) async {
|
||||||
|
final response = await deleteDuplicateWithHttpInfo(id,);
|
||||||
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs an HTTP 'DELETE /duplicates' operation and returns the [Response].
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [BulkIdsDto] bulkIdsDto (required):
|
||||||
|
Future<Response> deleteDuplicatesWithHttpInfo(BulkIdsDto bulkIdsDto,) async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final apiPath = r'/duplicates';
|
||||||
|
|
||||||
|
// ignore: prefer_final_locals
|
||||||
|
Object? postBody = bulkIdsDto;
|
||||||
|
|
||||||
|
final queryParams = <QueryParam>[];
|
||||||
|
final headerParams = <String, String>{};
|
||||||
|
final formParams = <String, String>{};
|
||||||
|
|
||||||
|
const contentTypes = <String>['application/json'];
|
||||||
|
|
||||||
|
|
||||||
|
return apiClient.invokeAPI(
|
||||||
|
apiPath,
|
||||||
|
'DELETE',
|
||||||
|
queryParams,
|
||||||
|
postBody,
|
||||||
|
headerParams,
|
||||||
|
formParams,
|
||||||
|
contentTypes.isEmpty ? null : contentTypes.first,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [BulkIdsDto] bulkIdsDto (required):
|
||||||
|
Future<void> deleteDuplicates(BulkIdsDto bulkIdsDto,) async {
|
||||||
|
final response = await deleteDuplicatesWithHttpInfo(bulkIdsDto,);
|
||||||
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Performs an HTTP 'GET /duplicates' operation and returns the [Response].
|
/// Performs an HTTP 'GET /duplicates' operation and returns the [Response].
|
||||||
Future<Response> getAssetDuplicatesWithHttpInfo() async {
|
Future<Response> getAssetDuplicatesWithHttpInfo() async {
|
||||||
// ignore: prefer_const_declarations
|
// ignore: prefer_const_declarations
|
||||||
|
|||||||
+7
-3
@@ -43,7 +43,7 @@ class SystemConfigOAuthDto {
|
|||||||
String clientSecret;
|
String clientSecret;
|
||||||
|
|
||||||
/// Minimum value: 0
|
/// Minimum value: 0
|
||||||
num defaultStorageQuota;
|
int? defaultStorageQuota;
|
||||||
|
|
||||||
bool enabled;
|
bool enabled;
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ class SystemConfigOAuthDto {
|
|||||||
(buttonText.hashCode) +
|
(buttonText.hashCode) +
|
||||||
(clientId.hashCode) +
|
(clientId.hashCode) +
|
||||||
(clientSecret.hashCode) +
|
(clientSecret.hashCode) +
|
||||||
(defaultStorageQuota.hashCode) +
|
(defaultStorageQuota == null ? 0 : defaultStorageQuota!.hashCode) +
|
||||||
(enabled.hashCode) +
|
(enabled.hashCode) +
|
||||||
(issuerUrl.hashCode) +
|
(issuerUrl.hashCode) +
|
||||||
(mobileOverrideEnabled.hashCode) +
|
(mobileOverrideEnabled.hashCode) +
|
||||||
@@ -119,7 +119,11 @@ class SystemConfigOAuthDto {
|
|||||||
json[r'buttonText'] = this.buttonText;
|
json[r'buttonText'] = this.buttonText;
|
||||||
json[r'clientId'] = this.clientId;
|
json[r'clientId'] = this.clientId;
|
||||||
json[r'clientSecret'] = this.clientSecret;
|
json[r'clientSecret'] = this.clientSecret;
|
||||||
|
if (this.defaultStorageQuota != null) {
|
||||||
json[r'defaultStorageQuota'] = this.defaultStorageQuota;
|
json[r'defaultStorageQuota'] = this.defaultStorageQuota;
|
||||||
|
} else {
|
||||||
|
// json[r'defaultStorageQuota'] = null;
|
||||||
|
}
|
||||||
json[r'enabled'] = this.enabled;
|
json[r'enabled'] = this.enabled;
|
||||||
json[r'issuerUrl'] = this.issuerUrl;
|
json[r'issuerUrl'] = this.issuerUrl;
|
||||||
json[r'mobileOverrideEnabled'] = this.mobileOverrideEnabled;
|
json[r'mobileOverrideEnabled'] = this.mobileOverrideEnabled;
|
||||||
@@ -148,7 +152,7 @@ class SystemConfigOAuthDto {
|
|||||||
buttonText: mapValueOfType<String>(json, r'buttonText')!,
|
buttonText: mapValueOfType<String>(json, r'buttonText')!,
|
||||||
clientId: mapValueOfType<String>(json, r'clientId')!,
|
clientId: mapValueOfType<String>(json, r'clientId')!,
|
||||||
clientSecret: mapValueOfType<String>(json, r'clientSecret')!,
|
clientSecret: mapValueOfType<String>(json, r'clientSecret')!,
|
||||||
defaultStorageQuota: num.parse('${json[r'defaultStorageQuota']}'),
|
defaultStorageQuota: mapValueOfType<int>(json, r'defaultStorageQuota'),
|
||||||
enabled: mapValueOfType<bool>(json, r'enabled')!,
|
enabled: mapValueOfType<bool>(json, r'enabled')!,
|
||||||
issuerUrl: mapValueOfType<String>(json, r'issuerUrl')!,
|
issuerUrl: mapValueOfType<String>(json, r'issuerUrl')!,
|
||||||
mobileOverrideEnabled: mapValueOfType<bool>(json, r'mobileOverrideEnabled')!,
|
mobileOverrideEnabled: mapValueOfType<bool>(json, r'mobileOverrideEnabled')!,
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/exif.model.dart';
|
||||||
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
|
import 'package:immich_mobile/utils/thumbnail_utils.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
final dateTime = DateTime(2025, 04, 25, 12, 13, 14);
|
||||||
|
final dateTimeString = DateFormat.yMMMMd().format(dateTime);
|
||||||
|
|
||||||
|
test('returns description if it has one', () {
|
||||||
|
final result = getAltText(
|
||||||
|
const ExifInfo(description: 'description'),
|
||||||
|
dateTime,
|
||||||
|
AssetType.image,
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
expect(result, 'description');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns image alt text with date if no location', () {
|
||||||
|
final (template, args) = getAltTextTemplate(
|
||||||
|
const ExifInfo(),
|
||||||
|
dateTime,
|
||||||
|
AssetType.image,
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
expect(template, "image_alt_text_date");
|
||||||
|
expect(args["isVideo"], "false");
|
||||||
|
expect(args["date"], dateTimeString);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns image alt text with date and place', () {
|
||||||
|
final (template, args) = getAltTextTemplate(
|
||||||
|
const ExifInfo(city: 'city', country: 'country'),
|
||||||
|
dateTime,
|
||||||
|
AssetType.video,
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
expect(template, "image_alt_text_date_place");
|
||||||
|
expect(args["isVideo"], "true");
|
||||||
|
expect(args["date"], dateTimeString);
|
||||||
|
expect(args["city"], "city");
|
||||||
|
expect(args["country"], "country");
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns image alt text with date and some people', () {
|
||||||
|
final (template, args) = getAltTextTemplate(
|
||||||
|
const ExifInfo(),
|
||||||
|
dateTime,
|
||||||
|
AssetType.image,
|
||||||
|
["Alice", "Bob"],
|
||||||
|
);
|
||||||
|
expect(template, "image_alt_text_date_2_people");
|
||||||
|
expect(args["isVideo"], "false");
|
||||||
|
expect(args["date"], dateTimeString);
|
||||||
|
expect(args["person1"], "Alice");
|
||||||
|
expect(args["person2"], "Bob");
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns image alt text with date and location and many people', () {
|
||||||
|
final (template, args) = getAltTextTemplate(
|
||||||
|
const ExifInfo(city: "city", country: 'country'),
|
||||||
|
dateTime,
|
||||||
|
AssetType.video,
|
||||||
|
["Alice", "Bob", "Carol", "David", "Eve"],
|
||||||
|
);
|
||||||
|
expect(template, "image_alt_text_date_place_4_or_more_people");
|
||||||
|
expect(args["isVideo"], "true");
|
||||||
|
expect(args["date"], dateTimeString);
|
||||||
|
expect(args["city"], "city");
|
||||||
|
expect(args["country"], "country");
|
||||||
|
expect(args["person1"], "Alice");
|
||||||
|
expect(args["person2"], "Bob");
|
||||||
|
expect(args["person3"], "Carol");
|
||||||
|
expect(args["additionalCount"], "2");
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -2698,6 +2698,39 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/duplicates": {
|
"/duplicates": {
|
||||||
|
"delete": {
|
||||||
|
"operationId": "deleteDuplicates",
|
||||||
|
"parameters": [],
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/BulkIdsDto"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"bearer": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cookie": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"api_key": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Duplicates"
|
||||||
|
]
|
||||||
|
},
|
||||||
"get": {
|
"get": {
|
||||||
"operationId": "getAssetDuplicates",
|
"operationId": "getAssetDuplicates",
|
||||||
"parameters": [],
|
"parameters": [],
|
||||||
@@ -2732,6 +2765,41 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/duplicates/{id}": {
|
||||||
|
"delete": {
|
||||||
|
"operationId": "deleteDuplicate",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"required": true,
|
||||||
|
"in": "path",
|
||||||
|
"schema": {
|
||||||
|
"format": "uuid",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"bearer": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cookie": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"api_key": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Duplicates"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"/faces": {
|
"/faces": {
|
||||||
"get": {
|
"get": {
|
||||||
"operationId": "getFaces",
|
"operationId": "getFaces",
|
||||||
@@ -9401,6 +9469,9 @@
|
|||||||
"format": "date-time",
|
"format": "date-time",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"filename": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"isFavorite": {
|
"isFavorite": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
@@ -9451,6 +9522,9 @@
|
|||||||
"fileModifiedAt": {
|
"fileModifiedAt": {
|
||||||
"format": "date-time",
|
"format": "date-time",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"filename": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
@@ -14270,8 +14344,10 @@
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"defaultStorageQuota": {
|
"defaultStorageQuota": {
|
||||||
|
"format": "int64",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"type": "number"
|
"nullable": true,
|
||||||
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"enabled": {
|
"enabled": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
|
|||||||
@@ -444,6 +444,7 @@ export type AssetMediaCreateDto = {
|
|||||||
duration?: string;
|
duration?: string;
|
||||||
fileCreatedAt: string;
|
fileCreatedAt: string;
|
||||||
fileModifiedAt: string;
|
fileModifiedAt: string;
|
||||||
|
filename?: string;
|
||||||
isFavorite?: boolean;
|
isFavorite?: boolean;
|
||||||
livePhotoVideoId?: string;
|
livePhotoVideoId?: string;
|
||||||
sidecarData?: Blob;
|
sidecarData?: Blob;
|
||||||
@@ -510,6 +511,7 @@ export type AssetMediaReplaceDto = {
|
|||||||
duration?: string;
|
duration?: string;
|
||||||
fileCreatedAt: string;
|
fileCreatedAt: string;
|
||||||
fileModifiedAt: string;
|
fileModifiedAt: string;
|
||||||
|
filename?: string;
|
||||||
};
|
};
|
||||||
export type SignUpDto = {
|
export type SignUpDto = {
|
||||||
email: string;
|
email: string;
|
||||||
@@ -1390,7 +1392,7 @@ export type SystemConfigOAuthDto = {
|
|||||||
buttonText: string;
|
buttonText: string;
|
||||||
clientId: string;
|
clientId: string;
|
||||||
clientSecret: string;
|
clientSecret: string;
|
||||||
defaultStorageQuota: number;
|
defaultStorageQuota: number | null;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
issuerUrl: string;
|
issuerUrl: string;
|
||||||
mobileOverrideEnabled: boolean;
|
mobileOverrideEnabled: boolean;
|
||||||
@@ -2284,6 +2286,15 @@ export function getDownloadInfo({ key, downloadInfoDto }: {
|
|||||||
body: downloadInfoDto
|
body: downloadInfoDto
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
export function deleteDuplicates({ bulkIdsDto }: {
|
||||||
|
bulkIdsDto: BulkIdsDto;
|
||||||
|
}, opts?: Oazapfts.RequestOpts) {
|
||||||
|
return oazapfts.ok(oazapfts.fetchText("/duplicates", oazapfts.json({
|
||||||
|
...opts,
|
||||||
|
method: "DELETE",
|
||||||
|
body: bulkIdsDto
|
||||||
|
})));
|
||||||
|
}
|
||||||
export function getAssetDuplicates(opts?: Oazapfts.RequestOpts) {
|
export function getAssetDuplicates(opts?: Oazapfts.RequestOpts) {
|
||||||
return oazapfts.ok(oazapfts.fetchJson<{
|
return oazapfts.ok(oazapfts.fetchJson<{
|
||||||
status: 200;
|
status: 200;
|
||||||
@@ -2292,6 +2303,14 @@ export function getAssetDuplicates(opts?: Oazapfts.RequestOpts) {
|
|||||||
...opts
|
...opts
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
export function deleteDuplicate({ id }: {
|
||||||
|
id: string;
|
||||||
|
}, opts?: Oazapfts.RequestOpts) {
|
||||||
|
return oazapfts.ok(oazapfts.fetchText(`/duplicates/${encodeURIComponent(id)}`, {
|
||||||
|
...opts,
|
||||||
|
method: "DELETE"
|
||||||
|
}));
|
||||||
|
}
|
||||||
export function getFaces({ id }: {
|
export function getFaces({ id }: {
|
||||||
id: string;
|
id: string;
|
||||||
}, opts?: Oazapfts.RequestOpts) {
|
}, opts?: Oazapfts.RequestOpts) {
|
||||||
|
|||||||
@@ -16,6 +16,58 @@ ENV PATH="${PATH}:/usr/src/app/bin" \
|
|||||||
NVIDIA_VISIBLE_DEVICES=all
|
NVIDIA_VISIBLE_DEVICES=all
|
||||||
ENTRYPOINT ["tini", "--", "/bin/sh"]
|
ENTRYPOINT ["tini", "--", "/bin/sh"]
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
RUN usermod -aG sudo node
|
||||||
|
RUN echo "node ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
||||||
|
RUN mkdir -p /workspaces/immich
|
||||||
|
RUN chown node -R /workspaces
|
||||||
|
|
||||||
|
RUN mkdir /immich-devcontainer && chown node -R /immich-devcontainer
|
||||||
|
COPY --chmod=777 ../.devcontainer/server/*.sh /immich-devcontainer/
|
||||||
|
|
||||||
|
FROM dev-container-server AS dev-container-mobile
|
||||||
|
|
||||||
|
# Enable multiarch for arm64 if necessary
|
||||||
|
RUN if [ "$(dpkg --print-architecture)" = "arm64" ]; then \
|
||||||
|
dpkg --add-architecture amd64 && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
qemu-user-static \
|
||||||
|
libc6:amd64 \
|
||||||
|
libstdc++6:amd64 \
|
||||||
|
libgcc1:amd64; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Flutter SDK
|
||||||
|
# https://flutter.dev/docs/development/tools/sdk/releases?tab=linux
|
||||||
|
ENV FLUTTER_CHANNEL="stable"
|
||||||
|
ENV FLUTTER_VERSION="3.29.3"
|
||||||
|
ENV FLUTTER_HOME=/flutter
|
||||||
|
ENV PATH=${PATH}:${FLUTTER_HOME}/bin
|
||||||
|
|
||||||
|
# Flutter SDK
|
||||||
|
RUN mkdir -p ${FLUTTER_HOME} \
|
||||||
|
&& curl -C - --output flutter.tar.xz https://storage.googleapis.com/flutter_infra_release/releases/${FLUTTER_CHANNEL}/linux/flutter_linux_${FLUTTER_VERSION}-${FLUTTER_CHANNEL}.tar.xz \
|
||||||
|
&& tar -xf flutter.tar.xz --strip-components=1 -C ${FLUTTER_HOME} \
|
||||||
|
&& rm flutter.tar.xz \
|
||||||
|
&& chown -R node ${FLUTTER_HOME}
|
||||||
|
|
||||||
|
USER node
|
||||||
|
RUN sudo apt-get update \
|
||||||
|
&& wget -qO- https://dcm.dev/pgp-key.public | sudo gpg --dearmor -o /usr/share/keyrings/dcm.gpg \
|
||||||
|
&& echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list \
|
||||||
|
&& sudo apt-get update \
|
||||||
|
&& sudo apt-get install dcm -y
|
||||||
|
|
||||||
|
COPY --chmod=777 ../.devcontainer/mobile/container-mobile-post-create.sh /immich-devcontainer/container-mobile-post-create.sh
|
||||||
|
|
||||||
|
RUN dart --disable-analytics
|
||||||
|
|
||||||
FROM dev AS prod
|
FROM dev AS prod
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export interface SystemConfig {
|
|||||||
buttonText: string;
|
buttonText: string;
|
||||||
clientId: string;
|
clientId: string;
|
||||||
clientSecret: string;
|
clientSecret: string;
|
||||||
defaultStorageQuota: number;
|
defaultStorageQuota: number | null;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
issuerUrl: string;
|
issuerUrl: string;
|
||||||
mobileOverrideEnabled: boolean;
|
mobileOverrideEnabled: boolean;
|
||||||
@@ -253,7 +253,7 @@ export const defaults = Object.freeze<SystemConfig>({
|
|||||||
buttonText: 'Login with OAuth',
|
buttonText: 'Login with OAuth',
|
||||||
clientId: '',
|
clientId: '',
|
||||||
clientSecret: '',
|
clientSecret: '',
|
||||||
defaultStorageQuota: 0,
|
defaultStorageQuota: null,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
issuerUrl: '',
|
issuerUrl: '',
|
||||||
mobileOverrideEnabled: false,
|
mobileOverrideEnabled: false,
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { Controller, Get } from '@nestjs/common';
|
import { Body, Controller, Delete, Get, Param } from '@nestjs/common';
|
||||||
import { ApiTags } from '@nestjs/swagger';
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
|
import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { DuplicateResponseDto } from 'src/dtos/duplicate.dto';
|
import { DuplicateResponseDto } from 'src/dtos/duplicate.dto';
|
||||||
import { Auth, Authenticated } from 'src/middleware/auth.guard';
|
import { Auth, Authenticated } from 'src/middleware/auth.guard';
|
||||||
import { DuplicateService } from 'src/services/duplicate.service';
|
import { DuplicateService } from 'src/services/duplicate.service';
|
||||||
|
import { UUIDParamDto } from 'src/validation';
|
||||||
|
|
||||||
@ApiTags('Duplicates')
|
@ApiTags('Duplicates')
|
||||||
@Controller('duplicates')
|
@Controller('duplicates')
|
||||||
@@ -15,4 +17,16 @@ export class DuplicateController {
|
|||||||
getAssetDuplicates(@Auth() auth: AuthDto): Promise<DuplicateResponseDto[]> {
|
getAssetDuplicates(@Auth() auth: AuthDto): Promise<DuplicateResponseDto[]> {
|
||||||
return this.service.getDuplicates(auth);
|
return this.service.getDuplicates(auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Delete()
|
||||||
|
@Authenticated()
|
||||||
|
deleteDuplicates(@Auth() auth: AuthDto, @Body() dto: BulkIdsDto): Promise<void> {
|
||||||
|
return this.service.deleteAll(auth, dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(':id')
|
||||||
|
@Authenticated()
|
||||||
|
deleteDuplicate(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> {
|
||||||
|
return this.service.delete(auth, id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,10 @@ class AssetMediaBase {
|
|||||||
@IsString()
|
@IsString()
|
||||||
duration?: string;
|
duration?: string;
|
||||||
|
|
||||||
|
@Optional()
|
||||||
|
@IsString()
|
||||||
|
filename?: string;
|
||||||
|
|
||||||
// The properties below are added to correctly generate the API docs
|
// The properties below are added to correctly generate the API docs
|
||||||
// and client SDKs. Validation should be handled in the controller.
|
// and client SDKs. Validation should be handled in the controller.
|
||||||
@ApiProperty({ type: 'string', format: 'binary' })
|
@ApiProperty({ type: 'string', format: 'binary' })
|
||||||
|
|||||||
@@ -1,14 +1,6 @@
|
|||||||
import { IsNotEmpty } from 'class-validator';
|
|
||||||
import { AssetResponseDto } from 'src/dtos/asset-response.dto';
|
import { AssetResponseDto } from 'src/dtos/asset-response.dto';
|
||||||
import { ValidateUUID } from 'src/validation';
|
|
||||||
|
|
||||||
export class DuplicateResponseDto {
|
export class DuplicateResponseDto {
|
||||||
duplicateId!: string;
|
duplicateId!: string;
|
||||||
assets!: AssetResponseDto[];
|
assets!: AssetResponseDto[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ResolveDuplicatesDto {
|
|
||||||
@IsNotEmpty()
|
|
||||||
@ValidateUUID({ each: true })
|
|
||||||
assetIds!: string[];
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -360,7 +360,9 @@ class SystemConfigOAuthDto {
|
|||||||
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@Min(0)
|
@Min(0)
|
||||||
defaultStorageQuota!: number;
|
@Optional({ nullable: true })
|
||||||
|
@ApiProperty({ type: 'integer', format: 'int64' })
|
||||||
|
defaultStorageQuota!: number | null;
|
||||||
|
|
||||||
@ValidateBoolean()
|
@ValidateBoolean()
|
||||||
enabled!: boolean;
|
enabled!: boolean;
|
||||||
|
|||||||
@@ -60,6 +60,22 @@ where
|
|||||||
"unique"."duplicateId" = "duplicates"."duplicateId"
|
"unique"."duplicateId" = "duplicates"."duplicateId"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
-- DuplicateRepository.delete
|
||||||
|
update "assets"
|
||||||
|
set
|
||||||
|
"duplicateId" = $1
|
||||||
|
where
|
||||||
|
"ownerId" = $2
|
||||||
|
and "duplicateId" = $3
|
||||||
|
|
||||||
|
-- DuplicateRepository.deleteAll
|
||||||
|
update "assets"
|
||||||
|
set
|
||||||
|
"duplicateId" = $1
|
||||||
|
where
|
||||||
|
"ownerId" = $2
|
||||||
|
and "duplicateId" in ($3)
|
||||||
|
|
||||||
-- DuplicateRepository.search
|
-- DuplicateRepository.search
|
||||||
begin
|
begin
|
||||||
set
|
set
|
||||||
|
|||||||
@@ -279,6 +279,15 @@ where
|
|||||||
"asset_faces"."personId" = $1
|
"asset_faces"."personId" = $1
|
||||||
and "asset_faces"."deletedAt" is null
|
and "asset_faces"."deletedAt" is null
|
||||||
|
|
||||||
|
-- PersonRepository.getAssetPersonByFaceId
|
||||||
|
select
|
||||||
|
"asset_faces"."assetId",
|
||||||
|
"asset_faces"."personId"
|
||||||
|
from
|
||||||
|
"asset_faces"
|
||||||
|
where
|
||||||
|
"asset_faces"."id" = $1
|
||||||
|
|
||||||
-- PersonRepository.getLatestFaceDate
|
-- PersonRepository.getLatestFaceDate
|
||||||
select
|
select
|
||||||
max("asset_job_status"."facesRecognizedAt")::text as "latestDate"
|
max("asset_job_status"."facesRecognizedAt")::text as "latestDate"
|
||||||
|
|||||||
@@ -403,8 +403,6 @@ export class AssetRepository {
|
|||||||
.$call((qb) => qb.select(withFacesAndPeople))
|
.$call((qb) => qb.select(withFacesAndPeople))
|
||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.getById(asset.id, { exifInfo: true, faces: { person: true } });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async remove(asset: { id: string }): Promise<void> {
|
async remove(asset: { id: string }): Promise<void> {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import { Kysely, NotNull, sql } from 'kysely';
|
import { Kysely, NotNull, sql } from 'kysely';
|
||||||
import { InjectKysely } from 'nestjs-kysely';
|
import { InjectKysely } from 'nestjs-kysely';
|
||||||
import { DB } from 'src/db';
|
import { DB } from 'src/db';
|
||||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
import { Chunked, DummyValue, GenerateSql } from 'src/decorators';
|
||||||
import { MapAsset } from 'src/dtos/asset-response.dto';
|
import { MapAsset } from 'src/dtos/asset-response.dto';
|
||||||
import { AssetType, VectorIndex } from 'src/enum';
|
import { AssetType, VectorIndex } from 'src/enum';
|
||||||
import { probes } from 'src/repositories/database.repository';
|
import { probes } from 'src/repositories/database.repository';
|
||||||
@@ -78,6 +78,31 @@ export class DuplicateRepository {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] })
|
||||||
|
async delete(userId: string, id: string): Promise<void> {
|
||||||
|
await this.db
|
||||||
|
.updateTable('assets')
|
||||||
|
.set({ duplicateId: null })
|
||||||
|
.where('ownerId', '=', userId)
|
||||||
|
.where('duplicateId', '=', id)
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] })
|
||||||
|
@Chunked({ paramIndex: 1 })
|
||||||
|
async deleteAll(userId: string, ids: string[]): Promise<void> {
|
||||||
|
if (ids.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.db
|
||||||
|
.updateTable('assets')
|
||||||
|
.set({ duplicateId: null })
|
||||||
|
.where('ownerId', '=', userId)
|
||||||
|
.where('duplicateId', 'in', ids)
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
@GenerateSql({
|
@GenerateSql({
|
||||||
params: [
|
params: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -47,11 +47,20 @@ type EventMap = {
|
|||||||
];
|
];
|
||||||
'config.validate': [{ newConfig: SystemConfig; oldConfig: SystemConfig }];
|
'config.validate': [{ newConfig: SystemConfig; oldConfig: SystemConfig }];
|
||||||
|
|
||||||
|
// activity events
|
||||||
|
'activity.change': [{ recipientId: string[]; userId: string; albumId: string; assetId: string | null }];
|
||||||
|
|
||||||
// album events
|
// album events
|
||||||
'album.update': [{ id: string; recipientId: string }];
|
'album.update': [
|
||||||
|
{ id: string; recipientId: string[]; assetId: string[]; userId: string; status: 'added' | 'removed' },
|
||||||
|
];
|
||||||
'album.invite': [{ id: string; userId: string }];
|
'album.invite': [{ id: string; userId: string }];
|
||||||
|
|
||||||
// asset events
|
// asset events
|
||||||
|
'asset.update': [{ assetIds: string[]; userId: string }];
|
||||||
|
'asset.person': [
|
||||||
|
{ assetId: string; userId: string; personId: string | undefined; status: 'created' | 'removed' | 'removed_soft' },
|
||||||
|
];
|
||||||
'asset.tag': [{ assetId: string }];
|
'asset.tag': [{ assetId: string }];
|
||||||
'asset.untag': [{ assetId: string }];
|
'asset.untag': [{ assetId: string }];
|
||||||
'asset.hide': [{ assetId: string; userId: string }];
|
'asset.hide': [{ assetId: string; userId: string }];
|
||||||
@@ -97,9 +106,12 @@ export type ArgsOf<T extends EmitEvent> = EventMap[T];
|
|||||||
export interface ClientEventMap {
|
export interface ClientEventMap {
|
||||||
on_upload_success: [AssetResponseDto];
|
on_upload_success: [AssetResponseDto];
|
||||||
on_user_delete: [string];
|
on_user_delete: [string];
|
||||||
|
on_activity_change: [{ albumId: string; assetId: string | null }];
|
||||||
|
on_album_update: [{ albumId: string; assetId: string[]; status: 'added' | 'removed' }];
|
||||||
|
on_asset_person: [{ assetId: string; personId: string | undefined; status: 'created' | 'removed' | 'removed_soft' }];
|
||||||
on_asset_delete: [string];
|
on_asset_delete: [string];
|
||||||
on_asset_trash: [string[]];
|
on_asset_trash: [string[]];
|
||||||
on_asset_update: [AssetResponseDto];
|
on_asset_update: [string[]];
|
||||||
on_asset_hidden: [string];
|
on_asset_hidden: [string];
|
||||||
on_asset_restore: [string[]];
|
on_asset_restore: [string[]];
|
||||||
on_asset_stack_update: string[];
|
on_asset_stack_update: string[];
|
||||||
|
|||||||
@@ -179,9 +179,8 @@ export class PersonRepository {
|
|||||||
)
|
)
|
||||||
.$if(!options?.closestFaceAssetId, (qb) =>
|
.$if(!options?.closestFaceAssetId, (qb) =>
|
||||||
qb
|
qb
|
||||||
.orderBy(sql`NULLIF(person.name, '') is null`, 'asc')
|
|
||||||
.orderBy((eb) => eb.fn.count('asset_faces.assetId'), 'desc')
|
.orderBy((eb) => eb.fn.count('asset_faces.assetId'), 'desc')
|
||||||
.orderBy(sql`NULLIF(person.name, '')`, sql`asc nulls last`)
|
.orderBy(sql`NULLIF(person.name, '') asc nulls last`)
|
||||||
.orderBy('person.createdAt'),
|
.orderBy('person.createdAt'),
|
||||||
)
|
)
|
||||||
.$if(!options?.withHidden, (qb) => qb.where('person.isHidden', '=', false))
|
.$if(!options?.withHidden, (qb) => qb.where('person.isHidden', '=', false))
|
||||||
@@ -484,6 +483,15 @@ export class PersonRepository {
|
|||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
|
async getAssetPersonByFaceId(id: string) {
|
||||||
|
return this.db
|
||||||
|
.selectFrom('asset_faces')
|
||||||
|
.select(['asset_faces.assetId', 'asset_faces.personId'])
|
||||||
|
.where('asset_faces.id', '=', id)
|
||||||
|
.executeTakeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
@GenerateSql()
|
@GenerateSql()
|
||||||
async getLatestFaceDate(): Promise<string | undefined> {
|
async getLatestFaceDate(): Promise<string | undefined> {
|
||||||
const result = (await this.db
|
const result = (await this.db
|
||||||
|
|||||||
@@ -11,6 +11,15 @@ export class TrashRepository {
|
|||||||
return this.db.selectFrom('assets').select(['id']).where('status', '=', AssetStatus.DELETED).stream();
|
return this.db.selectFrom('assets').select(['id']).where('status', '=', AssetStatus.DELETED).stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTrashedIds(userId: string): AsyncIterableIterator<{ id: string }> {
|
||||||
|
return this.db
|
||||||
|
.selectFrom('assets')
|
||||||
|
.select(['id'])
|
||||||
|
.where('ownerId', '=', userId)
|
||||||
|
.where('status', '=', AssetStatus.TRASHED)
|
||||||
|
.stream();
|
||||||
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
async restore(userId: string): Promise<number> {
|
async restore(userId: string): Promise<number> {
|
||||||
const { numUpdatedRows } = await this.db
|
const { numUpdatedRows } = await this.db
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { BadRequestException } from '@nestjs/common';
|
import { BadRequestException } from '@nestjs/common';
|
||||||
import { ReactionType } from 'src/dtos/activity.dto';
|
import { ReactionType } from 'src/dtos/activity.dto';
|
||||||
import { ActivityService } from 'src/services/activity.service';
|
import { ActivityService } from 'src/services/activity.service';
|
||||||
|
import { albumStub } from 'test/fixtures/album.stub';
|
||||||
import { factory, newUuid, newUuids } from 'test/small.factory';
|
import { factory, newUuid, newUuids } from 'test/small.factory';
|
||||||
import { newTestService, ServiceMocks } from 'test/utils';
|
import { newTestService, ServiceMocks } from 'test/utils';
|
||||||
|
|
||||||
@@ -79,6 +80,11 @@ describe(ActivityService.name, () => {
|
|||||||
|
|
||||||
mocks.access.activity.checkCreateAccess.mockResolvedValue(new Set([albumId]));
|
mocks.access.activity.checkCreateAccess.mockResolvedValue(new Set([albumId]));
|
||||||
mocks.activity.create.mockResolvedValue(activity);
|
mocks.activity.create.mockResolvedValue(activity);
|
||||||
|
mocks.album.getById.mockResolvedValue({
|
||||||
|
...albumStub.empty,
|
||||||
|
owner: factory.user({ id: userId }),
|
||||||
|
albumUsers: [],
|
||||||
|
});
|
||||||
|
|
||||||
await sut.create(factory.auth({ user: { id: userId } }), {
|
await sut.create(factory.auth({ user: { id: userId } }), {
|
||||||
albumId,
|
albumId,
|
||||||
@@ -115,6 +121,11 @@ describe(ActivityService.name, () => {
|
|||||||
mocks.access.activity.checkCreateAccess.mockResolvedValue(new Set([albumId]));
|
mocks.access.activity.checkCreateAccess.mockResolvedValue(new Set([albumId]));
|
||||||
mocks.activity.create.mockResolvedValue(activity);
|
mocks.activity.create.mockResolvedValue(activity);
|
||||||
mocks.activity.search.mockResolvedValue([]);
|
mocks.activity.search.mockResolvedValue([]);
|
||||||
|
mocks.album.getById.mockResolvedValue({
|
||||||
|
...albumStub.empty,
|
||||||
|
owner: factory.user({ id: userId }),
|
||||||
|
albumUsers: [],
|
||||||
|
});
|
||||||
|
|
||||||
await sut.create(factory.auth({ user: { id: userId } }), { albumId, assetId, type: ReactionType.LIKE });
|
await sut.create(factory.auth({ user: { id: userId } }), { albumId, assetId, type: ReactionType.LIKE });
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||||
import { Activity } from 'src/database';
|
import { Activity } from 'src/database';
|
||||||
import {
|
import {
|
||||||
ActivityCreateDto,
|
ActivityCreateDto,
|
||||||
@@ -58,11 +58,24 @@ export class ActivityService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!activity) {
|
if (!activity) {
|
||||||
|
const album = await this.albumRepository.getById(common.albumId, { withAssets: false });
|
||||||
|
if (!album) {
|
||||||
|
throw new BadRequestException('Album not found');
|
||||||
|
}
|
||||||
activity = await this.activityRepository.create({
|
activity = await this.activityRepository.create({
|
||||||
...common,
|
...common,
|
||||||
isLiked: dto.type === ReactionType.LIKE,
|
isLiked: dto.type === ReactionType.LIKE,
|
||||||
comment: dto.comment,
|
comment: dto.comment,
|
||||||
});
|
});
|
||||||
|
const allUsersExceptUs = [...album.albumUsers.map(({ user }) => user.id), album.owner.id].filter(
|
||||||
|
(userId) => userId !== auth.user.id,
|
||||||
|
);
|
||||||
|
await this.eventRepository.emit('activity.change', {
|
||||||
|
recipientId: allUsersExceptUs,
|
||||||
|
userId: common.userId,
|
||||||
|
albumId: activity.albumId,
|
||||||
|
assetId: activity.assetId,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return { duplicate, value: mapActivity(activity) };
|
return { duplicate, value: mapActivity(activity) };
|
||||||
|
|||||||
@@ -664,7 +664,10 @@ describe(AlbumService.name, () => {
|
|||||||
expect(mocks.album.addAssetIds).toHaveBeenCalledWith('album-123', ['asset-1', 'asset-2', 'asset-3']);
|
expect(mocks.album.addAssetIds).toHaveBeenCalledWith('album-123', ['asset-1', 'asset-2', 'asset-3']);
|
||||||
expect(mocks.event.emit).toHaveBeenCalledWith('album.update', {
|
expect(mocks.event.emit).toHaveBeenCalledWith('album.update', {
|
||||||
id: 'album-123',
|
id: 'album-123',
|
||||||
recipientId: 'admin_id',
|
userId: 'user-id',
|
||||||
|
assetId: ['asset-1', 'asset-2', 'asset-3'],
|
||||||
|
recipientId: ['admin_id'],
|
||||||
|
status: 'added',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -178,9 +178,13 @@ export class AlbumService extends BaseService {
|
|||||||
(userId) => userId !== auth.user.id,
|
(userId) => userId !== auth.user.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const recipientId of allUsersExceptUs) {
|
await this.eventRepository.emit('album.update', {
|
||||||
await this.eventRepository.emit('album.update', { id, recipientId });
|
id,
|
||||||
}
|
userId: auth.user.id,
|
||||||
|
assetId: dto.ids,
|
||||||
|
recipientId: allUsersExceptUs,
|
||||||
|
status: 'added',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
@@ -200,7 +204,16 @@ export class AlbumService extends BaseService {
|
|||||||
if (removedIds.length > 0 && album.albumThumbnailAssetId && removedIds.includes(album.albumThumbnailAssetId)) {
|
if (removedIds.length > 0 && album.albumThumbnailAssetId && removedIds.includes(album.albumThumbnailAssetId)) {
|
||||||
await this.albumRepository.updateThumbnails();
|
await this.albumRepository.updateThumbnails();
|
||||||
}
|
}
|
||||||
|
const allUsersExceptUs = [...album.albumUsers.map(({ user }) => user.id), album.owner.id].filter(
|
||||||
|
(userId) => userId !== auth.user.id,
|
||||||
|
);
|
||||||
|
await this.eventRepository.emit('album.update', {
|
||||||
|
id,
|
||||||
|
userId: auth.user.id,
|
||||||
|
assetId: dto.ids,
|
||||||
|
recipientId: allUsersExceptUs,
|
||||||
|
status: 'removed',
|
||||||
|
});
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -418,7 +418,7 @@ export class AssetMediaService extends BaseService {
|
|||||||
duration: dto.duration || null,
|
duration: dto.duration || null,
|
||||||
visibility: dto.visibility ?? AssetVisibility.TIMELINE,
|
visibility: dto.visibility ?? AssetVisibility.TIMELINE,
|
||||||
livePhotoVideoId: dto.livePhotoVideoId,
|
livePhotoVideoId: dto.livePhotoVideoId,
|
||||||
originalFileName: file.originalName,
|
originalFileName: dto.filename || file.originalName,
|
||||||
sidecarPath: sidecarFile?.originalPath,
|
sidecarPath: sidecarFile?.originalPath,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -93,9 +93,26 @@ export class AssetService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.updateMetadata({ id, description, dateTimeOriginal, latitude, longitude, rating });
|
const metadataUpdated = await this.updateMetadata({
|
||||||
|
id,
|
||||||
|
description,
|
||||||
|
dateTimeOriginal,
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
rating,
|
||||||
|
});
|
||||||
|
|
||||||
const asset = await this.assetRepository.update({ id, ...rest });
|
const updatedAsset = await this.assetRepository.update({ id, ...rest });
|
||||||
|
|
||||||
|
// If update returned undefined (no changes), fetch the asset
|
||||||
|
// Match the relations that update() returns when it does update
|
||||||
|
const asset = updatedAsset ?? (await this.assetRepository.getById(id, { exifInfo: true, faces: { person: true } }));
|
||||||
|
|
||||||
|
if (!metadataUpdated && updatedAsset) {
|
||||||
|
// updateMetadata will send an event, but assetRepository.update() won't.
|
||||||
|
// to prevent doubles, only send an event if asset was updated
|
||||||
|
await this.eventRepository.emit('asset.update', { assetIds: [id], userId: auth.user.id });
|
||||||
|
}
|
||||||
|
|
||||||
if (previousMotion && asset) {
|
if (previousMotion && asset) {
|
||||||
await onAfterUnlink(repos, {
|
await onAfterUnlink(repos, {
|
||||||
@@ -113,35 +130,27 @@ export class AssetService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async updateAll(auth: AuthDto, dto: AssetBulkUpdateDto): Promise<void> {
|
async updateAll(auth: AuthDto, dto: AssetBulkUpdateDto): Promise<void> {
|
||||||
const { ids, description, dateTimeOriginal, latitude, longitude, ...options } = dto;
|
const { ids, description, dateTimeOriginal, latitude, longitude, rating, ...rest } = dto;
|
||||||
await this.requireAccess({ auth, permission: Permission.ASSET_UPDATE, ids });
|
await this.requireAccess({ auth, permission: Permission.ASSET_UPDATE, ids });
|
||||||
|
|
||||||
if (
|
const metadataUpdated = await this.updateAllMetadata(ids, {
|
||||||
description !== undefined ||
|
description,
|
||||||
dateTimeOriginal !== undefined ||
|
dateTimeOriginal,
|
||||||
latitude !== undefined ||
|
latitude,
|
||||||
longitude !== undefined
|
longitude,
|
||||||
) {
|
rating,
|
||||||
await this.assetRepository.updateAllExif(ids, { description, dateTimeOriginal, latitude, longitude });
|
});
|
||||||
await this.jobRepository.queueAll(
|
|
||||||
ids.map((id) => ({
|
|
||||||
name: JobName.SIDECAR_WRITE,
|
|
||||||
data: { id, description, dateTimeOriginal, latitude, longitude },
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (rest.visibility !== undefined || rest.isFavorite !== undefined || rest.duplicateId !== undefined) {
|
||||||
options.visibility !== undefined ||
|
await this.assetRepository.updateAll(ids, rest);
|
||||||
options.isFavorite !== undefined ||
|
|
||||||
options.duplicateId !== undefined ||
|
|
||||||
options.rating !== undefined
|
|
||||||
) {
|
|
||||||
await this.assetRepository.updateAll(ids, options);
|
|
||||||
|
|
||||||
if (options.visibility === AssetVisibility.LOCKED) {
|
if (rest.visibility === AssetVisibility.LOCKED) {
|
||||||
await this.albumRepository.removeAssetsFromAll(ids);
|
await this.albumRepository.removeAssetsFromAll(ids);
|
||||||
}
|
}
|
||||||
|
if (!metadataUpdated) {
|
||||||
|
// If no metadata was updated, we still need to emit an event for the bulk update
|
||||||
|
await this.eventRepository.emit('asset.update', { assetIds: ids, userId: auth.user.id });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,6 +299,26 @@ export class AssetService extends BaseService {
|
|||||||
if (Object.keys(writes).length > 0) {
|
if (Object.keys(writes).length > 0) {
|
||||||
await this.assetRepository.upsertExif({ assetId: id, ...writes });
|
await this.assetRepository.upsertExif({ assetId: id, ...writes });
|
||||||
await this.jobRepository.queue({ name: JobName.SIDECAR_WRITE, data: { id, ...writes } });
|
await this.jobRepository.queue({ name: JobName.SIDECAR_WRITE, data: { id, ...writes } });
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async updateAllMetadata(
|
||||||
|
ids: string[],
|
||||||
|
dto: Pick<AssetBulkUpdateDto, 'description' | 'dateTimeOriginal' | 'latitude' | 'longitude' | 'rating'>,
|
||||||
|
) {
|
||||||
|
const { description, dateTimeOriginal, latitude, longitude, rating } = dto;
|
||||||
|
const writes = _.omitBy({ description, dateTimeOriginal, latitude, longitude, rating }, _.isUndefined);
|
||||||
|
if (Object.keys(writes).length > 0) {
|
||||||
|
await this.assetRepository.updateAllExif(ids, writes);
|
||||||
|
const jobs: JobItem[] = ids.map((id) => ({
|
||||||
|
name: JobName.SIDECAR_WRITE,
|
||||||
|
data: { id, ...writes },
|
||||||
|
}));
|
||||||
|
await this.jobRepository.queueAll(jobs);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -704,7 +704,7 @@ describe(AuthService.name, () => {
|
|||||||
expect(mocks.user.create).toHaveBeenCalledWith(expect.objectContaining({ quotaSizeInBytes: 1_073_741_824 }));
|
expect(mocks.user.create).toHaveBeenCalledWith(expect.objectContaining({ quotaSizeInBytes: 1_073_741_824 }));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not set quota for 0 quota', async () => {
|
it('should set quota for 0 quota', async () => {
|
||||||
const user = factory.userAdmin({ oauthId: 'oauth-id' });
|
const user = factory.userAdmin({ oauthId: 'oauth-id' });
|
||||||
|
|
||||||
mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.oauthWithStorageQuota);
|
mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.oauthWithStorageQuota);
|
||||||
@@ -726,7 +726,7 @@ describe(AuthService.name, () => {
|
|||||||
email: user.email,
|
email: user.email,
|
||||||
name: ' ',
|
name: ' ',
|
||||||
oauthId: user.oauthId,
|
oauthId: user.oauthId,
|
||||||
quotaSizeInBytes: null,
|
quotaSizeInBytes: 0,
|
||||||
storageLabel: null,
|
storageLabel: null,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -300,7 +300,7 @@ export class AuthService extends BaseService {
|
|||||||
name: userName,
|
name: userName,
|
||||||
email: profile.email,
|
email: profile.email,
|
||||||
oauthId: profile.sub,
|
oauthId: profile.sub,
|
||||||
quotaSizeInBytes: storageQuota * HumanReadableSize.GiB || null,
|
quotaSizeInBytes: storageQuota === null ? null : storageQuota * HumanReadableSize.GiB,
|
||||||
storageLabel: storageLabel || null,
|
storageLabel: storageLabel || null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants';
|
import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants';
|
||||||
import { OnJob } from 'src/decorators';
|
import { OnJob } from 'src/decorators';
|
||||||
|
import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
||||||
import { mapAsset } from 'src/dtos/asset-response.dto';
|
import { mapAsset } from 'src/dtos/asset-response.dto';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { DuplicateResponseDto } from 'src/dtos/duplicate.dto';
|
import { DuplicateResponseDto } from 'src/dtos/duplicate.dto';
|
||||||
@@ -20,6 +21,14 @@ export class DuplicateService extends BaseService {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async delete(auth: AuthDto, id: string): Promise<void> {
|
||||||
|
await this.duplicateRepository.delete(auth.user.id, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteAll(auth: AuthDto, dto: BulkIdsDto) {
|
||||||
|
await this.duplicateRepository.deleteAll(auth.user.id, dto.ids);
|
||||||
|
}
|
||||||
|
|
||||||
@OnJob({ name: JobName.QUEUE_DUPLICATE_DETECTION, queue: QueueName.DUPLICATE_DETECTION })
|
@OnJob({ name: JobName.QUEUE_DUPLICATE_DETECTION, queue: QueueName.DUPLICATE_DETECTION })
|
||||||
async handleQueueSearchDuplicates({ force }: JobOf<JobName.QUEUE_DUPLICATE_DETECTION>): Promise<JobStatus> {
|
async handleQueueSearchDuplicates({ force }: JobOf<JobName.QUEUE_DUPLICATE_DETECTION>): Promise<JobStatus> {
|
||||||
const { machineLearning } = await this.getConfig({ withCache: false });
|
const { machineLearning } = await this.getConfig({ withCache: false });
|
||||||
@@ -69,6 +78,11 @@ export class DuplicateService extends BaseService {
|
|||||||
return JobStatus.SKIPPED;
|
return JobStatus.SKIPPED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (asset.visibility === AssetVisibility.LOCKED) {
|
||||||
|
this.logger.debug(`Asset ${id} is locked, skipping`);
|
||||||
|
return JobStatus.SKIPPED;
|
||||||
|
}
|
||||||
|
|
||||||
if (!asset.embedding) {
|
if (!asset.embedding) {
|
||||||
this.logger.debug(`Asset ${id} is missing embedding`);
|
this.logger.debug(`Asset ${id} is missing embedding`);
|
||||||
return JobStatus.FAILED;
|
return JobStatus.FAILED;
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { AlbumUser } from 'src/database';
|
|||||||
import { SystemConfigDto } from 'src/dtos/system-config.dto';
|
import { SystemConfigDto } from 'src/dtos/system-config.dto';
|
||||||
import { AssetFileType, JobName, JobStatus, UserMetadataKey } from 'src/enum';
|
import { AssetFileType, JobName, JobStatus, UserMetadataKey } from 'src/enum';
|
||||||
import { NotificationService } from 'src/services/notification.service';
|
import { NotificationService } from 'src/services/notification.service';
|
||||||
import { INotifyAlbumUpdateJob } from 'src/types';
|
|
||||||
import { albumStub } from 'test/fixtures/album.stub';
|
import { albumStub } from 'test/fixtures/album.stub';
|
||||||
import { assetStub } from 'test/fixtures/asset.stub';
|
import { assetStub } from 'test/fixtures/asset.stub';
|
||||||
import { userStub } from 'test/fixtures/user.stub';
|
import { userStub } from 'test/fixtures/user.stub';
|
||||||
@@ -154,7 +153,7 @@ describe(NotificationService.name, () => {
|
|||||||
|
|
||||||
describe('onAlbumUpdateEvent', () => {
|
describe('onAlbumUpdateEvent', () => {
|
||||||
it('should queue notify album update event', async () => {
|
it('should queue notify album update event', async () => {
|
||||||
await sut.onAlbumUpdate({ id: 'album', recipientId: '42' });
|
await sut.onAlbumUpdate({ id: 'album', recipientId: ['42'], userId: '', assetId: [], status: 'added' });
|
||||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||||
name: JobName.NOTIFY_ALBUM_UPDATE,
|
name: JobName.NOTIFY_ALBUM_UPDATE,
|
||||||
data: { id: 'album', recipientId: '42', delay: 300_000 },
|
data: { id: 'album', recipientId: '42', delay: 300_000 },
|
||||||
@@ -499,7 +498,13 @@ describe(NotificationService.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should add new recipients for new images if job is already queued', async () => {
|
it('should add new recipients for new images if job is already queued', async () => {
|
||||||
await sut.onAlbumUpdate({ id: '1', recipientId: '2' } as INotifyAlbumUpdateJob);
|
await sut.onAlbumUpdate({
|
||||||
|
id: '1',
|
||||||
|
recipientId: ['2'],
|
||||||
|
userId: '',
|
||||||
|
assetId: [],
|
||||||
|
status: 'added',
|
||||||
|
});
|
||||||
expect(mocks.job.removeJob).toHaveBeenCalledWith(JobName.NOTIFY_ALBUM_UPDATE, '1/2');
|
expect(mocks.job.removeJob).toHaveBeenCalledWith(JobName.NOTIFY_ALBUM_UPDATE, '1/2');
|
||||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||||
name: JobName.NOTIFY_ALBUM_UPDATE,
|
name: JobName.NOTIFY_ALBUM_UPDATE,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||||
import { OnEvent, OnJob } from 'src/decorators';
|
import { OnEvent, OnJob } from 'src/decorators';
|
||||||
import { mapAsset } from 'src/dtos/asset-response.dto';
|
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import {
|
import {
|
||||||
mapNotification,
|
mapNotification,
|
||||||
@@ -128,6 +127,20 @@ export class NotificationService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OnEvent({ name: 'activity.change' })
|
||||||
|
onActivityChange({ recipientId, assetId, userId, albumId }: ArgOf<'activity.change'>) {
|
||||||
|
for (const recipient of recipientId) {
|
||||||
|
this.eventRepository.clientSend('on_activity_change', recipient, { albumId, assetId });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.eventRepository.clientSend('on_activity_change', userId, { albumId, assetId });
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnEvent({ name: 'asset.person' })
|
||||||
|
onAssetPerson({ assetId, userId, personId, status }: ArgOf<'asset.person'>) {
|
||||||
|
this.eventRepository.clientSend('on_asset_person', userId, { assetId, personId, status });
|
||||||
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'asset.hide' })
|
@OnEvent({ name: 'asset.hide' })
|
||||||
onAssetHide({ assetId, userId }: ArgOf<'asset.hide'>) {
|
onAssetHide({ assetId, userId }: ArgOf<'asset.hide'>) {
|
||||||
this.eventRepository.clientSend('on_asset_hidden', userId, assetId);
|
this.eventRepository.clientSend('on_asset_hidden', userId, assetId);
|
||||||
@@ -153,16 +166,17 @@ export class NotificationService extends BaseService {
|
|||||||
this.eventRepository.clientSend('on_asset_trash', userId, assetIds);
|
this.eventRepository.clientSend('on_asset_trash', userId, assetIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OnEvent({ name: 'asset.update' })
|
||||||
|
onAssetUpdate({ assetIds, userId }: ArgOf<'asset.update'>) {
|
||||||
|
this.eventRepository.clientSend('on_asset_update', userId, assetIds);
|
||||||
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'asset.metadataExtracted' })
|
@OnEvent({ name: 'asset.metadataExtracted' })
|
||||||
async onAssetMetadataExtracted({ assetId, userId, source }: ArgOf<'asset.metadataExtracted'>) {
|
onAssetMetadataExtracted({ assetId, userId, source }: ArgOf<'asset.metadataExtracted'>) {
|
||||||
if (source !== 'sidecar-write') {
|
if (source !== 'sidecar-write') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.eventRepository.clientSend('on_asset_update', userId, [assetId]);
|
||||||
const [asset] = await this.assetRepository.getByIdsWithAllRelationsButStacks([assetId]);
|
|
||||||
if (asset) {
|
|
||||||
this.eventRepository.clientSend('on_asset_update', userId, mapAsset(asset));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'assets.restore' })
|
@OnEvent({ name: 'assets.restore' })
|
||||||
@@ -198,12 +212,23 @@ export class NotificationService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'album.update' })
|
@OnEvent({ name: 'album.update' })
|
||||||
async onAlbumUpdate({ id, recipientId }: ArgOf<'album.update'>) {
|
async onAlbumUpdate({ id, recipientId, userId, assetId, status }: ArgOf<'album.update'>) {
|
||||||
await this.jobRepository.removeJob(JobName.NOTIFY_ALBUM_UPDATE, `${id}/${recipientId}`);
|
if (status === 'added') {
|
||||||
await this.jobRepository.queue({
|
for (const recipient of recipientId) {
|
||||||
name: JobName.NOTIFY_ALBUM_UPDATE,
|
await this.jobRepository.removeJob(JobName.NOTIFY_ALBUM_UPDATE, `${id}/${recipientId}`);
|
||||||
data: { id, recipientId, delay: NotificationService.albumUpdateEmailDelayMs },
|
await this.jobRepository.queue({
|
||||||
});
|
name: JobName.NOTIFY_ALBUM_UPDATE,
|
||||||
|
data: { id, recipientId: recipient, delay: NotificationService.albumUpdateEmailDelayMs },
|
||||||
|
});
|
||||||
|
this.eventRepository.clientSend('on_album_update', recipient, { albumId: id, assetId, status });
|
||||||
|
}
|
||||||
|
} else if (status === 'removed') {
|
||||||
|
for (const recipient of recipientId) {
|
||||||
|
this.eventRepository.clientSend('on_album_update', recipient, { albumId: id, assetId, status });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.eventRepository.clientSend('on_album_update', userId, { albumId: id, assetId, status });
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnEvent({ name: 'album.invite' })
|
@OnEvent({ name: 'album.invite' })
|
||||||
|
|||||||
@@ -627,11 +627,28 @@ export class PersonService extends BaseService {
|
|||||||
boundingBoxY2: dto.y + dto.height,
|
boundingBoxY2: dto.y + dto.height,
|
||||||
sourceType: SourceType.MANUAL,
|
sourceType: SourceType.MANUAL,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await this.eventRepository.emit('asset.person', {
|
||||||
|
assetId: dto.assetId,
|
||||||
|
userId: auth.user.id,
|
||||||
|
personId: dto.personId,
|
||||||
|
status: 'created',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteFace(auth: AuthDto, id: string, dto: AssetFaceDeleteDto): Promise<void> {
|
async deleteFace(auth: AuthDto, id: string, dto: AssetFaceDeleteDto): Promise<void> {
|
||||||
await this.requireAccess({ auth, permission: Permission.FACE_DELETE, ids: [id] });
|
await this.requireAccess({ auth, permission: Permission.FACE_DELETE, ids: [id] });
|
||||||
|
const assetPerson = await this.personRepository.getAssetPersonByFaceId(id);
|
||||||
|
if (!assetPerson) {
|
||||||
|
throw new NotFoundException('Asset face not found');
|
||||||
|
}
|
||||||
|
|
||||||
return dto.force ? this.personRepository.deleteAssetFace(id) : this.personRepository.softDeleteAssetFaces(id);
|
await (dto.force ? this.personRepository.deleteAssetFace(id) : this.personRepository.softDeleteAssetFaces(id));
|
||||||
|
await this.eventRepository.emit('asset.person', {
|
||||||
|
userId: auth.user.id,
|
||||||
|
assetId: assetPerson.assetId,
|
||||||
|
personId: assetPerson.personId ?? undefined,
|
||||||
|
status: dto.force ? 'removed' : 'removed_soft',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ const updatedConfig = Object.freeze<SystemConfig>({
|
|||||||
buttonText: 'Login with OAuth',
|
buttonText: 'Login with OAuth',
|
||||||
clientId: '',
|
clientId: '',
|
||||||
clientSecret: '',
|
clientSecret: '',
|
||||||
defaultStorageQuota: 0,
|
defaultStorageQuota: null,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
issuerUrl: '',
|
issuerUrl: '',
|
||||||
mobileOverrideEnabled: false,
|
mobileOverrideEnabled: false,
|
||||||
|
|||||||
@@ -50,30 +50,28 @@ describe(TrashService.name, () => {
|
|||||||
|
|
||||||
describe('restore', () => {
|
describe('restore', () => {
|
||||||
it('should handle an empty trash', async () => {
|
it('should handle an empty trash', async () => {
|
||||||
mocks.trash.getDeletedIds.mockResolvedValue(makeAssetIdStream(0));
|
mocks.trash.getTrashedIds.mockReturnValue(makeAssetIdStream(0));
|
||||||
mocks.trash.restore.mockResolvedValue(0);
|
|
||||||
await expect(sut.restore(authStub.user1)).resolves.toEqual({ count: 0 });
|
await expect(sut.restore(authStub.user1)).resolves.toEqual({ count: 0 });
|
||||||
expect(mocks.trash.restore).toHaveBeenCalledWith('user-id');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should restore', async () => {
|
it('should restore', async () => {
|
||||||
mocks.trash.getDeletedIds.mockResolvedValue(makeAssetIdStream(1));
|
mocks.trash.getTrashedIds.mockReturnValue(makeAssetIdStream(1));
|
||||||
mocks.trash.restore.mockResolvedValue(1);
|
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1']));
|
||||||
|
mocks.trash.restoreAll.mockResolvedValue(1);
|
||||||
await expect(sut.restore(authStub.user1)).resolves.toEqual({ count: 1 });
|
await expect(sut.restore(authStub.user1)).resolves.toEqual({ count: 1 });
|
||||||
expect(mocks.trash.restore).toHaveBeenCalledWith('user-id');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('empty', () => {
|
describe('empty', () => {
|
||||||
it('should handle an empty trash', async () => {
|
it('should handle an empty trash', async () => {
|
||||||
mocks.trash.getDeletedIds.mockResolvedValue(makeAssetIdStream(0));
|
mocks.trash.getTrashedIds.mockReturnValue(makeAssetIdStream(0));
|
||||||
mocks.trash.empty.mockResolvedValue(0);
|
mocks.trash.empty.mockResolvedValue(0);
|
||||||
await expect(sut.empty(authStub.user1)).resolves.toEqual({ count: 0 });
|
await expect(sut.empty(authStub.user1)).resolves.toEqual({ count: 0 });
|
||||||
expect(mocks.job.queue).not.toHaveBeenCalled();
|
expect(mocks.job.queue).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should empty the trash', async () => {
|
it('should empty the trash', async () => {
|
||||||
mocks.trash.getDeletedIds.mockResolvedValue(makeAssetIdStream(1));
|
mocks.trash.getTrashedIds.mockReturnValue(makeAssetIdStream(1));
|
||||||
mocks.trash.empty.mockResolvedValue(1);
|
mocks.trash.empty.mockResolvedValue(1);
|
||||||
await expect(sut.empty(authStub.user1)).resolves.toEqual({ count: 1 });
|
await expect(sut.empty(authStub.user1)).resolves.toEqual({ count: 1 });
|
||||||
expect(mocks.trash.empty).toHaveBeenCalledWith('user-id');
|
expect(mocks.trash.empty).toHaveBeenCalledWith('user-id');
|
||||||
|
|||||||
@@ -25,11 +25,22 @@ export class TrashService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async restore(auth: AuthDto): Promise<TrashResponseDto> {
|
async restore(auth: AuthDto): Promise<TrashResponseDto> {
|
||||||
const count = await this.trashRepository.restore(auth.user.id);
|
const assets = this.trashRepository.getTrashedIds(auth.user.id);
|
||||||
if (count > 0) {
|
let total = 0;
|
||||||
this.logger.log(`Restored ${count} asset(s) from trash`);
|
let batch = new BulkIdsDto();
|
||||||
|
batch.ids = [];
|
||||||
|
for await (const { id } of assets) {
|
||||||
|
batch.ids.push(id);
|
||||||
|
if (batch.ids.length === JOBS_ASSET_PAGINATION_SIZE) {
|
||||||
|
const { count } = await this.restoreAssets(auth, batch);
|
||||||
|
total += count;
|
||||||
|
batch = new BulkIdsDto();
|
||||||
|
batch.ids = [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return { count };
|
const { count } = await this.restoreAssets(auth, batch);
|
||||||
|
total += count;
|
||||||
|
return { count: total };
|
||||||
}
|
}
|
||||||
|
|
||||||
async empty(auth: AuthDto): Promise<TrashResponseDto> {
|
async empty(auth: AuthDto): Promise<TrashResponseDto> {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export const newPersonRepositoryMock = (): Mocked<RepositoryInterface<PersonRepo
|
|||||||
createAssetFace: vitest.fn(),
|
createAssetFace: vitest.fn(),
|
||||||
deleteAssetFace: vitest.fn(),
|
deleteAssetFace: vitest.fn(),
|
||||||
softDeleteAssetFaces: vitest.fn(),
|
softDeleteAssetFaces: vitest.fn(),
|
||||||
|
getAssetPersonByFaceId: vitest.fn(),
|
||||||
vacuum: vitest.fn(),
|
vacuum: vitest.fn(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
> 0.2% and last 4 major versions
|
||||||
|
> 0.5%
|
||||||
|
not dead
|
||||||
|
edge >= 135
|
||||||
|
not edge < 135
|
||||||
@@ -1,263 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
|
||||||
"plugins": [
|
|
||||||
"unicorn",
|
|
||||||
"typescript"
|
|
||||||
],
|
|
||||||
"categories": {
|
|
||||||
"correctness": "off"
|
|
||||||
},
|
|
||||||
"env": {
|
|
||||||
"builtin": true,
|
|
||||||
"browser": true,
|
|
||||||
"commonjs": true,
|
|
||||||
"es2025": true,
|
|
||||||
"node": true,
|
|
||||||
"shared-node-browser": true
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"no-negated-condition": "off",
|
|
||||||
"no-nested-ternary": "off",
|
|
||||||
"unicorn/catch-error-name": "error",
|
|
||||||
"unicorn/consistent-assert": "error",
|
|
||||||
"unicorn/consistent-date-clone": "error",
|
|
||||||
"unicorn/consistent-empty-array-spread": "error",
|
|
||||||
"unicorn/consistent-existence-index-check": "error",
|
|
||||||
"unicorn/consistent-function-scoping": "off",
|
|
||||||
"unicorn/empty-brace-spaces": "error",
|
|
||||||
"unicorn/error-message": "error",
|
|
||||||
"unicorn/escape-case": "error",
|
|
||||||
"unicorn/explicit-length-check": "error",
|
|
||||||
"unicorn/filename-case": "off",
|
|
||||||
"unicorn/new-for-builtins": "error",
|
|
||||||
"unicorn/no-abusive-eslint-disable": "error",
|
|
||||||
"unicorn/no-accessor-recursion": "error",
|
|
||||||
"unicorn/no-anonymous-default-export": "error",
|
|
||||||
"unicorn/no-array-for-each": "error",
|
|
||||||
"unicorn/no-array-method-this-argument": "error",
|
|
||||||
"unicorn/no-array-reduce": "error",
|
|
||||||
"unicorn/no-await-expression-member": "error",
|
|
||||||
"unicorn/no-await-in-promise-methods": "error",
|
|
||||||
"unicorn/no-console-spaces": "error",
|
|
||||||
"unicorn/no-document-cookie": "error",
|
|
||||||
"unicorn/no-empty-file": "error",
|
|
||||||
"unicorn/no-hex-escape": "error",
|
|
||||||
"unicorn/no-instanceof-builtins": "error",
|
|
||||||
"unicorn/no-invalid-fetch-options": "error",
|
|
||||||
"unicorn/no-invalid-remove-event-listener": "error",
|
|
||||||
"unicorn/no-lonely-if": "error",
|
|
||||||
"unicorn/no-magic-array-flat-depth": "error",
|
|
||||||
"unicorn/no-negated-condition": "error",
|
|
||||||
"unicorn/no-negation-in-equality-check": "error",
|
|
||||||
"unicorn/no-nested-ternary": "off",
|
|
||||||
"unicorn/no-new-array": "error",
|
|
||||||
"unicorn/no-new-buffer": "error",
|
|
||||||
"unicorn/no-null": "off",
|
|
||||||
"unicorn/no-object-as-default-parameter": "error",
|
|
||||||
"unicorn/no-process-exit": "error",
|
|
||||||
"unicorn/no-single-promise-in-promise-methods": "error",
|
|
||||||
"unicorn/no-static-only-class": "error",
|
|
||||||
"unicorn/no-thenable": "error",
|
|
||||||
"unicorn/no-this-assignment": "error",
|
|
||||||
"unicorn/no-typeof-undefined": "error",
|
|
||||||
"unicorn/no-unnecessary-array-flat-depth": "error",
|
|
||||||
"unicorn/no-unnecessary-await": "error",
|
|
||||||
"unicorn/no-unnecessary-slice-end": "error",
|
|
||||||
"unicorn/no-unreadable-array-destructuring": "error",
|
|
||||||
"unicorn/no-unreadable-iife": "error",
|
|
||||||
"unicorn/no-useless-fallback-in-spread": "error",
|
|
||||||
"unicorn/no-useless-length-check": "error",
|
|
||||||
"unicorn/no-useless-promise-resolve-reject": "error",
|
|
||||||
"unicorn/no-useless-spread": "error",
|
|
||||||
"unicorn/no-useless-switch-case": "error",
|
|
||||||
"unicorn/no-useless-undefined": "off",
|
|
||||||
"unicorn/no-zero-fractions": "error",
|
|
||||||
"unicorn/number-literal-case": "error",
|
|
||||||
"unicorn/numeric-separators-style": "error",
|
|
||||||
"unicorn/prefer-add-event-listener": "error",
|
|
||||||
"unicorn/prefer-array-find": "error",
|
|
||||||
"unicorn/prefer-array-flat-map": "error",
|
|
||||||
"unicorn/prefer-array-flat": "error",
|
|
||||||
"unicorn/prefer-array-index-of": "error",
|
|
||||||
"unicorn/prefer-array-some": "error",
|
|
||||||
"unicorn/prefer-blob-reading-methods": "error",
|
|
||||||
"unicorn/prefer-code-point": "error",
|
|
||||||
"unicorn/prefer-date-now": "error",
|
|
||||||
"unicorn/prefer-dom-node-append": "error",
|
|
||||||
"unicorn/prefer-dom-node-dataset": "error",
|
|
||||||
"unicorn/prefer-dom-node-remove": "error",
|
|
||||||
"unicorn/prefer-dom-node-text-content": "error",
|
|
||||||
"unicorn/prefer-event-target": "error",
|
|
||||||
"unicorn/prefer-global-this": "error",
|
|
||||||
"unicorn/prefer-includes": "error",
|
|
||||||
"unicorn/prefer-logical-operator-over-ternary": "error",
|
|
||||||
"unicorn/prefer-math-min-max": "error",
|
|
||||||
"unicorn/prefer-math-trunc": "error",
|
|
||||||
"unicorn/prefer-modern-dom-apis": "error",
|
|
||||||
"unicorn/prefer-modern-math-apis": "error",
|
|
||||||
"unicorn/prefer-native-coercion-functions": "error",
|
|
||||||
"unicorn/prefer-negative-index": "error",
|
|
||||||
"unicorn/prefer-node-protocol": "error",
|
|
||||||
"unicorn/prefer-number-properties": "error",
|
|
||||||
"unicorn/prefer-object-from-entries": "error",
|
|
||||||
"unicorn/prefer-optional-catch-binding": "error",
|
|
||||||
"unicorn/prefer-prototype-methods": "error",
|
|
||||||
"unicorn/prefer-query-selector": "error",
|
|
||||||
"unicorn/prefer-reflect-apply": "error",
|
|
||||||
"unicorn/prefer-regexp-test": "error",
|
|
||||||
"unicorn/prefer-set-has": "error",
|
|
||||||
"unicorn/prefer-set-size": "error",
|
|
||||||
"unicorn/prefer-spread": "off",
|
|
||||||
"unicorn/prefer-string-raw": "error",
|
|
||||||
"unicorn/prefer-string-replace-all": "error",
|
|
||||||
"unicorn/prefer-string-slice": "error",
|
|
||||||
"unicorn/prefer-string-starts-ends-with": "error",
|
|
||||||
"unicorn/prefer-string-trim-start-end": "error",
|
|
||||||
"unicorn/prefer-structured-clone": "error",
|
|
||||||
"unicorn/prefer-type-error": "error",
|
|
||||||
"unicorn/require-array-join-separator": "error",
|
|
||||||
"unicorn/require-number-to-fixed-digits-argument": "error",
|
|
||||||
"unicorn/require-post-message-target-origin": "off",
|
|
||||||
"unicorn/switch-case-braces": "error",
|
|
||||||
"unicorn/text-encoding-identifier-case": "error",
|
|
||||||
"unicorn/throw-new-error": "error",
|
|
||||||
"for-direction": "error",
|
|
||||||
"no-async-promise-executor": "error",
|
|
||||||
"no-case-declarations": "error",
|
|
||||||
"no-class-assign": "error",
|
|
||||||
"no-compare-neg-zero": "error",
|
|
||||||
"no-cond-assign": "error",
|
|
||||||
"no-const-assign": "error",
|
|
||||||
"no-constant-binary-expression": "error",
|
|
||||||
"no-constant-condition": "error",
|
|
||||||
"no-control-regex": "error",
|
|
||||||
"no-debugger": "error",
|
|
||||||
"no-delete-var": "error",
|
|
||||||
"no-dupe-class-members": "error",
|
|
||||||
"no-dupe-else-if": "error",
|
|
||||||
"no-dupe-keys": "error",
|
|
||||||
"no-duplicate-case": "error",
|
|
||||||
"no-empty": "error",
|
|
||||||
"no-empty-character-class": "error",
|
|
||||||
"no-empty-pattern": "error",
|
|
||||||
"no-empty-static-block": "error",
|
|
||||||
"no-ex-assign": "error",
|
|
||||||
"no-extra-boolean-cast": "error",
|
|
||||||
"no-fallthrough": "error",
|
|
||||||
"no-func-assign": "error",
|
|
||||||
"no-global-assign": "error",
|
|
||||||
"no-import-assign": "error",
|
|
||||||
"no-invalid-regexp": "error",
|
|
||||||
"no-irregular-whitespace": "error",
|
|
||||||
"no-loss-of-precision": "error",
|
|
||||||
"no-new-native-nonconstructor": "error",
|
|
||||||
"no-nonoctal-decimal-escape": "error",
|
|
||||||
"no-obj-calls": "error",
|
|
||||||
"no-prototype-builtins": "error",
|
|
||||||
"no-redeclare": "error",
|
|
||||||
"no-regex-spaces": "error",
|
|
||||||
"no-self-assign": "error",
|
|
||||||
"no-setter-return": "error",
|
|
||||||
"no-shadow-restricted-names": "error",
|
|
||||||
"no-sparse-arrays": "error",
|
|
||||||
"no-this-before-super": "error",
|
|
||||||
"no-unexpected-multiline": "error",
|
|
||||||
"no-unsafe-finally": "error",
|
|
||||||
"no-unsafe-negation": "error",
|
|
||||||
"no-unsafe-optional-chaining": "error",
|
|
||||||
"no-unused-labels": "error",
|
|
||||||
"no-unused-private-class-members": "error",
|
|
||||||
"no-unused-vars": [
|
|
||||||
"warn",
|
|
||||||
{
|
|
||||||
"argsIgnorePattern": "^_$",
|
|
||||||
"varsIgnorePattern": "^_$"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"no-useless-backreference": "error",
|
|
||||||
"no-useless-catch": "error",
|
|
||||||
"no-useless-escape": "error",
|
|
||||||
"no-with": "error",
|
|
||||||
"require-yield": "error",
|
|
||||||
"use-isnan": "error",
|
|
||||||
"valid-typeof": "error",
|
|
||||||
"@typescript-eslint/ban-ts-comment": "error",
|
|
||||||
"no-array-constructor": "error",
|
|
||||||
"@typescript-eslint/no-duplicate-enum-values": "error",
|
|
||||||
"@typescript-eslint/no-empty-object-type": "error",
|
|
||||||
"@typescript-eslint/no-explicit-any": "error",
|
|
||||||
"@typescript-eslint/no-extra-non-null-assertion": "error",
|
|
||||||
"@typescript-eslint/no-misused-new": "error",
|
|
||||||
"@typescript-eslint/no-namespace": "error",
|
|
||||||
"@typescript-eslint/no-non-null-asserted-optional-chain": "error",
|
|
||||||
"@typescript-eslint/no-require-imports": "error",
|
|
||||||
"@typescript-eslint/no-this-alias": "error",
|
|
||||||
"@typescript-eslint/no-unnecessary-type-constraint": "error",
|
|
||||||
"@typescript-eslint/no-unsafe-declaration-merging": "error",
|
|
||||||
"@typescript-eslint/no-unsafe-function-type": "error",
|
|
||||||
"no-unused-expressions": "error",
|
|
||||||
"@typescript-eslint/no-wrapper-object-types": "error",
|
|
||||||
"@typescript-eslint/prefer-as-const": "error",
|
|
||||||
"@typescript-eslint/prefer-namespace-keyword": "error",
|
|
||||||
"@typescript-eslint/triple-slash-reference": "error",
|
|
||||||
"curly": "warn",
|
|
||||||
"eslint/no-undef": "error"
|
|
||||||
},
|
|
||||||
"globals": {
|
|
||||||
"NodeJS": "writeable"
|
|
||||||
},
|
|
||||||
"ignorePatterns": [
|
|
||||||
"**/.DS_Store",
|
|
||||||
"**/node_modules",
|
|
||||||
"build",
|
|
||||||
".svelte-kit",
|
|
||||||
"package",
|
|
||||||
"**/.env",
|
|
||||||
"**/.env.*",
|
|
||||||
"!**/.env.example",
|
|
||||||
"**/pnpm-lock.yaml",
|
|
||||||
"**/package-lock.json",
|
|
||||||
"**/yarn.lock",
|
|
||||||
"**/svelte.config.js",
|
|
||||||
"eslint.config.js",
|
|
||||||
"tailwind.config.js",
|
|
||||||
"coverage",
|
|
||||||
"**/service-worker/**"
|
|
||||||
],
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": ["*.svelte", "**/*.svelte", "**/*.svelte.ts", "**/*.svelte.js"],
|
|
||||||
"rules": {
|
|
||||||
"eslint/no-undef": "off",
|
|
||||||
"no-inner-declarations": "off",
|
|
||||||
"no-self-assign": "off"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files": [
|
|
||||||
"**/*.ts",
|
|
||||||
"**/*.tsx",
|
|
||||||
"**/*.mts",
|
|
||||||
"**/*.cts"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"no-class-assign": "off",
|
|
||||||
"no-const-assign": "off",
|
|
||||||
"no-dupe-class-members": "off",
|
|
||||||
"no-dupe-keys": "off",
|
|
||||||
"no-func-assign": "off",
|
|
||||||
"no-import-assign": "off",
|
|
||||||
"no-new-native-nonconstructor": "off",
|
|
||||||
"no-obj-calls": "off",
|
|
||||||
"no-redeclare": "off",
|
|
||||||
"no-setter-return": "off",
|
|
||||||
"no-this-before-super": "off",
|
|
||||||
"no-unsafe-negation": "off",
|
|
||||||
"no-var": "error",
|
|
||||||
"no-with": "off",
|
|
||||||
"prefer-rest-params": "error",
|
|
||||||
"prefer-spread": "error"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -13,5 +13,3 @@ node_modules
|
|||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
package-lock.json
|
package-lock.json
|
||||||
yarn.lock
|
yarn.lock
|
||||||
biome.json
|
|
||||||
.oxlintrc.json
|
|
||||||
-347
@@ -1,347 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://biomejs.dev/schemas/2.0.0-beta.6/schema.json",
|
|
||||||
"assist": {
|
|
||||||
"actions": { "source": { "organizeImports": "on" } },
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
"files": { "ignoreUnknown": false },
|
|
||||||
"formatter": { "enabled": true, "indentStyle": "tab" },
|
|
||||||
"javascript": { "formatter": { "quoteStyle": "double" }, "globals": [] },
|
|
||||||
"linter": {
|
|
||||||
"enabled": true,
|
|
||||||
"includes": [
|
|
||||||
"**",
|
|
||||||
"!**/.DS_Store",
|
|
||||||
"!**/node_modules",
|
|
||||||
"!build",
|
|
||||||
"!.svelte-kit",
|
|
||||||
"!package",
|
|
||||||
"!**/.env",
|
|
||||||
"!**/.env.*",
|
|
||||||
"**/.env.example",
|
|
||||||
"!**/pnpm-lock.yaml",
|
|
||||||
"!**/package-lock.json",
|
|
||||||
"!**/yarn.lock",
|
|
||||||
"!**/svelte.config.js",
|
|
||||||
"!eslint.config.js",
|
|
||||||
"!tailwind.config.js",
|
|
||||||
"!coverage",
|
|
||||||
"**",
|
|
||||||
"!**/.DS_Store",
|
|
||||||
"!**/node_modules",
|
|
||||||
"!build",
|
|
||||||
"!.svelte-kit",
|
|
||||||
"!package",
|
|
||||||
"!**/.env",
|
|
||||||
"!**/.env.*",
|
|
||||||
"**/.env.example",
|
|
||||||
"!**/pnpm-lock.yaml",
|
|
||||||
"!**/package-lock.json",
|
|
||||||
"!**/yarn.lock",
|
|
||||||
"!**/svelte.config.js",
|
|
||||||
"!eslint.config.js",
|
|
||||||
"!tailwind.config.js",
|
|
||||||
"!coverage"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"complexity": {
|
|
||||||
"noAdjacentSpacesInRegex": "error",
|
|
||||||
"noExtraBooleanCast": "error",
|
|
||||||
"noForEach": "error",
|
|
||||||
"noStaticOnlyClass": "error",
|
|
||||||
"noUselessCatch": "error",
|
|
||||||
"noUselessEscapeInRegex": "error",
|
|
||||||
"noUselessSwitchCase": "error",
|
|
||||||
"noUselessThisAlias": "error",
|
|
||||||
"noUselessTypeConstraint": "error",
|
|
||||||
"useDateNow": "error",
|
|
||||||
"useFlatMap": "error"
|
|
||||||
},
|
|
||||||
"correctness": {
|
|
||||||
"noConstAssign": "error",
|
|
||||||
"noConstantCondition": "error",
|
|
||||||
"noEmptyCharacterClassInRegex": "error",
|
|
||||||
"noEmptyPattern": "error",
|
|
||||||
"noGlobalObjectCalls": "error",
|
|
||||||
"noInvalidBuiltinInstantiation": "error",
|
|
||||||
"noInvalidConstructorSuper": "error",
|
|
||||||
"noNonoctalDecimalEscape": "error",
|
|
||||||
"noPrecisionLoss": "error",
|
|
||||||
"noSelfAssign": "error",
|
|
||||||
"noSetterReturn": "error",
|
|
||||||
"noSwitchDeclarations": "error",
|
|
||||||
"noUndeclaredVariables": "error",
|
|
||||||
"noUnreachable": "error",
|
|
||||||
"noUnreachableSuper": "error",
|
|
||||||
"noUnsafeFinally": "error",
|
|
||||||
"noUnsafeOptionalChaining": "error",
|
|
||||||
"noUnusedLabels": "error",
|
|
||||||
"noUnusedPrivateClassMembers": "error",
|
|
||||||
"noUnusedVariables": "off",
|
|
||||||
"useIsNan": "error",
|
|
||||||
"useValidForDirection": "error",
|
|
||||||
"useValidTypeof": "error",
|
|
||||||
"useYield": "error"
|
|
||||||
},
|
|
||||||
"recommended": false,
|
|
||||||
"style": {
|
|
||||||
"noCommonJs": "error",
|
|
||||||
"noNamespace": "error",
|
|
||||||
"noNegationElse": "off",
|
|
||||||
"noNestedTernary": "off",
|
|
||||||
"noSubstr": "error",
|
|
||||||
"useArrayLiterals": "error",
|
|
||||||
"useAsConstAssertion": "error",
|
|
||||||
"useAtIndex": "error",
|
|
||||||
"useCollapsedIf": "error",
|
|
||||||
"useExplicitLengthCheck": "error",
|
|
||||||
"useFilenamingConvention": {
|
|
||||||
"level": "error",
|
|
||||||
"options": { "filenameCases": ["kebab-case"] }
|
|
||||||
},
|
|
||||||
"useForOf": "error",
|
|
||||||
"useNodejsImportProtocol": "error",
|
|
||||||
"useNumberNamespace": "error",
|
|
||||||
"useThrowNewError": "error",
|
|
||||||
"useTrimStartEnd": "error"
|
|
||||||
},
|
|
||||||
"suspicious": {
|
|
||||||
"noAssignInExpressions": "error",
|
|
||||||
"noAsyncPromiseExecutor": "error",
|
|
||||||
"noCatchAssign": "error",
|
|
||||||
"noClassAssign": "error",
|
|
||||||
"noCompareNegZero": "error",
|
|
||||||
"noControlCharactersInRegex": "error",
|
|
||||||
"noDebugger": "error",
|
|
||||||
"noDocumentCookie": "error",
|
|
||||||
"noDuplicateCase": "error",
|
|
||||||
"noDuplicateClassMembers": "error",
|
|
||||||
"noDuplicateElseIf": "error",
|
|
||||||
"noDuplicateObjectKeys": "error",
|
|
||||||
"noDuplicateParameters": "error",
|
|
||||||
"noEmptyBlockStatements": "error",
|
|
||||||
"noExplicitAny": "error",
|
|
||||||
"noExtraNonNullAssertion": "error",
|
|
||||||
"noFallthroughSwitchClause": "error",
|
|
||||||
"noFunctionAssign": "error",
|
|
||||||
"noGlobalAssign": "error",
|
|
||||||
"noImportAssign": "error",
|
|
||||||
"noIrregularWhitespace": "error",
|
|
||||||
"noMisleadingCharacterClass": "error",
|
|
||||||
"noMisleadingInstantiator": "error",
|
|
||||||
"noPrototypeBuiltins": "error",
|
|
||||||
"noRedeclare": "error",
|
|
||||||
"noShadowRestrictedNames": "error",
|
|
||||||
"noSparseArray": "error",
|
|
||||||
"noThenProperty": "error",
|
|
||||||
"noUnsafeDeclarationMerging": "error",
|
|
||||||
"noUnsafeNegation": "error",
|
|
||||||
"noWith": "error",
|
|
||||||
"useErrorMessage": "error",
|
|
||||||
"useGetterReturn": "error",
|
|
||||||
"useNamespaceKeyword": "error",
|
|
||||||
"useNumberToFixedDigitsArgument": "error"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"includes": ["*.svelte.js", "*.svelte.ts", "**/*.svelte.js", "**/*.svelte.ts"],
|
|
||||||
"javascript": { "globals": [] }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"includes": ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"],
|
|
||||||
"linter": {
|
|
||||||
"rules": {
|
|
||||||
"complexity": { "noArguments": "error" },
|
|
||||||
"correctness": {
|
|
||||||
"noConstAssign": "off",
|
|
||||||
"noGlobalObjectCalls": "off",
|
|
||||||
"noInvalidBuiltinInstantiation": "off",
|
|
||||||
"noInvalidConstructorSuper": "off",
|
|
||||||
"noSetterReturn": "off",
|
|
||||||
"noUndeclaredVariables": "off",
|
|
||||||
"noUnreachable": "off",
|
|
||||||
"noUnreachableSuper": "off"
|
|
||||||
},
|
|
||||||
"style": { "useConst": "error" },
|
|
||||||
"suspicious": {
|
|
||||||
"noClassAssign": "off",
|
|
||||||
"noDuplicateClassMembers": "off",
|
|
||||||
"noDuplicateObjectKeys": "off",
|
|
||||||
"noDuplicateParameters": "off",
|
|
||||||
"noFunctionAssign": "off",
|
|
||||||
"noImportAssign": "off",
|
|
||||||
"noRedeclare": "off",
|
|
||||||
"noUnsafeNegation": "off",
|
|
||||||
"noVar": "error",
|
|
||||||
"noWith": "off",
|
|
||||||
"useGetterReturn": "off"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"includes": ["**", "!**/service-worker/**"],
|
|
||||||
"javascript": {
|
|
||||||
"globals": [
|
|
||||||
"onanimationend",
|
|
||||||
"exports",
|
|
||||||
"ongamepadconnected",
|
|
||||||
"onlostpointercapture",
|
|
||||||
"onanimationiteration",
|
|
||||||
"onkeyup",
|
|
||||||
"onmousedown",
|
|
||||||
"onanimationstart",
|
|
||||||
"onslotchange",
|
|
||||||
"onprogress",
|
|
||||||
"ontransitionstart",
|
|
||||||
"onpause",
|
|
||||||
"onended",
|
|
||||||
"onpointerover",
|
|
||||||
"onscrollend",
|
|
||||||
"onformdata",
|
|
||||||
"ontransitionrun",
|
|
||||||
"onanimationcancel",
|
|
||||||
"ondrag",
|
|
||||||
"onchange",
|
|
||||||
"onbeforeinstallprompt",
|
|
||||||
"onbeforexrselect",
|
|
||||||
"onmessage",
|
|
||||||
"ontransitioncancel",
|
|
||||||
"onpointerdown",
|
|
||||||
"onabort",
|
|
||||||
"onpointerout",
|
|
||||||
"oncuechange",
|
|
||||||
"ongotpointercapture",
|
|
||||||
"onscrollsnapchanging",
|
|
||||||
"onsearch",
|
|
||||||
"onsubmit",
|
|
||||||
"onstalled",
|
|
||||||
"onsuspend",
|
|
||||||
"onreset",
|
|
||||||
"onerror",
|
|
||||||
"onmouseenter",
|
|
||||||
"ongamepaddisconnected",
|
|
||||||
"onresize",
|
|
||||||
"ondragover",
|
|
||||||
"onbeforetoggle",
|
|
||||||
"onmouseover",
|
|
||||||
"onpagehide",
|
|
||||||
"onmousemove",
|
|
||||||
"onratechange",
|
|
||||||
"oncommand",
|
|
||||||
"onmessageerror",
|
|
||||||
"onwheel",
|
|
||||||
"ondevicemotion",
|
|
||||||
"onauxclick",
|
|
||||||
"ontransitionend",
|
|
||||||
"onpaste",
|
|
||||||
"onpageswap",
|
|
||||||
"ononline",
|
|
||||||
"ondeviceorientationabsolute",
|
|
||||||
"onkeydown",
|
|
||||||
"onclose",
|
|
||||||
"onselect",
|
|
||||||
"onpageshow",
|
|
||||||
"onpointercancel",
|
|
||||||
"onbeforematch",
|
|
||||||
"onpointerrawupdate",
|
|
||||||
"ondragleave",
|
|
||||||
"onscrollsnapchange",
|
|
||||||
"onseeked",
|
|
||||||
"onwaiting",
|
|
||||||
"onbeforeunload",
|
|
||||||
"onplaying",
|
|
||||||
"onvolumechange",
|
|
||||||
"ondragend",
|
|
||||||
"onstorage",
|
|
||||||
"onloadeddata",
|
|
||||||
"onfocus",
|
|
||||||
"onoffline",
|
|
||||||
"onplay",
|
|
||||||
"onafterprint",
|
|
||||||
"onclick",
|
|
||||||
"oncut",
|
|
||||||
"onmouseout",
|
|
||||||
"ondblclick",
|
|
||||||
"oncanplay",
|
|
||||||
"onloadstart",
|
|
||||||
"onappinstalled",
|
|
||||||
"onpointermove",
|
|
||||||
"ontoggle",
|
|
||||||
"oncontextmenu",
|
|
||||||
"NodeJS",
|
|
||||||
"onblur",
|
|
||||||
"oncancel",
|
|
||||||
"onbeforeprint",
|
|
||||||
"oncontextrestored",
|
|
||||||
"onloadedmetadata",
|
|
||||||
"onpointerup",
|
|
||||||
"onlanguagechange",
|
|
||||||
"oncopy",
|
|
||||||
"onselectstart",
|
|
||||||
"onscroll",
|
|
||||||
"onload",
|
|
||||||
"ondragstart",
|
|
||||||
"onbeforeinput",
|
|
||||||
"oncanplaythrough",
|
|
||||||
"oninput",
|
|
||||||
"oninvalid",
|
|
||||||
"ontimeupdate",
|
|
||||||
"ondurationchange",
|
|
||||||
"onselectionchange",
|
|
||||||
"onmouseup",
|
|
||||||
"location",
|
|
||||||
"onkeypress",
|
|
||||||
"onpointerleave",
|
|
||||||
"oncontextlost",
|
|
||||||
"ondrop",
|
|
||||||
"onsecuritypolicyviolation",
|
|
||||||
"oncontentvisibilityautostatechange",
|
|
||||||
"ondeviceorientation",
|
|
||||||
"onseeking",
|
|
||||||
"onrejectionhandled",
|
|
||||||
"onunload",
|
|
||||||
"onmouseleave",
|
|
||||||
"onhashchange",
|
|
||||||
"onpointerenter",
|
|
||||||
"onmousewheel",
|
|
||||||
"onunhandledrejection",
|
|
||||||
"ondragenter",
|
|
||||||
"onpopstate",
|
|
||||||
"onpagereveal",
|
|
||||||
"onemptied"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"linter": {
|
|
||||||
"rules": {
|
|
||||||
"correctness": { "noUnusedVariables": "warn" },
|
|
||||||
"style": {
|
|
||||||
"useBlockStatements": "error",
|
|
||||||
"useFilenamingConvention": {
|
|
||||||
"level": "off",
|
|
||||||
"options": { "filenameCases": ["kebab-case"] }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"suspicious": { "useAwait": "error" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"includes": ["*.svelte", "**/*.svelte"],
|
|
||||||
"javascript": { "globals": [] },
|
|
||||||
"linter": {
|
|
||||||
"rules": {
|
|
||||||
"correctness": {
|
|
||||||
"noInnerDeclarations": "off",
|
|
||||||
"noSelfAssign": "off",
|
|
||||||
"noUnusedImports": "off",
|
|
||||||
"noUnusedVariables": "off"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"vcs": { "clientKind": "git", "enabled": false, "useIgnoreFile": false }
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
import js from '@eslint/js';
|
import js from '@eslint/js';
|
||||||
|
import tslintPluginCompat from '@koddsson/eslint-plugin-tscompat';
|
||||||
|
import eslintPluginCompat from 'eslint-plugin-compat';
|
||||||
import eslintPluginSvelte from 'eslint-plugin-svelte';
|
import eslintPluginSvelte from 'eslint-plugin-svelte';
|
||||||
import eslintPluginUnicorn from 'eslint-plugin-unicorn';
|
import eslintPluginUnicorn from 'eslint-plugin-unicorn';
|
||||||
import globals from 'globals';
|
import globals from 'globals';
|
||||||
@@ -14,6 +16,37 @@ export default typescriptEslint.config(
|
|||||||
...eslintPluginSvelte.configs.recommended,
|
...eslintPluginSvelte.configs.recommended,
|
||||||
eslintPluginUnicorn.configs.recommended,
|
eslintPluginUnicorn.configs.recommended,
|
||||||
js.configs.recommended,
|
js.configs.recommended,
|
||||||
|
{
|
||||||
|
plugins: {
|
||||||
|
tscompat: tslintPluginCompat,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'tscompat/tscompat': [
|
||||||
|
'error',
|
||||||
|
{ browserslist: ['> 0.2% and last 4 major versions', '> 0.5%', 'not dead', 'edge >= 135', 'not edge < 135'] },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
languageOptions: {
|
||||||
|
parser,
|
||||||
|
parserOptions: {
|
||||||
|
project: ['./tsconfig.json'],
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ignores: ['**/service-worker/**'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
plugins: {
|
||||||
|
compat: eslintPluginCompat,
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
polyfills: [],
|
||||||
|
lintAllEsApis: true,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'compat/compat': 'error',
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
ignores: [
|
ignores: [
|
||||||
'**/.DS_Store',
|
'**/.DS_Store',
|
||||||
|
|||||||
Generated
+82
-321
@@ -11,7 +11,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@formatjs/icu-messageformat-parser": "^2.9.8",
|
"@formatjs/icu-messageformat-parser": "^2.9.8",
|
||||||
"@immich/sdk": "file:../open-api/typescript-sdk",
|
"@immich/sdk": "file:../open-api/typescript-sdk",
|
||||||
"@immich/ui": "^0.22.4",
|
"@immich/ui": "^0.22.7",
|
||||||
"@mapbox/mapbox-gl-rtl-text": "0.2.3",
|
"@mapbox/mapbox-gl-rtl-text": "0.2.3",
|
||||||
"@mdi/js": "^7.4.47",
|
"@mdi/js": "^7.4.47",
|
||||||
"@photo-sphere-viewer/core": "^5.11.5",
|
"@photo-sphere-viewer/core": "^5.11.5",
|
||||||
@@ -39,10 +39,10 @@
|
|||||||
"thumbhash": "^0.1.1"
|
"thumbhash": "^0.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "2.0.0-beta.6",
|
|
||||||
"@eslint/eslintrc": "^3.1.0",
|
"@eslint/eslintrc": "^3.1.0",
|
||||||
"@eslint/js": "^9.18.0",
|
"@eslint/js": "^9.18.0",
|
||||||
"@faker-js/faker": "^9.3.0",
|
"@faker-js/faker": "^9.3.0",
|
||||||
|
"@koddsson/eslint-plugin-tscompat": "^0.2.0",
|
||||||
"@socket.io/component-emitter": "^3.1.0",
|
"@socket.io/component-emitter": "^3.1.0",
|
||||||
"@sveltejs/adapter-static": "^3.0.8",
|
"@sveltejs/adapter-static": "^3.0.8",
|
||||||
"@sveltejs/enhanced-img": "^0.6.0",
|
"@sveltejs/enhanced-img": "^0.6.0",
|
||||||
@@ -64,12 +64,11 @@
|
|||||||
"eslint": "^9.18.0",
|
"eslint": "^9.18.0",
|
||||||
"eslint-config-prettier": "^10.0.0",
|
"eslint-config-prettier": "^10.0.0",
|
||||||
"eslint-p": "^0.23.0",
|
"eslint-p": "^0.23.0",
|
||||||
"eslint-plugin-oxlint": "^1.1.0",
|
"eslint-plugin-compat": "^6.0.2",
|
||||||
"eslint-plugin-svelte": "^3.9.0",
|
"eslint-plugin-svelte": "^3.9.0",
|
||||||
"eslint-plugin-unicorn": "^59.0.0",
|
"eslint-plugin-unicorn": "^59.0.0",
|
||||||
"factory.ts": "^1.4.1",
|
"factory.ts": "^1.4.1",
|
||||||
"globals": "^16.0.0",
|
"globals": "^16.0.0",
|
||||||
"oxlint": "^1.1.0",
|
|
||||||
"prettier": "^3.4.2",
|
"prettier": "^3.4.2",
|
||||||
"prettier-plugin-organize-imports": "^4.0.0",
|
"prettier-plugin-organize-imports": "^4.0.0",
|
||||||
"prettier-plugin-sort-json": "^4.1.1",
|
"prettier-plugin-sort-json": "^4.1.1",
|
||||||
@@ -77,6 +76,7 @@
|
|||||||
"rollup-plugin-visualizer": "^6.0.0",
|
"rollup-plugin-visualizer": "^6.0.0",
|
||||||
"svelte": "^5.25.3",
|
"svelte": "^5.25.3",
|
||||||
"svelte-check": "^4.1.5",
|
"svelte-check": "^4.1.5",
|
||||||
|
"svelte-eslint-parser": "^1.2.0",
|
||||||
"tailwindcss": "^4.1.7",
|
"tailwindcss": "^4.1.7",
|
||||||
"tslib": "^2.6.2",
|
"tslib": "^2.6.2",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.7.3",
|
||||||
@@ -205,169 +205,6 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/biome": {
|
|
||||||
"version": "2.0.0-beta.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.0.0-beta.6.tgz",
|
|
||||||
"integrity": "sha512-14vw9b5QJxrcP7WLkCeRiB/fft9wNZwx6yEiikBDxFbN7IAp39Xtvt/gJPq4ifhZ5IS25CnQEAkLLwfBIDMjsA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT OR Apache-2.0",
|
|
||||||
"bin": {
|
|
||||||
"biome": "bin/biome"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.21.3"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/biome"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"@biomejs/cli-darwin-arm64": "2.0.0-beta.6",
|
|
||||||
"@biomejs/cli-darwin-x64": "2.0.0-beta.6",
|
|
||||||
"@biomejs/cli-linux-arm64": "2.0.0-beta.6",
|
|
||||||
"@biomejs/cli-linux-arm64-musl": "2.0.0-beta.6",
|
|
||||||
"@biomejs/cli-linux-x64": "2.0.0-beta.6",
|
|
||||||
"@biomejs/cli-linux-x64-musl": "2.0.0-beta.6",
|
|
||||||
"@biomejs/cli-win32-arm64": "2.0.0-beta.6",
|
|
||||||
"@biomejs/cli-win32-x64": "2.0.0-beta.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@biomejs/cli-darwin-arm64": {
|
|
||||||
"version": "2.0.0-beta.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.0.0-beta.6.tgz",
|
|
||||||
"integrity": "sha512-L7PBLJlGTz5anougOMJQvEbzgG9sT1wKIXvgjFhu0dIsDZ/px2caWFCnv7Q9L2K0+yF08EYRTTZVvoVO5D//sQ==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT OR Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.21.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@biomejs/cli-darwin-x64": {
|
|
||||||
"version": "2.0.0-beta.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.0.0-beta.6.tgz",
|
|
||||||
"integrity": "sha512-ekhOOyhcVJ1ZRqHjq+eUOv8/3XMRKQ9Qf0URuO/PvHgopejv+PEoix0RIyxholYELKc049M4J3IJgsX4q2pZzw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT OR Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.21.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@biomejs/cli-linux-arm64": {
|
|
||||||
"version": "2.0.0-beta.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.0.0-beta.6.tgz",
|
|
||||||
"integrity": "sha512-pu+rCLI36ziPtwnJY53HRr154711uVeCt1i2KNXehvwNZZMK141wwg4yPkXkBdBvw7H7sez0HE/rCQR2fByJnQ==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT OR Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.21.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@biomejs/cli-linux-arm64-musl": {
|
|
||||||
"version": "2.0.0-beta.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.0.0-beta.6.tgz",
|
|
||||||
"integrity": "sha512-70WOWJI1/vZ97OUAt6r9HpiP5+vlL7yAdIoVQzVLjQy1TArfltN38KKqp9fnhgX173liUh0gry//MrWkKHYrIQ==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT OR Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.21.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@biomejs/cli-linux-x64": {
|
|
||||||
"version": "2.0.0-beta.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.0.0-beta.6.tgz",
|
|
||||||
"integrity": "sha512-emqZAuAyRw4Ug4B+CTgozIxVg1QLol28oZyIWuIjWEDr7eOo6Ek9zSZGeusmbwIEPu6r6qon8JAV6OdukxEwIg==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT OR Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.21.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@biomejs/cli-linux-x64-musl": {
|
|
||||||
"version": "2.0.0-beta.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.0.0-beta.6.tgz",
|
|
||||||
"integrity": "sha512-G9ZIoaNs6q9+mOoMURoXvNRfCOs28jrS4R8+3/y0h9ttOXpd4VALPOAfjzBGPpMd/4RoEMHXw/1Ts4dKvrv9zw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT OR Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.21.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@biomejs/cli-win32-arm64": {
|
|
||||||
"version": "2.0.0-beta.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.0.0-beta.6.tgz",
|
|
||||||
"integrity": "sha512-JijYVZC6R5qq94yLaElowLLzbZ4xR2qDiOVPQV8H1+ru3IqVOjQu5f/lIt4uuea1iRFbxS+mOaxOZM9tUl1pTQ==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT OR Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.21.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@biomejs/cli-win32-x64": {
|
|
||||||
"version": "2.0.0-beta.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.0.0-beta.6.tgz",
|
|
||||||
"integrity": "sha512-zs29t/nxon11dKV+ckQB1yUOmhYx17e2+cHGK8PCVamqVGSMbjrd5evjtlfbnVJXP0ar7nNKhcg4ZWYGJ6aR1w==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT OR Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.21.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@emnapi/runtime": {
|
"node_modules/@emnapi/runtime": {
|
||||||
"version": "1.4.3",
|
"version": "1.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz",
|
||||||
@@ -1496,9 +1333,9 @@
|
|||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
"node_modules/@immich/ui": {
|
"node_modules/@immich/ui": {
|
||||||
"version": "0.22.4",
|
"version": "0.22.7",
|
||||||
"resolved": "https://registry.npmjs.org/@immich/ui/-/ui-0.22.4.tgz",
|
"resolved": "https://registry.npmjs.org/@immich/ui/-/ui-0.22.7.tgz",
|
||||||
"integrity": "sha512-l0H8G8XZ3YaP/pA8NsLhGsNZpTAwcOyEFmF88D5HZkK3nFTZOQFxvzcMfyOeMS6Nevv0CHdvJp3ns0zajfvNzw==",
|
"integrity": "sha512-FdA0RDSOO1IDSTQmCbW9u5yXFl59EHu++tYonDR/FEZUKrMwfmQEanePSW5g5KofdumKEuxBN1fWFym3NbB0jQ==",
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mdi/js": "^7.4.47",
|
"@mdi/js": "^7.4.47",
|
||||||
@@ -1703,6 +1540,26 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@koddsson/eslint-plugin-tscompat": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@koddsson/eslint-plugin-tscompat/-/eslint-plugin-tscompat-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-Oqd4kWSX0LiO9wWHjcmDfXZNC7TotFV/tLRhwCFU3XUeb//KYvJ75c9OmeSJ+vBv5lkCeB+xYsqyNrBc5j18XA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@mdn/browser-compat-data": "^6.0.17",
|
||||||
|
"@typescript-eslint/type-utils": "^8.0.1",
|
||||||
|
"@typescript-eslint/utils": "^8.0.0",
|
||||||
|
"browserslist": "^4.23.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@koddsson/eslint-plugin-tscompat/node_modules/@mdn/browser-compat-data": {
|
||||||
|
"version": "6.0.22",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-6.0.22.tgz",
|
||||||
|
"integrity": "sha512-zhgOBTouJOd8IbE5dEEcfzg83l+nxKL/7Ru2HPeCVbog9I0JGHg3QZab9IxZquKFTUsc+c7QqU4EVENeZzZWRg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "CC0-1.0"
|
||||||
|
},
|
||||||
"node_modules/@mapbox/geojson-rewind": {
|
"node_modules/@mapbox/geojson-rewind": {
|
||||||
"version": "0.5.2",
|
"version": "0.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz",
|
||||||
@@ -1853,6 +1710,13 @@
|
|||||||
"integrity": "sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ==",
|
"integrity": "sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ==",
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
|
"node_modules/@mdn/browser-compat-data": {
|
||||||
|
"version": "5.7.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.7.6.tgz",
|
||||||
|
"integrity": "sha512-7xdrMX0Wk7grrTZQwAoy1GkvPMFoizStUoL+VmtUkAxegbCCec+3FKwOM6yc/uGU5+BEczQHXAlWiqvM8JeENg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "CC0-1.0"
|
||||||
|
},
|
||||||
"node_modules/@namnode/store": {
|
"node_modules/@namnode/store": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@namnode/store/-/store-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@namnode/store/-/store-0.1.0.tgz",
|
||||||
@@ -1901,118 +1765,6 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@oxlint/darwin-arm64": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@oxlint/darwin-arm64/-/darwin-arm64-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-sSnR3SOxIU/QfaqXrcQ0UVUkzJO0bcInQ7dMhHa102gVAgWjp1fBeMVCM0adEY0UNmEXrRkgD/rQtQgn9YAU+w==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@oxlint/darwin-x64": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@oxlint/darwin-x64/-/darwin-x64-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-Jvd3fHnzY2OYbmsg9NSGPoBkGViDGHSFnBKyJQ9LOIw7lxAyQBG2Quxc3GYPFR/f9OYho9C3p4+dIaAJfKhnsw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@oxlint/linux-arm64-gnu": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-gnu/-/linux-arm64-gnu-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-MgW4iskOdXuoR+wDXIJUfbdnTg2eo2FnQRaD6ZqhnDTDa7LnV+06rp/Cg3aGj2X9jSEcKDv/bMbYQuot7WRs6Q==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@oxlint/linux-arm64-musl": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-musl/-/linux-arm64-musl-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-a+pkEKmDRdrW+y0gtZ/m68ElVW2VZgATGbMxDgDYFpdiMx9Y0pUPwTMZ2EX/17Aslop4c1BiDSFDK7aEBxKR2g==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@oxlint/linux-x64-gnu": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@oxlint/linux-x64-gnu/-/linux-x64-gnu-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-wNBsXCKVZMvUTcFitrV1wTsdhUAv8l+XQxHxciZ2SO6dpNnWEb2YCxSAIOXeyzBLdO4pIODYcSy38CvGue7TwA==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@oxlint/linux-x64-musl": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@oxlint/linux-x64-musl/-/linux-x64-musl-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-pZD0lt6A5j2Wp70fgIYk4GoPfKTZ8mHWamWIpKFT7aSkFkiOi6nhLWDFvMEIHWRTK3LgkWUNcnWPp4brvin4wQ==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@oxlint/win32-arm64": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@oxlint/win32-arm64/-/win32-arm64-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-rT6uXQvE80+B+L04HJf30uF26426FPI9i9DAY2AxBUhrpNwhqkDEhQdd9ilFWVC7SSbpHgAs50lo+ImSAAkHPQ==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@oxlint/win32-x64": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@oxlint/win32-x64/-/win32-x64-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-x6r5yvM3wEty93Bx0NuNK+kutUyS/K55itkUrxdExoK6GcmVDboGGuhju9HyU2cM/IWLEWO8RHcXSyaxr9GR5g==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@photo-sphere-viewer/core": {
|
"node_modules/@photo-sphere-viewer/core": {
|
||||||
"version": "5.13.2",
|
"version": "5.13.2",
|
||||||
"resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.13.2.tgz",
|
"resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.13.2.tgz",
|
||||||
@@ -3783,6 +3535,16 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ast-metadata-inferer": {
|
||||||
|
"version": "0.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ast-metadata-inferer/-/ast-metadata-inferer-0.8.1.tgz",
|
||||||
|
"integrity": "sha512-ht3Dm6Zr7SXv6t1Ra6gFo0+kLDglHGrEbYihTkcycrbHw7WCcuhBzPlJYHEsIpycaUwzsJHje+vUcxXUX4ztTA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@mdn/browser-compat-data": "^5.6.19"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/asynckit": {
|
"node_modules/asynckit": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
@@ -4995,14 +4757,40 @@
|
|||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-oxlint": {
|
"node_modules/eslint-plugin-compat": {
|
||||||
"version": "1.1.0",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-oxlint/-/eslint-plugin-oxlint-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-6.0.2.tgz",
|
||||||
"integrity": "sha512-spDWxcsAfoUDjSwxPrP2gfuOJ2Hrv8faqQ5Vkm90lURp4no5aWJQ09xRKmZroIPTuQCKYgG9nvnakdIbXGlijg==",
|
"integrity": "sha512-1ME+YfJjmOz1blH0nPZpHgjMGK4kjgEeoYqGCqoBPQ/mGu/dJzdoP0f1C8H2jcWZjzhZjAMccbM/VdXhPORIfA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"jsonc-parser": "^3.3.1"
|
"@mdn/browser-compat-data": "^5.5.35",
|
||||||
|
"ast-metadata-inferer": "^0.8.1",
|
||||||
|
"browserslist": "^4.24.2",
|
||||||
|
"caniuse-lite": "^1.0.30001687",
|
||||||
|
"find-up": "^5.0.0",
|
||||||
|
"globals": "^15.7.0",
|
||||||
|
"lodash.memoize": "^4.1.2",
|
||||||
|
"semver": "^7.6.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.x"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"eslint": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/eslint-plugin-compat/node_modules/globals": {
|
||||||
|
"version": "15.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
|
||||||
|
"integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-svelte": {
|
"node_modules/eslint-plugin-svelte": {
|
||||||
@@ -6430,13 +6218,6 @@
|
|||||||
"integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==",
|
"integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/jsonc-parser": {
|
|
||||||
"version": "3.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
|
|
||||||
"integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/just-compare": {
|
"node_modules/just-compare": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/just-compare/-/just-compare-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/just-compare/-/just-compare-2.3.0.tgz",
|
||||||
@@ -6789,6 +6570,13 @@
|
|||||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
|
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash.memoize": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/lodash.merge": {
|
"node_modules/lodash.merge": {
|
||||||
"version": "4.6.2",
|
"version": "4.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||||
@@ -7427,33 +7215,6 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/oxlint": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-OVNpaoaQCUHHhCv5sYMPJ7Ts5k7ziw0QteH1gBSwF3elf/8GAew2Uh/0S7HsU1iGtjhlFy80+A8nwIb3Tq6m1w==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
|
||||||
"oxc_language_server": "bin/oxc_language_server",
|
|
||||||
"oxlint": "bin/oxlint"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8.*"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/Boshen"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"@oxlint/darwin-arm64": "1.1.0",
|
|
||||||
"@oxlint/darwin-x64": "1.1.0",
|
|
||||||
"@oxlint/linux-arm64-gnu": "1.1.0",
|
|
||||||
"@oxlint/linux-arm64-musl": "1.1.0",
|
|
||||||
"@oxlint/linux-x64-gnu": "1.1.0",
|
|
||||||
"@oxlint/linux-x64-musl": "1.1.0",
|
|
||||||
"@oxlint/win32-arm64": "1.1.0",
|
|
||||||
"@oxlint/win32-x64": "1.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/p-limit": {
|
"node_modules/p-limit": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
||||||
|
|||||||
+4
-6
@@ -15,8 +15,6 @@
|
|||||||
"check:code": "npm run format && npm run lint:p && npm run check:svelte && npm run check:typescript",
|
"check:code": "npm run format && npm run lint:p && npm run check:svelte && npm run check:typescript",
|
||||||
"check:all": "npm run check:code && npm run test:cov",
|
"check:all": "npm run check:code && npm run test:cov",
|
||||||
"lint": "eslint . --max-warnings 0",
|
"lint": "eslint . --max-warnings 0",
|
||||||
"lint:ox": "oxlint .",
|
|
||||||
"lint:ox-full": "npm run lint:ox && npm run lint:p",
|
|
||||||
"lint:p": "eslint-p . --max-warnings 0 --concurrency=4",
|
"lint:p": "eslint-p . --max-warnings 0 --concurrency=4",
|
||||||
"lint:fix": "npm run lint -- --fix",
|
"lint:fix": "npm run lint -- --fix",
|
||||||
"format": "prettier --check .",
|
"format": "prettier --check .",
|
||||||
@@ -30,7 +28,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@formatjs/icu-messageformat-parser": "^2.9.8",
|
"@formatjs/icu-messageformat-parser": "^2.9.8",
|
||||||
"@immich/sdk": "file:../open-api/typescript-sdk",
|
"@immich/sdk": "file:../open-api/typescript-sdk",
|
||||||
"@immich/ui": "^0.22.4",
|
"@immich/ui": "^0.22.7",
|
||||||
"@mapbox/mapbox-gl-rtl-text": "0.2.3",
|
"@mapbox/mapbox-gl-rtl-text": "0.2.3",
|
||||||
"@mdi/js": "^7.4.47",
|
"@mdi/js": "^7.4.47",
|
||||||
"@photo-sphere-viewer/core": "^5.11.5",
|
"@photo-sphere-viewer/core": "^5.11.5",
|
||||||
@@ -58,10 +56,10 @@
|
|||||||
"thumbhash": "^0.1.1"
|
"thumbhash": "^0.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "2.0.0-beta.6",
|
|
||||||
"@eslint/eslintrc": "^3.1.0",
|
"@eslint/eslintrc": "^3.1.0",
|
||||||
"@eslint/js": "^9.18.0",
|
"@eslint/js": "^9.18.0",
|
||||||
"@faker-js/faker": "^9.3.0",
|
"@faker-js/faker": "^9.3.0",
|
||||||
|
"@koddsson/eslint-plugin-tscompat": "^0.2.0",
|
||||||
"@socket.io/component-emitter": "^3.1.0",
|
"@socket.io/component-emitter": "^3.1.0",
|
||||||
"@sveltejs/adapter-static": "^3.0.8",
|
"@sveltejs/adapter-static": "^3.0.8",
|
||||||
"@sveltejs/enhanced-img": "^0.6.0",
|
"@sveltejs/enhanced-img": "^0.6.0",
|
||||||
@@ -83,12 +81,11 @@
|
|||||||
"eslint": "^9.18.0",
|
"eslint": "^9.18.0",
|
||||||
"eslint-config-prettier": "^10.0.0",
|
"eslint-config-prettier": "^10.0.0",
|
||||||
"eslint-p": "^0.23.0",
|
"eslint-p": "^0.23.0",
|
||||||
"eslint-plugin-oxlint": "^1.1.0",
|
"eslint-plugin-compat": "^6.0.2",
|
||||||
"eslint-plugin-svelte": "^3.9.0",
|
"eslint-plugin-svelte": "^3.9.0",
|
||||||
"eslint-plugin-unicorn": "^59.0.0",
|
"eslint-plugin-unicorn": "^59.0.0",
|
||||||
"factory.ts": "^1.4.1",
|
"factory.ts": "^1.4.1",
|
||||||
"globals": "^16.0.0",
|
"globals": "^16.0.0",
|
||||||
"oxlint": "^1.1.0",
|
|
||||||
"prettier": "^3.4.2",
|
"prettier": "^3.4.2",
|
||||||
"prettier-plugin-organize-imports": "^4.0.0",
|
"prettier-plugin-organize-imports": "^4.0.0",
|
||||||
"prettier-plugin-sort-json": "^4.1.1",
|
"prettier-plugin-sort-json": "^4.1.1",
|
||||||
@@ -96,6 +93,7 @@
|
|||||||
"rollup-plugin-visualizer": "^6.0.0",
|
"rollup-plugin-visualizer": "^6.0.0",
|
||||||
"svelte": "^5.25.3",
|
"svelte": "^5.25.3",
|
||||||
"svelte-check": "^4.1.5",
|
"svelte-check": "^4.1.5",
|
||||||
|
"svelte-eslint-parser": "^1.2.0",
|
||||||
"tailwindcss": "^4.1.7",
|
"tailwindcss": "^4.1.7",
|
||||||
"tslib": "^2.6.2",
|
"tslib": "^2.6.2",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.7.3",
|
||||||
|
|||||||
@@ -41,17 +41,11 @@
|
|||||||
--color-immich-bg: rgb(var(--immich-bg));
|
--color-immich-bg: rgb(var(--immich-bg));
|
||||||
--color-immich-fg: rgb(var(--immich-fg));
|
--color-immich-fg: rgb(var(--immich-fg));
|
||||||
--color-immich-gray: rgb(var(--immich-gray));
|
--color-immich-gray: rgb(var(--immich-gray));
|
||||||
--color-immich-error: rgb(var(--immich-error));
|
|
||||||
--color-immich-success: rgb(var(--immich-success));
|
|
||||||
--color-immich-warning: rgb(var(--immich-warning));
|
|
||||||
|
|
||||||
--color-immich-dark-primary: rgb(var(--immich-dark-primary));
|
--color-immich-dark-primary: rgb(var(--immich-dark-primary));
|
||||||
--color-immich-dark-bg: rgb(var(--immich-dark-bg));
|
--color-immich-dark-bg: rgb(var(--immich-dark-bg));
|
||||||
--color-immich-dark-fg: rgb(var(--immich-dark-fg));
|
--color-immich-dark-fg: rgb(var(--immich-dark-fg));
|
||||||
--color-immich-dark-gray: rgb(var(--immich-dark-gray));
|
--color-immich-dark-gray: rgb(var(--immich-dark-gray));
|
||||||
--color-immich-dark-error: rgb(var(--immich-dark-error));
|
|
||||||
--color-immich-dark-success: rgb(var(--immich-dark-success));
|
|
||||||
--color-immich-dark-warning: rgb(var(--immich-dark-warning));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@theme {
|
@theme {
|
||||||
@@ -74,18 +68,12 @@
|
|||||||
--immich-primary: 66 80 175;
|
--immich-primary: 66 80 175;
|
||||||
--immich-bg: 255 255 255;
|
--immich-bg: 255 255 255;
|
||||||
--immich-fg: 0 0 0;
|
--immich-fg: 0 0 0;
|
||||||
--immich-error: 229 115 115;
|
|
||||||
--immich-success: 129 199 132;
|
|
||||||
--immich-warning: 255 183 77;
|
|
||||||
|
|
||||||
/* dark */
|
/* dark */
|
||||||
--immich-dark-primary: 172 203 250;
|
--immich-dark-primary: 172 203 250;
|
||||||
--immich-dark-bg: 10 10 10;
|
--immich-dark-bg: 10 10 10;
|
||||||
--immich-dark-fg: 229 231 235;
|
--immich-dark-fg: 229 231 235;
|
||||||
--immich-dark-gray: 33 33 33;
|
--immich-dark-gray: 33 33 33;
|
||||||
--immich-dark-error: 211 47 47;
|
|
||||||
--immich-dark-success: 56 142 60;
|
|
||||||
--immich-dark-warning: 245 124 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*,
|
*,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import Icon from '$lib/components/elements/icon.svelte';
|
import Icon from '$lib/components/elements/icon.svelte';
|
||||||
import { locale } from '$lib/stores/preferences.store';
|
import { locale } from '$lib/stores/preferences.store';
|
||||||
import { JobCommand, type JobCommandDto, type JobCountsDto, type QueueStatusDto } from '@immich/sdk';
|
import { JobCommand, type JobCommandDto, type JobCountsDto, type QueueStatusDto } from '@immich/sdk';
|
||||||
|
import { IconButton } from '@immich/ui';
|
||||||
import {
|
import {
|
||||||
mdiAlertCircle,
|
mdiAlertCircle,
|
||||||
mdiAllInclusive,
|
mdiAllInclusive,
|
||||||
@@ -17,7 +18,6 @@
|
|||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import JobTileButton from './job-tile-button.svelte';
|
import JobTileButton from './job-tile-button.svelte';
|
||||||
import JobTileStatus from './job-tile-status.svelte';
|
import JobTileStatus from './job-tile-status.svelte';
|
||||||
import { IconButton } from '@immich/ui';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -71,7 +71,7 @@
|
|||||||
</span>
|
</span>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
{#if jobCounts.failed > 0}
|
{#if jobCounts.failed > 0}
|
||||||
<Badge color="primary">
|
<Badge>
|
||||||
<div class="flex flex-row gap-1">
|
<div class="flex flex-row gap-1">
|
||||||
<span class="text-sm">
|
<span class="text-sm">
|
||||||
{$t('admin.jobs_failed', { values: { jobCount: jobCounts.failed.toLocaleString($locale) } })}
|
{$t('admin.jobs_failed', { values: { jobCount: jobCounts.failed.toLocaleString($locale) } })}
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
</Badge>
|
</Badge>
|
||||||
{/if}
|
{/if}
|
||||||
{#if jobCounts.delayed > 0}
|
{#if jobCounts.delayed > 0}
|
||||||
<Badge color="secondary">
|
<Badge>
|
||||||
<span class="text-sm">
|
<span class="text-sm">
|
||||||
{$t('admin.jobs_delayed', { values: { jobCount: jobCounts.delayed.toLocaleString($locale) } })}
|
{$t('admin.jobs_delayed', { values: { jobCount: jobCounts.delayed.toLocaleString($locale) } })}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -182,7 +182,7 @@
|
|||||||
label={$t('admin.oauth_storage_quota_default').toUpperCase()}
|
label={$t('admin.oauth_storage_quota_default').toUpperCase()}
|
||||||
description={$t('admin.oauth_storage_quota_default_description')}
|
description={$t('admin.oauth_storage_quota_default_description')}
|
||||||
bind:value={config.oauth.defaultStorageQuota}
|
bind:value={config.oauth.defaultStorageQuota}
|
||||||
required={true}
|
required={false}
|
||||||
disabled={disabled || !config.oauth.enabled}
|
disabled={disabled || !config.oauth.enabled}
|
||||||
isEdited={!(config.oauth.defaultStorageQuota == savedConfig.oauth.defaultStorageQuota)}
|
isEdited={!(config.oauth.defaultStorageQuota == savedConfig.oauth.defaultStorageQuota)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+2
-3
@@ -4,6 +4,7 @@
|
|||||||
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
||||||
import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
|
import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
|
||||||
import SettingTextarea from '$lib/components/shared-components/settings/setting-textarea.svelte';
|
import SettingTextarea from '$lib/components/shared-components/settings/setting-textarea.svelte';
|
||||||
|
import { modalManager } from '$lib/managers/modal-manager.svelte';
|
||||||
import EmailTemplatePreviewModal from '$lib/modals/EmailTemplatePreviewModal.svelte';
|
import EmailTemplatePreviewModal from '$lib/modals/EmailTemplatePreviewModal.svelte';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { type SystemConfigDto, type SystemConfigTemplateEmailsDto, getNotificationTemplateAdmin } from '@immich/sdk';
|
import { type SystemConfigDto, type SystemConfigTemplateEmailsDto, getNotificationTemplateAdmin } from '@immich/sdk';
|
||||||
@@ -21,13 +22,11 @@
|
|||||||
|
|
||||||
let loadingPreview = $state(false);
|
let loadingPreview = $state(false);
|
||||||
|
|
||||||
const myCoolVariable = 'hello!';
|
|
||||||
|
|
||||||
const getTemplate = async (name: string, template: string) => {
|
const getTemplate = async (name: string, template: string) => {
|
||||||
try {
|
try {
|
||||||
loadingPreview = true;
|
loadingPreview = true;
|
||||||
const { html } = await getNotificationTemplateAdmin({ name, templateDto: { template } });
|
const { html } = await getNotificationTemplateAdmin({ name, templateDto: { template } });
|
||||||
modalManger.show(EmailTemplatePreviewModal, { html });
|
await modalManager.show(EmailTemplatePreviewModal, { html });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, 'Could not load template.');
|
handleError(error, 'Could not load template.');
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,202 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import Icon from '$lib/components/elements/icon.svelte';
|
|
||||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
|
||||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
|
||||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
|
||||||
import UserAvatar from '$lib/components/shared-components/user-avatar.svelte';
|
|
||||||
import ConfirmModal from '$lib/modals/ConfirmModal.svelte';
|
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
|
||||||
import {
|
|
||||||
AlbumUserRole,
|
|
||||||
AssetOrder,
|
|
||||||
removeUserFromAlbum,
|
|
||||||
updateAlbumInfo,
|
|
||||||
updateAlbumUser,
|
|
||||||
type AlbumResponseDto,
|
|
||||||
type UserResponseDto,
|
|
||||||
} from '@immich/sdk';
|
|
||||||
import { Modal, ModalBody } from '@immich/ui';
|
|
||||||
import { mdiArrowDownThin, mdiArrowUpThin, mdiDotsVertical, mdiPlus } from '@mdi/js';
|
|
||||||
import { findKey } from 'lodash-es';
|
|
||||||
import { t } from 'svelte-i18n';
|
|
||||||
import type { RenderedOption } from '../elements/dropdown.svelte';
|
|
||||||
import { notificationController, NotificationType } from '../shared-components/notification/notification';
|
|
||||||
import SettingDropdown from '../shared-components/settings/setting-dropdown.svelte';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
album: AlbumResponseDto;
|
|
||||||
order: AssetOrder | undefined;
|
|
||||||
user: UserResponseDto;
|
|
||||||
onChangeOrder: (order: AssetOrder) => void;
|
|
||||||
onClose: () => void;
|
|
||||||
onToggleEnabledActivity: () => void;
|
|
||||||
onShowSelectSharedUser: () => void;
|
|
||||||
onRemove: (userId: string) => void;
|
|
||||||
onRefreshAlbum: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
let {
|
|
||||||
album,
|
|
||||||
order,
|
|
||||||
user,
|
|
||||||
onChangeOrder,
|
|
||||||
onClose,
|
|
||||||
onToggleEnabledActivity,
|
|
||||||
onShowSelectSharedUser,
|
|
||||||
onRemove,
|
|
||||||
onRefreshAlbum,
|
|
||||||
}: Props = $props();
|
|
||||||
|
|
||||||
let selectedRemoveUser: UserResponseDto | null = $state(null);
|
|
||||||
|
|
||||||
const options: Record<AssetOrder, RenderedOption> = {
|
|
||||||
[AssetOrder.Asc]: { icon: mdiArrowUpThin, title: $t('oldest_first') },
|
|
||||||
[AssetOrder.Desc]: { icon: mdiArrowDownThin, title: $t('newest_first') },
|
|
||||||
};
|
|
||||||
|
|
||||||
let selectedOption = $derived(order ? options[order] : options[AssetOrder.Desc]);
|
|
||||||
|
|
||||||
const handleToggle = async (returnedOption: RenderedOption): Promise<void> => {
|
|
||||||
if (selectedOption === returnedOption) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let order: AssetOrder = AssetOrder.Desc;
|
|
||||||
order = findKey(options, (option) => option === returnedOption) as AssetOrder;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await updateAlbumInfo({
|
|
||||||
id: album.id,
|
|
||||||
updateAlbumDto: {
|
|
||||||
order,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
onChangeOrder(order);
|
|
||||||
} catch (error) {
|
|
||||||
handleError(error, $t('errors.unable_to_save_album'));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMenuRemove = (user: UserResponseDto): void => {
|
|
||||||
selectedRemoveUser = user;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRemoveUser = async (): Promise<void> => {
|
|
||||||
if (!selectedRemoveUser) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await removeUserFromAlbum({ id: album.id, userId: selectedRemoveUser.id });
|
|
||||||
onRemove(selectedRemoveUser.id);
|
|
||||||
notificationController.show({
|
|
||||||
type: NotificationType.Info,
|
|
||||||
message: $t('album_user_removed', { values: { user: selectedRemoveUser.name } }),
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
handleError(error, $t('errors.unable_to_remove_album_users'));
|
|
||||||
} finally {
|
|
||||||
selectedRemoveUser = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleUpdateSharedUserRole = async (user: UserResponseDto, role: AlbumUserRole) => {
|
|
||||||
try {
|
|
||||||
await updateAlbumUser({ id: album.id, userId: user.id, updateAlbumUserDto: { role } });
|
|
||||||
const message = $t('user_role_set', {
|
|
||||||
values: { user: user.name, role: role == AlbumUserRole.Viewer ? $t('role_viewer') : $t('role_editor') },
|
|
||||||
});
|
|
||||||
onRefreshAlbum();
|
|
||||||
notificationController.show({ type: NotificationType.Info, message });
|
|
||||||
} catch (error) {
|
|
||||||
handleError(error, $t('errors.unable_to_change_album_user_role'));
|
|
||||||
} finally {
|
|
||||||
selectedRemoveUser = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if !selectedRemoveUser}
|
|
||||||
<Modal title={$t('options')} {onClose} size="small">
|
|
||||||
<ModalBody>
|
|
||||||
<div class="items-center justify-center">
|
|
||||||
<div class="py-2">
|
|
||||||
<h2 class="text-gray text-sm mb-2">{$t('settings').toUpperCase()}</h2>
|
|
||||||
<div class="grid p-2 gap-y-2">
|
|
||||||
{#if order}
|
|
||||||
<SettingDropdown
|
|
||||||
title={$t('display_order')}
|
|
||||||
options={Object.values(options)}
|
|
||||||
selectedOption={options[order]}
|
|
||||||
onToggle={handleToggle}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
<SettingSwitch
|
|
||||||
title={$t('comments_and_likes')}
|
|
||||||
subtitle={$t('let_others_respond')}
|
|
||||||
checked={album.isActivityEnabled}
|
|
||||||
onToggle={onToggleEnabledActivity}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="py-2">
|
|
||||||
<div class="text-gray text-sm mb-3">{$t('people').toUpperCase()}</div>
|
|
||||||
<div class="p-2">
|
|
||||||
<button type="button" class="flex items-center gap-2" onclick={onShowSelectSharedUser}>
|
|
||||||
<div class="rounded-full w-10 h-10 border border-gray-500 flex items-center justify-center">
|
|
||||||
<div><Icon path={mdiPlus} size="25" /></div>
|
|
||||||
</div>
|
|
||||||
<div>{$t('invite_people')}</div>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div class="flex items-center gap-2 py-2 mt-2">
|
|
||||||
<div>
|
|
||||||
<UserAvatar {user} size="md" />
|
|
||||||
</div>
|
|
||||||
<div class="w-full">{user.name}</div>
|
|
||||||
<div>{$t('owner')}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#each album.albumUsers as { user, role } (user.id)}
|
|
||||||
<div class="flex items-center gap-2 py-2">
|
|
||||||
<div>
|
|
||||||
<UserAvatar {user} size="md" />
|
|
||||||
</div>
|
|
||||||
<div class="w-full">{user.name}</div>
|
|
||||||
{#if role === AlbumUserRole.Viewer}
|
|
||||||
{$t('role_viewer')}
|
|
||||||
{:else}
|
|
||||||
{$t('role_editor')}
|
|
||||||
{/if}
|
|
||||||
{#if user.id !== album.ownerId}
|
|
||||||
<ButtonContextMenu icon={mdiDotsVertical} size="medium" title={$t('options')}>
|
|
||||||
{#if role === AlbumUserRole.Viewer}
|
|
||||||
<MenuOption
|
|
||||||
onClick={() => handleUpdateSharedUserRole(user, AlbumUserRole.Editor)}
|
|
||||||
text={$t('allow_edits')}
|
|
||||||
/>
|
|
||||||
{:else}
|
|
||||||
<MenuOption
|
|
||||||
onClick={() => handleUpdateSharedUserRole(user, AlbumUserRole.Viewer)}
|
|
||||||
text={$t('disallow_edits')}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
<!-- Allow deletion for non-owners -->
|
|
||||||
<MenuOption onClick={() => handleMenuRemove(user)} text={$t('remove')} />
|
|
||||||
</ButtonContextMenu>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ModalBody>
|
|
||||||
</Modal>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if selectedRemoveUser}
|
|
||||||
<ConfirmModal
|
|
||||||
title={$t('album_remove_user')}
|
|
||||||
prompt={$t('album_remove_user_confirmation', { values: { user: selectedRemoveUser.name } })}
|
|
||||||
confirmText={$t('remove_user')}
|
|
||||||
onClose={(confirmed) => (confirmed ? handleRemoveUser() : (selectedRemoveUser = null))}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
@@ -8,9 +8,9 @@
|
|||||||
import { AssetAction, ProjectionType } from '$lib/constants';
|
import { AssetAction, ProjectionType } from '$lib/constants';
|
||||||
import { activityManager } from '$lib/managers/activity-manager.svelte';
|
import { activityManager } from '$lib/managers/activity-manager.svelte';
|
||||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
|
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||||
import { closeEditorCofirm } from '$lib/stores/asset-editor.store';
|
import { closeEditorCofirm } from '$lib/stores/asset-editor.store';
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
|
||||||
import { isShowDetail } from '$lib/stores/preferences.store';
|
import { isShowDetail } from '$lib/stores/preferences.store';
|
||||||
import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
|
import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
|
||||||
import { user } from '$lib/stores/user.store';
|
import { user } from '$lib/stores/user.store';
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
AssetJobName,
|
AssetJobName,
|
||||||
AssetTypeEnum,
|
AssetTypeEnum,
|
||||||
getAllAlbums,
|
getAllAlbums,
|
||||||
|
getAssetInfo,
|
||||||
getStack,
|
getStack,
|
||||||
runAssetJobs,
|
runAssetJobs,
|
||||||
type AlbumResponseDto,
|
type AlbumResponseDto,
|
||||||
@@ -138,16 +139,20 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onAssetUpdate = ({ asset: assetUpdate }: { event: 'upload' | 'update'; asset: AssetResponseDto }) => {
|
const onAssetUpdate = async (assetId: string) => {
|
||||||
if (assetUpdate.id === asset.id) {
|
if (assetId === asset.id) {
|
||||||
asset = assetUpdate;
|
asset = await getAssetInfo({ id: assetId, key: authManager.key });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
unsubscribes.push(
|
unsubscribes.push(
|
||||||
websocketEvents.on('on_upload_success', (asset) => onAssetUpdate({ event: 'upload', asset })),
|
websocketEvents.on('on_upload_success', (asset) => onAssetUpdate(asset.id)),
|
||||||
websocketEvents.on('on_asset_update', (asset) => onAssetUpdate({ event: 'update', asset })),
|
websocketEvents.on('on_asset_update', async (assetsIds) => {
|
||||||
|
for (const assetId of assetsIds) {
|
||||||
|
await onAssetUpdate(assetId);
|
||||||
|
}
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
slideshowStateUnsubscribe = slideshowState.subscribe((value) => {
|
slideshowStateUnsubscribe = slideshowState.subscribe((value) => {
|
||||||
@@ -300,8 +305,10 @@
|
|||||||
|
|
||||||
const handleStopSlideshow = async () => {
|
const handleStopSlideshow = async () => {
|
||||||
try {
|
try {
|
||||||
|
// eslint-disable-next-line tscompat/tscompat
|
||||||
if (document.fullscreenElement) {
|
if (document.fullscreenElement) {
|
||||||
document.body.style.cursor = '';
|
document.body.style.cursor = '';
|
||||||
|
// eslint-disable-next-line tscompat/tscompat
|
||||||
await document.exitFullscreen();
|
await document.exitFullscreen();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
{#each tags as tag (tag.id)}
|
{#each tags as tag (tag.id)}
|
||||||
<div class="flex group transition-all">
|
<div class="flex group transition-all">
|
||||||
<a
|
<a
|
||||||
class="inline-block h-min whitespace-nowrap ps-3 pe-1 group-hover:ps-3 py-1 text-center align-baseline leading-none text-gray-100 dark:text-immich-dark-gray bg-immich-primary dark:bg-immich-dark-primary rounded-s-full hover:bg-immich-primary/80 dark:hover:bg-immich-dark-primary/80 transition-all"
|
class="inline-block h-min whitespace-nowrap ps-3 pe-1 group-hover:ps-3 py-1 text-center align-baseline leading-none text-gray-100 dark:text-immich-dark-gray bg-primary rounded-s-full hover:bg-immich-primary/80 dark:hover:bg-immich-dark-primary/80 transition-all"
|
||||||
href={encodeURI(`${AppRoute.TAGS}/?path=${tag.value}`)}
|
href={encodeURI(`${AppRoute.TAGS}/?path=${tag.value}`)}
|
||||||
>
|
>
|
||||||
<p class="text-sm">
|
<p class="text-sm">
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { shortcut } from '$lib/actions/shortcut';
|
import { shortcut } from '$lib/actions/shortcut';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import ConfirmModal from '$lib/modals/ConfirmModal.svelte';
|
import ConfirmModal from '$lib/modals/ConfirmModal.svelte';
|
||||||
import { editTypes, showCancelConfirmDialog } from '$lib/stores/asset-editor.store';
|
import { editTypes, showCancelConfirmDialog } from '$lib/stores/asset-editor.store';
|
||||||
import { websocketEvents } from '$lib/stores/websocket';
|
import { websocketEvents } from '$lib/stores/websocket';
|
||||||
import { type AssetResponseDto } from '@immich/sdk';
|
import { getAssetInfo, type AssetResponseDto } from '@immich/sdk';
|
||||||
import { IconButton } from '@immich/ui';
|
import { IconButton } from '@immich/ui';
|
||||||
import { mdiClose } from '@mdi/js';
|
import { mdiClose } from '@mdi/js';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
return websocketEvents.on('on_asset_update', (assetUpdate) => {
|
return websocketEvents.on('on_asset_update', async (assetIds) => {
|
||||||
if (assetUpdate.id === asset.id) {
|
for (const assetId of assetIds) {
|
||||||
asset = assetUpdate;
|
if (assetId === asset.id) {
|
||||||
|
asset = await getAssetInfo({ id: assetId, key: authManager.key });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -57,6 +57,7 @@
|
|||||||
canvas = new Canvas(canvasEl);
|
canvas = new Canvas(canvasEl);
|
||||||
configureControlStyle();
|
configureControlStyle();
|
||||||
|
|
||||||
|
// eslint-disable-next-line tscompat/tscompat
|
||||||
faceRect = new Rect({
|
faceRect = new Rect({
|
||||||
fill: 'rgba(66,80,175,0.25)',
|
fill: 'rgba(66,80,175,0.25)',
|
||||||
stroke: 'rgb(66,80,175)',
|
stroke: 'rgb(66,80,175)',
|
||||||
|
|||||||
@@ -101,7 +101,9 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onShowSettings = async () => {
|
const onShowSettings = async () => {
|
||||||
|
// eslint-disable-next-line tscompat/tscompat
|
||||||
if (document.fullscreenElement) {
|
if (document.fullscreenElement) {
|
||||||
|
// eslint-disable-next-line tscompat/tscompat
|
||||||
await document.exitFullscreen();
|
await document.exitFullscreen();
|
||||||
}
|
}
|
||||||
await modalManager.show(SlideshowSettingsModal);
|
await modalManager.show(SlideshowSettingsModal);
|
||||||
|
|||||||
@@ -1,29 +1,16 @@
|
|||||||
<script lang="ts" module>
|
|
||||||
export type Color = 'primary' | 'secondary';
|
|
||||||
export type Rounded = false | true | 'full';
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Snippet } from 'svelte';
|
import type { Snippet } from 'svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
color?: Color;
|
rounded?: boolean | 'full';
|
||||||
rounded?: Rounded;
|
|
||||||
children?: Snippet;
|
children?: Snippet;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { color = 'primary', rounded = true, children }: Props = $props();
|
let { rounded = true, children }: Props = $props();
|
||||||
|
|
||||||
const colorClasses: Record<Color, string> = {
|
|
||||||
primary: 'text-gray-100 dark:text-immich-dark-gray bg-immich-primary dark:bg-immich-dark-primary',
|
|
||||||
secondary: 'text-immich-dark-bg dark:text-immich-gray dark:bg-gray-600 bg-gray-300 dark:text-immich-gray',
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
class="inline-block h-min whitespace-nowrap px-3 py-1 text-center align-baseline text-xs leading-none {colorClasses[
|
class="bg-primary text-subtle inline-block h-min whitespace-nowrap px-3 py-1 text-center align-baseline text-xs leading-none"
|
||||||
color
|
|
||||||
]}"
|
|
||||||
class:rounded-md={rounded === true}
|
class:rounded-md={rounded === true}
|
||||||
class:rounded-full={rounded === 'full'}
|
class:rounded-full={rounded === 'full'}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -169,19 +169,9 @@
|
|||||||
>
|
>
|
||||||
<td class="w-1/8 text-ellipsis ps-8 text-sm">
|
<td class="w-1/8 text-ellipsis ps-8 text-sm">
|
||||||
{#if validatedPath.isValid}
|
{#if validatedPath.isValid}
|
||||||
<Icon
|
<Icon path={mdiCheckCircleOutline} size="24" title={validatedPath.message} class="text-success" />
|
||||||
path={mdiCheckCircleOutline}
|
|
||||||
size="24"
|
|
||||||
title={validatedPath.message}
|
|
||||||
class="text-immich-success dark:text-immich-dark-success"
|
|
||||||
/>
|
|
||||||
{:else}
|
{:else}
|
||||||
<Icon
|
<Icon path={mdiAlertOutline} size="24" title={validatedPath.message} class="text-warning" />
|
||||||
path={mdiAlertOutline}
|
|
||||||
size="24"
|
|
||||||
title={validatedPath.message}
|
|
||||||
class="text-immich-warning dark:text-immich-dark-warning"
|
|
||||||
/>
|
|
||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@
|
|||||||
const entries: FileSystemEntry[] = [];
|
const entries: FileSystemEntry[] = [];
|
||||||
const files: File[] = [];
|
const files: File[] = [];
|
||||||
for (const item of dataTransfer.items) {
|
for (const item of dataTransfer.items) {
|
||||||
|
// eslint-disable-next-line tscompat/tscompat
|
||||||
const entry = item.webkitGetAsEntry();
|
const entry = item.webkitGetAsEntry();
|
||||||
if (entry) {
|
if (entry) {
|
||||||
entries.push(entry);
|
entries.push(entry);
|
||||||
@@ -67,6 +68,7 @@
|
|||||||
return handleFiles([...files, ...directoryFiles]);
|
return handleFiles([...files, ...directoryFiles]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line tscompat/tscompat
|
||||||
const browserSupportsDirectoryUpload = () => typeof DataTransferItem.prototype.webkitGetAsEntry === 'function';
|
const browserSupportsDirectoryUpload = () => typeof DataTransferItem.prototype.webkitGetAsEntry === 'function';
|
||||||
|
|
||||||
const getAllFilesFromTransferEntries = async (transferEntries: FileSystemEntry[]): Promise<File[]> => {
|
const getAllFilesFromTransferEntries = async (transferEntries: FileSystemEntry[]): Promise<File[]> => {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
type ComponentNotification,
|
type ComponentNotification,
|
||||||
type Notification,
|
type Notification,
|
||||||
} from '$lib/components/shared-components/notification/notification';
|
} from '$lib/components/shared-components/notification/notification';
|
||||||
import { IconButton } from '@immich/ui';
|
import { Button, IconButton, type Color } from '@immich/ui';
|
||||||
import { mdiCloseCircleOutline, mdiInformationOutline, mdiWindowClose } from '@mdi/js';
|
import { mdiCloseCircleOutline, mdiInformationOutline, mdiWindowClose } from '@mdi/js';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
@@ -40,10 +40,10 @@
|
|||||||
[NotificationType.Warning]: '#D08613',
|
[NotificationType.Warning]: '#D08613',
|
||||||
};
|
};
|
||||||
|
|
||||||
const buttonStyle: Record<NotificationType, string> = {
|
const colors: Record<NotificationType, Color> = {
|
||||||
[NotificationType.Info]: 'text-white bg-immich-primary hover:bg-immich-primary/75',
|
[NotificationType.Info]: 'primary',
|
||||||
[NotificationType.Error]: 'text-white bg-immich-error hover:bg-immich-error/75',
|
[NotificationType.Error]: 'danger',
|
||||||
[NotificationType.Warning]: 'text-white bg-immich-warning hover:bg-immich-warning/75',
|
[NotificationType.Warning]: 'warning',
|
||||||
};
|
};
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
@@ -111,16 +111,16 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
{#if notification.button}
|
{#if notification.button}
|
||||||
<p class="ps-[28px] mt-2.5 text-sm">
|
<p class="ps-[28px] mt-2.5 light text-light">
|
||||||
<button
|
<Button
|
||||||
type="button"
|
size="small"
|
||||||
class="{buttonStyle[notification.type]} rounded px-3 pt-1.5 pb-1 transition-all duration-200"
|
color={colors[notification.type]}
|
||||||
onclick={handleButtonClick}
|
onclick={handleButtonClick}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
tabindex={-1}
|
tabindex={-1}
|
||||||
>
|
>
|
||||||
{notification.button.text}
|
{notification.button.text}
|
||||||
</button>
|
</Button>
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -310,6 +310,7 @@
|
|||||||
|
|
||||||
void onScrub?.(segmentDate!, scrollPercent, monthGroupPercentY);
|
void onScrub?.(segmentDate!, scrollPercent, monthGroupPercentY);
|
||||||
};
|
};
|
||||||
|
/* eslint-disable tscompat/tscompat */
|
||||||
const getTouch = (event: TouchEvent) => {
|
const getTouch = (event: TouchEvent) => {
|
||||||
if (event.touches.length === 1) {
|
if (event.touches.length === 1) {
|
||||||
return event.touches[0];
|
return event.touches[0];
|
||||||
@@ -354,6 +355,7 @@
|
|||||||
isHover = false;
|
isHover = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
/* eslint-enable tscompat/tscompat */
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
document.addEventListener('touchmove', onTouchMove, true);
|
document.addEventListener('touchmove', onTouchMove, true);
|
||||||
return () => {
|
return () => {
|
||||||
@@ -505,10 +507,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<!-- Scroll Position Indicator Line -->
|
<!-- Scroll Position Indicator Line -->
|
||||||
{#if !usingMobileDevice && !isDragging}
|
{#if !usingMobileDevice && !isDragging}
|
||||||
<div
|
<div class="absolute end-0 h-[2px] w-10 bg-primary" style:top="{scrollY + PADDING_TOP - 2}px">
|
||||||
class="absolute end-0 h-[2px] w-10 bg-immich-primary dark:bg-immich-dark-primary"
|
|
||||||
style:top="{scrollY + PADDING_TOP - 2}px"
|
|
||||||
>
|
|
||||||
{#if timelineManager.scrolling && scrollHoverLabel && !isHover}
|
{#if timelineManager.scrolling && scrollHoverLabel && !isHover}
|
||||||
<p
|
<p
|
||||||
transition:fade={{ duration: 200 }}
|
transition:fade={{ duration: 200 }}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user