Compare commits
1 Commits
v1.135.3
...
fix/map-pa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c7a29b2f4 |
@@ -55,7 +55,7 @@
|
||||
"userEnvProbe": "loginInteractiveShell",
|
||||
"remoteEnv": {
|
||||
// The location where your uploaded files are stored
|
||||
"UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./library}",
|
||||
"UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:upload-devcontainer-volume}",
|
||||
// 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}",
|
||||
|
||||
@@ -51,19 +51,14 @@ fix_permissions() {
|
||||
|
||||
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 {} +
|
||||
|
||||
# Change ownership for directories that exist
|
||||
for dir in "${IMMICH_WORKSPACE}/.vscode" \
|
||||
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"; do
|
||||
if [ -d "$dir" ]; then
|
||||
run_cmd sudo chown node -R "$dir"
|
||||
fi
|
||||
done
|
||||
"${IMMICH_WORKSPACE}/web/dist"
|
||||
|
||||
log ""
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ services:
|
||||
- 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:-upload1-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/workspaces/immich/server/upload
|
||||
- ${UPLOAD_LOCATION:-upload2-devcontainer-volume}${UPLOAD_LOCATION:+/photos/upload}:/workspaces/immich/server/upload/upload
|
||||
- ${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:
|
||||
@@ -29,9 +29,8 @@ services:
|
||||
POSTGRES_USER: ${DB_USERNAME-postgres}
|
||||
POSTGRES_DB: ${DB_DATABASE_NAME-immich}
|
||||
POSTGRES_INITDB_ARGS: '--data-checksums'
|
||||
POSTGRES_HOST_AUTH_METHOD: md5
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION:-postgres-devcontainer-volume}${UPLOAD_LOCATION:+/postgres}:/var/lib/postgresql/data
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION-./Library}/postgres:/var/lib/postgresql/data
|
||||
|
||||
redis:
|
||||
env_file: !reset []
|
||||
@@ -43,6 +42,4 @@ volumes:
|
||||
open_api_node_modules:
|
||||
server_node_modules:
|
||||
web_node_modules:
|
||||
upload1-devcontainer-volume:
|
||||
upload2-devcontainer-volume:
|
||||
postgres-devcontainer-volume:
|
||||
upload-devcontainer-volume:
|
||||
|
||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -644,7 +644,7 @@ jobs:
|
||||
contents: read
|
||||
services:
|
||||
postgres:
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1
|
||||
env:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_USER: postgres
|
||||
|
||||
6
cli/package-lock.json
generated
6
cli/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@immich/cli",
|
||||
"version": "2.2.72",
|
||||
"version": "2.2.69",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@immich/cli",
|
||||
"version": "2.2.72",
|
||||
"version": "2.2.69",
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"dependencies": {
|
||||
"chokidar": "^4.0.3",
|
||||
@@ -54,7 +54,7 @@
|
||||
},
|
||||
"../open-api/typescript-sdk": {
|
||||
"name": "@immich/sdk",
|
||||
"version": "1.135.3",
|
||||
"version": "1.135.0",
|
||||
"dev": true,
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@immich/cli",
|
||||
"version": "2.2.72",
|
||||
"version": "2.2.69",
|
||||
"description": "Command Line Interface (CLI) for Immich",
|
||||
"type": "module",
|
||||
"exports": "./dist/index.js",
|
||||
|
||||
@@ -16,7 +16,7 @@ name: immich-dev
|
||||
services:
|
||||
immich-server:
|
||||
container_name: immich_server
|
||||
command: ['/usr/src/app/bin/immich-dev']
|
||||
command: [ '/usr/src/app/bin/immich-dev' ]
|
||||
image: immich-server-dev:latest
|
||||
# extends:
|
||||
# file: hwaccel.transcoding.yml
|
||||
@@ -70,7 +70,7 @@ services:
|
||||
# user: 0:0
|
||||
build:
|
||||
context: ../web
|
||||
command: ['/usr/src/app/bin/immich-web']
|
||||
command: [ '/usr/src/app/bin/immich-web' ]
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
@@ -122,7 +122,7 @@ services:
|
||||
|
||||
database:
|
||||
container_name: immich_postgres
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1-pgvectors0.2.0
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
|
||||
@@ -63,7 +63,7 @@ services:
|
||||
|
||||
database:
|
||||
container_name: immich_postgres
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1-pgvectors0.2.0
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
@@ -91,7 +91,7 @@ services:
|
||||
# add data source for http://immich-prometheus:9090 to get started
|
||||
immich-grafana:
|
||||
container_name: immich_grafana
|
||||
command: ['./run.sh', '-disable-reporting']
|
||||
command: [ './run.sh', '-disable-reporting' ]
|
||||
ports:
|
||||
- 3000:3000
|
||||
image: grafana/grafana:12.0.1-ubuntu@sha256:65575bb9c761335e2ff30e364f21d38632e3b2e75f5f81d83cc92f44b9bbc055
|
||||
|
||||
@@ -56,7 +56,7 @@ services:
|
||||
|
||||
database:
|
||||
container_name: immich_postgres
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
|
||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1-pgvectors0.2.0
|
||||
environment:
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
POSTGRES_USER: ${DB_USERNAME}
|
||||
|
||||
@@ -64,13 +64,7 @@ COMMIT;
|
||||
|
||||
### Updating VectorChord
|
||||
|
||||
When installing a new version of VectorChord, you will need to manually update the extension and reindex by connecting to the Immich database and running:
|
||||
|
||||
```
|
||||
ALTER EXTENSION vchord UPDATE;
|
||||
REINDEX INDEX face_index;
|
||||
REINDEX INDEX clip_index;
|
||||
```
|
||||
When installing a new version of VectorChord, you will need to manually update the extension by connecting to the Immich database and running `ALTER EXTENSION vchord UPDATE;`.
|
||||
|
||||
## Migrating to VectorChord
|
||||
|
||||
@@ -82,8 +76,6 @@ Support for pgvecto.rs will be dropped in a later release, hence we recommend al
|
||||
|
||||
The easiest option is to have both extensions installed during the migration:
|
||||
|
||||
<details>
|
||||
<summary>Migration steps (automatic)</summary>
|
||||
1. Ensure you still have pgvecto.rs installed
|
||||
2. Install `pgvector` (`>= 0.7.0, < 1.0.0`). The easiest way to do this is on Debian/Ubuntu by adding the [PostgreSQL Apt repository][pg-apt] and then running `apt install postgresql-NN-pgvector`, where `NN` is your Postgres version (e.g., `16`)
|
||||
3. [Install VectorChord][vchord-install]
|
||||
@@ -97,12 +89,8 @@ The easiest option is to have both extensions installed during the migration:
|
||||
11. Restart the Postgres database
|
||||
12. Uninstall pgvecto.rs (e.g. `apt-get purge vectors-pg14` on Debian-based environments, replacing `pg14` as appropriate). `pgvector` must remain installed as it provides the data types used by `vchord`
|
||||
|
||||
</details>
|
||||
|
||||
If it is not possible to have both VectorChord and pgvecto.rs installed at the same time, you can perform the migration with more manual steps:
|
||||
|
||||
<details>
|
||||
<summary>Migration steps (manual)</summary>
|
||||
1. While pgvecto.rs is still installed, run the following SQL command using psql or your choice of database client. Take note of the number outputted by this command as you will need it later
|
||||
|
||||
```sql
|
||||
@@ -135,20 +123,14 @@ ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE vector(512);
|
||||
|
||||
5. Start Immich and let it create new indices using VectorChord
|
||||
|
||||
</details>
|
||||
|
||||
### Migrating from pgvector
|
||||
|
||||
<details>
|
||||
<summary>Migration steps</summary>
|
||||
1. Ensure you have at least 0.7.0 of pgvector installed. If it is below that, please upgrade it and run the SQL command `ALTER EXTENSION vector UPDATE;` using psql or your choice of database client
|
||||
2. Follow the Prerequisites to install VectorChord
|
||||
3. If Immich does not have superuser permissions, run the SQL command `CREATE EXTENSION vchord CASCADE;`
|
||||
4. Remove the `DB_VECTOR_EXTENSION=pgvector` environmental variable as it will make Immich still use pgvector if set
|
||||
5. Start Immich and let it create new indices using VectorChord
|
||||
|
||||
</details>
|
||||
|
||||
Note that VectorChord itself uses pgvector types, so you should not uninstall pgvector after following these steps.
|
||||
|
||||
[vchord-install]: https://docs.vectorchord.ai/vectorchord/getting-started/installation.html
|
||||
|
||||
12
docs/static/archived-versions.json
vendored
12
docs/static/archived-versions.json
vendored
@@ -1,16 +1,4 @@
|
||||
[
|
||||
{
|
||||
"label": "v1.135.3",
|
||||
"url": "https://v1.135.3.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.135.2",
|
||||
"url": "https://v1.135.2.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.135.1",
|
||||
"url": "https://v1.135.1.archive.immich.app"
|
||||
},
|
||||
{
|
||||
"label": "v1.135.0",
|
||||
"url": "https://v1.135.0.archive.immich.app"
|
||||
|
||||
8
e2e/package-lock.json
generated
8
e2e/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "immich-e2e",
|
||||
"version": "1.135.3",
|
||||
"version": "1.135.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "immich-e2e",
|
||||
"version": "1.135.3",
|
||||
"version": "1.135.0",
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.1.0",
|
||||
@@ -44,7 +44,7 @@
|
||||
},
|
||||
"../cli": {
|
||||
"name": "@immich/cli",
|
||||
"version": "2.2.72",
|
||||
"version": "2.2.69",
|
||||
"dev": true,
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"dependencies": {
|
||||
@@ -93,7 +93,7 @@
|
||||
},
|
||||
"../open-api/typescript-sdk": {
|
||||
"name": "@immich/sdk",
|
||||
"version": "1.135.3",
|
||||
"version": "1.135.0",
|
||||
"dev": true,
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "immich-e2e",
|
||||
"version": "1.135.3",
|
||||
"version": "1.135.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
|
||||
@@ -14,11 +14,7 @@ describe('/people', () => {
|
||||
let nameAlicePerson: PersonResponseDto;
|
||||
let nameBobPerson: PersonResponseDto;
|
||||
let nameCharliePerson: PersonResponseDto;
|
||||
let nameNullPerson4Assets: PersonResponseDto;
|
||||
let nameNullPerson3Assets: PersonResponseDto;
|
||||
let nameNullPerson1Asset: PersonResponseDto;
|
||||
let nameBillPersonFavourite: PersonResponseDto;
|
||||
let nameFreddyPersonFavourite: PersonResponseDto;
|
||||
let nameNullPerson: PersonResponseDto;
|
||||
|
||||
beforeAll(async () => {
|
||||
await utils.resetDatabase();
|
||||
@@ -31,11 +27,7 @@ describe('/people', () => {
|
||||
nameCharliePerson,
|
||||
nameBobPerson,
|
||||
nameAlicePerson,
|
||||
nameNullPerson4Assets,
|
||||
nameNullPerson3Assets,
|
||||
nameNullPerson1Asset,
|
||||
nameBillPersonFavourite,
|
||||
nameFreddyPersonFavourite,
|
||||
nameNullPerson,
|
||||
] = await Promise.all([
|
||||
utils.createPerson(admin.accessToken, {
|
||||
name: 'visible_person',
|
||||
@@ -60,26 +52,11 @@ describe('/people', () => {
|
||||
utils.createPerson(admin.accessToken, {
|
||||
name: '',
|
||||
}),
|
||||
utils.createPerson(admin.accessToken, {
|
||||
name: '',
|
||||
}),
|
||||
utils.createPerson(admin.accessToken, {
|
||||
name: '',
|
||||
}),
|
||||
utils.createPerson(admin.accessToken, {
|
||||
name: 'Bill',
|
||||
isFavorite: true,
|
||||
}),
|
||||
utils.createPerson(admin.accessToken, {
|
||||
name: 'Freddy',
|
||||
isFavorite: true,
|
||||
}),
|
||||
]);
|
||||
|
||||
const asset1 = await utils.createAsset(admin.accessToken);
|
||||
const asset2 = await utils.createAsset(admin.accessToken);
|
||||
const asset3 = await utils.createAsset(admin.accessToken);
|
||||
const asset4 = await utils.createAsset(admin.accessToken);
|
||||
|
||||
await Promise.all([
|
||||
utils.createFace({ assetId: asset1.id, personId: visiblePerson.id }),
|
||||
@@ -87,27 +64,15 @@ describe('/people', () => {
|
||||
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: asset3.id, personId: multipleAssetsPerson.id }), // 4 assets
|
||||
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 4 assets
|
||||
utils.createFace({ assetId: asset1.id, personId: nameNullPerson4Assets.id }),
|
||||
utils.createFace({ assetId: asset2.id, personId: nameNullPerson4Assets.id }),
|
||||
utils.createFace({ assetId: asset3.id, personId: nameNullPerson4Assets.id }),
|
||||
utils.createFace({ assetId: asset4.id, personId: nameNullPerson4Assets.id }), // 4 assets
|
||||
// Null-named person 3 assets
|
||||
utils.createFace({ assetId: asset1.id, personId: nameNullPerson3Assets.id }),
|
||||
utils.createFace({ assetId: asset2.id, personId: nameNullPerson3Assets.id }),
|
||||
utils.createFace({ assetId: asset3.id, personId: nameNullPerson3Assets.id }), // 3 assets
|
||||
// Null-named person 1 asset
|
||||
utils.createFace({ assetId: asset3.id, personId: nameNullPerson1Asset.id }),
|
||||
// Favourite People
|
||||
utils.createFace({ assetId: asset1.id, personId: nameFreddyPersonFavourite.id }),
|
||||
utils.createFace({ assetId: asset2.id, personId: nameFreddyPersonFavourite.id }),
|
||||
utils.createFace({ assetId: asset1.id, personId: nameBillPersonFavourite.id }),
|
||||
// Null-named person
|
||||
utils.createFace({ assetId: asset1.id, personId: nameNullPerson.id }),
|
||||
utils.createFace({ assetId: asset2.id, personId: nameNullPerson.id }), // 2 assets
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -122,19 +87,15 @@ describe('/people', () => {
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual({
|
||||
hasNextPage: false,
|
||||
total: 11,
|
||||
total: 7,
|
||||
hidden: 1,
|
||||
people: [
|
||||
expect.objectContaining({ name: 'Freddy' }),
|
||||
expect.objectContaining({ name: 'Bill' }),
|
||||
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({ id: nameNullPerson4Assets.id, name: '' }),
|
||||
expect.objectContaining({ id: nameNullPerson3Assets.id, name: '' }),
|
||||
expect.objectContaining({ name: 'hidden_person' }), // Should really be before the null names
|
||||
expect.objectContaining({ name: 'hidden_person' }),
|
||||
],
|
||||
});
|
||||
});
|
||||
@@ -144,21 +105,17 @@ describe('/people', () => {
|
||||
|
||||
expect(status).toBe(200);
|
||||
expect(body.hasNextPage).toBe(false);
|
||||
expect(body.total).toBe(11); // All persons
|
||||
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([
|
||||
nameFreddyPersonFavourite.id, // name: 'Freddy', count: 2
|
||||
nameBillPersonFavourite.id, // name: 'Bill', count: 1
|
||||
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
|
||||
nameNullPerson4Assets.id, // name: '', count: 4
|
||||
nameNullPerson3Assets.id, // name: '', count: 3
|
||||
]);
|
||||
|
||||
expect(people.some((p) => p.id === hiddenPerson.id)).toBe(false);
|
||||
@@ -170,18 +127,14 @@ describe('/people', () => {
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual({
|
||||
hasNextPage: false,
|
||||
total: 11,
|
||||
total: 7,
|
||||
hidden: 1,
|
||||
people: [
|
||||
expect.objectContaining({ name: 'Freddy' }),
|
||||
expect.objectContaining({ name: 'Bill' }),
|
||||
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({ id: nameNullPerson4Assets.id, name: '' }),
|
||||
expect.objectContaining({ id: nameNullPerson3Assets.id, name: '' }),
|
||||
],
|
||||
});
|
||||
});
|
||||
@@ -195,9 +148,9 @@ describe('/people', () => {
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual({
|
||||
hasNextPage: true,
|
||||
total: 11,
|
||||
total: 7,
|
||||
hidden: 1,
|
||||
people: [expect.objectContaining({ name: 'Alice' })],
|
||||
people: [expect.objectContaining({ name: 'visible_person' })],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -244,6 +244,7 @@ async def load(model: InferenceModel) -> InferenceModel:
|
||||
|
||||
async def idle_shutdown_task() -> None:
|
||||
while True:
|
||||
log.debug("Checking for inactivity...")
|
||||
if (
|
||||
last_called is not None
|
||||
and not active_requests
|
||||
|
||||
@@ -35,8 +35,8 @@ platform :android do
|
||||
task: 'bundle',
|
||||
build_type: 'Release',
|
||||
properties: {
|
||||
"android.injected.version.code" => 204,
|
||||
"android.injected.version.name" => "1.135.3",
|
||||
"android.injected.version.code" => 201,
|
||||
"android.injected.version.name" => "1.135.0",
|
||||
}
|
||||
)
|
||||
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
|
||||
|
||||
@@ -346,7 +346,6 @@
|
||||
};
|
||||
F0B57D372DF764BD00DC5BCC = {
|
||||
CreatedOnToolsVersion = 16.4;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
FAC6F88F2D287C890078CB2F = {
|
||||
CreatedOnToolsVersion = 16.0;
|
||||
@@ -649,7 +648,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 210;
|
||||
CURRENT_PROJECT_VERSION = 208;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -793,7 +792,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 210;
|
||||
CURRENT_PROJECT_VERSION = 208;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -823,7 +822,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 210;
|
||||
CURRENT_PROJECT_VERSION = 208;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -857,7 +856,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 210;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
@@ -865,7 +864,7 @@
|
||||
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Widget;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.5;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@@ -900,7 +899,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 210;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
@@ -908,7 +907,7 @@
|
||||
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Widget;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.5;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@@ -940,7 +939,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 210;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
@@ -948,7 +947,7 @@
|
||||
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Widget;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.5;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@@ -979,7 +978,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 210;
|
||||
CURRENT_PROJECT_VERSION = 208;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
@@ -1023,7 +1022,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 210;
|
||||
CURRENT_PROJECT_VERSION = 208;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
@@ -1064,7 +1063,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 210;
|
||||
CURRENT_PROJECT_VERSION = 208;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.135.1</string>
|
||||
<string>1.134.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
@@ -93,7 +93,7 @@
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>210</string>
|
||||
<string>208</string>
|
||||
<key>FLTEnableImpeller</key>
|
||||
<true />
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
@@ -115,8 +115,8 @@
|
||||
</dict>
|
||||
<key>NSBonjourServices</key>
|
||||
<array>
|
||||
<string>_googlecast._tcp</string>
|
||||
<string>_CC1AD845._googlecast._tcp</string>
|
||||
<string>_googlecast._tcp</string>
|
||||
<string>_CC1AD845._googlecast._tcp</string>
|
||||
</array>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>We need to access the camera to let you take beautiful video using this app</string>
|
||||
@@ -168,8 +168,5 @@
|
||||
<true />
|
||||
<key>NSFaceIDUsageDescription</key>
|
||||
<string>We need to use FaceID to allow access to your locked folder</string>
|
||||
<key>NSLocalNetworkUsageDescription</key>
|
||||
<string>We need local network permission to connect to the local server using IP address and
|
||||
allow the casting feature to work</string>
|
||||
</dict>
|
||||
</plist>
|
||||
</plist>
|
||||
|
||||
@@ -43,7 +43,7 @@ struct RandomConfigurationAppIntent: WidgetConfigurationIntent {
|
||||
"Choose an album to show images from"
|
||||
}
|
||||
|
||||
@Parameter(title: "Album")
|
||||
@Parameter(title: "Album", default: NO_ALBUM)
|
||||
var album: Album?
|
||||
|
||||
@Parameter(title: "Show Album Name", default: false)
|
||||
|
||||
@@ -22,7 +22,7 @@ platform :ios do
|
||||
path: "./Runner.xcodeproj",
|
||||
)
|
||||
increment_version_number(
|
||||
version_number: "1.135.3"
|
||||
version_number: "1.135.0"
|
||||
)
|
||||
increment_build_number(
|
||||
build_number: latest_testflight_build_number + 1,
|
||||
|
||||
@@ -10,5 +10,3 @@ enum TextSearchType {
|
||||
}
|
||||
|
||||
enum AssetVisibilityEnum { timeline, hidden, archive, locked }
|
||||
|
||||
enum SortUserBy { id }
|
||||
|
||||
18
mobile/lib/domain/interfaces/sync_stream.interface.dart
Normal file
18
mobile/lib/domain/interfaces/sync_stream.interface.dart
Normal file
@@ -0,0 +1,18 @@
|
||||
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
abstract interface class ISyncStreamRepository implements IDatabaseRepository {
|
||||
Future<void> updateUsersV1(Iterable<SyncUserV1> data);
|
||||
Future<void> deleteUsersV1(Iterable<SyncUserDeleteV1> data);
|
||||
|
||||
Future<void> updatePartnerV1(Iterable<SyncPartnerV1> data);
|
||||
Future<void> deletePartnerV1(Iterable<SyncPartnerDeleteV1> data);
|
||||
|
||||
Future<void> updateAssetsV1(Iterable<SyncAssetV1> data);
|
||||
Future<void> deleteAssetsV1(Iterable<SyncAssetDeleteV1> data);
|
||||
Future<void> updateAssetsExifV1(Iterable<SyncAssetExifV1> data);
|
||||
|
||||
Future<void> updatePartnerAssetsV1(Iterable<SyncAssetV1> data);
|
||||
Future<void> deletePartnerAssetsV1(Iterable<SyncAssetDeleteV1> data);
|
||||
Future<void> updatePartnerAssetsExifV1(Iterable<SyncAssetExifV1> data);
|
||||
}
|
||||
22
mobile/lib/domain/interfaces/user.interface.dart
Normal file
22
mobile/lib/domain/interfaces/user.interface.dart
Normal file
@@ -0,0 +1,22 @@
|
||||
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
|
||||
abstract interface class IUserRepository implements IDatabaseRepository {
|
||||
Future<bool> insert(UserDto user);
|
||||
|
||||
Future<UserDto?> getByUserId(String id);
|
||||
|
||||
Future<List<UserDto?>> getByUserIds(List<String> ids);
|
||||
|
||||
Future<List<UserDto>> getAll({SortUserBy? sortBy});
|
||||
|
||||
Future<bool> updateAll(List<UserDto> users);
|
||||
|
||||
Future<UserDto> update(UserDto user);
|
||||
|
||||
Future<void> delete(List<String> ids);
|
||||
|
||||
Future<void> deleteAll();
|
||||
}
|
||||
|
||||
enum SortUserBy { id }
|
||||
15
mobile/lib/domain/interfaces/user_api.interface.dart
Normal file
15
mobile/lib/domain/interfaces/user_api.interface.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
|
||||
abstract interface class IUserApiRepository {
|
||||
Future<UserDto?> getMyUser();
|
||||
|
||||
Future<List<UserDto>> getAll();
|
||||
|
||||
/// Saves the [data] in the server and uses it as the current users profile image
|
||||
Future<String> createProfileImage({
|
||||
required String name,
|
||||
required Uint8List data,
|
||||
});
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:immich_mobile/domain/interfaces/sync_api.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/sync_stream.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/sync_event.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
@@ -10,12 +10,12 @@ class SyncStreamService {
|
||||
final Logger _logger = Logger('SyncStreamService');
|
||||
|
||||
final ISyncApiRepository _syncApiRepository;
|
||||
final SyncStreamRepository _syncStreamRepository;
|
||||
final ISyncStreamRepository _syncStreamRepository;
|
||||
final bool Function()? _cancelChecker;
|
||||
|
||||
SyncStreamService({
|
||||
required ISyncApiRepository syncApiRepository,
|
||||
required SyncStreamRepository syncStreamRepository,
|
||||
required ISyncStreamRepository syncStreamRepository,
|
||||
bool Function()? cancelChecker,
|
||||
}) : _syncApiRepository = syncApiRepository,
|
||||
_syncStreamRepository = syncStreamRepository,
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user_api.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/user_api.repository.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
class UserService {
|
||||
final Logger _log = Logger("UserService");
|
||||
final IsarUserRepository _isarUserRepository;
|
||||
final UserApiRepository _userApiRepository;
|
||||
final IUserRepository _userRepository;
|
||||
final IUserApiRepository _userApiRepository;
|
||||
final StoreService _storeService;
|
||||
|
||||
UserService({
|
||||
required IsarUserRepository isarUserRepository,
|
||||
required UserApiRepository userApiRepository,
|
||||
required IUserRepository userRepository,
|
||||
required IUserApiRepository userApiRepository,
|
||||
required StoreService storeService,
|
||||
}) : _isarUserRepository = isarUserRepository,
|
||||
}) : _userRepository = userRepository,
|
||||
_userApiRepository = userApiRepository,
|
||||
_storeService = storeService;
|
||||
|
||||
@@ -38,7 +38,7 @@ class UserService {
|
||||
final user = await _userApiRepository.getMyUser();
|
||||
if (user == null) return null;
|
||||
await _storeService.put(StoreKey.currentUser, user);
|
||||
await _isarUserRepository.update(user);
|
||||
await _userRepository.update(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ class UserService {
|
||||
);
|
||||
final updatedUser = getMyUser().copyWith(profileImagePath: path);
|
||||
await _storeService.put(StoreKey.currentUser, updatedUser);
|
||||
await _isarUserRepository.update(updatedUser);
|
||||
await _userRepository.update(updatedUser);
|
||||
return path;
|
||||
} catch (e) {
|
||||
_log.warning("Failed to upload profile image", e);
|
||||
@@ -59,10 +59,10 @@ class UserService {
|
||||
}
|
||||
|
||||
Future<List<UserDto>> getAll() async {
|
||||
return await _isarUserRepository.getAll();
|
||||
return await _userRepository.getAll();
|
||||
}
|
||||
|
||||
Future<void> deleteAll() {
|
||||
return _isarUserRepository.deleteAll();
|
||||
return _userRepository.deleteAll();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:drift/drift.dart';
|
||||
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/infrastructure/entities/exif.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart';
|
||||
@@ -9,12 +10,14 @@ import 'package:logging/logging.dart';
|
||||
import 'package:openapi/api.dart' as api show AssetVisibility;
|
||||
import 'package:openapi/api.dart' hide AssetVisibility;
|
||||
|
||||
class SyncStreamRepository extends DriftDatabaseRepository {
|
||||
class DriftSyncStreamRepository extends DriftDatabaseRepository
|
||||
implements ISyncStreamRepository {
|
||||
final Logger _logger = Logger('DriftSyncStreamRepository');
|
||||
final Drift _db;
|
||||
|
||||
SyncStreamRepository(super.db) : _db = db;
|
||||
DriftSyncStreamRepository(super.db) : _db = db;
|
||||
|
||||
@override
|
||||
Future<void> deleteUsersV1(Iterable<SyncUserDeleteV1> data) async {
|
||||
try {
|
||||
await _db.batch((batch) {
|
||||
@@ -31,6 +34,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateUsersV1(Iterable<SyncUserV1> data) async {
|
||||
try {
|
||||
await _db.batch((batch) {
|
||||
@@ -53,6 +57,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deletePartnerV1(Iterable<SyncPartnerDeleteV1> data) async {
|
||||
try {
|
||||
await _db.batch((batch) {
|
||||
@@ -72,6 +77,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updatePartnerV1(Iterable<SyncPartnerV1> data) async {
|
||||
try {
|
||||
await _db.batch((batch) {
|
||||
@@ -95,6 +101,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteAssetsV1(Iterable<SyncAssetDeleteV1> data) async {
|
||||
try {
|
||||
await _deleteAssetsV1(data);
|
||||
@@ -104,6 +111,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateAssetsV1(Iterable<SyncAssetV1> data) async {
|
||||
try {
|
||||
await _updateAssetsV1(data);
|
||||
@@ -113,6 +121,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deletePartnerAssetsV1(Iterable<SyncAssetDeleteV1> data) async {
|
||||
try {
|
||||
await _deleteAssetsV1(data);
|
||||
@@ -122,6 +131,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updatePartnerAssetsV1(Iterable<SyncAssetV1> data) async {
|
||||
try {
|
||||
await _updateAssetsV1(data);
|
||||
@@ -131,6 +141,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateAssetsExifV1(Iterable<SyncAssetExifV1> data) async {
|
||||
try {
|
||||
await _updateAssetExifV1(data);
|
||||
@@ -140,6 +151,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updatePartnerAssetsExifV1(Iterable<SyncAssetExifV1> data) async {
|
||||
try {
|
||||
await _updateAssetExifV1(data);
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
import 'package:immich_mobile/constants/enums.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
|
||||
as entity;
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
class IsarUserRepository extends IsarDatabaseRepository {
|
||||
class IsarUserRepository extends IsarDatabaseRepository
|
||||
implements IUserRepository {
|
||||
final Isar _db;
|
||||
const IsarUserRepository(super.db) : _db = db;
|
||||
|
||||
@override
|
||||
Future<void> delete(List<String> ids) async {
|
||||
await transaction(() async {
|
||||
await _db.users.deleteAllById(ids);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteAll() async {
|
||||
await transaction(() async {
|
||||
await _db.users.clear();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<UserDto>> getAll({SortUserBy? sortBy}) async {
|
||||
return (await _db.users
|
||||
.where()
|
||||
@@ -35,14 +39,17 @@ class IsarUserRepository extends IsarDatabaseRepository {
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<UserDto?> getByUserId(String id) async {
|
||||
return (await _db.users.getById(id))?.toDto();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<UserDto?>> getByUserIds(List<String> ids) async {
|
||||
return (await _db.users.getAllById(ids)).map((u) => u?.toDto()).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> insert(UserDto user) async {
|
||||
await transaction(() async {
|
||||
await _db.users.put(entity.User.fromDto(user));
|
||||
@@ -50,6 +57,7 @@ class IsarUserRepository extends IsarDatabaseRepository {
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<UserDto> update(UserDto user) async {
|
||||
await transaction(() async {
|
||||
await _db.users.put(entity.User.fromDto(user));
|
||||
@@ -57,6 +65,7 @@ class IsarUserRepository extends IsarDatabaseRepository {
|
||||
return user;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> updateAll(List<UserDto> users) async {
|
||||
await transaction(() async {
|
||||
await _db.users.putAll(users.map(entity.User.fromDto).toList());
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:http/http.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user_api.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/api.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/user.converter.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class UserApiRepository extends ApiRepository {
|
||||
class UserApiRepository extends ApiRepository implements IUserApiRepository {
|
||||
final UsersApi _api;
|
||||
const UserApiRepository(this._api);
|
||||
|
||||
@override
|
||||
Future<UserDto?> getMyUser() async {
|
||||
final (adminDto, preferenceDto) =
|
||||
await (_api.getMyUser(), _api.getMyPreferences()).wait;
|
||||
@@ -18,6 +20,7 @@ class UserApiRepository extends ApiRepository {
|
||||
return UserConverter.fromAdminDto(adminDto, preferenceDto);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> createProfileImage({
|
||||
required String name,
|
||||
required Uint8List data,
|
||||
@@ -30,6 +33,7 @@ class UserApiRepository extends ApiRepository {
|
||||
return res.profileImagePath;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<UserDto>> getAll() async {
|
||||
final dto = await checkNull(_api.searchUsers());
|
||||
return dto.map(UserConverter.fromSimpleUserDto).toList();
|
||||
|
||||
16
mobile/lib/interfaces/activity_api.interface.dart
Normal file
16
mobile/lib/interfaces/activity_api.interface.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
import 'package:immich_mobile/models/activities/activity.model.dart';
|
||||
|
||||
abstract interface class IActivityApiRepository {
|
||||
Future<List<Activity>> getAll(
|
||||
String albumId, {
|
||||
String? assetId,
|
||||
});
|
||||
Future<Activity> create(
|
||||
String albumId,
|
||||
ActivityType type, {
|
||||
String? assetId,
|
||||
String? comment,
|
||||
});
|
||||
Future<void> delete(String id);
|
||||
Future<ActivityStats> getStats(String albumId, {String? assetId});
|
||||
}
|
||||
42
mobile/lib/interfaces/album_api.interface.dart
Normal file
42
mobile/lib/interfaces/album_api.interface.dart
Normal file
@@ -0,0 +1,42 @@
|
||||
import 'package:immich_mobile/constants/enums.dart';
|
||||
import 'package:immich_mobile/entities/album.entity.dart';
|
||||
|
||||
abstract interface class IAlbumApiRepository {
|
||||
Future<Album> get(String id);
|
||||
|
||||
Future<List<Album>> getAll({bool? shared});
|
||||
|
||||
Future<Album> create(
|
||||
String name, {
|
||||
required Iterable<String> assetIds,
|
||||
Iterable<String> sharedUserIds = const [],
|
||||
});
|
||||
|
||||
Future<Album> update(
|
||||
String albumId, {
|
||||
String? name,
|
||||
String? thumbnailAssetId,
|
||||
String? description,
|
||||
bool? activityEnabled,
|
||||
SortOrder? sortOrder,
|
||||
});
|
||||
|
||||
Future<void> delete(String albumId);
|
||||
|
||||
Future<({List<String> added, List<String> duplicates})> addAssets(
|
||||
String albumId,
|
||||
Iterable<String> assetIds,
|
||||
);
|
||||
|
||||
Future<({List<String> removed, List<String> failed})> removeAssets(
|
||||
String albumId,
|
||||
Iterable<String> assetIds,
|
||||
);
|
||||
|
||||
Future<Album> addUsers(
|
||||
String albumId,
|
||||
Iterable<String> userIds,
|
||||
);
|
||||
|
||||
Future<void> removeUser(String albumId, {required String userId});
|
||||
}
|
||||
21
mobile/lib/interfaces/album_media.interface.dart
Normal file
21
mobile/lib/interfaces/album_media.interface.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
import 'package:immich_mobile/entities/album.entity.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
|
||||
abstract interface class IAlbumMediaRepository {
|
||||
Future<List<Album>> getAll();
|
||||
|
||||
Future<List<String>> getAssetIds(String albumId);
|
||||
|
||||
Future<int> getAssetCount(String albumId);
|
||||
|
||||
Future<List<Asset>> getAssets(
|
||||
String albumId, {
|
||||
int start = 0,
|
||||
int end = 0x7fffffffffffffff,
|
||||
DateTime? modifiedFrom,
|
||||
DateTime? modifiedUntil,
|
||||
bool orderByModificationDate = false,
|
||||
});
|
||||
|
||||
Future<Album> get(String id);
|
||||
}
|
||||
6
mobile/lib/interfaces/biometric.interface.dart
Normal file
6
mobile/lib/interfaces/biometric.interface.dart
Normal file
@@ -0,0 +1,6 @@
|
||||
import 'package:immich_mobile/models/auth/biometric_status.model.dart';
|
||||
|
||||
abstract interface class IBiometricRepository {
|
||||
Future<BiometricStatus> getStatus();
|
||||
Future<bool> authenticate(String? message);
|
||||
}
|
||||
15
mobile/lib/interfaces/download.interface.dart
Normal file
15
mobile/lib/interfaces/download.interface.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
import 'package:background_downloader/background_downloader.dart';
|
||||
|
||||
abstract interface class IDownloadRepository {
|
||||
void Function(TaskStatusUpdate)? onImageDownloadStatus;
|
||||
void Function(TaskStatusUpdate)? onVideoDownloadStatus;
|
||||
void Function(TaskStatusUpdate)? onLivePhotoDownloadStatus;
|
||||
void Function(TaskProgressUpdate)? onTaskProgress;
|
||||
|
||||
Future<List<TaskRecord>> getLiveVideoTasks();
|
||||
Future<List<bool>> downloadAll(List<DownloadTask> tasks);
|
||||
|
||||
Future<bool> cancel(String id);
|
||||
Future<void> deleteAllTrackingRecords();
|
||||
Future<void> deleteRecordsWithIds(List<String> id);
|
||||
}
|
||||
4
mobile/lib/interfaces/network.interface.dart
Normal file
4
mobile/lib/interfaces/network.interface.dart
Normal file
@@ -0,0 +1,4 @@
|
||||
abstract interface class INetworkRepository {
|
||||
Future<String?> getWifiName();
|
||||
Future<String?> getWifiIp();
|
||||
}
|
||||
8
mobile/lib/interfaces/partner.interface.dart
Normal file
8
mobile/lib/interfaces/partner.interface.dart
Normal file
@@ -0,0 +1,8 @@
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
|
||||
abstract class IPartnerRepository {
|
||||
Future<List<UserDto>> getSharedWith();
|
||||
Future<List<UserDto>> getSharedBy();
|
||||
Stream<List<UserDto>> watchSharedWith();
|
||||
Stream<List<UserDto>> watchSharedBy();
|
||||
}
|
||||
13
mobile/lib/interfaces/partner_api.interface.dart
Normal file
13
mobile/lib/interfaces/partner_api.interface.dart
Normal file
@@ -0,0 +1,13 @@
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
|
||||
abstract interface class IPartnerApiRepository {
|
||||
Future<List<UserDto>> getAll(Direction direction);
|
||||
Future<UserDto> create(String id);
|
||||
Future<UserDto> update(String id, {required bool inTimeline});
|
||||
Future<void> delete(String id);
|
||||
}
|
||||
|
||||
enum Direction {
|
||||
sharedWithMe,
|
||||
sharedByMe,
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
import 'dart:convert';
|
||||
|
||||
abstract interface class IPersonApiRepository {
|
||||
Future<List<Person>> getAll();
|
||||
Future<Person> update(String id, {String? name});
|
||||
}
|
||||
|
||||
class Person {
|
||||
Person({
|
||||
required this.id,
|
||||
5
mobile/lib/interfaces/secure_storage.interface.dart
Normal file
5
mobile/lib/interfaces/secure_storage.interface.dart
Normal file
@@ -0,0 +1,5 @@
|
||||
abstract interface class ISecureStorageRepository {
|
||||
Future<String?> read(String key);
|
||||
Future<void> write(String key, String value);
|
||||
Future<void> delete(String key);
|
||||
}
|
||||
9
mobile/lib/interfaces/sessions_api.interface.dart
Normal file
9
mobile/lib/interfaces/sessions_api.interface.dart
Normal file
@@ -0,0 +1,9 @@
|
||||
import 'package:immich_mobile/models/sessions/session_create_response.model.dart';
|
||||
|
||||
abstract interface class ISessionAPIRepository {
|
||||
Future<SessionCreateResponse> createSession(
|
||||
String deviceName,
|
||||
String deviceOS, {
|
||||
int? duration,
|
||||
});
|
||||
}
|
||||
7
mobile/lib/interfaces/share_handler.interface.dart
Normal file
7
mobile/lib/interfaces/share_handler.interface.dart
Normal file
@@ -0,0 +1,7 @@
|
||||
import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart';
|
||||
|
||||
abstract interface class IShareHandlerRepository {
|
||||
void Function(List<ShareIntentAttachment>)? onSharedMedia;
|
||||
|
||||
Future<void> init();
|
||||
}
|
||||
39
mobile/lib/interfaces/timeline.interface.dart
Normal file
39
mobile/lib/interfaces/timeline.interface.dart
Normal file
@@ -0,0 +1,39 @@
|
||||
import 'package:immich_mobile/entities/album.entity.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
||||
|
||||
abstract class ITimelineRepository {
|
||||
Future<List<String>> getTimelineUserIds(String id);
|
||||
|
||||
Stream<List<String>> watchTimelineUsers(String id);
|
||||
|
||||
Stream<RenderList> watchArchiveTimeline(String userId);
|
||||
Stream<RenderList> watchFavoriteTimeline(String userId);
|
||||
Stream<RenderList> watchTrashTimeline(String userId);
|
||||
Stream<RenderList> watchAlbumTimeline(
|
||||
Album album,
|
||||
GroupAssetsBy groupAssetsBy,
|
||||
);
|
||||
Stream<RenderList> watchAllVideosTimeline(String userId);
|
||||
|
||||
Stream<RenderList> watchHomeTimeline(
|
||||
String userId,
|
||||
GroupAssetsBy groupAssetsBy,
|
||||
);
|
||||
Stream<RenderList> watchMultiUsersTimeline(
|
||||
List<String> userIds,
|
||||
GroupAssetsBy groupAssetsBy,
|
||||
);
|
||||
|
||||
Future<RenderList> getTimelineFromAssets(
|
||||
List<Asset> assets,
|
||||
GroupAssetsBy getGroupByOption,
|
||||
);
|
||||
|
||||
Stream<RenderList> watchAssetSelectionTimeline(String userId);
|
||||
|
||||
Stream<RenderList> watchLockedTimeline(
|
||||
String userId,
|
||||
GroupAssetsBy groupAssetsBy,
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:immich_mobile/domain/models/person.model.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/person_api.interface.dart';
|
||||
|
||||
class SearchLocationFilter {
|
||||
String? country;
|
||||
|
||||
@@ -6,9 +6,9 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/enums.dart';
|
||||
import 'package:immich_mobile/domain/models/person.model.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/interfaces/person_api.interface.dart';
|
||||
import 'package:immich_mobile/models/search/search_filter.model.dart';
|
||||
import 'package:immich_mobile/providers/search/paginated_search.provider.dart';
|
||||
import 'package:immich_mobile/providers/search/search_input_focus.provider.dart';
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/entities/album.entity.dart';
|
||||
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/album_media.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/file_media.interface.dart';
|
||||
import 'package:immich_mobile/models/auth/auth_state.model.dart';
|
||||
@@ -107,7 +108,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||
final AuthState _authState;
|
||||
final BackgroundService _backgroundService;
|
||||
final GalleryPermissionNotifier _galleryPermissionNotifier;
|
||||
final AlbumMediaRepository _albumMediaRepository;
|
||||
final IAlbumMediaRepository _albumMediaRepository;
|
||||
final IFileMediaRepository _fileMediaRepository;
|
||||
final BackupAlbumService _backupAlbumService;
|
||||
final Ref ref;
|
||||
|
||||
@@ -26,7 +26,7 @@ final syncApiRepositoryProvider = Provider(
|
||||
);
|
||||
|
||||
final syncStreamRepositoryProvider = Provider(
|
||||
(ref) => SyncStreamRepository(ref.watch(driftProvider)),
|
||||
(ref) => DriftSyncStreamRepository(ref.watch(driftProvider)),
|
||||
);
|
||||
|
||||
final localSyncServiceProvider = Provider(
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user_api.interface.dart';
|
||||
import 'package:immich_mobile/domain/services/user.service.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/user_api.repository.dart';
|
||||
@@ -10,16 +12,16 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'user.provider.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
IsarUserRepository userRepository(Ref ref) =>
|
||||
IUserRepository userRepository(Ref ref) =>
|
||||
IsarUserRepository(ref.watch(isarProvider));
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
UserApiRepository userApiRepository(Ref ref) =>
|
||||
IUserApiRepository userApiRepository(Ref ref) =>
|
||||
UserApiRepository(ref.watch(apiServiceProvider).usersApi);
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
UserService userService(Ref ref) => UserService(
|
||||
isarUserRepository: ref.watch(userRepositoryProvider),
|
||||
userRepository: ref.watch(userRepositoryProvider),
|
||||
userApiRepository: ref.watch(userApiRepositoryProvider),
|
||||
storeService: ref.watch(storeServiceProvider),
|
||||
);
|
||||
|
||||
@@ -6,11 +6,11 @@ part of 'user.provider.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$userRepositoryHash() => r'538791a4ad126ed086c9db682c67fc5c654d54f3';
|
||||
String _$userRepositoryHash() => r'1a2ac726bcc44397dcaecf449084fefd336696d4';
|
||||
|
||||
/// See also [userRepository].
|
||||
@ProviderFor(userRepository)
|
||||
final userRepositoryProvider = Provider<IsarUserRepository>.internal(
|
||||
final userRepositoryProvider = Provider<IUserRepository>.internal(
|
||||
userRepository,
|
||||
name: r'userRepositoryProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
@@ -22,12 +22,12 @@ final userRepositoryProvider = Provider<IsarUserRepository>.internal(
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef UserRepositoryRef = ProviderRef<IsarUserRepository>;
|
||||
String _$userApiRepositoryHash() => r'8a7340ca4544c8c6b20225c65bff2abb9e96baa2';
|
||||
typedef UserRepositoryRef = ProviderRef<IUserRepository>;
|
||||
String _$userApiRepositoryHash() => r'6b19f2c99fb83162a5ceb91adb8589eaae01bc92';
|
||||
|
||||
/// See also [userApiRepository].
|
||||
@ProviderFor(userApiRepository)
|
||||
final userApiRepositoryProvider = Provider<UserApiRepository>.internal(
|
||||
final userApiRepositoryProvider = Provider<IUserApiRepository>.internal(
|
||||
userApiRepository,
|
||||
name: r'userApiRepositoryProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
@@ -39,8 +39,8 @@ final userApiRepositoryProvider = Provider<UserApiRepository>.internal(
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef UserApiRepositoryRef = ProviderRef<UserApiRepository>;
|
||||
String _$userServiceHash() => r'181414dddc7891be6237e13d568c287a804228d1';
|
||||
typedef UserApiRepositoryRef = ProviderRef<IUserApiRepository>;
|
||||
String _$userServiceHash() => r'4a0873357b7115b4d6bfa8e89b847c0b74ce0d93';
|
||||
|
||||
/// See also [userService].
|
||||
@ProviderFor(userService)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/person.model.dart';
|
||||
import 'package:immich_mobile/interfaces/person_api.interface.dart';
|
||||
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
||||
import 'package:immich_mobile/services/person.service.dart';
|
||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/user.converter.dart';
|
||||
import 'package:immich_mobile/interfaces/activity_api.interface.dart';
|
||||
import 'package:immich_mobile/models/activities/activity.model.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/repositories/api.repository.dart';
|
||||
@@ -9,17 +10,20 @@ final activityApiRepositoryProvider = Provider(
|
||||
(ref) => ActivityApiRepository(ref.watch(apiServiceProvider).activitiesApi),
|
||||
);
|
||||
|
||||
class ActivityApiRepository extends ApiRepository {
|
||||
class ActivityApiRepository extends ApiRepository
|
||||
implements IActivityApiRepository {
|
||||
final ActivitiesApi _api;
|
||||
|
||||
ActivityApiRepository(this._api);
|
||||
|
||||
@override
|
||||
Future<List<Activity>> getAll(String albumId, {String? assetId}) async {
|
||||
final response =
|
||||
await checkNull(_api.getActivities(albumId, assetId: assetId));
|
||||
return response.map(_toActivity).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Activity> create(
|
||||
String albumId,
|
||||
ActivityType type, {
|
||||
@@ -38,10 +42,12 @@ class ActivityApiRepository extends ApiRepository {
|
||||
return _toActivity(response);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> delete(String id) {
|
||||
return checkNull(_api.deleteActivity(id));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ActivityStats> getStats(String albumId, {String? assetId}) async {
|
||||
final response =
|
||||
await checkNull(_api.getActivityStatistics(albumId, assetId: assetId));
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
|
||||
as entity;
|
||||
import 'package:immich_mobile/infrastructure/utils/user.converter.dart';
|
||||
import 'package:immich_mobile/interfaces/album_api.interface.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/repositories/api.repository.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
@@ -13,21 +14,24 @@ final albumApiRepositoryProvider = Provider(
|
||||
(ref) => AlbumApiRepository(ref.watch(apiServiceProvider).albumsApi),
|
||||
);
|
||||
|
||||
class AlbumApiRepository extends ApiRepository {
|
||||
class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository {
|
||||
final AlbumsApi _api;
|
||||
|
||||
AlbumApiRepository(this._api);
|
||||
|
||||
@override
|
||||
Future<Album> get(String id) async {
|
||||
final dto = await checkNull(_api.getAlbumInfo(id));
|
||||
return _toAlbum(dto);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Album>> getAll({bool? shared}) async {
|
||||
final dtos = await checkNull(_api.getAllAlbums(shared: shared));
|
||||
return dtos.map(_toAlbum).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Album> create(
|
||||
String name, {
|
||||
required Iterable<String> assetIds,
|
||||
@@ -50,6 +54,7 @@ class AlbumApiRepository extends ApiRepository {
|
||||
return _toAlbum(responseDto);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Album> update(
|
||||
String albumId, {
|
||||
String? name,
|
||||
@@ -79,10 +84,12 @@ class AlbumApiRepository extends ApiRepository {
|
||||
return _toAlbum(response);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> delete(String albumId) {
|
||||
return _api.deleteAlbum(albumId);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<({List<String> added, List<String> duplicates})> addAssets(
|
||||
String albumId,
|
||||
Iterable<String> assetIds,
|
||||
@@ -107,6 +114,7 @@ class AlbumApiRepository extends ApiRepository {
|
||||
return (added: added, duplicates: duplicates);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<({List<String> removed, List<String> failed})> removeAssets(
|
||||
String albumId,
|
||||
Iterable<String> assetIds,
|
||||
@@ -128,6 +136,7 @@ class AlbumApiRepository extends ApiRepository {
|
||||
return (removed: removed, failed: failed);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Album> addUsers(String albumId, Iterable<String> userIds) async {
|
||||
final albumUsers =
|
||||
userIds.map((userId) => AlbumUserAddDto(userId: userId)).toList();
|
||||
@@ -140,6 +149,7 @@ class AlbumApiRepository extends ApiRepository {
|
||||
return _toAlbum(response);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> removeUser(String albumId, {required String userId}) {
|
||||
return _api.removeUserFromAlbum(albumId, userId);
|
||||
}
|
||||
|
||||
@@ -4,13 +4,14 @@ import 'package:immich_mobile/entities/album.entity.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/album_media.interface.dart';
|
||||
import 'package:immich_mobile/repositories/asset_media.repository.dart';
|
||||
import 'package:photo_manager/photo_manager.dart' hide AssetType;
|
||||
|
||||
final albumMediaRepositoryProvider =
|
||||
Provider((ref) => const AlbumMediaRepository());
|
||||
|
||||
class AlbumMediaRepository {
|
||||
class AlbumMediaRepository implements IAlbumMediaRepository {
|
||||
const AlbumMediaRepository();
|
||||
|
||||
bool get useCustomFilter =>
|
||||
@@ -40,6 +41,7 @@ class AlbumMediaRepository {
|
||||
)
|
||||
: null;
|
||||
|
||||
@override
|
||||
Future<List<Album>> getAll() async {
|
||||
final filter = useCustomFilter
|
||||
? CustomFilter.sql(where: '${CustomColumns.base.width} > 0')
|
||||
@@ -50,6 +52,7 @@ class AlbumMediaRepository {
|
||||
return assetPathEntities.map(_toAlbum).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<String>> getAssetIds(String albumId) async {
|
||||
final album =
|
||||
await AssetPathEntity.fromId(albumId, filterOption: _getAlbumFilter());
|
||||
@@ -58,12 +61,14 @@ class AlbumMediaRepository {
|
||||
return assets.map((e) => e.id).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> getAssetCount(String albumId) async {
|
||||
final album =
|
||||
await AssetPathEntity.fromId(albumId, filterOption: _getAlbumFilter());
|
||||
return album.assetCountAsync;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Asset>> getAssets(
|
||||
String albumId, {
|
||||
int start = 0,
|
||||
@@ -92,7 +97,12 @@ class AlbumMediaRepository {
|
||||
return assets.map(AssetMediaRepository.toAsset).toList().cast();
|
||||
}
|
||||
|
||||
Future<Album> get(String id) async {
|
||||
@override
|
||||
Future<Album> get(
|
||||
String id, {
|
||||
DateTime? modifiedFrom,
|
||||
DateTime? modifiedUntil,
|
||||
}) async {
|
||||
final assetPathEntity = await AssetPathEntity.fromId(
|
||||
id,
|
||||
filterOption: _getAlbumFilter(containsPathModified: true),
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/interfaces/biometric.interface.dart';
|
||||
import 'package:immich_mobile/models/auth/biometric_status.model.dart';
|
||||
import 'package:local_auth/local_auth.dart';
|
||||
|
||||
final biometricRepositoryProvider =
|
||||
Provider((ref) => BiometricRepository(LocalAuthentication()));
|
||||
|
||||
class BiometricRepository {
|
||||
class BiometricRepository implements IBiometricRepository {
|
||||
final LocalAuthentication _localAuth;
|
||||
|
||||
BiometricRepository(this._localAuth);
|
||||
|
||||
@override
|
||||
Future<BiometricStatus> getStatus() async {
|
||||
final bool canAuthenticateWithBiometrics =
|
||||
await _localAuth.canCheckBiometrics;
|
||||
@@ -24,6 +26,7 @@ class BiometricRepository {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> authenticate(String? message) async {
|
||||
return _localAuth.authenticate(
|
||||
localizedReason: message ?? 'please_auth_to_access'.tr(),
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
import 'package:background_downloader/background_downloader.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/interfaces/download.interface.dart';
|
||||
import 'package:immich_mobile/utils/download.dart';
|
||||
|
||||
final downloadRepositoryProvider = Provider((ref) => DownloadRepository());
|
||||
|
||||
class DownloadRepository {
|
||||
class DownloadRepository implements IDownloadRepository {
|
||||
@override
|
||||
void Function(TaskStatusUpdate)? onImageDownloadStatus;
|
||||
|
||||
@override
|
||||
void Function(TaskStatusUpdate)? onVideoDownloadStatus;
|
||||
|
||||
@override
|
||||
void Function(TaskStatusUpdate)? onLivePhotoDownloadStatus;
|
||||
|
||||
@override
|
||||
void Function(TaskProgressUpdate)? onTaskProgress;
|
||||
|
||||
DownloadRepository() {
|
||||
@@ -33,18 +38,22 @@ class DownloadRepository {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<bool>> downloadAll(List<DownloadTask> tasks) {
|
||||
return FileDownloader().enqueueAll(tasks);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteAllTrackingRecords() {
|
||||
return FileDownloader().database.deleteAllRecords();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> cancel(String id) {
|
||||
return FileDownloader().cancelTaskWithId(id);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<TaskRecord>> getLiveVideoTasks() {
|
||||
return FileDownloader().database.allRecordsWithStatus(
|
||||
TaskStatus.complete,
|
||||
@@ -52,6 +61,7 @@ class DownloadRepository {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteRecordsWithIds(List<String> ids) {
|
||||
return FileDownloader().database.deleteRecordsWithIds(ids);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/interfaces/network.interface.dart';
|
||||
import 'package:network_info_plus/network_info_plus.dart';
|
||||
|
||||
final networkRepositoryProvider = Provider((_) {
|
||||
@@ -9,11 +10,12 @@ final networkRepositoryProvider = Provider((_) {
|
||||
return NetworkRepository(networkInfo);
|
||||
});
|
||||
|
||||
class NetworkRepository {
|
||||
class NetworkRepository implements INetworkRepository {
|
||||
final NetworkInfo _networkInfo;
|
||||
|
||||
NetworkRepository(this._networkInfo);
|
||||
|
||||
@override
|
||||
Future<String?> getWifiName() {
|
||||
if (Platform.isAndroid) {
|
||||
// remove quote around the return value on Android
|
||||
@@ -28,6 +30,7 @@ class NetworkRepository {
|
||||
return _networkInfo.getWifiName();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> getWifiIp() {
|
||||
return _networkInfo.getWifiIP();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
|
||||
as entity;
|
||||
import 'package:immich_mobile/interfaces/partner.interface.dart';
|
||||
import 'package:immich_mobile/providers/db.provider.dart';
|
||||
import 'package:immich_mobile/repositories/database.repository.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
@@ -10,9 +11,11 @@ final partnerRepositoryProvider = Provider(
|
||||
(ref) => PartnerRepository(ref.watch(dbProvider)),
|
||||
);
|
||||
|
||||
class PartnerRepository extends DatabaseRepository {
|
||||
class PartnerRepository extends DatabaseRepository
|
||||
implements IPartnerRepository {
|
||||
PartnerRepository(super.db);
|
||||
|
||||
@override
|
||||
Future<List<UserDto>> getSharedBy() async {
|
||||
return (await db.users
|
||||
.filter()
|
||||
@@ -23,6 +26,7 @@ class PartnerRepository extends DatabaseRepository {
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<UserDto>> getSharedWith() async {
|
||||
return (await db.users
|
||||
.filter()
|
||||
@@ -33,11 +37,13 @@ class PartnerRepository extends DatabaseRepository {
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<List<UserDto>> watchSharedBy() {
|
||||
return (db.users.filter().isPartnerSharedByEqualTo(true).sortById().watch())
|
||||
.map((users) => users.map((u) => u.toDto()).toList());
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<List<UserDto>> watchSharedWith() {
|
||||
return (db.users
|
||||
.filter()
|
||||
|
||||
@@ -1,26 +1,24 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/user.converter.dart';
|
||||
import 'package:immich_mobile/interfaces/partner_api.interface.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/repositories/api.repository.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
enum Direction {
|
||||
sharedWithMe,
|
||||
sharedByMe,
|
||||
}
|
||||
|
||||
final partnerApiRepositoryProvider = Provider(
|
||||
(ref) => PartnerApiRepository(
|
||||
ref.watch(apiServiceProvider).partnersApi,
|
||||
),
|
||||
);
|
||||
|
||||
class PartnerApiRepository extends ApiRepository {
|
||||
class PartnerApiRepository extends ApiRepository
|
||||
implements IPartnerApiRepository {
|
||||
final PartnersApi _api;
|
||||
|
||||
PartnerApiRepository(this._api);
|
||||
|
||||
@override
|
||||
Future<List<UserDto>> getAll(Direction direction) async {
|
||||
final response = await checkNull(
|
||||
_api.getPartners(
|
||||
@@ -32,13 +30,16 @@ class PartnerApiRepository extends ApiRepository {
|
||||
return response.map(UserConverter.fromPartnerDto).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<UserDto> create(String id) async {
|
||||
final dto = await checkNull(_api.createPartner(id));
|
||||
return UserConverter.fromPartnerDto(dto);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> delete(String id) => _api.removePartner(id);
|
||||
|
||||
@override
|
||||
Future<UserDto> update(String id, {required bool inTimeline}) async {
|
||||
final dto = await checkNull(
|
||||
_api.updatePartner(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/person.model.dart';
|
||||
import 'package:immich_mobile/interfaces/person_api.interface.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/repositories/api.repository.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
@@ -8,16 +8,19 @@ final personApiRepositoryProvider = Provider(
|
||||
(ref) => PersonApiRepository(ref.watch(apiServiceProvider).peopleApi),
|
||||
);
|
||||
|
||||
class PersonApiRepository extends ApiRepository {
|
||||
class PersonApiRepository extends ApiRepository
|
||||
implements IPersonApiRepository {
|
||||
final PeopleApi _api;
|
||||
|
||||
PersonApiRepository(this._api);
|
||||
|
||||
@override
|
||||
Future<List<Person>> getAll() async {
|
||||
final dto = await checkNull(_api.getAllPeople());
|
||||
return dto.people.map(_toPerson).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Person> update(String id, {String? name}) async {
|
||||
final dto = await checkNull(
|
||||
_api.updatePerson(id, PersonUpdateDto(name: name)),
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/interfaces/secure_storage.interface.dart';
|
||||
|
||||
final secureStorageRepositoryProvider =
|
||||
Provider((ref) => SecureStorageRepository(const FlutterSecureStorage()));
|
||||
|
||||
class SecureStorageRepository {
|
||||
class SecureStorageRepository implements ISecureStorageRepository {
|
||||
final FlutterSecureStorage _secureStorage;
|
||||
|
||||
SecureStorageRepository(this._secureStorage);
|
||||
|
||||
@override
|
||||
Future<String?> read(String key) {
|
||||
return _secureStorage.read(key: key);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> write(String key, String value) {
|
||||
return _secureStorage.write(key: key, value: value);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> delete(String key) {
|
||||
return _secureStorage.delete(key: key);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/interfaces/sessions_api.interface.dart';
|
||||
import 'package:immich_mobile/models/sessions/session_create_response.model.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/repositories/api.repository.dart';
|
||||
@@ -10,11 +11,13 @@ final sessionsAPIRepositoryProvider = Provider(
|
||||
),
|
||||
);
|
||||
|
||||
class SessionsAPIRepository extends ApiRepository {
|
||||
class SessionsAPIRepository extends ApiRepository
|
||||
implements ISessionAPIRepository {
|
||||
final SessionsApi _api;
|
||||
|
||||
SessionsAPIRepository(this._api);
|
||||
|
||||
@override
|
||||
Future<SessionCreateResponse> createSession(
|
||||
String deviceType,
|
||||
String deviceOS, {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/interfaces/share_handler.interface.dart';
|
||||
import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart';
|
||||
import 'package:share_handler/share_handler.dart';
|
||||
|
||||
@@ -8,11 +9,13 @@ final shareHandlerRepositoryProvider = Provider(
|
||||
(ref) => ShareHandlerRepository(),
|
||||
);
|
||||
|
||||
class ShareHandlerRepository {
|
||||
class ShareHandlerRepository implements IShareHandlerRepository {
|
||||
ShareHandlerRepository();
|
||||
|
||||
@override
|
||||
void Function(List<ShareIntentAttachment> attachments)? onSharedMedia;
|
||||
|
||||
@override
|
||||
Future<void> init() async {
|
||||
final handler = ShareHandlerPlatform.instance;
|
||||
final media = await handler.getInitialSharedMedia();
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:immich_mobile/constants/enums.dart';
|
||||
import 'package:immich_mobile/entities/album.entity.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/timeline.interface.dart';
|
||||
import 'package:immich_mobile/providers/db.provider.dart';
|
||||
import 'package:immich_mobile/repositories/database.repository.dart';
|
||||
import 'package:immich_mobile/utils/hash.dart';
|
||||
@@ -12,9 +13,11 @@ import 'package:isar/isar.dart';
|
||||
final timelineRepositoryProvider =
|
||||
Provider((ref) => TimelineRepository(ref.watch(dbProvider)));
|
||||
|
||||
class TimelineRepository extends DatabaseRepository {
|
||||
class TimelineRepository extends DatabaseRepository
|
||||
implements ITimelineRepository {
|
||||
TimelineRepository(super.db);
|
||||
|
||||
@override
|
||||
Future<List<String>> getTimelineUserIds(String id) {
|
||||
return db.users
|
||||
.filter()
|
||||
@@ -25,6 +28,7 @@ class TimelineRepository extends DatabaseRepository {
|
||||
.findAll();
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<List<String>> watchTimelineUsers(String id) {
|
||||
return db.users
|
||||
.filter()
|
||||
@@ -35,6 +39,7 @@ class TimelineRepository extends DatabaseRepository {
|
||||
.watch();
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<RenderList> watchArchiveTimeline(String userId) {
|
||||
final query = db.assets
|
||||
.where()
|
||||
@@ -47,6 +52,7 @@ class TimelineRepository extends DatabaseRepository {
|
||||
return _watchRenderList(query, GroupAssetsBy.none);
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<RenderList> watchFavoriteTimeline(String userId) {
|
||||
final query = db.assets
|
||||
.where()
|
||||
@@ -61,6 +67,7 @@ class TimelineRepository extends DatabaseRepository {
|
||||
return _watchRenderList(query, GroupAssetsBy.none);
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<RenderList> watchAlbumTimeline(
|
||||
Album album,
|
||||
GroupAssetsBy groupAssetByOption,
|
||||
@@ -79,6 +86,7 @@ class TimelineRepository extends DatabaseRepository {
|
||||
return _watchRenderList(withSortedOption, groupAssetByOption);
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<RenderList> watchTrashTimeline(String userId) {
|
||||
final query = db.assets
|
||||
.filter()
|
||||
@@ -89,6 +97,7 @@ class TimelineRepository extends DatabaseRepository {
|
||||
return _watchRenderList(query, GroupAssetsBy.none);
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<RenderList> watchAllVideosTimeline(String userId) {
|
||||
final query = db.assets
|
||||
.where()
|
||||
@@ -102,6 +111,7 @@ class TimelineRepository extends DatabaseRepository {
|
||||
return _watchRenderList(query, GroupAssetsBy.none);
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<RenderList> watchHomeTimeline(
|
||||
String userId,
|
||||
GroupAssetsBy groupAssetByOption,
|
||||
@@ -118,6 +128,7 @@ class TimelineRepository extends DatabaseRepository {
|
||||
return _watchRenderList(query, groupAssetByOption);
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<RenderList> watchMultiUsersTimeline(
|
||||
List<String> userIds,
|
||||
GroupAssetsBy groupAssetByOption,
|
||||
@@ -134,6 +145,7 @@ class TimelineRepository extends DatabaseRepository {
|
||||
return _watchRenderList(query, groupAssetByOption);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<RenderList> getTimelineFromAssets(
|
||||
List<Asset> assets,
|
||||
GroupAssetsBy getGroupByOption,
|
||||
@@ -141,6 +153,7 @@ class TimelineRepository extends DatabaseRepository {
|
||||
return RenderList.fromAssets(assets, getGroupByOption);
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<RenderList> watchAssetSelectionTimeline(String userId) {
|
||||
final query = db.assets
|
||||
.where()
|
||||
@@ -155,6 +168,7 @@ class TimelineRepository extends DatabaseRepository {
|
||||
return _watchRenderList(query, GroupAssetsBy.none);
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<RenderList> watchLockedTimeline(
|
||||
String userId,
|
||||
GroupAssetsBy getGroupByOption,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:immich_mobile/interfaces/activity_api.interface.dart';
|
||||
import 'package:immich_mobile/mixins/error_logger.mixin.dart';
|
||||
import 'package:immich_mobile/models/activities/activity.model.dart';
|
||||
import 'package:immich_mobile/repositories/activity_api.repository.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
class ActivityService with ErrorLoggerMixin {
|
||||
final ActivityApiRepository _activityApiRepository;
|
||||
final IActivityApiRepository _activityApiRepository;
|
||||
|
||||
@override
|
||||
final Logger logger = Logger("ActivityService");
|
||||
|
||||
@@ -14,6 +14,8 @@ import 'package:immich_mobile/entities/backup_album.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
|
||||
as entity;
|
||||
import 'package:immich_mobile/interfaces/album.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/album_api.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/album_media.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
|
||||
import 'package:immich_mobile/models/albums/album_add_asset_response.model.dart';
|
||||
@@ -49,8 +51,8 @@ class AlbumService {
|
||||
final IAlbumRepository _albumRepository;
|
||||
final IAssetRepository _assetRepository;
|
||||
final IBackupAlbumRepository _backupAlbumRepository;
|
||||
final AlbumMediaRepository _albumMediaRepository;
|
||||
final AlbumApiRepository _albumApiRepository;
|
||||
final IAlbumMediaRepository _albumMediaRepository;
|
||||
final IAlbumApiRepository _albumApiRepository;
|
||||
final Logger _log = Logger('AlbumService');
|
||||
Completer<bool> _localCompleter = Completer()..complete(false);
|
||||
Completer<bool> _remoteCompleter = Completer()..complete(false);
|
||||
|
||||
@@ -5,11 +5,11 @@ import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/enums.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/domain/services/user.service.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
|
||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/asset_api.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/asset_media.interface.dart';
|
||||
@@ -53,7 +53,7 @@ class AssetService {
|
||||
final IAssetApiRepository _assetApiRepository;
|
||||
final IAssetRepository _assetRepository;
|
||||
final IExifInfoRepository _exifInfoRepository;
|
||||
final IsarUserRepository _isarUserRepository;
|
||||
final IUserRepository _userRepository;
|
||||
final IETagRepository _etagRepository;
|
||||
final IBackupAlbumRepository _backupRepository;
|
||||
final ApiService _apiService;
|
||||
@@ -68,7 +68,7 @@ class AssetService {
|
||||
this._assetApiRepository,
|
||||
this._assetRepository,
|
||||
this._exifInfoRepository,
|
||||
this._isarUserRepository,
|
||||
this._userRepository,
|
||||
this._etagRepository,
|
||||
this._backupRepository,
|
||||
this._apiService,
|
||||
@@ -85,9 +85,7 @@ class AssetService {
|
||||
final syncedUserIds = await _etagRepository.getAllIds();
|
||||
final List<UserDto> syncedUsers = syncedUserIds.isEmpty
|
||||
? []
|
||||
: (await _isarUserRepository.getByUserIds(syncedUserIds))
|
||||
.nonNulls
|
||||
.toList();
|
||||
: (await _userRepository.getByUserIds(syncedUserIds)).nonNulls.toList();
|
||||
final Stopwatch sw = Stopwatch()..start();
|
||||
final bool changes = await _syncService.syncRemoteAssetsToDb(
|
||||
users: syncedUsers,
|
||||
|
||||
@@ -11,6 +11,7 @@ import 'package:immich_mobile/entities/album.entity.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/album_media.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/asset_media.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/file_media.interface.dart';
|
||||
@@ -51,7 +52,7 @@ class BackupService {
|
||||
final Logger _log = Logger("BackupService");
|
||||
final AppSettingsService _appSetting;
|
||||
final AlbumService _albumService;
|
||||
final AlbumMediaRepository _albumMediaRepository;
|
||||
final IAlbumMediaRepository _albumMediaRepository;
|
||||
final IFileMediaRepository _fileMediaRepository;
|
||||
final IAssetRepository _assetRepository;
|
||||
final IAssetMediaRepository _assetMediaRepository;
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/download.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/file_media.interface.dart';
|
||||
import 'package:immich_mobile/models/download/livephotos_medatada.model.dart';
|
||||
import 'package:immich_mobile/repositories/download.repository.dart';
|
||||
@@ -22,7 +23,7 @@ final downloadServiceProvider = Provider(
|
||||
);
|
||||
|
||||
class DownloadService {
|
||||
final DownloadRepository _downloadRepository;
|
||||
final IDownloadRepository _downloadRepository;
|
||||
final IFileMediaRepository _fileMediaRepository;
|
||||
final Logger _log = Logger("DownloadService");
|
||||
void Function(TaskStatusUpdate)? onImageDownloadStatus;
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/entities/album.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
|
||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
|
||||
import 'package:immich_mobile/repositories/asset.repository.dart';
|
||||
|
||||
class EntityService {
|
||||
final IAssetRepository _assetRepository;
|
||||
final IsarUserRepository _isarUserRepository;
|
||||
final IUserRepository _userRepository;
|
||||
EntityService(
|
||||
this._assetRepository,
|
||||
this._isarUserRepository,
|
||||
this._userRepository,
|
||||
);
|
||||
|
||||
Future<Album> fillAlbumWithDatabaseEntities(Album album) async {
|
||||
final ownerId = album.ownerId;
|
||||
if (ownerId != null) {
|
||||
// replace owner with user from database
|
||||
final user = await _isarUserRepository.getByUserId(ownerId);
|
||||
final user = await _userRepository.getByUserId(ownerId);
|
||||
album.owner.value = user == null ? null : User.fromDto(user);
|
||||
}
|
||||
final thumbnailAssetId =
|
||||
@@ -30,7 +30,7 @@ class EntityService {
|
||||
}
|
||||
if (album.remoteUsers.isNotEmpty) {
|
||||
// replace all users with users from database
|
||||
final users = await _isarUserRepository
|
||||
final users = await _userRepository
|
||||
.getByUserIds(album.remoteUsers.map((user) => user.id).toList());
|
||||
album.sharedUsers.clear();
|
||||
album.sharedUsers.addAll(users.nonNulls.map(User.fromDto));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/interfaces/biometric.interface.dart';
|
||||
import 'package:immich_mobile/models/auth/biometric_status.model.dart';
|
||||
import 'package:immich_mobile/repositories/biometric.repository.dart';
|
||||
|
||||
@@ -9,7 +10,9 @@ final localAuthServiceProvider = Provider(
|
||||
);
|
||||
|
||||
class LocalAuthService {
|
||||
final BiometricRepository _biometricRepository;
|
||||
// final _log = Logger("LocalAuthService");
|
||||
|
||||
final IBiometricRepository _biometricRepository;
|
||||
|
||||
LocalAuthService(this._biometricRepository);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/interfaces/network.interface.dart';
|
||||
import 'package:immich_mobile/repositories/network.repository.dart';
|
||||
import 'package:immich_mobile/repositories/permission.repository.dart';
|
||||
|
||||
@@ -10,7 +11,7 @@ final networkServiceProvider = Provider((ref) {
|
||||
});
|
||||
|
||||
class NetworkService {
|
||||
final NetworkRepository _repository;
|
||||
final INetworkRepository _repository;
|
||||
final IPermissionRepository _permissionRepository;
|
||||
|
||||
NetworkService(this._repository, this._permissionRepository);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
|
||||
import 'package:immich_mobile/interfaces/partner.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/partner_api.interface.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
|
||||
import 'package:immich_mobile/repositories/partner.repository.dart';
|
||||
import 'package:immich_mobile/repositories/partner_api.repository.dart';
|
||||
@@ -15,14 +17,14 @@ final partnerServiceProvider = Provider(
|
||||
);
|
||||
|
||||
class PartnerService {
|
||||
final PartnerApiRepository _partnerApiRepository;
|
||||
final PartnerRepository _partnerRepository;
|
||||
final IsarUserRepository _isarUserRepository;
|
||||
final IPartnerApiRepository _partnerApiRepository;
|
||||
final IPartnerRepository _partnerRepository;
|
||||
final IUserRepository _userRepository;
|
||||
final Logger _log = Logger("PartnerService");
|
||||
|
||||
PartnerService(
|
||||
this._partnerApiRepository,
|
||||
this._isarUserRepository,
|
||||
this._userRepository,
|
||||
this._partnerRepository,
|
||||
);
|
||||
|
||||
@@ -45,8 +47,7 @@ class PartnerService {
|
||||
Future<bool> removePartner(UserDto partner) async {
|
||||
try {
|
||||
await _partnerApiRepository.delete(partner.id);
|
||||
await _isarUserRepository
|
||||
.update(partner.copyWith(isPartnerSharedBy: false));
|
||||
await _userRepository.update(partner.copyWith(isPartnerSharedBy: false));
|
||||
} catch (e) {
|
||||
_log.warning("Failed to remove partner ${partner.id}", e);
|
||||
return false;
|
||||
@@ -57,8 +58,7 @@ class PartnerService {
|
||||
Future<bool> addPartner(UserDto partner) async {
|
||||
try {
|
||||
await _partnerApiRepository.create(partner.id);
|
||||
await _isarUserRepository
|
||||
.update(partner.copyWith(isPartnerSharedBy: true));
|
||||
await _userRepository.update(partner.copyWith(isPartnerSharedBy: true));
|
||||
return true;
|
||||
} catch (e) {
|
||||
_log.warning("Failed to add partner ${partner.id}", e);
|
||||
@@ -75,7 +75,7 @@ class PartnerService {
|
||||
partner.id,
|
||||
inTimeline: inTimeline,
|
||||
);
|
||||
await _isarUserRepository
|
||||
await _userRepository
|
||||
.update(partner.copyWith(inTimeline: dto.inTimeline));
|
||||
return true;
|
||||
} catch (e) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/person.model.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/asset_api.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/person_api.interface.dart';
|
||||
import 'package:immich_mobile/repositories/asset.repository.dart';
|
||||
import 'package:immich_mobile/repositories/asset_api.repository.dart';
|
||||
import 'package:immich_mobile/repositories/person_api.repository.dart';
|
||||
@@ -20,7 +20,7 @@ PersonService personService(Ref ref) => PersonService(
|
||||
|
||||
class PersonService {
|
||||
final Logger _log = Logger("PersonService");
|
||||
final PersonApiRepository _personApiRepository;
|
||||
final IPersonApiRepository _personApiRepository;
|
||||
final IAssetApiRepository _assetApiRepository;
|
||||
final IAssetRepository _assetRepository;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/interfaces/secure_storage.interface.dart';
|
||||
import 'package:immich_mobile/repositories/secure_storage.repository.dart';
|
||||
|
||||
final secureStorageServiceProvider = Provider(
|
||||
@@ -8,7 +9,9 @@ final secureStorageServiceProvider = Provider(
|
||||
);
|
||||
|
||||
class SecureStorageService {
|
||||
final SecureStorageRepository _secureStorageRepository;
|
||||
// final _log = Logger("LocalAuthService");
|
||||
|
||||
final ISecureStorageRepository _secureStorageRepository;
|
||||
|
||||
SecureStorageService(this._secureStorageRepository);
|
||||
|
||||
|
||||
@@ -3,20 +3,23 @@ import 'dart:io';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/enums.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user_api.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/domain/services/user.service.dart';
|
||||
import 'package:immich_mobile/entities/album.entity.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/entities/etag.entity.dart';
|
||||
import 'package:immich_mobile/extensions/collection_extensions.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/user_api.repository.dart';
|
||||
import 'package:immich_mobile/interfaces/album.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/album_api.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/album_media.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/etag.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/local_files_manager.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/partner.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/partner_api.interface.dart';
|
||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/exif.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
|
||||
@@ -60,17 +63,17 @@ final syncServiceProvider = Provider(
|
||||
class SyncService {
|
||||
final HashService _hashService;
|
||||
final EntityService _entityService;
|
||||
final AlbumMediaRepository _albumMediaRepository;
|
||||
final AlbumApiRepository _albumApiRepository;
|
||||
final IAlbumMediaRepository _albumMediaRepository;
|
||||
final IAlbumApiRepository _albumApiRepository;
|
||||
final IAlbumRepository _albumRepository;
|
||||
final IAssetRepository _assetRepository;
|
||||
final IExifInfoRepository _exifInfoRepository;
|
||||
final IsarUserRepository _isarUserRepository;
|
||||
final IUserRepository _userRepository;
|
||||
final UserService _userService;
|
||||
final PartnerRepository _partnerRepository;
|
||||
final IPartnerRepository _partnerRepository;
|
||||
final IETagRepository _eTagRepository;
|
||||
final PartnerApiRepository _partnerApiRepository;
|
||||
final UserApiRepository _userApiRepository;
|
||||
final IPartnerApiRepository _partnerApiRepository;
|
||||
final IUserApiRepository _userApiRepository;
|
||||
final AsyncMutex _lock = AsyncMutex();
|
||||
final Logger _log = Logger('SyncService');
|
||||
final AppSettingsService _appSettingsService;
|
||||
@@ -85,7 +88,7 @@ class SyncService {
|
||||
this._assetRepository,
|
||||
this._exifInfoRepository,
|
||||
this._partnerRepository,
|
||||
this._isarUserRepository,
|
||||
this._userRepository,
|
||||
this._userService,
|
||||
this._eTagRepository,
|
||||
this._appSettingsService,
|
||||
@@ -162,7 +165,7 @@ class SyncService {
|
||||
/// Returns `true`if there were any changes
|
||||
Future<bool> _syncUsersFromServer(List<UserDto> users) async {
|
||||
users.sortBy((u) => u.id);
|
||||
final dbUsers = await _isarUserRepository.getAll(sortBy: SortUserBy.id);
|
||||
final dbUsers = await _userRepository.getAll(sortBy: SortUserBy.id);
|
||||
final List<String> toDelete = [];
|
||||
final List<UserDto> toUpsert = [];
|
||||
final changes = diffSortedListsSync(
|
||||
@@ -183,9 +186,9 @@ class SyncService {
|
||||
onlySecond: (UserDto b) => toDelete.add(b.id),
|
||||
);
|
||||
if (changes) {
|
||||
await _isarUserRepository.transaction(() async {
|
||||
await _isarUserRepository.delete(toDelete);
|
||||
await _isarUserRepository.updateAll(toUpsert);
|
||||
await _userRepository.transaction(() async {
|
||||
await _userRepository.delete(toDelete);
|
||||
await _userRepository.updateAll(toUpsert);
|
||||
});
|
||||
}
|
||||
return changes;
|
||||
@@ -445,7 +448,7 @@ class SyncService {
|
||||
final (existingInDb, updated) = await _linkWithExistingFromDb(toAdd);
|
||||
await upsertAssetsWithExif(updated);
|
||||
final assetsToLink = existingInDb + updated;
|
||||
final usersToLink = await _isarUserRepository.getByUserIds(userIdsToAdd);
|
||||
final usersToLink = await _userRepository.getByUserIds(userIdsToAdd);
|
||||
|
||||
album.name = dto.name;
|
||||
album.description = dto.description;
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/services/user.service.dart';
|
||||
import 'package:immich_mobile/entities/album.entity.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/timeline.interface.dart';
|
||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
|
||||
import 'package:immich_mobile/repositories/timeline.repository.dart';
|
||||
@@ -17,7 +18,7 @@ final timelineServiceProvider = Provider<TimelineService>((ref) {
|
||||
});
|
||||
|
||||
class TimelineService {
|
||||
final TimelineRepository _timelineRepository;
|
||||
final ITimelineRepository _timelineRepository;
|
||||
final AppSettingsService _appSettingsService;
|
||||
final UserService _userService;
|
||||
|
||||
|
||||
@@ -57,13 +57,7 @@ Future<void> migrateDatabaseIfNeeded(Isar db) async {
|
||||
}
|
||||
|
||||
final shouldTruncate = version < 8 || version < targetVersion;
|
||||
|
||||
if (shouldTruncate) {
|
||||
if (targetVersion == 12) {
|
||||
await Store.put(StoreKey.version, targetVersion);
|
||||
return;
|
||||
}
|
||||
|
||||
await _migrateTo(db, targetVersion);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ import 'package:flutter/material.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/person.model.dart';
|
||||
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/interfaces/person_api.interface.dart';
|
||||
import 'package:immich_mobile/pages/common/large_leading_tile.dart';
|
||||
import 'package:immich_mobile/providers/search/people.provider.dart';
|
||||
import 'package:immich_mobile/services/api.service.dart';
|
||||
|
||||
2
mobile/openapi/README.md
generated
2
mobile/openapi/README.md
generated
@@ -3,7 +3,7 @@ Immich API
|
||||
|
||||
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
|
||||
|
||||
- API version: 1.135.3
|
||||
- API version: 1.135.0
|
||||
- Generator version: 7.8.0
|
||||
- Build package: org.openapitools.codegen.languages.DartClientCodegen
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ name: immich_mobile
|
||||
description: Immich - selfhosted backup media file on mobile phone
|
||||
|
||||
publish_to: 'none'
|
||||
version: 1.135.3+204
|
||||
version: 1.135.0+201
|
||||
|
||||
environment:
|
||||
sdk: '>=3.3.0 <4.0.0'
|
||||
|
||||
@@ -4,9 +4,9 @@ import 'dart:async';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/sync_api.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/sync_stream.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/sync_event.model.dart';
|
||||
import 'package:immich_mobile/domain/services/sync_stream.service.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
import '../../fixtures/sync_stream.stub.dart';
|
||||
@@ -30,7 +30,7 @@ class _MockCancellationWrapper extends Mock implements _CancellationWrapper {}
|
||||
|
||||
void main() {
|
||||
late SyncStreamService sut;
|
||||
late SyncStreamRepository mockSyncStreamRepo;
|
||||
late ISyncStreamRepository mockSyncStreamRepo;
|
||||
late ISyncApiRepository mockSyncApiRepo;
|
||||
late Function(List<SyncEvent>, Function()) handleEventsCallback;
|
||||
late _MockAbortCallbackWrapper mockAbortCallbackWrapper;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user_api.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||
import 'package:immich_mobile/domain/services/user.service.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/user_api.repository.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
import '../../fixtures/user.stub.dart';
|
||||
@@ -14,16 +14,16 @@ import '../service.mock.dart';
|
||||
|
||||
void main() {
|
||||
late UserService sut;
|
||||
late IsarUserRepository mockUserRepo;
|
||||
late UserApiRepository mockUserApiRepo;
|
||||
late IUserRepository mockUserRepo;
|
||||
late IUserApiRepository mockUserApiRepo;
|
||||
late StoreService mockStoreService;
|
||||
|
||||
setUp(() {
|
||||
mockUserRepo = MockIsarUserRepository();
|
||||
mockUserRepo = MockUserRepository();
|
||||
mockUserApiRepo = MockUserApiRepository();
|
||||
mockStoreService = MockStoreService();
|
||||
sut = UserService(
|
||||
isarUserRepository: mockUserRepo,
|
||||
userRepository: mockUserRepo,
|
||||
userApiRepository: mockUserApiRepo,
|
||||
storeService: mockStoreService,
|
||||
);
|
||||
|
||||
@@ -5,21 +5,21 @@ import 'package:immich_mobile/domain/interfaces/log.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/storage.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/store.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/sync_api.interface.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/user_api.repository.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/sync_stream.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user_api.interface.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockStoreRepository extends Mock implements IStoreRepository {}
|
||||
|
||||
class MockLogRepository extends Mock implements ILogRepository {}
|
||||
|
||||
class MockIsarUserRepository extends Mock implements IsarUserRepository {}
|
||||
class MockUserRepository extends Mock implements IUserRepository {}
|
||||
|
||||
class MockDeviceAssetRepository extends Mock
|
||||
implements IDeviceAssetRepository {}
|
||||
|
||||
class MockSyncStreamRepository extends Mock implements SyncStreamRepository {}
|
||||
class MockSyncStreamRepository extends Mock implements ISyncStreamRepository {}
|
||||
|
||||
class MockLocalAlbumRepository extends Mock implements ILocalAlbumRepository {}
|
||||
|
||||
@@ -28,6 +28,6 @@ class MockLocalAssetRepository extends Mock implements ILocalAssetRepository {}
|
||||
class MockStorageRepository extends Mock implements IStorageRepository {}
|
||||
|
||||
// API Repos
|
||||
class MockUserApiRepository extends Mock implements UserApiRepository {}
|
||||
class MockUserApiRepository extends Mock implements IUserApiRepository {}
|
||||
|
||||
class MockSyncApiRepository extends Mock implements ISyncApiRepository {}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:immich_mobile/constants/enums.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/domain/services/log.service.dart';
|
||||
@@ -11,7 +11,7 @@ import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/log.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/repositories/partner_api.repository.dart';
|
||||
import 'package:immich_mobile/interfaces/partner_api.interface.dart';
|
||||
import 'package:immich_mobile/services/sync.service.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
@@ -55,7 +55,7 @@ void main() {
|
||||
final MockAlbumRepository albumRepository = MockAlbumRepository();
|
||||
final MockAssetRepository assetRepository = MockAssetRepository();
|
||||
final MockExifInfoRepository exifInfoRepository = MockExifInfoRepository();
|
||||
final MockIsarUserRepository userRepository = MockIsarUserRepository();
|
||||
final MockUserRepository userRepository = MockUserRepository();
|
||||
final MockETagRepository eTagRepository = MockETagRepository();
|
||||
final MockAlbumMediaRepository albumMediaRepository =
|
||||
MockAlbumMediaRepository();
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/album.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/album_api.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/album_media.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/asset_api.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/asset_media.interface.dart';
|
||||
@@ -9,10 +11,8 @@ import 'package:immich_mobile/interfaces/backup_album.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/etag.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/file_media.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/local_files_manager.interface.dart';
|
||||
import 'package:immich_mobile/repositories/partner_api.repository.dart';
|
||||
import 'package:immich_mobile/repositories/album_media.repository.dart';
|
||||
import 'package:immich_mobile/repositories/album_api.repository.dart';
|
||||
import 'package:immich_mobile/repositories/partner.repository.dart';
|
||||
import 'package:immich_mobile/interfaces/partner.interface.dart';
|
||||
import 'package:immich_mobile/interfaces/partner_api.interface.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockAlbumRepository extends Mock implements IAlbumRepository {}
|
||||
@@ -25,7 +25,7 @@ class MockExifInfoRepository extends Mock implements IExifInfoRepository {}
|
||||
|
||||
class MockETagRepository extends Mock implements IETagRepository {}
|
||||
|
||||
class MockAlbumMediaRepository extends Mock implements AlbumMediaRepository {}
|
||||
class MockAlbumMediaRepository extends Mock implements IAlbumMediaRepository {}
|
||||
|
||||
class MockBackupAlbumRepository extends Mock
|
||||
implements IBackupAlbumRepository {}
|
||||
@@ -36,15 +36,15 @@ class MockAssetMediaRepository extends Mock implements IAssetMediaRepository {}
|
||||
|
||||
class MockFileMediaRepository extends Mock implements IFileMediaRepository {}
|
||||
|
||||
class MockAlbumApiRepository extends Mock implements AlbumApiRepository {}
|
||||
class MockAlbumApiRepository extends Mock implements IAlbumApiRepository {}
|
||||
|
||||
class MockAuthApiRepository extends Mock implements IAuthApiRepository {}
|
||||
|
||||
class MockAuthRepository extends Mock implements IAuthRepository {}
|
||||
|
||||
class MockPartnerRepository extends Mock implements PartnerRepository {}
|
||||
class MockPartnerRepository extends Mock implements IPartnerRepository {}
|
||||
|
||||
class MockPartnerApiRepository extends Mock implements PartnerApiRepository {}
|
||||
class MockPartnerApiRepository extends Mock implements IPartnerApiRepository {}
|
||||
|
||||
class MockLocalFilesManagerRepository extends Mock
|
||||
implements ILocalFilesManager {}
|
||||
|
||||
@@ -22,7 +22,7 @@ void main() {
|
||||
late MockExifInfoRepository exifInfoRepository;
|
||||
late MockETagRepository eTagRepository;
|
||||
late MockBackupAlbumRepository backupAlbumRepository;
|
||||
late MockIsarUserRepository userRepository;
|
||||
late MockUserRepository userRepository;
|
||||
late MockAssetMediaRepository assetMediaRepository;
|
||||
late MockApiService apiService;
|
||||
|
||||
@@ -35,7 +35,7 @@ void main() {
|
||||
assetRepository = MockAssetRepository();
|
||||
assetApiRepository = MockAssetApiRepository();
|
||||
exifInfoRepository = MockExifInfoRepository();
|
||||
userRepository = MockIsarUserRepository();
|
||||
userRepository = MockUserRepository();
|
||||
eTagRepository = MockETagRepository();
|
||||
backupAlbumRepository = MockBackupAlbumRepository();
|
||||
apiService = MockApiService();
|
||||
|
||||
@@ -12,11 +12,11 @@ import '../repository.mocks.dart';
|
||||
void main() {
|
||||
late EntityService sut;
|
||||
late MockAssetRepository assetRepository;
|
||||
late MockIsarUserRepository userRepository;
|
||||
late MockUserRepository userRepository;
|
||||
|
||||
setUp(() {
|
||||
assetRepository = MockAssetRepository();
|
||||
userRepository = MockIsarUserRepository();
|
||||
userRepository = MockUserRepository();
|
||||
sut = EntityService(assetRepository, userRepository);
|
||||
});
|
||||
|
||||
|
||||
@@ -8503,7 +8503,7 @@
|
||||
"info": {
|
||||
"title": "Immich",
|
||||
"description": "Immich API",
|
||||
"version": "1.135.3",
|
||||
"version": "1.135.0",
|
||||
"contact": {}
|
||||
},
|
||||
"tags": [],
|
||||
|
||||
4
open-api/typescript-sdk/package-lock.json
generated
4
open-api/typescript-sdk/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@immich/sdk",
|
||||
"version": "1.135.3",
|
||||
"version": "1.135.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@immich/sdk",
|
||||
"version": "1.135.3",
|
||||
"version": "1.135.0",
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"dependencies": {
|
||||
"@oazapfts/runtime": "^1.0.2"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@immich/sdk",
|
||||
"version": "1.135.3",
|
||||
"version": "1.135.0",
|
||||
"description": "Auto-generated TypeScript SDK for the Immich API",
|
||||
"type": "module",
|
||||
"main": "./build/index.js",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Immich
|
||||
* 1.135.3
|
||||
* 1.135.0
|
||||
* DO NOT MODIFY - This file has been generated using oazapfts.
|
||||
* See https://www.npmjs.com/package/oazapfts
|
||||
*/
|
||||
|
||||
54
server/package-lock.json
generated
54
server/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "immich",
|
||||
"version": "1.135.3",
|
||||
"version": "1.135.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "immich",
|
||||
"version": "1.135.3",
|
||||
"version": "1.135.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"dependencies": {
|
||||
@@ -42,6 +42,7 @@
|
||||
"handlebars": "^4.7.8",
|
||||
"i18n-iso-countries": "^7.6.0",
|
||||
"ioredis": "^5.3.2",
|
||||
"joi": "^17.10.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"kysely": "^0.28.0",
|
||||
"kysely-postgres-js": "^2.0.0",
|
||||
@@ -1348,6 +1349,21 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@hapi/hoek": {
|
||||
"version": "9.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
|
||||
"integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@hapi/topo": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz",
|
||||
"integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@hapi/hoek": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanfs/core": {
|
||||
"version": "0.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
||||
@@ -5036,6 +5052,27 @@
|
||||
"url": "https://ko-fi.com/killymxi"
|
||||
}
|
||||
},
|
||||
"node_modules/@sideway/address": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
|
||||
"integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@hapi/hoek": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@sideway/formula": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz",
|
||||
"integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@sideway/pinpoint": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
|
||||
"integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||
@@ -11296,6 +11333,19 @@
|
||||
"jiti": "bin/jiti.js"
|
||||
}
|
||||
},
|
||||
"node_modules/joi": {
|
||||
"version": "17.13.3",
|
||||
"resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz",
|
||||
"integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@hapi/hoek": "^9.3.0",
|
||||
"@hapi/topo": "^5.1.0",
|
||||
"@sideway/address": "^4.1.5",
|
||||
"@sideway/formula": "^3.0.1",
|
||||
"@sideway/pinpoint": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jose": {
|
||||
"version": "6.0.10",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-6.0.10.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "immich",
|
||||
"version": "1.135.3",
|
||||
"version": "1.135.0",
|
||||
"description": "",
|
||||
"author": "",
|
||||
"private": true,
|
||||
@@ -68,6 +68,7 @@
|
||||
"handlebars": "^4.7.8",
|
||||
"i18n-iso-countries": "^7.6.0",
|
||||
"ioredis": "^5.3.2",
|
||||
"joi": "^17.10.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"kysely": "^0.28.0",
|
||||
"kysely-postgres-js": "^2.0.0",
|
||||
|
||||
@@ -224,7 +224,7 @@ limit
|
||||
with
|
||||
"assets" as (
|
||||
select
|
||||
date_trunc('MONTH', "localDateTime" AT TIME ZONE 'UTC') AT TIME ZONE 'UTC' as "timeBucket"
|
||||
date_trunc('MONTH', "localDateTime" at time zone 'UTC') at time zone 'UTC' as "timeBucket"
|
||||
from
|
||||
"assets"
|
||||
where
|
||||
@@ -232,7 +232,7 @@ with
|
||||
and "assets"."visibility" in ('archive', 'timeline')
|
||||
)
|
||||
select
|
||||
("timeBucket" AT TIME ZONE 'UTC')::date::text as "timeBucket",
|
||||
"timeBucket"::date::text as "timeBucket",
|
||||
count(*) as "count"
|
||||
from
|
||||
"assets"
|
||||
@@ -300,7 +300,7 @@ with
|
||||
where
|
||||
"assets"."deletedAt" is null
|
||||
and "assets"."visibility" in ('archive', 'timeline')
|
||||
and date_trunc('MONTH', "localDateTime" AT TIME ZONE 'UTC') AT TIME ZONE 'UTC' = $2
|
||||
and date_trunc('MONTH', "localDateTime" at time zone 'UTC') at time zone 'UTC' = $2
|
||||
and not exists (
|
||||
select
|
||||
from
|
||||
|
||||
@@ -12,38 +12,6 @@ delete from "person"
|
||||
where
|
||||
"person"."id" in ($1)
|
||||
|
||||
-- PersonRepository.getAllForUser
|
||||
select
|
||||
"person".*
|
||||
from
|
||||
"person"
|
||||
inner join "asset_faces" on "asset_faces"."personId" = "person"."id"
|
||||
inner join "assets" on "asset_faces"."assetId" = "assets"."id"
|
||||
and "assets"."visibility" = 'timeline'
|
||||
and "assets"."deletedAt" is null
|
||||
where
|
||||
"person"."ownerId" = $1
|
||||
and "asset_faces"."deletedAt" is null
|
||||
and "person"."isHidden" = $2
|
||||
group by
|
||||
"person"."id"
|
||||
having
|
||||
(
|
||||
"person"."name" != $3
|
||||
or count("asset_faces"."assetId") >= $4
|
||||
)
|
||||
order by
|
||||
"person"."isHidden" asc,
|
||||
"person"."isFavorite" desc,
|
||||
NULLIF(person.name, '') is null asc,
|
||||
count("asset_faces"."assetId") desc,
|
||||
NULLIF(person.name, '') asc nulls last,
|
||||
"person"."createdAt"
|
||||
limit
|
||||
$5
|
||||
offset
|
||||
$6
|
||||
|
||||
-- PersonRepository.getAllWithoutFaces
|
||||
select
|
||||
"person".*
|
||||
|
||||
@@ -130,7 +130,7 @@ from
|
||||
where
|
||||
"ownerId" = $1
|
||||
and "updatedAt" < now() - interval '1 millisecond'
|
||||
and "updateId" <= $2
|
||||
and "updateId" < $2
|
||||
and "updateId" >= $3
|
||||
order by
|
||||
"updateId" asc
|
||||
@@ -274,7 +274,7 @@ from
|
||||
where
|
||||
"assets"."ownerId" = $1
|
||||
and "exif"."updatedAt" < now() - interval '1 millisecond'
|
||||
and "exif"."updateId" <= $2
|
||||
and "exif"."updateId" < $2
|
||||
and "exif"."updateId" >= $3
|
||||
order by
|
||||
"exif"."updateId" asc
|
||||
@@ -418,7 +418,7 @@ from
|
||||
where
|
||||
"albumsId" = $1
|
||||
and "updatedAt" < now() - interval '1 millisecond'
|
||||
and "updateId" <= $2
|
||||
and "updateId" < $2
|
||||
and "updateId" >= $3
|
||||
order by
|
||||
"updateId" asc
|
||||
|
||||
@@ -42,6 +42,11 @@ interface LivePhotoSearchOptions {
|
||||
type: AssetType;
|
||||
}
|
||||
|
||||
export enum TimeBucketSize {
|
||||
DAY = 'DAY',
|
||||
MONTH = 'MONTH',
|
||||
}
|
||||
|
||||
interface AssetBuilderOptions {
|
||||
isFavorite?: boolean;
|
||||
isTrashed?: boolean;
|
||||
@@ -485,13 +490,13 @@ export class AssetRepository {
|
||||
.execute();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [{}] })
|
||||
@GenerateSql({ params: [{ size: TimeBucketSize.MONTH }] })
|
||||
async getTimeBuckets(options: TimeBucketOptions): Promise<TimeBucketItem[]> {
|
||||
return this.db
|
||||
.with('assets', (qb) =>
|
||||
qb
|
||||
.selectFrom('assets')
|
||||
.select(truncatedDate<Date>().as('timeBucket'))
|
||||
.select(truncatedDate<Date>(TimeBucketSize.MONTH).as('timeBucket'))
|
||||
.$if(!!options.isTrashed, (qb) => qb.where('assets.status', '!=', AssetStatus.DELETED))
|
||||
.where('assets.deletedAt', options.isTrashed ? 'is not' : 'is', null)
|
||||
.$if(options.visibility === undefined, withDefaultVisibility)
|
||||
@@ -520,7 +525,7 @@ export class AssetRepository {
|
||||
.$if(!!options.tagId, (qb) => withTagId(qb, options.tagId!)),
|
||||
)
|
||||
.selectFrom('assets')
|
||||
.select(sql<string>`("timeBucket" AT TIME ZONE 'UTC')::date::text`.as('timeBucket'))
|
||||
.select(sql<string>`"timeBucket"::date::text`.as('timeBucket'))
|
||||
.select((eb) => eb.fn.countAll<number>().as('count'))
|
||||
.groupBy('timeBucket')
|
||||
.orderBy('timeBucket', options.order ?? 'desc')
|
||||
@@ -571,7 +576,7 @@ export class AssetRepository {
|
||||
.where('assets.deletedAt', options.isTrashed ? 'is not' : 'is', null)
|
||||
.$if(options.visibility == undefined, withDefaultVisibility)
|
||||
.$if(!!options.visibility, (qb) => qb.where('assets.visibility', '=', options.visibility!))
|
||||
.where(truncatedDate(), '=', timeBucket.replace(/^[+-]/, ''))
|
||||
.where(truncatedDate(TimeBucketSize.MONTH), '=', timeBucket.replace(/^[+-]/, ''))
|
||||
.$if(!!options.albumId, (qb) =>
|
||||
qb.where((eb) =>
|
||||
eb.exists(
|
||||
|
||||
@@ -119,6 +119,8 @@ export class DatabaseRepository {
|
||||
await sql`CREATE EXTENSION IF NOT EXISTS ${sql.raw(extension)} CASCADE`.execute(this.db);
|
||||
if (extension === DatabaseExtension.VECTORCHORD) {
|
||||
const dbName = sql.id(await this.getDatabaseName());
|
||||
await sql`ALTER DATABASE ${dbName} SET vchordrq.prewarm_dim = '512,640,768,1024,1152,1536'`.execute(this.db);
|
||||
await sql`SET vchordrq.prewarm_dim = '512,640,768,1024,1152,1536'`.execute(this.db);
|
||||
await sql`ALTER DATABASE ${dbName} SET vchordrq.probes = 1`.execute(this.db);
|
||||
await sql`SET vchordrq.probes = 1`.execute(this.db);
|
||||
}
|
||||
@@ -140,29 +142,21 @@ export class DatabaseRepository {
|
||||
}
|
||||
targetVersion ??= availableVersion;
|
||||
|
||||
const isVectors = extension === DatabaseExtension.VECTORS;
|
||||
let restartRequired = false;
|
||||
const diff = semver.diff(installedVersion, targetVersion);
|
||||
if (!diff) {
|
||||
return { restartRequired: false };
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
this.db.schema.dropIndex(VectorIndex.CLIP).ifExists().execute(),
|
||||
this.db.schema.dropIndex(VectorIndex.FACE).ifExists().execute(),
|
||||
]);
|
||||
|
||||
await this.db.transaction().execute(async (tx) => {
|
||||
await this.setSearchPath(tx);
|
||||
|
||||
await sql`ALTER EXTENSION ${sql.raw(extension)} UPDATE TO ${sql.lit(targetVersion)}`.execute(tx);
|
||||
|
||||
if (extension === DatabaseExtension.VECTORS && (diff === 'major' || diff === 'minor')) {
|
||||
if (isVectors && (diff === 'major' || diff === 'minor')) {
|
||||
await sql`SELECT pgvectors_upgrade()`.execute(tx);
|
||||
restartRequired = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (!restartRequired) {
|
||||
if (diff && !restartRequired) {
|
||||
await Promise.all([this.reindexVectors(VectorIndex.CLIP), this.reindexVectors(VectorIndex.FACE)]);
|
||||
}
|
||||
|
||||
|
||||
@@ -138,7 +138,6 @@ export class PersonRepository {
|
||||
.stream();
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [{ take: 1, skip: 0 }, DummyValue.UUID] })
|
||||
async getAllForUser(pagination: PaginationOptions, userId: string, options?: PersonSearchOptions) {
|
||||
const items = await this.db
|
||||
.selectFrom('person')
|
||||
@@ -180,9 +179,8 @@ export class PersonRepository {
|
||||
)
|
||||
.$if(!options?.closestFaceAssetId, (qb) =>
|
||||
qb
|
||||
.orderBy(sql`NULLIF(person.name, '') is null`, 'asc')
|
||||
.orderBy((eb) => eb.fn.count('asset_faces.assetId'), 'desc')
|
||||
.orderBy(sql`NULLIF(person.name, '')`, (om) => om.asc().nullsLast())
|
||||
.orderBy(sql`NULLIF(person.name, '') asc nulls last`)
|
||||
.orderBy('person.createdAt'),
|
||||
)
|
||||
.$if(!options?.withHidden, (qb) => qb.where('person.isHidden', '=', false))
|
||||
|
||||
@@ -111,7 +111,7 @@ export class SyncRepository {
|
||||
.select(columns.syncAsset)
|
||||
.where('ownerId', '=', partnerId)
|
||||
.where('updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
.where('updateId', '<=', beforeUpdateId)
|
||||
.where('updateId', '<', beforeUpdateId)
|
||||
.$if(!!afterUpdateId, (eb) => eb.where('updateId', '>=', afterUpdateId!))
|
||||
.orderBy('updateId', 'asc')
|
||||
.stream();
|
||||
@@ -169,7 +169,7 @@ export class SyncRepository {
|
||||
.innerJoin('assets', 'assets.id', 'exif.assetId')
|
||||
.where('assets.ownerId', '=', partnerId)
|
||||
.where('exif.updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
.where('exif.updateId', '<=', beforeUpdateId)
|
||||
.where('exif.updateId', '<', beforeUpdateId)
|
||||
.$if(!!afterUpdateId, (eb) => eb.where('exif.updateId', '>=', afterUpdateId!))
|
||||
.orderBy('exif.updateId', 'asc')
|
||||
.stream();
|
||||
@@ -273,7 +273,7 @@ export class SyncRepository {
|
||||
.select(columns.syncAlbumUser)
|
||||
.where('albumsId', '=', albumId)
|
||||
.where('updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||
.where('updateId', '<=', beforeUpdateId)
|
||||
.where('updateId', '<', beforeUpdateId)
|
||||
.$if(!!afterUpdateId, (eb) => eb.where('updateId', '>=', afterUpdateId!))
|
||||
.orderBy('updateId', 'asc')
|
||||
.stream();
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import { Kysely, sql } from 'kysely';
|
||||
|
||||
export async function up(qb: Kysely<any>): Promise<void> {
|
||||
type Conf = { db: string; guc: string[] };
|
||||
const res = await sql<Conf>`select current_database() db, to_json(setconfig) guc from pg_db_role_setting`.execute(qb);
|
||||
if (res.rows.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { db, guc } = res.rows[0];
|
||||
await sql.raw(`alter database "${db}" reset all;`).execute(qb);
|
||||
for (const parameter of guc) {
|
||||
const [key, value] = parameter.split('=');
|
||||
if (key === 'vchordrq.prewarm_dim') {
|
||||
continue;
|
||||
}
|
||||
await sql.raw(`alter database "${db}" set ${key} to ${value};`).execute(qb);
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(): Promise<void> {}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user