Compare commits
41 Commits
fix/web-no
...
chore/medi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66bde40ef8 | ||
|
|
c707f9cef4 | ||
|
|
6fda863c08 | ||
|
|
373b654156 | ||
|
|
a5d84ba552 | ||
|
|
1dc8fa2979 | ||
|
|
0426699f13 | ||
|
|
8154ec29df | ||
|
|
3024cd343b | ||
|
|
0b44d4b6f2 | ||
|
|
a04c6ed80d | ||
|
|
1c50e19894 | ||
|
|
e61d7f2616 | ||
|
|
a6b0869714 | ||
|
|
9c25b8ba7d | ||
|
|
3c72f489d8 | ||
|
|
1f2c779b36 | ||
|
|
5c74f634b7 | ||
|
|
ecc99bfd16 | ||
|
|
ff4d70e351 | ||
|
|
42c2389eb5 | ||
|
|
33c9f88ba4 | ||
|
|
11c469907f | ||
|
|
7c43e6c3c8 | ||
|
|
00aa385972 | ||
|
|
a5ed453929 | ||
|
|
dd8969cb7d | ||
|
|
bce4f93c90 | ||
|
|
a4c0dc5007 | ||
|
|
d233a7d97a | ||
|
|
5cdbb65d28 | ||
|
|
3434544864 | ||
|
|
269bf4b344 | ||
|
|
f9435a538b | ||
|
|
10e2ec2841 | ||
|
|
fe91b44ab9 | ||
|
|
747a72120e | ||
|
|
910661e75c | ||
|
|
c8a135a7ae | ||
|
|
08d1cf5bde | ||
|
|
3e62497fd0 |
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -644,7 +644,7 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.1
|
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3
|
||||||
env:
|
env:
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
|
|||||||
6
cli/package-lock.json
generated
6
cli/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.69",
|
"version": "2.2.71",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.69",
|
"version": "2.2.71",
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": "^4.0.3",
|
"chokidar": "^4.0.3",
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
},
|
},
|
||||||
"../open-api/typescript-sdk": {
|
"../open-api/typescript-sdk": {
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "1.135.0",
|
"version": "1.135.2",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.69",
|
"version": "2.2.71",
|
||||||
"description": "Command Line Interface (CLI) for Immich",
|
"description": "Command Line Interface (CLI) for Immich",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": "./dist/index.js",
|
"exports": "./dist/index.js",
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ services:
|
|||||||
|
|
||||||
database:
|
database:
|
||||||
container_name: immich_postgres
|
container_name: immich_postgres
|
||||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.2-pgvectors0.2.0
|
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ services:
|
|||||||
|
|
||||||
database:
|
database:
|
||||||
container_name: immich_postgres
|
container_name: immich_postgres
|
||||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.2-pgvectors0.2.0
|
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ services:
|
|||||||
|
|
||||||
database:
|
database:
|
||||||
container_name: immich_postgres
|
container_name: immich_postgres
|
||||||
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.2-pgvectors0.2.0
|
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||||
POSTGRES_USER: ${DB_USERNAME}
|
POSTGRES_USER: ${DB_USERNAME}
|
||||||
|
|||||||
8
docs/static/archived-versions.json
vendored
8
docs/static/archived-versions.json
vendored
@@ -1,4 +1,12 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"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",
|
"label": "v1.135.0",
|
||||||
"url": "https://v1.135.0.archive.immich.app"
|
"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",
|
"name": "immich-e2e",
|
||||||
"version": "1.135.0",
|
"version": "1.135.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "1.135.0",
|
"version": "1.135.2",
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3.1.0",
|
"@eslint/eslintrc": "^3.1.0",
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
},
|
},
|
||||||
"../cli": {
|
"../cli": {
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.69",
|
"version": "2.2.71",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -93,7 +93,7 @@
|
|||||||
},
|
},
|
||||||
"../open-api/typescript-sdk": {
|
"../open-api/typescript-sdk": {
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "1.135.0",
|
"version": "1.135.2",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "1.135.0",
|
"version": "1.135.2",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -14,7 +14,11 @@ describe('/people', () => {
|
|||||||
let nameAlicePerson: PersonResponseDto;
|
let nameAlicePerson: PersonResponseDto;
|
||||||
let nameBobPerson: PersonResponseDto;
|
let nameBobPerson: PersonResponseDto;
|
||||||
let nameCharliePerson: PersonResponseDto;
|
let nameCharliePerson: PersonResponseDto;
|
||||||
let nameNullPerson: PersonResponseDto;
|
let nameNullPerson4Assets: PersonResponseDto;
|
||||||
|
let nameNullPerson3Assets: PersonResponseDto;
|
||||||
|
let nameNullPerson1Asset: PersonResponseDto;
|
||||||
|
let nameBillPersonFavourite: PersonResponseDto;
|
||||||
|
let nameFreddyPersonFavourite: PersonResponseDto;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await utils.resetDatabase();
|
await utils.resetDatabase();
|
||||||
@@ -27,7 +31,11 @@ describe('/people', () => {
|
|||||||
nameCharliePerson,
|
nameCharliePerson,
|
||||||
nameBobPerson,
|
nameBobPerson,
|
||||||
nameAlicePerson,
|
nameAlicePerson,
|
||||||
nameNullPerson,
|
nameNullPerson4Assets,
|
||||||
|
nameNullPerson3Assets,
|
||||||
|
nameNullPerson1Asset,
|
||||||
|
nameBillPersonFavourite,
|
||||||
|
nameFreddyPersonFavourite,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
utils.createPerson(admin.accessToken, {
|
utils.createPerson(admin.accessToken, {
|
||||||
name: 'visible_person',
|
name: 'visible_person',
|
||||||
@@ -52,11 +60,26 @@ describe('/people', () => {
|
|||||||
utils.createPerson(admin.accessToken, {
|
utils.createPerson(admin.accessToken, {
|
||||||
name: '',
|
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 asset1 = await utils.createAsset(admin.accessToken);
|
||||||
const asset2 = await utils.createAsset(admin.accessToken);
|
const asset2 = await utils.createAsset(admin.accessToken);
|
||||||
const asset3 = await utils.createAsset(admin.accessToken);
|
const asset3 = await utils.createAsset(admin.accessToken);
|
||||||
|
const asset4 = await utils.createAsset(admin.accessToken);
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
utils.createFace({ assetId: asset1.id, personId: visiblePerson.id }),
|
utils.createFace({ assetId: asset1.id, personId: visiblePerson.id }),
|
||||||
@@ -64,15 +87,27 @@ describe('/people', () => {
|
|||||||
utils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }),
|
utils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }),
|
||||||
utils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }),
|
utils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }),
|
||||||
utils.createFace({ assetId: asset2.id, personId: multipleAssetsPerson.id }),
|
utils.createFace({ assetId: asset2.id, personId: multipleAssetsPerson.id }),
|
||||||
utils.createFace({ assetId: asset3.id, personId: multipleAssetsPerson.id }),
|
utils.createFace({ assetId: asset3.id, personId: multipleAssetsPerson.id }), // 4 assets
|
||||||
// Named persons
|
// Named persons
|
||||||
utils.createFace({ assetId: asset1.id, personId: nameCharliePerson.id }), // 1 asset
|
utils.createFace({ assetId: asset1.id, personId: nameCharliePerson.id }), // 1 asset
|
||||||
utils.createFace({ assetId: asset1.id, personId: nameBobPerson.id }),
|
utils.createFace({ assetId: asset1.id, personId: nameBobPerson.id }),
|
||||||
utils.createFace({ assetId: asset2.id, personId: nameBobPerson.id }), // 2 assets
|
utils.createFace({ assetId: asset2.id, personId: nameBobPerson.id }), // 2 assets
|
||||||
utils.createFace({ assetId: asset1.id, personId: nameAlicePerson.id }), // 1 asset
|
utils.createFace({ assetId: asset1.id, personId: nameAlicePerson.id }), // 1 asset
|
||||||
// Null-named person
|
// Null-named person 4 assets
|
||||||
utils.createFace({ assetId: asset1.id, personId: nameNullPerson.id }),
|
utils.createFace({ assetId: asset1.id, personId: nameNullPerson4Assets.id }),
|
||||||
utils.createFace({ assetId: asset2.id, personId: nameNullPerson.id }), // 2 assets
|
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 }),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -87,15 +122,19 @@ describe('/people', () => {
|
|||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual({
|
expect(body).toEqual({
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
total: 7,
|
total: 11,
|
||||||
hidden: 1,
|
hidden: 1,
|
||||||
people: [
|
people: [
|
||||||
|
expect.objectContaining({ name: 'Freddy' }),
|
||||||
|
expect.objectContaining({ name: 'Bill' }),
|
||||||
expect.objectContaining({ name: 'multiple_assets_person' }),
|
expect.objectContaining({ name: 'multiple_assets_person' }),
|
||||||
expect.objectContaining({ name: 'Bob' }),
|
expect.objectContaining({ name: 'Bob' }),
|
||||||
expect.objectContaining({ name: 'Alice' }),
|
expect.objectContaining({ name: 'Alice' }),
|
||||||
expect.objectContaining({ name: 'Charlie' }),
|
expect.objectContaining({ name: 'Charlie' }),
|
||||||
expect.objectContaining({ name: 'visible_person' }),
|
expect.objectContaining({ name: 'visible_person' }),
|
||||||
expect.objectContaining({ name: 'hidden_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
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -105,17 +144,21 @@ describe('/people', () => {
|
|||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body.hasNextPage).toBe(false);
|
expect(body.hasNextPage).toBe(false);
|
||||||
expect(body.total).toBe(7); // All persons
|
expect(body.total).toBe(11); // All persons
|
||||||
expect(body.hidden).toBe(1); // 'hidden_person'
|
expect(body.hidden).toBe(1); // 'hidden_person'
|
||||||
|
|
||||||
const people = body.people as PersonResponseDto[];
|
const people = body.people as PersonResponseDto[];
|
||||||
|
|
||||||
expect(people.map((p) => p.id)).toEqual([
|
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
|
multipleAssetsPerson.id, // name: 'multiple_assets_person', count: 3
|
||||||
nameBobPerson.id, // name: 'Bob', count: 2
|
nameBobPerson.id, // name: 'Bob', count: 2
|
||||||
nameAlicePerson.id, // name: 'Alice', count: 1
|
nameAlicePerson.id, // name: 'Alice', count: 1
|
||||||
nameCharliePerson.id, // name: 'Charlie', count: 1
|
nameCharliePerson.id, // name: 'Charlie', count: 1
|
||||||
visiblePerson.id, // name: 'visible_person', 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);
|
expect(people.some((p) => p.id === hiddenPerson.id)).toBe(false);
|
||||||
@@ -127,14 +170,18 @@ describe('/people', () => {
|
|||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual({
|
expect(body).toEqual({
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
total: 7,
|
total: 11,
|
||||||
hidden: 1,
|
hidden: 1,
|
||||||
people: [
|
people: [
|
||||||
|
expect.objectContaining({ name: 'Freddy' }),
|
||||||
|
expect.objectContaining({ name: 'Bill' }),
|
||||||
expect.objectContaining({ name: 'multiple_assets_person' }),
|
expect.objectContaining({ name: 'multiple_assets_person' }),
|
||||||
expect.objectContaining({ name: 'Bob' }),
|
expect.objectContaining({ name: 'Bob' }),
|
||||||
expect.objectContaining({ name: 'Alice' }),
|
expect.objectContaining({ name: 'Alice' }),
|
||||||
expect.objectContaining({ name: 'Charlie' }),
|
expect.objectContaining({ name: 'Charlie' }),
|
||||||
expect.objectContaining({ name: 'visible_person' }),
|
expect.objectContaining({ name: 'visible_person' }),
|
||||||
|
expect.objectContaining({ id: nameNullPerson4Assets.id, name: '' }),
|
||||||
|
expect.objectContaining({ id: nameNullPerson3Assets.id, name: '' }),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -148,9 +195,9 @@ describe('/people', () => {
|
|||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual({
|
expect(body).toEqual({
|
||||||
hasNextPage: true,
|
hasNextPage: true,
|
||||||
total: 7,
|
total: 11,
|
||||||
hidden: 1,
|
hidden: 1,
|
||||||
people: [expect.objectContaining({ name: 'visible_person' })],
|
people: [expect.objectContaining({ name: 'Alice' })],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -244,7 +244,6 @@ async def load(model: InferenceModel) -> InferenceModel:
|
|||||||
|
|
||||||
async def idle_shutdown_task() -> None:
|
async def idle_shutdown_task() -> None:
|
||||||
while True:
|
while True:
|
||||||
log.debug("Checking for inactivity...")
|
|
||||||
if (
|
if (
|
||||||
last_called is not None
|
last_called is not None
|
||||||
and not active_requests
|
and not active_requests
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ platform :android do
|
|||||||
task: 'bundle',
|
task: 'bundle',
|
||||||
build_type: 'Release',
|
build_type: 'Release',
|
||||||
properties: {
|
properties: {
|
||||||
"android.injected.version.code" => 201,
|
"android.injected.version.code" => 203,
|
||||||
"android.injected.version.name" => "1.135.0",
|
"android.injected.version.name" => "1.135.2",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
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')
|
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')
|
||||||
|
|||||||
@@ -649,7 +649,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 209;
|
CURRENT_PROJECT_VERSION = 210;
|
||||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
@@ -793,7 +793,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 209;
|
CURRENT_PROJECT_VERSION = 210;
|
||||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
@@ -823,7 +823,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 209;
|
CURRENT_PROJECT_VERSION = 210;
|
||||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
@@ -857,7 +857,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 209;
|
CURRENT_PROJECT_VERSION = 210;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
@@ -865,7 +865,7 @@
|
|||||||
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = Widget;
|
INFOPLIST_KEY_CFBundleDisplayName = Widget;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 18.5;
|
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
@@ -900,7 +900,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 209;
|
CURRENT_PROJECT_VERSION = 210;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
@@ -908,7 +908,7 @@
|
|||||||
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = Widget;
|
INFOPLIST_KEY_CFBundleDisplayName = Widget;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 18.5;
|
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
@@ -940,7 +940,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 209;
|
CURRENT_PROJECT_VERSION = 210;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
@@ -948,7 +948,7 @@
|
|||||||
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = Widget;
|
INFOPLIST_KEY_CFBundleDisplayName = Widget;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 18.5;
|
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
@@ -979,7 +979,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 209;
|
CURRENT_PROJECT_VERSION = 210;
|
||||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
@@ -1023,7 +1023,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 209;
|
CURRENT_PROJECT_VERSION = 210;
|
||||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
@@ -1064,7 +1064,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 209;
|
CURRENT_PROJECT_VERSION = 210;
|
||||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
|||||||
@@ -78,7 +78,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.135.0</string>
|
<string>1.135.1</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
@@ -93,7 +93,7 @@
|
|||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>209</string>
|
<string>210</string>
|
||||||
<key>FLTEnableImpeller</key>
|
<key>FLTEnableImpeller</key>
|
||||||
<true />
|
<true />
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
@@ -115,8 +115,8 @@
|
|||||||
</dict>
|
</dict>
|
||||||
<key>NSBonjourServices</key>
|
<key>NSBonjourServices</key>
|
||||||
<array>
|
<array>
|
||||||
<string>_googlecast._tcp</string>
|
<string>_googlecast._tcp</string>
|
||||||
<string>_CC1AD845._googlecast._tcp</string>
|
<string>_CC1AD845._googlecast._tcp</string>
|
||||||
</array>
|
</array>
|
||||||
<key>NSCameraUsageDescription</key>
|
<key>NSCameraUsageDescription</key>
|
||||||
<string>We need to access the camera to let you take beautiful video using this app</string>
|
<string>We need to access the camera to let you take beautiful video using this app</string>
|
||||||
@@ -168,5 +168,8 @@
|
|||||||
<true />
|
<true />
|
||||||
<key>NSFaceIDUsageDescription</key>
|
<key>NSFaceIDUsageDescription</key>
|
||||||
<string>We need to use FaceID to allow access to your locked folder</string>
|
<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>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
@@ -43,7 +43,7 @@ struct RandomConfigurationAppIntent: WidgetConfigurationIntent {
|
|||||||
"Choose an album to show images from"
|
"Choose an album to show images from"
|
||||||
}
|
}
|
||||||
|
|
||||||
@Parameter(title: "Album", default: NO_ALBUM)
|
@Parameter(title: "Album")
|
||||||
var album: Album?
|
var album: Album?
|
||||||
|
|
||||||
@Parameter(title: "Show Album Name", default: false)
|
@Parameter(title: "Show Album Name", default: false)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ platform :ios do
|
|||||||
path: "./Runner.xcodeproj",
|
path: "./Runner.xcodeproj",
|
||||||
)
|
)
|
||||||
increment_version_number(
|
increment_version_number(
|
||||||
version_number: "1.135.0"
|
version_number: "1.135.2"
|
||||||
)
|
)
|
||||||
increment_build_number(
|
increment_build_number(
|
||||||
build_number: latest_testflight_build_number + 1,
|
build_number: latest_testflight_build_number + 1,
|
||||||
|
|||||||
@@ -10,3 +10,5 @@ enum TextSearchType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum AssetVisibilityEnum { timeline, hidden, archive, locked }
|
enum AssetVisibilityEnum { timeline, hidden, archive, locked }
|
||||||
|
|
||||||
|
enum SortUserBy { id }
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
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 }
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
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,11 +1,5 @@
|
|||||||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
abstract interface class IPersonApiRepository {
|
|
||||||
Future<List<Person>> getAll();
|
|
||||||
Future<Person> update(String id, {String? name});
|
|
||||||
}
|
|
||||||
|
|
||||||
class Person {
|
class Person {
|
||||||
Person({
|
Person({
|
||||||
required this.id,
|
required this.id,
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:immich_mobile/domain/interfaces/sync_api.interface.dart';
|
import 'package:immich_mobile/domain/interfaces/sync_api.interface.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/sync_stream.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/sync_event.model.dart';
|
import 'package:immich_mobile/domain/models/sync_event.model.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
|
|
||||||
@@ -10,12 +10,12 @@ class SyncStreamService {
|
|||||||
final Logger _logger = Logger('SyncStreamService');
|
final Logger _logger = Logger('SyncStreamService');
|
||||||
|
|
||||||
final ISyncApiRepository _syncApiRepository;
|
final ISyncApiRepository _syncApiRepository;
|
||||||
final ISyncStreamRepository _syncStreamRepository;
|
final SyncStreamRepository _syncStreamRepository;
|
||||||
final bool Function()? _cancelChecker;
|
final bool Function()? _cancelChecker;
|
||||||
|
|
||||||
SyncStreamService({
|
SyncStreamService({
|
||||||
required ISyncApiRepository syncApiRepository,
|
required ISyncApiRepository syncApiRepository,
|
||||||
required ISyncStreamRepository syncStreamRepository,
|
required SyncStreamRepository syncStreamRepository,
|
||||||
bool Function()? cancelChecker,
|
bool Function()? cancelChecker,
|
||||||
}) : _syncApiRepository = syncApiRepository,
|
}) : _syncApiRepository = syncApiRepository,
|
||||||
_syncStreamRepository = syncStreamRepository,
|
_syncStreamRepository = syncStreamRepository,
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:typed_data';
|
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/store.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||||
import 'package:immich_mobile/domain/services/store.service.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';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
class UserService {
|
class UserService {
|
||||||
final Logger _log = Logger("UserService");
|
final Logger _log = Logger("UserService");
|
||||||
final IUserRepository _userRepository;
|
final IsarUserRepository _isarUserRepository;
|
||||||
final IUserApiRepository _userApiRepository;
|
final UserApiRepository _userApiRepository;
|
||||||
final StoreService _storeService;
|
final StoreService _storeService;
|
||||||
|
|
||||||
UserService({
|
UserService({
|
||||||
required IUserRepository userRepository,
|
required IsarUserRepository isarUserRepository,
|
||||||
required IUserApiRepository userApiRepository,
|
required UserApiRepository userApiRepository,
|
||||||
required StoreService storeService,
|
required StoreService storeService,
|
||||||
}) : _userRepository = userRepository,
|
}) : _isarUserRepository = isarUserRepository,
|
||||||
_userApiRepository = userApiRepository,
|
_userApiRepository = userApiRepository,
|
||||||
_storeService = storeService;
|
_storeService = storeService;
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ class UserService {
|
|||||||
final user = await _userApiRepository.getMyUser();
|
final user = await _userApiRepository.getMyUser();
|
||||||
if (user == null) return null;
|
if (user == null) return null;
|
||||||
await _storeService.put(StoreKey.currentUser, user);
|
await _storeService.put(StoreKey.currentUser, user);
|
||||||
await _userRepository.update(user);
|
await _isarUserRepository.update(user);
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ class UserService {
|
|||||||
);
|
);
|
||||||
final updatedUser = getMyUser().copyWith(profileImagePath: path);
|
final updatedUser = getMyUser().copyWith(profileImagePath: path);
|
||||||
await _storeService.put(StoreKey.currentUser, updatedUser);
|
await _storeService.put(StoreKey.currentUser, updatedUser);
|
||||||
await _userRepository.update(updatedUser);
|
await _isarUserRepository.update(updatedUser);
|
||||||
return path;
|
return path;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_log.warning("Failed to upload profile image", e);
|
_log.warning("Failed to upload profile image", e);
|
||||||
@@ -59,10 +59,10 @@ class UserService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<List<UserDto>> getAll() async {
|
Future<List<UserDto>> getAll() async {
|
||||||
return await _userRepository.getAll();
|
return await _isarUserRepository.getAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteAll() {
|
Future<void> deleteAll() {
|
||||||
return _userRepository.deleteAll();
|
return _isarUserRepository.deleteAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:drift/drift.dart';
|
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/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart';
|
||||||
@@ -10,14 +9,12 @@ import 'package:logging/logging.dart';
|
|||||||
import 'package:openapi/api.dart' as api show AssetVisibility;
|
import 'package:openapi/api.dart' as api show AssetVisibility;
|
||||||
import 'package:openapi/api.dart' hide AssetVisibility;
|
import 'package:openapi/api.dart' hide AssetVisibility;
|
||||||
|
|
||||||
class DriftSyncStreamRepository extends DriftDatabaseRepository
|
class SyncStreamRepository extends DriftDatabaseRepository {
|
||||||
implements ISyncStreamRepository {
|
|
||||||
final Logger _logger = Logger('DriftSyncStreamRepository');
|
final Logger _logger = Logger('DriftSyncStreamRepository');
|
||||||
final Drift _db;
|
final Drift _db;
|
||||||
|
|
||||||
DriftSyncStreamRepository(super.db) : _db = db;
|
SyncStreamRepository(super.db) : _db = db;
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> deleteUsersV1(Iterable<SyncUserDeleteV1> data) async {
|
Future<void> deleteUsersV1(Iterable<SyncUserDeleteV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _db.batch((batch) {
|
await _db.batch((batch) {
|
||||||
@@ -34,7 +31,6 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> updateUsersV1(Iterable<SyncUserV1> data) async {
|
Future<void> updateUsersV1(Iterable<SyncUserV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _db.batch((batch) {
|
await _db.batch((batch) {
|
||||||
@@ -57,7 +53,6 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> deletePartnerV1(Iterable<SyncPartnerDeleteV1> data) async {
|
Future<void> deletePartnerV1(Iterable<SyncPartnerDeleteV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _db.batch((batch) {
|
await _db.batch((batch) {
|
||||||
@@ -77,7 +72,6 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> updatePartnerV1(Iterable<SyncPartnerV1> data) async {
|
Future<void> updatePartnerV1(Iterable<SyncPartnerV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _db.batch((batch) {
|
await _db.batch((batch) {
|
||||||
@@ -101,7 +95,6 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> deleteAssetsV1(Iterable<SyncAssetDeleteV1> data) async {
|
Future<void> deleteAssetsV1(Iterable<SyncAssetDeleteV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _deleteAssetsV1(data);
|
await _deleteAssetsV1(data);
|
||||||
@@ -111,7 +104,6 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> updateAssetsV1(Iterable<SyncAssetV1> data) async {
|
Future<void> updateAssetsV1(Iterable<SyncAssetV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _updateAssetsV1(data);
|
await _updateAssetsV1(data);
|
||||||
@@ -121,7 +113,6 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> deletePartnerAssetsV1(Iterable<SyncAssetDeleteV1> data) async {
|
Future<void> deletePartnerAssetsV1(Iterable<SyncAssetDeleteV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _deleteAssetsV1(data);
|
await _deleteAssetsV1(data);
|
||||||
@@ -131,7 +122,6 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> updatePartnerAssetsV1(Iterable<SyncAssetV1> data) async {
|
Future<void> updatePartnerAssetsV1(Iterable<SyncAssetV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _updateAssetsV1(data);
|
await _updateAssetsV1(data);
|
||||||
@@ -141,7 +131,6 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> updateAssetsExifV1(Iterable<SyncAssetExifV1> data) async {
|
Future<void> updateAssetsExifV1(Iterable<SyncAssetExifV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _updateAssetExifV1(data);
|
await _updateAssetExifV1(data);
|
||||||
@@ -151,7 +140,6 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> updatePartnerAssetsExifV1(Iterable<SyncAssetExifV1> data) async {
|
Future<void> updatePartnerAssetsExifV1(Iterable<SyncAssetExifV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _updateAssetExifV1(data);
|
await _updateAssetExifV1(data);
|
||||||
|
|||||||
@@ -1,30 +1,26 @@
|
|||||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
import 'package:immich_mobile/constants/enums.dart';
|
||||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
|
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
|
||||||
as entity;
|
as entity;
|
||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
|
|
||||||
class IsarUserRepository extends IsarDatabaseRepository
|
class IsarUserRepository extends IsarDatabaseRepository {
|
||||||
implements IUserRepository {
|
|
||||||
final Isar _db;
|
final Isar _db;
|
||||||
const IsarUserRepository(super.db) : _db = db;
|
const IsarUserRepository(super.db) : _db = db;
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> delete(List<String> ids) async {
|
Future<void> delete(List<String> ids) async {
|
||||||
await transaction(() async {
|
await transaction(() async {
|
||||||
await _db.users.deleteAllById(ids);
|
await _db.users.deleteAllById(ids);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> deleteAll() async {
|
Future<void> deleteAll() async {
|
||||||
await transaction(() async {
|
await transaction(() async {
|
||||||
await _db.users.clear();
|
await _db.users.clear();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<UserDto>> getAll({SortUserBy? sortBy}) async {
|
Future<List<UserDto>> getAll({SortUserBy? sortBy}) async {
|
||||||
return (await _db.users
|
return (await _db.users
|
||||||
.where()
|
.where()
|
||||||
@@ -39,17 +35,14 @@ class IsarUserRepository extends IsarDatabaseRepository
|
|||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<UserDto?> getByUserId(String id) async {
|
Future<UserDto?> getByUserId(String id) async {
|
||||||
return (await _db.users.getById(id))?.toDto();
|
return (await _db.users.getById(id))?.toDto();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<UserDto?>> getByUserIds(List<String> ids) async {
|
Future<List<UserDto?>> getByUserIds(List<String> ids) async {
|
||||||
return (await _db.users.getAllById(ids)).map((u) => u?.toDto()).toList();
|
return (await _db.users.getAllById(ids)).map((u) => u?.toDto()).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<bool> insert(UserDto user) async {
|
Future<bool> insert(UserDto user) async {
|
||||||
await transaction(() async {
|
await transaction(() async {
|
||||||
await _db.users.put(entity.User.fromDto(user));
|
await _db.users.put(entity.User.fromDto(user));
|
||||||
@@ -57,7 +50,6 @@ class IsarUserRepository extends IsarDatabaseRepository
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<UserDto> update(UserDto user) async {
|
Future<UserDto> update(UserDto user) async {
|
||||||
await transaction(() async {
|
await transaction(() async {
|
||||||
await _db.users.put(entity.User.fromDto(user));
|
await _db.users.put(entity.User.fromDto(user));
|
||||||
@@ -65,7 +57,6 @@ class IsarUserRepository extends IsarDatabaseRepository
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<bool> updateAll(List<UserDto> users) async {
|
Future<bool> updateAll(List<UserDto> users) async {
|
||||||
await transaction(() async {
|
await transaction(() async {
|
||||||
await _db.users.putAll(users.map(entity.User.fromDto).toList());
|
await _db.users.putAll(users.map(entity.User.fromDto).toList());
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:http/http.dart';
|
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/domain/models/user.model.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/api.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/api.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/utils/user.converter.dart';
|
import 'package:immich_mobile/infrastructure/utils/user.converter.dart';
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
|
|
||||||
class UserApiRepository extends ApiRepository implements IUserApiRepository {
|
class UserApiRepository extends ApiRepository {
|
||||||
final UsersApi _api;
|
final UsersApi _api;
|
||||||
const UserApiRepository(this._api);
|
const UserApiRepository(this._api);
|
||||||
|
|
||||||
@override
|
|
||||||
Future<UserDto?> getMyUser() async {
|
Future<UserDto?> getMyUser() async {
|
||||||
final (adminDto, preferenceDto) =
|
final (adminDto, preferenceDto) =
|
||||||
await (_api.getMyUser(), _api.getMyPreferences()).wait;
|
await (_api.getMyUser(), _api.getMyPreferences()).wait;
|
||||||
@@ -20,7 +18,6 @@ class UserApiRepository extends ApiRepository implements IUserApiRepository {
|
|||||||
return UserConverter.fromAdminDto(adminDto, preferenceDto);
|
return UserConverter.fromAdminDto(adminDto, preferenceDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<String> createProfileImage({
|
Future<String> createProfileImage({
|
||||||
required String name,
|
required String name,
|
||||||
required Uint8List data,
|
required Uint8List data,
|
||||||
@@ -33,7 +30,6 @@ class UserApiRepository extends ApiRepository implements IUserApiRepository {
|
|||||||
return res.profileImagePath;
|
return res.profileImagePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<UserDto>> getAll() async {
|
Future<List<UserDto>> getAll() async {
|
||||||
final dto = await checkNull(_api.searchUsers());
|
final dto = await checkNull(_api.searchUsers());
|
||||||
return dto.map(UserConverter.fromSimpleUserDto).toList();
|
return dto.map(UserConverter.fromSimpleUserDto).toList();
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
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});
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
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});
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import 'package:immich_mobile/models/auth/biometric_status.model.dart';
|
|
||||||
|
|
||||||
abstract interface class IBiometricRepository {
|
|
||||||
Future<BiometricStatus> getStatus();
|
|
||||||
Future<bool> authenticate(String? message);
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
abstract interface class INetworkRepository {
|
|
||||||
Future<String?> getWifiName();
|
|
||||||
Future<String?> getWifiIp();
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
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 +0,0 @@
|
|||||||
abstract interface class ISecureStorageRepository {
|
|
||||||
Future<String?> read(String key);
|
|
||||||
Future<void> write(String key, String value);
|
|
||||||
Future<void> delete(String key);
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import 'package:immich_mobile/models/sessions/session_create_response.model.dart';
|
|
||||||
|
|
||||||
abstract interface class ISessionAPIRepository {
|
|
||||||
Future<SessionCreateResponse> createSession(
|
|
||||||
String deviceName,
|
|
||||||
String deviceOS, {
|
|
||||||
int? duration,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart';
|
|
||||||
|
|
||||||
abstract interface class IShareHandlerRepository {
|
|
||||||
void Function(List<ShareIntentAttachment>)? onSharedMedia;
|
|
||||||
|
|
||||||
Future<void> init();
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
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
|
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:immich_mobile/domain/models/person.model.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/interfaces/person_api.interface.dart';
|
|
||||||
|
|
||||||
class SearchLocationFilter {
|
class SearchLocationFilter {
|
||||||
String? country;
|
String? country;
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/constants/enums.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/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/interfaces/person_api.interface.dart';
|
|
||||||
import 'package:immich_mobile/models/search/search_filter.model.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/paginated_search.provider.dart';
|
||||||
import 'package:immich_mobile/providers/search/search_input_focus.provider.dart';
|
import 'package:immich_mobile/providers/search/search_input_focus.provider.dart';
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import 'package:immich_mobile/domain/models/store.model.dart';
|
|||||||
import 'package:immich_mobile/entities/album.entity.dart';
|
import 'package:immich_mobile/entities/album.entity.dart';
|
||||||
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
||||||
import 'package:immich_mobile/entities/store.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/backup_album.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/file_media.interface.dart';
|
import 'package:immich_mobile/interfaces/file_media.interface.dart';
|
||||||
import 'package:immich_mobile/models/auth/auth_state.model.dart';
|
import 'package:immich_mobile/models/auth/auth_state.model.dart';
|
||||||
@@ -108,7 +107,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|||||||
final AuthState _authState;
|
final AuthState _authState;
|
||||||
final BackgroundService _backgroundService;
|
final BackgroundService _backgroundService;
|
||||||
final GalleryPermissionNotifier _galleryPermissionNotifier;
|
final GalleryPermissionNotifier _galleryPermissionNotifier;
|
||||||
final IAlbumMediaRepository _albumMediaRepository;
|
final AlbumMediaRepository _albumMediaRepository;
|
||||||
final IFileMediaRepository _fileMediaRepository;
|
final IFileMediaRepository _fileMediaRepository;
|
||||||
final BackupAlbumService _backupAlbumService;
|
final BackupAlbumService _backupAlbumService;
|
||||||
final Ref ref;
|
final Ref ref;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ final syncApiRepositoryProvider = Provider(
|
|||||||
);
|
);
|
||||||
|
|
||||||
final syncStreamRepositoryProvider = Provider(
|
final syncStreamRepositoryProvider = Provider(
|
||||||
(ref) => DriftSyncStreamRepository(ref.watch(driftProvider)),
|
(ref) => SyncStreamRepository(ref.watch(driftProvider)),
|
||||||
);
|
);
|
||||||
|
|
||||||
final localSyncServiceProvider = Provider(
|
final localSyncServiceProvider = Provider(
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
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/domain/services/user.service.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/user.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/infrastructure/repositories/user_api.repository.dart';
|
||||||
@@ -12,16 +10,16 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
|
|||||||
part 'user.provider.g.dart';
|
part 'user.provider.g.dart';
|
||||||
|
|
||||||
@Riverpod(keepAlive: true)
|
@Riverpod(keepAlive: true)
|
||||||
IUserRepository userRepository(Ref ref) =>
|
IsarUserRepository userRepository(Ref ref) =>
|
||||||
IsarUserRepository(ref.watch(isarProvider));
|
IsarUserRepository(ref.watch(isarProvider));
|
||||||
|
|
||||||
@Riverpod(keepAlive: true)
|
@Riverpod(keepAlive: true)
|
||||||
IUserApiRepository userApiRepository(Ref ref) =>
|
UserApiRepository userApiRepository(Ref ref) =>
|
||||||
UserApiRepository(ref.watch(apiServiceProvider).usersApi);
|
UserApiRepository(ref.watch(apiServiceProvider).usersApi);
|
||||||
|
|
||||||
@Riverpod(keepAlive: true)
|
@Riverpod(keepAlive: true)
|
||||||
UserService userService(Ref ref) => UserService(
|
UserService userService(Ref ref) => UserService(
|
||||||
userRepository: ref.watch(userRepositoryProvider),
|
isarUserRepository: ref.watch(userRepositoryProvider),
|
||||||
userApiRepository: ref.watch(userApiRepositoryProvider),
|
userApiRepository: ref.watch(userApiRepositoryProvider),
|
||||||
storeService: ref.watch(storeServiceProvider),
|
storeService: ref.watch(storeServiceProvider),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ part of 'user.provider.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$userRepositoryHash() => r'1a2ac726bcc44397dcaecf449084fefd336696d4';
|
String _$userRepositoryHash() => r'538791a4ad126ed086c9db682c67fc5c654d54f3';
|
||||||
|
|
||||||
/// See also [userRepository].
|
/// See also [userRepository].
|
||||||
@ProviderFor(userRepository)
|
@ProviderFor(userRepository)
|
||||||
final userRepositoryProvider = Provider<IUserRepository>.internal(
|
final userRepositoryProvider = Provider<IsarUserRepository>.internal(
|
||||||
userRepository,
|
userRepository,
|
||||||
name: r'userRepositoryProvider',
|
name: r'userRepositoryProvider',
|
||||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||||
@@ -22,12 +22,12 @@ final userRepositoryProvider = Provider<IUserRepository>.internal(
|
|||||||
|
|
||||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
// ignore: unused_element
|
// ignore: unused_element
|
||||||
typedef UserRepositoryRef = ProviderRef<IUserRepository>;
|
typedef UserRepositoryRef = ProviderRef<IsarUserRepository>;
|
||||||
String _$userApiRepositoryHash() => r'6b19f2c99fb83162a5ceb91adb8589eaae01bc92';
|
String _$userApiRepositoryHash() => r'8a7340ca4544c8c6b20225c65bff2abb9e96baa2';
|
||||||
|
|
||||||
/// See also [userApiRepository].
|
/// See also [userApiRepository].
|
||||||
@ProviderFor(userApiRepository)
|
@ProviderFor(userApiRepository)
|
||||||
final userApiRepositoryProvider = Provider<IUserApiRepository>.internal(
|
final userApiRepositoryProvider = Provider<UserApiRepository>.internal(
|
||||||
userApiRepository,
|
userApiRepository,
|
||||||
name: r'userApiRepositoryProvider',
|
name: r'userApiRepositoryProvider',
|
||||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||||
@@ -39,8 +39,8 @@ final userApiRepositoryProvider = Provider<IUserApiRepository>.internal(
|
|||||||
|
|
||||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
// ignore: unused_element
|
// ignore: unused_element
|
||||||
typedef UserApiRepositoryRef = ProviderRef<IUserApiRepository>;
|
typedef UserApiRepositoryRef = ProviderRef<UserApiRepository>;
|
||||||
String _$userServiceHash() => r'4a0873357b7115b4d6bfa8e89b847c0b74ce0d93';
|
String _$userServiceHash() => r'181414dddc7891be6237e13d568c287a804228d1';
|
||||||
|
|
||||||
/// See also [userService].
|
/// See also [userService].
|
||||||
@ProviderFor(userService)
|
@ProviderFor(userService)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/interfaces/person_api.interface.dart';
|
import 'package:immich_mobile/domain/models/person.model.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.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/services/person.service.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/infrastructure/utils/user.converter.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/models/activities/activity.model.dart';
|
||||||
import 'package:immich_mobile/providers/api.provider.dart';
|
import 'package:immich_mobile/providers/api.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/api.repository.dart';
|
import 'package:immich_mobile/repositories/api.repository.dart';
|
||||||
@@ -10,20 +9,17 @@ final activityApiRepositoryProvider = Provider(
|
|||||||
(ref) => ActivityApiRepository(ref.watch(apiServiceProvider).activitiesApi),
|
(ref) => ActivityApiRepository(ref.watch(apiServiceProvider).activitiesApi),
|
||||||
);
|
);
|
||||||
|
|
||||||
class ActivityApiRepository extends ApiRepository
|
class ActivityApiRepository extends ApiRepository {
|
||||||
implements IActivityApiRepository {
|
|
||||||
final ActivitiesApi _api;
|
final ActivitiesApi _api;
|
||||||
|
|
||||||
ActivityApiRepository(this._api);
|
ActivityApiRepository(this._api);
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<Activity>> getAll(String albumId, {String? assetId}) async {
|
Future<List<Activity>> getAll(String albumId, {String? assetId}) async {
|
||||||
final response =
|
final response =
|
||||||
await checkNull(_api.getActivities(albumId, assetId: assetId));
|
await checkNull(_api.getActivities(albumId, assetId: assetId));
|
||||||
return response.map(_toActivity).toList();
|
return response.map(_toActivity).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Activity> create(
|
Future<Activity> create(
|
||||||
String albumId,
|
String albumId,
|
||||||
ActivityType type, {
|
ActivityType type, {
|
||||||
@@ -42,12 +38,10 @@ class ActivityApiRepository extends ApiRepository
|
|||||||
return _toActivity(response);
|
return _toActivity(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> delete(String id) {
|
Future<void> delete(String id) {
|
||||||
return checkNull(_api.deleteActivity(id));
|
return checkNull(_api.deleteActivity(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<ActivityStats> getStats(String albumId, {String? assetId}) async {
|
Future<ActivityStats> getStats(String albumId, {String? assetId}) async {
|
||||||
final response =
|
final response =
|
||||||
await checkNull(_api.getActivityStatistics(albumId, assetId: assetId));
|
await checkNull(_api.getActivityStatistics(albumId, assetId: assetId));
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import 'package:immich_mobile/entities/asset.entity.dart';
|
|||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
|
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
|
||||||
as entity;
|
as entity;
|
||||||
import 'package:immich_mobile/infrastructure/utils/user.converter.dart';
|
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/providers/api.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/api.repository.dart';
|
import 'package:immich_mobile/repositories/api.repository.dart';
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
@@ -14,24 +13,21 @@ final albumApiRepositoryProvider = Provider(
|
|||||||
(ref) => AlbumApiRepository(ref.watch(apiServiceProvider).albumsApi),
|
(ref) => AlbumApiRepository(ref.watch(apiServiceProvider).albumsApi),
|
||||||
);
|
);
|
||||||
|
|
||||||
class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository {
|
class AlbumApiRepository extends ApiRepository {
|
||||||
final AlbumsApi _api;
|
final AlbumsApi _api;
|
||||||
|
|
||||||
AlbumApiRepository(this._api);
|
AlbumApiRepository(this._api);
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Album> get(String id) async {
|
Future<Album> get(String id) async {
|
||||||
final dto = await checkNull(_api.getAlbumInfo(id));
|
final dto = await checkNull(_api.getAlbumInfo(id));
|
||||||
return _toAlbum(dto);
|
return _toAlbum(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<Album>> getAll({bool? shared}) async {
|
Future<List<Album>> getAll({bool? shared}) async {
|
||||||
final dtos = await checkNull(_api.getAllAlbums(shared: shared));
|
final dtos = await checkNull(_api.getAllAlbums(shared: shared));
|
||||||
return dtos.map(_toAlbum).toList();
|
return dtos.map(_toAlbum).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Album> create(
|
Future<Album> create(
|
||||||
String name, {
|
String name, {
|
||||||
required Iterable<String> assetIds,
|
required Iterable<String> assetIds,
|
||||||
@@ -54,7 +50,6 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository {
|
|||||||
return _toAlbum(responseDto);
|
return _toAlbum(responseDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Album> update(
|
Future<Album> update(
|
||||||
String albumId, {
|
String albumId, {
|
||||||
String? name,
|
String? name,
|
||||||
@@ -84,12 +79,10 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository {
|
|||||||
return _toAlbum(response);
|
return _toAlbum(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> delete(String albumId) {
|
Future<void> delete(String albumId) {
|
||||||
return _api.deleteAlbum(albumId);
|
return _api.deleteAlbum(albumId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<({List<String> added, List<String> duplicates})> addAssets(
|
Future<({List<String> added, List<String> duplicates})> addAssets(
|
||||||
String albumId,
|
String albumId,
|
||||||
Iterable<String> assetIds,
|
Iterable<String> assetIds,
|
||||||
@@ -114,7 +107,6 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository {
|
|||||||
return (added: added, duplicates: duplicates);
|
return (added: added, duplicates: duplicates);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<({List<String> removed, List<String> failed})> removeAssets(
|
Future<({List<String> removed, List<String> failed})> removeAssets(
|
||||||
String albumId,
|
String albumId,
|
||||||
Iterable<String> assetIds,
|
Iterable<String> assetIds,
|
||||||
@@ -136,7 +128,6 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository {
|
|||||||
return (removed: removed, failed: failed);
|
return (removed: removed, failed: failed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Album> addUsers(String albumId, Iterable<String> userIds) async {
|
Future<Album> addUsers(String albumId, Iterable<String> userIds) async {
|
||||||
final albumUsers =
|
final albumUsers =
|
||||||
userIds.map((userId) => AlbumUserAddDto(userId: userId)).toList();
|
userIds.map((userId) => AlbumUserAddDto(userId: userId)).toList();
|
||||||
@@ -149,7 +140,6 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository {
|
|||||||
return _toAlbum(response);
|
return _toAlbum(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> removeUser(String albumId, {required String userId}) {
|
Future<void> removeUser(String albumId, {required String userId}) {
|
||||||
return _api.removeUserFromAlbum(albumId, userId);
|
return _api.removeUserFromAlbum(albumId, userId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,13 @@ import 'package:immich_mobile/entities/album.entity.dart';
|
|||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.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:immich_mobile/repositories/asset_media.repository.dart';
|
||||||
import 'package:photo_manager/photo_manager.dart' hide AssetType;
|
import 'package:photo_manager/photo_manager.dart' hide AssetType;
|
||||||
|
|
||||||
final albumMediaRepositoryProvider =
|
final albumMediaRepositoryProvider =
|
||||||
Provider((ref) => const AlbumMediaRepository());
|
Provider((ref) => const AlbumMediaRepository());
|
||||||
|
|
||||||
class AlbumMediaRepository implements IAlbumMediaRepository {
|
class AlbumMediaRepository {
|
||||||
const AlbumMediaRepository();
|
const AlbumMediaRepository();
|
||||||
|
|
||||||
bool get useCustomFilter =>
|
bool get useCustomFilter =>
|
||||||
@@ -41,7 +40,6 @@ class AlbumMediaRepository implements IAlbumMediaRepository {
|
|||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<Album>> getAll() async {
|
Future<List<Album>> getAll() async {
|
||||||
final filter = useCustomFilter
|
final filter = useCustomFilter
|
||||||
? CustomFilter.sql(where: '${CustomColumns.base.width} > 0')
|
? CustomFilter.sql(where: '${CustomColumns.base.width} > 0')
|
||||||
@@ -52,7 +50,6 @@ class AlbumMediaRepository implements IAlbumMediaRepository {
|
|||||||
return assetPathEntities.map(_toAlbum).toList();
|
return assetPathEntities.map(_toAlbum).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<String>> getAssetIds(String albumId) async {
|
Future<List<String>> getAssetIds(String albumId) async {
|
||||||
final album =
|
final album =
|
||||||
await AssetPathEntity.fromId(albumId, filterOption: _getAlbumFilter());
|
await AssetPathEntity.fromId(albumId, filterOption: _getAlbumFilter());
|
||||||
@@ -61,14 +58,12 @@ class AlbumMediaRepository implements IAlbumMediaRepository {
|
|||||||
return assets.map((e) => e.id).toList();
|
return assets.map((e) => e.id).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<int> getAssetCount(String albumId) async {
|
Future<int> getAssetCount(String albumId) async {
|
||||||
final album =
|
final album =
|
||||||
await AssetPathEntity.fromId(albumId, filterOption: _getAlbumFilter());
|
await AssetPathEntity.fromId(albumId, filterOption: _getAlbumFilter());
|
||||||
return album.assetCountAsync;
|
return album.assetCountAsync;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<Asset>> getAssets(
|
Future<List<Asset>> getAssets(
|
||||||
String albumId, {
|
String albumId, {
|
||||||
int start = 0,
|
int start = 0,
|
||||||
@@ -97,12 +92,7 @@ class AlbumMediaRepository implements IAlbumMediaRepository {
|
|||||||
return assets.map(AssetMediaRepository.toAsset).toList().cast();
|
return assets.map(AssetMediaRepository.toAsset).toList().cast();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
Future<Album> get(String id) async {
|
||||||
Future<Album> get(
|
|
||||||
String id, {
|
|
||||||
DateTime? modifiedFrom,
|
|
||||||
DateTime? modifiedUntil,
|
|
||||||
}) async {
|
|
||||||
final assetPathEntity = await AssetPathEntity.fromId(
|
final assetPathEntity = await AssetPathEntity.fromId(
|
||||||
id,
|
id,
|
||||||
filterOption: _getAlbumFilter(containsPathModified: true),
|
filterOption: _getAlbumFilter(containsPathModified: true),
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.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:immich_mobile/models/auth/biometric_status.model.dart';
|
||||||
import 'package:local_auth/local_auth.dart';
|
import 'package:local_auth/local_auth.dart';
|
||||||
|
|
||||||
final biometricRepositoryProvider =
|
final biometricRepositoryProvider =
|
||||||
Provider((ref) => BiometricRepository(LocalAuthentication()));
|
Provider((ref) => BiometricRepository(LocalAuthentication()));
|
||||||
|
|
||||||
class BiometricRepository implements IBiometricRepository {
|
class BiometricRepository {
|
||||||
final LocalAuthentication _localAuth;
|
final LocalAuthentication _localAuth;
|
||||||
|
|
||||||
BiometricRepository(this._localAuth);
|
BiometricRepository(this._localAuth);
|
||||||
|
|
||||||
@override
|
|
||||||
Future<BiometricStatus> getStatus() async {
|
Future<BiometricStatus> getStatus() async {
|
||||||
final bool canAuthenticateWithBiometrics =
|
final bool canAuthenticateWithBiometrics =
|
||||||
await _localAuth.canCheckBiometrics;
|
await _localAuth.canCheckBiometrics;
|
||||||
@@ -26,7 +24,6 @@ class BiometricRepository implements IBiometricRepository {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<bool> authenticate(String? message) async {
|
Future<bool> authenticate(String? message) async {
|
||||||
return _localAuth.authenticate(
|
return _localAuth.authenticate(
|
||||||
localizedReason: message ?? 'please_auth_to_access'.tr(),
|
localizedReason: message ?? 'please_auth_to_access'.tr(),
|
||||||
|
|||||||
@@ -1,21 +1,16 @@
|
|||||||
import 'package:background_downloader/background_downloader.dart';
|
import 'package:background_downloader/background_downloader.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/interfaces/download.interface.dart';
|
|
||||||
import 'package:immich_mobile/utils/download.dart';
|
import 'package:immich_mobile/utils/download.dart';
|
||||||
|
|
||||||
final downloadRepositoryProvider = Provider((ref) => DownloadRepository());
|
final downloadRepositoryProvider = Provider((ref) => DownloadRepository());
|
||||||
|
|
||||||
class DownloadRepository implements IDownloadRepository {
|
class DownloadRepository {
|
||||||
@override
|
|
||||||
void Function(TaskStatusUpdate)? onImageDownloadStatus;
|
void Function(TaskStatusUpdate)? onImageDownloadStatus;
|
||||||
|
|
||||||
@override
|
|
||||||
void Function(TaskStatusUpdate)? onVideoDownloadStatus;
|
void Function(TaskStatusUpdate)? onVideoDownloadStatus;
|
||||||
|
|
||||||
@override
|
|
||||||
void Function(TaskStatusUpdate)? onLivePhotoDownloadStatus;
|
void Function(TaskStatusUpdate)? onLivePhotoDownloadStatus;
|
||||||
|
|
||||||
@override
|
|
||||||
void Function(TaskProgressUpdate)? onTaskProgress;
|
void Function(TaskProgressUpdate)? onTaskProgress;
|
||||||
|
|
||||||
DownloadRepository() {
|
DownloadRepository() {
|
||||||
@@ -38,22 +33,18 @@ class DownloadRepository implements IDownloadRepository {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<bool>> downloadAll(List<DownloadTask> tasks) {
|
Future<List<bool>> downloadAll(List<DownloadTask> tasks) {
|
||||||
return FileDownloader().enqueueAll(tasks);
|
return FileDownloader().enqueueAll(tasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> deleteAllTrackingRecords() {
|
Future<void> deleteAllTrackingRecords() {
|
||||||
return FileDownloader().database.deleteAllRecords();
|
return FileDownloader().database.deleteAllRecords();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<bool> cancel(String id) {
|
Future<bool> cancel(String id) {
|
||||||
return FileDownloader().cancelTaskWithId(id);
|
return FileDownloader().cancelTaskWithId(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<TaskRecord>> getLiveVideoTasks() {
|
Future<List<TaskRecord>> getLiveVideoTasks() {
|
||||||
return FileDownloader().database.allRecordsWithStatus(
|
return FileDownloader().database.allRecordsWithStatus(
|
||||||
TaskStatus.complete,
|
TaskStatus.complete,
|
||||||
@@ -61,7 +52,6 @@ class DownloadRepository implements IDownloadRepository {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> deleteRecordsWithIds(List<String> ids) {
|
Future<void> deleteRecordsWithIds(List<String> ids) {
|
||||||
return FileDownloader().database.deleteRecordsWithIds(ids);
|
return FileDownloader().database.deleteRecordsWithIds(ids);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/interfaces/network.interface.dart';
|
|
||||||
import 'package:network_info_plus/network_info_plus.dart';
|
import 'package:network_info_plus/network_info_plus.dart';
|
||||||
|
|
||||||
final networkRepositoryProvider = Provider((_) {
|
final networkRepositoryProvider = Provider((_) {
|
||||||
@@ -10,12 +9,11 @@ final networkRepositoryProvider = Provider((_) {
|
|||||||
return NetworkRepository(networkInfo);
|
return NetworkRepository(networkInfo);
|
||||||
});
|
});
|
||||||
|
|
||||||
class NetworkRepository implements INetworkRepository {
|
class NetworkRepository {
|
||||||
final NetworkInfo _networkInfo;
|
final NetworkInfo _networkInfo;
|
||||||
|
|
||||||
NetworkRepository(this._networkInfo);
|
NetworkRepository(this._networkInfo);
|
||||||
|
|
||||||
@override
|
|
||||||
Future<String?> getWifiName() {
|
Future<String?> getWifiName() {
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
// remove quote around the return value on Android
|
// remove quote around the return value on Android
|
||||||
@@ -30,7 +28,6 @@ class NetworkRepository implements INetworkRepository {
|
|||||||
return _networkInfo.getWifiName();
|
return _networkInfo.getWifiName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<String?> getWifiIp() {
|
Future<String?> getWifiIp() {
|
||||||
return _networkInfo.getWifiIP();
|
return _networkInfo.getWifiIP();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
|
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
|
||||||
as entity;
|
as entity;
|
||||||
import 'package:immich_mobile/interfaces/partner.interface.dart';
|
|
||||||
import 'package:immich_mobile/providers/db.provider.dart';
|
import 'package:immich_mobile/providers/db.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/database.repository.dart';
|
import 'package:immich_mobile/repositories/database.repository.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
@@ -11,11 +10,9 @@ final partnerRepositoryProvider = Provider(
|
|||||||
(ref) => PartnerRepository(ref.watch(dbProvider)),
|
(ref) => PartnerRepository(ref.watch(dbProvider)),
|
||||||
);
|
);
|
||||||
|
|
||||||
class PartnerRepository extends DatabaseRepository
|
class PartnerRepository extends DatabaseRepository {
|
||||||
implements IPartnerRepository {
|
|
||||||
PartnerRepository(super.db);
|
PartnerRepository(super.db);
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<UserDto>> getSharedBy() async {
|
Future<List<UserDto>> getSharedBy() async {
|
||||||
return (await db.users
|
return (await db.users
|
||||||
.filter()
|
.filter()
|
||||||
@@ -26,7 +23,6 @@ class PartnerRepository extends DatabaseRepository
|
|||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<UserDto>> getSharedWith() async {
|
Future<List<UserDto>> getSharedWith() async {
|
||||||
return (await db.users
|
return (await db.users
|
||||||
.filter()
|
.filter()
|
||||||
@@ -37,13 +33,11 @@ class PartnerRepository extends DatabaseRepository
|
|||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<List<UserDto>> watchSharedBy() {
|
Stream<List<UserDto>> watchSharedBy() {
|
||||||
return (db.users.filter().isPartnerSharedByEqualTo(true).sortById().watch())
|
return (db.users.filter().isPartnerSharedByEqualTo(true).sortById().watch())
|
||||||
.map((users) => users.map((u) => u.toDto()).toList());
|
.map((users) => users.map((u) => u.toDto()).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<List<UserDto>> watchSharedWith() {
|
Stream<List<UserDto>> watchSharedWith() {
|
||||||
return (db.users
|
return (db.users
|
||||||
.filter()
|
.filter()
|
||||||
|
|||||||
@@ -1,24 +1,26 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||||
import 'package:immich_mobile/infrastructure/utils/user.converter.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/providers/api.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/api.repository.dart';
|
import 'package:immich_mobile/repositories/api.repository.dart';
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
|
|
||||||
|
enum Direction {
|
||||||
|
sharedWithMe,
|
||||||
|
sharedByMe,
|
||||||
|
}
|
||||||
|
|
||||||
final partnerApiRepositoryProvider = Provider(
|
final partnerApiRepositoryProvider = Provider(
|
||||||
(ref) => PartnerApiRepository(
|
(ref) => PartnerApiRepository(
|
||||||
ref.watch(apiServiceProvider).partnersApi,
|
ref.watch(apiServiceProvider).partnersApi,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
class PartnerApiRepository extends ApiRepository
|
class PartnerApiRepository extends ApiRepository {
|
||||||
implements IPartnerApiRepository {
|
|
||||||
final PartnersApi _api;
|
final PartnersApi _api;
|
||||||
|
|
||||||
PartnerApiRepository(this._api);
|
PartnerApiRepository(this._api);
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<UserDto>> getAll(Direction direction) async {
|
Future<List<UserDto>> getAll(Direction direction) async {
|
||||||
final response = await checkNull(
|
final response = await checkNull(
|
||||||
_api.getPartners(
|
_api.getPartners(
|
||||||
@@ -30,16 +32,13 @@ class PartnerApiRepository extends ApiRepository
|
|||||||
return response.map(UserConverter.fromPartnerDto).toList();
|
return response.map(UserConverter.fromPartnerDto).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<UserDto> create(String id) async {
|
Future<UserDto> create(String id) async {
|
||||||
final dto = await checkNull(_api.createPartner(id));
|
final dto = await checkNull(_api.createPartner(id));
|
||||||
return UserConverter.fromPartnerDto(dto);
|
return UserConverter.fromPartnerDto(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> delete(String id) => _api.removePartner(id);
|
Future<void> delete(String id) => _api.removePartner(id);
|
||||||
|
|
||||||
@override
|
|
||||||
Future<UserDto> update(String id, {required bool inTimeline}) async {
|
Future<UserDto> update(String id, {required bool inTimeline}) async {
|
||||||
final dto = await checkNull(
|
final dto = await checkNull(
|
||||||
_api.updatePartner(
|
_api.updatePartner(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/interfaces/person_api.interface.dart';
|
import 'package:immich_mobile/domain/models/person.model.dart';
|
||||||
import 'package:immich_mobile/providers/api.provider.dart';
|
import 'package:immich_mobile/providers/api.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/api.repository.dart';
|
import 'package:immich_mobile/repositories/api.repository.dart';
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
@@ -8,19 +8,16 @@ final personApiRepositoryProvider = Provider(
|
|||||||
(ref) => PersonApiRepository(ref.watch(apiServiceProvider).peopleApi),
|
(ref) => PersonApiRepository(ref.watch(apiServiceProvider).peopleApi),
|
||||||
);
|
);
|
||||||
|
|
||||||
class PersonApiRepository extends ApiRepository
|
class PersonApiRepository extends ApiRepository {
|
||||||
implements IPersonApiRepository {
|
|
||||||
final PeopleApi _api;
|
final PeopleApi _api;
|
||||||
|
|
||||||
PersonApiRepository(this._api);
|
PersonApiRepository(this._api);
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<Person>> getAll() async {
|
Future<List<Person>> getAll() async {
|
||||||
final dto = await checkNull(_api.getAllPeople());
|
final dto = await checkNull(_api.getAllPeople());
|
||||||
return dto.people.map(_toPerson).toList();
|
return dto.people.map(_toPerson).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Person> update(String id, {String? name}) async {
|
Future<Person> update(String id, {String? name}) async {
|
||||||
final dto = await checkNull(
|
final dto = await checkNull(
|
||||||
_api.updatePerson(id, PersonUpdateDto(name: name)),
|
_api.updatePerson(id, PersonUpdateDto(name: name)),
|
||||||
|
|||||||
@@ -1,26 +1,22 @@
|
|||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/interfaces/secure_storage.interface.dart';
|
|
||||||
|
|
||||||
final secureStorageRepositoryProvider =
|
final secureStorageRepositoryProvider =
|
||||||
Provider((ref) => SecureStorageRepository(const FlutterSecureStorage()));
|
Provider((ref) => SecureStorageRepository(const FlutterSecureStorage()));
|
||||||
|
|
||||||
class SecureStorageRepository implements ISecureStorageRepository {
|
class SecureStorageRepository {
|
||||||
final FlutterSecureStorage _secureStorage;
|
final FlutterSecureStorage _secureStorage;
|
||||||
|
|
||||||
SecureStorageRepository(this._secureStorage);
|
SecureStorageRepository(this._secureStorage);
|
||||||
|
|
||||||
@override
|
|
||||||
Future<String?> read(String key) {
|
Future<String?> read(String key) {
|
||||||
return _secureStorage.read(key: key);
|
return _secureStorage.read(key: key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> write(String key, String value) {
|
Future<void> write(String key, String value) {
|
||||||
return _secureStorage.write(key: key, value: value);
|
return _secureStorage.write(key: key, value: value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> delete(String key) {
|
Future<void> delete(String key) {
|
||||||
return _secureStorage.delete(key: key);
|
return _secureStorage.delete(key: key);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
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/models/sessions/session_create_response.model.dart';
|
||||||
import 'package:immich_mobile/providers/api.provider.dart';
|
import 'package:immich_mobile/providers/api.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/api.repository.dart';
|
import 'package:immich_mobile/repositories/api.repository.dart';
|
||||||
@@ -11,13 +10,11 @@ final sessionsAPIRepositoryProvider = Provider(
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
class SessionsAPIRepository extends ApiRepository
|
class SessionsAPIRepository extends ApiRepository {
|
||||||
implements ISessionAPIRepository {
|
|
||||||
final SessionsApi _api;
|
final SessionsApi _api;
|
||||||
|
|
||||||
SessionsAPIRepository(this._api);
|
SessionsAPIRepository(this._api);
|
||||||
|
|
||||||
@override
|
|
||||||
Future<SessionCreateResponse> createSession(
|
Future<SessionCreateResponse> createSession(
|
||||||
String deviceType,
|
String deviceType,
|
||||||
String deviceOS, {
|
String deviceOS, {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
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:immich_mobile/models/upload/share_intent_attachment.model.dart';
|
||||||
import 'package:share_handler/share_handler.dart';
|
import 'package:share_handler/share_handler.dart';
|
||||||
|
|
||||||
@@ -9,13 +8,11 @@ final shareHandlerRepositoryProvider = Provider(
|
|||||||
(ref) => ShareHandlerRepository(),
|
(ref) => ShareHandlerRepository(),
|
||||||
);
|
);
|
||||||
|
|
||||||
class ShareHandlerRepository implements IShareHandlerRepository {
|
class ShareHandlerRepository {
|
||||||
ShareHandlerRepository();
|
ShareHandlerRepository();
|
||||||
|
|
||||||
@override
|
|
||||||
void Function(List<ShareIntentAttachment> attachments)? onSharedMedia;
|
void Function(List<ShareIntentAttachment> attachments)? onSharedMedia;
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
final handler = ShareHandlerPlatform.instance;
|
final handler = ShareHandlerPlatform.instance;
|
||||||
final media = await handler.getInitialSharedMedia();
|
final media = await handler.getInitialSharedMedia();
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import 'package:immich_mobile/constants/enums.dart';
|
|||||||
import 'package:immich_mobile/entities/album.entity.dart';
|
import 'package:immich_mobile/entities/album.entity.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
||||||
import 'package:immich_mobile/interfaces/timeline.interface.dart';
|
|
||||||
import 'package:immich_mobile/providers/db.provider.dart';
|
import 'package:immich_mobile/providers/db.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/database.repository.dart';
|
import 'package:immich_mobile/repositories/database.repository.dart';
|
||||||
import 'package:immich_mobile/utils/hash.dart';
|
import 'package:immich_mobile/utils/hash.dart';
|
||||||
@@ -13,11 +12,9 @@ import 'package:isar/isar.dart';
|
|||||||
final timelineRepositoryProvider =
|
final timelineRepositoryProvider =
|
||||||
Provider((ref) => TimelineRepository(ref.watch(dbProvider)));
|
Provider((ref) => TimelineRepository(ref.watch(dbProvider)));
|
||||||
|
|
||||||
class TimelineRepository extends DatabaseRepository
|
class TimelineRepository extends DatabaseRepository {
|
||||||
implements ITimelineRepository {
|
|
||||||
TimelineRepository(super.db);
|
TimelineRepository(super.db);
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<String>> getTimelineUserIds(String id) {
|
Future<List<String>> getTimelineUserIds(String id) {
|
||||||
return db.users
|
return db.users
|
||||||
.filter()
|
.filter()
|
||||||
@@ -28,7 +25,6 @@ class TimelineRepository extends DatabaseRepository
|
|||||||
.findAll();
|
.findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<List<String>> watchTimelineUsers(String id) {
|
Stream<List<String>> watchTimelineUsers(String id) {
|
||||||
return db.users
|
return db.users
|
||||||
.filter()
|
.filter()
|
||||||
@@ -39,7 +35,6 @@ class TimelineRepository extends DatabaseRepository
|
|||||||
.watch();
|
.watch();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<RenderList> watchArchiveTimeline(String userId) {
|
Stream<RenderList> watchArchiveTimeline(String userId) {
|
||||||
final query = db.assets
|
final query = db.assets
|
||||||
.where()
|
.where()
|
||||||
@@ -52,7 +47,6 @@ class TimelineRepository extends DatabaseRepository
|
|||||||
return _watchRenderList(query, GroupAssetsBy.none);
|
return _watchRenderList(query, GroupAssetsBy.none);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<RenderList> watchFavoriteTimeline(String userId) {
|
Stream<RenderList> watchFavoriteTimeline(String userId) {
|
||||||
final query = db.assets
|
final query = db.assets
|
||||||
.where()
|
.where()
|
||||||
@@ -67,7 +61,6 @@ class TimelineRepository extends DatabaseRepository
|
|||||||
return _watchRenderList(query, GroupAssetsBy.none);
|
return _watchRenderList(query, GroupAssetsBy.none);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<RenderList> watchAlbumTimeline(
|
Stream<RenderList> watchAlbumTimeline(
|
||||||
Album album,
|
Album album,
|
||||||
GroupAssetsBy groupAssetByOption,
|
GroupAssetsBy groupAssetByOption,
|
||||||
@@ -86,7 +79,6 @@ class TimelineRepository extends DatabaseRepository
|
|||||||
return _watchRenderList(withSortedOption, groupAssetByOption);
|
return _watchRenderList(withSortedOption, groupAssetByOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<RenderList> watchTrashTimeline(String userId) {
|
Stream<RenderList> watchTrashTimeline(String userId) {
|
||||||
final query = db.assets
|
final query = db.assets
|
||||||
.filter()
|
.filter()
|
||||||
@@ -97,7 +89,6 @@ class TimelineRepository extends DatabaseRepository
|
|||||||
return _watchRenderList(query, GroupAssetsBy.none);
|
return _watchRenderList(query, GroupAssetsBy.none);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<RenderList> watchAllVideosTimeline(String userId) {
|
Stream<RenderList> watchAllVideosTimeline(String userId) {
|
||||||
final query = db.assets
|
final query = db.assets
|
||||||
.where()
|
.where()
|
||||||
@@ -111,7 +102,6 @@ class TimelineRepository extends DatabaseRepository
|
|||||||
return _watchRenderList(query, GroupAssetsBy.none);
|
return _watchRenderList(query, GroupAssetsBy.none);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<RenderList> watchHomeTimeline(
|
Stream<RenderList> watchHomeTimeline(
|
||||||
String userId,
|
String userId,
|
||||||
GroupAssetsBy groupAssetByOption,
|
GroupAssetsBy groupAssetByOption,
|
||||||
@@ -128,7 +118,6 @@ class TimelineRepository extends DatabaseRepository
|
|||||||
return _watchRenderList(query, groupAssetByOption);
|
return _watchRenderList(query, groupAssetByOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<RenderList> watchMultiUsersTimeline(
|
Stream<RenderList> watchMultiUsersTimeline(
|
||||||
List<String> userIds,
|
List<String> userIds,
|
||||||
GroupAssetsBy groupAssetByOption,
|
GroupAssetsBy groupAssetByOption,
|
||||||
@@ -145,7 +134,6 @@ class TimelineRepository extends DatabaseRepository
|
|||||||
return _watchRenderList(query, groupAssetByOption);
|
return _watchRenderList(query, groupAssetByOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<RenderList> getTimelineFromAssets(
|
Future<RenderList> getTimelineFromAssets(
|
||||||
List<Asset> assets,
|
List<Asset> assets,
|
||||||
GroupAssetsBy getGroupByOption,
|
GroupAssetsBy getGroupByOption,
|
||||||
@@ -153,7 +141,6 @@ class TimelineRepository extends DatabaseRepository
|
|||||||
return RenderList.fromAssets(assets, getGroupByOption);
|
return RenderList.fromAssets(assets, getGroupByOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<RenderList> watchAssetSelectionTimeline(String userId) {
|
Stream<RenderList> watchAssetSelectionTimeline(String userId) {
|
||||||
final query = db.assets
|
final query = db.assets
|
||||||
.where()
|
.where()
|
||||||
@@ -168,7 +155,6 @@ class TimelineRepository extends DatabaseRepository
|
|||||||
return _watchRenderList(query, GroupAssetsBy.none);
|
return _watchRenderList(query, GroupAssetsBy.none);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<RenderList> watchLockedTimeline(
|
Stream<RenderList> watchLockedTimeline(
|
||||||
String userId,
|
String userId,
|
||||||
GroupAssetsBy getGroupByOption,
|
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/mixins/error_logger.mixin.dart';
|
||||||
import 'package:immich_mobile/models/activities/activity.model.dart';
|
import 'package:immich_mobile/models/activities/activity.model.dart';
|
||||||
|
import 'package:immich_mobile/repositories/activity_api.repository.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
class ActivityService with ErrorLoggerMixin {
|
class ActivityService with ErrorLoggerMixin {
|
||||||
final IActivityApiRepository _activityApiRepository;
|
final ActivityApiRepository _activityApiRepository;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Logger logger = Logger("ActivityService");
|
final Logger logger = Logger("ActivityService");
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ import 'package:immich_mobile/entities/backup_album.entity.dart';
|
|||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
|
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
|
||||||
as entity;
|
as entity;
|
||||||
import 'package:immich_mobile/interfaces/album.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.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
|
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
|
||||||
import 'package:immich_mobile/models/albums/album_add_asset_response.model.dart';
|
import 'package:immich_mobile/models/albums/album_add_asset_response.model.dart';
|
||||||
@@ -51,8 +49,8 @@ class AlbumService {
|
|||||||
final IAlbumRepository _albumRepository;
|
final IAlbumRepository _albumRepository;
|
||||||
final IAssetRepository _assetRepository;
|
final IAssetRepository _assetRepository;
|
||||||
final IBackupAlbumRepository _backupAlbumRepository;
|
final IBackupAlbumRepository _backupAlbumRepository;
|
||||||
final IAlbumMediaRepository _albumMediaRepository;
|
final AlbumMediaRepository _albumMediaRepository;
|
||||||
final IAlbumApiRepository _albumApiRepository;
|
final AlbumApiRepository _albumApiRepository;
|
||||||
final Logger _log = Logger('AlbumService');
|
final Logger _log = Logger('AlbumService');
|
||||||
Completer<bool> _localCompleter = Completer()..complete(false);
|
Completer<bool> _localCompleter = Completer()..complete(false);
|
||||||
Completer<bool> _remoteCompleter = 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:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/constants/enums.dart';
|
import 'package:immich_mobile/constants/enums.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/exif.interface.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/models/user.model.dart';
|
||||||
import 'package:immich_mobile/domain/services/user.service.dart';
|
import 'package:immich_mobile/domain/services/user.service.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/backup_album.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.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/asset_api.interface.dart';
|
import 'package:immich_mobile/interfaces/asset_api.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/asset_media.interface.dart';
|
import 'package:immich_mobile/interfaces/asset_media.interface.dart';
|
||||||
@@ -53,7 +53,7 @@ class AssetService {
|
|||||||
final IAssetApiRepository _assetApiRepository;
|
final IAssetApiRepository _assetApiRepository;
|
||||||
final IAssetRepository _assetRepository;
|
final IAssetRepository _assetRepository;
|
||||||
final IExifInfoRepository _exifInfoRepository;
|
final IExifInfoRepository _exifInfoRepository;
|
||||||
final IUserRepository _userRepository;
|
final IsarUserRepository _isarUserRepository;
|
||||||
final IETagRepository _etagRepository;
|
final IETagRepository _etagRepository;
|
||||||
final IBackupAlbumRepository _backupRepository;
|
final IBackupAlbumRepository _backupRepository;
|
||||||
final ApiService _apiService;
|
final ApiService _apiService;
|
||||||
@@ -68,7 +68,7 @@ class AssetService {
|
|||||||
this._assetApiRepository,
|
this._assetApiRepository,
|
||||||
this._assetRepository,
|
this._assetRepository,
|
||||||
this._exifInfoRepository,
|
this._exifInfoRepository,
|
||||||
this._userRepository,
|
this._isarUserRepository,
|
||||||
this._etagRepository,
|
this._etagRepository,
|
||||||
this._backupRepository,
|
this._backupRepository,
|
||||||
this._apiService,
|
this._apiService,
|
||||||
@@ -85,7 +85,9 @@ class AssetService {
|
|||||||
final syncedUserIds = await _etagRepository.getAllIds();
|
final syncedUserIds = await _etagRepository.getAllIds();
|
||||||
final List<UserDto> syncedUsers = syncedUserIds.isEmpty
|
final List<UserDto> syncedUsers = syncedUserIds.isEmpty
|
||||||
? []
|
? []
|
||||||
: (await _userRepository.getByUserIds(syncedUserIds)).nonNulls.toList();
|
: (await _isarUserRepository.getByUserIds(syncedUserIds))
|
||||||
|
.nonNulls
|
||||||
|
.toList();
|
||||||
final Stopwatch sw = Stopwatch()..start();
|
final Stopwatch sw = Stopwatch()..start();
|
||||||
final bool changes = await _syncService.syncRemoteAssetsToDb(
|
final bool changes = await _syncService.syncRemoteAssetsToDb(
|
||||||
users: syncedUsers,
|
users: syncedUsers,
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import 'package:immich_mobile/entities/album.entity.dart';
|
|||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
||||||
import 'package:immich_mobile/entities/store.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.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/asset_media.interface.dart';
|
import 'package:immich_mobile/interfaces/asset_media.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/file_media.interface.dart';
|
import 'package:immich_mobile/interfaces/file_media.interface.dart';
|
||||||
@@ -52,7 +51,7 @@ class BackupService {
|
|||||||
final Logger _log = Logger("BackupService");
|
final Logger _log = Logger("BackupService");
|
||||||
final AppSettingsService _appSetting;
|
final AppSettingsService _appSetting;
|
||||||
final AlbumService _albumService;
|
final AlbumService _albumService;
|
||||||
final IAlbumMediaRepository _albumMediaRepository;
|
final AlbumMediaRepository _albumMediaRepository;
|
||||||
final IFileMediaRepository _fileMediaRepository;
|
final IFileMediaRepository _fileMediaRepository;
|
||||||
final IAssetRepository _assetRepository;
|
final IAssetRepository _assetRepository;
|
||||||
final IAssetMediaRepository _assetMediaRepository;
|
final IAssetMediaRepository _assetMediaRepository;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/store.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/interfaces/file_media.interface.dart';
|
||||||
import 'package:immich_mobile/models/download/livephotos_medatada.model.dart';
|
import 'package:immich_mobile/models/download/livephotos_medatada.model.dart';
|
||||||
import 'package:immich_mobile/repositories/download.repository.dart';
|
import 'package:immich_mobile/repositories/download.repository.dart';
|
||||||
@@ -23,7 +22,7 @@ final downloadServiceProvider = Provider(
|
|||||||
);
|
);
|
||||||
|
|
||||||
class DownloadService {
|
class DownloadService {
|
||||||
final IDownloadRepository _downloadRepository;
|
final DownloadRepository _downloadRepository;
|
||||||
final IFileMediaRepository _fileMediaRepository;
|
final IFileMediaRepository _fileMediaRepository;
|
||||||
final Logger _log = Logger("DownloadService");
|
final Logger _log = Logger("DownloadService");
|
||||||
void Function(TaskStatusUpdate)? onImageDownloadStatus;
|
void Function(TaskStatusUpdate)? onImageDownloadStatus;
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
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/entities/album.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
|
||||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/asset.repository.dart';
|
import 'package:immich_mobile/repositories/asset.repository.dart';
|
||||||
|
|
||||||
class EntityService {
|
class EntityService {
|
||||||
final IAssetRepository _assetRepository;
|
final IAssetRepository _assetRepository;
|
||||||
final IUserRepository _userRepository;
|
final IsarUserRepository _isarUserRepository;
|
||||||
EntityService(
|
EntityService(
|
||||||
this._assetRepository,
|
this._assetRepository,
|
||||||
this._userRepository,
|
this._isarUserRepository,
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<Album> fillAlbumWithDatabaseEntities(Album album) async {
|
Future<Album> fillAlbumWithDatabaseEntities(Album album) async {
|
||||||
final ownerId = album.ownerId;
|
final ownerId = album.ownerId;
|
||||||
if (ownerId != null) {
|
if (ownerId != null) {
|
||||||
// replace owner with user from database
|
// replace owner with user from database
|
||||||
final user = await _userRepository.getByUserId(ownerId);
|
final user = await _isarUserRepository.getByUserId(ownerId);
|
||||||
album.owner.value = user == null ? null : User.fromDto(user);
|
album.owner.value = user == null ? null : User.fromDto(user);
|
||||||
}
|
}
|
||||||
final thumbnailAssetId =
|
final thumbnailAssetId =
|
||||||
@@ -30,7 +30,7 @@ class EntityService {
|
|||||||
}
|
}
|
||||||
if (album.remoteUsers.isNotEmpty) {
|
if (album.remoteUsers.isNotEmpty) {
|
||||||
// replace all users with users from database
|
// replace all users with users from database
|
||||||
final users = await _userRepository
|
final users = await _isarUserRepository
|
||||||
.getByUserIds(album.remoteUsers.map((user) => user.id).toList());
|
.getByUserIds(album.remoteUsers.map((user) => user.id).toList());
|
||||||
album.sharedUsers.clear();
|
album.sharedUsers.clear();
|
||||||
album.sharedUsers.addAll(users.nonNulls.map(User.fromDto));
|
album.sharedUsers.addAll(users.nonNulls.map(User.fromDto));
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.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:immich_mobile/models/auth/biometric_status.model.dart';
|
||||||
import 'package:immich_mobile/repositories/biometric.repository.dart';
|
import 'package:immich_mobile/repositories/biometric.repository.dart';
|
||||||
|
|
||||||
@@ -10,9 +9,7 @@ final localAuthServiceProvider = Provider(
|
|||||||
);
|
);
|
||||||
|
|
||||||
class LocalAuthService {
|
class LocalAuthService {
|
||||||
// final _log = Logger("LocalAuthService");
|
final BiometricRepository _biometricRepository;
|
||||||
|
|
||||||
final IBiometricRepository _biometricRepository;
|
|
||||||
|
|
||||||
LocalAuthService(this._biometricRepository);
|
LocalAuthService(this._biometricRepository);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
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/network.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/permission.repository.dart';
|
import 'package:immich_mobile/repositories/permission.repository.dart';
|
||||||
|
|
||||||
@@ -11,7 +10,7 @@ final networkServiceProvider = Provider((ref) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
class NetworkService {
|
class NetworkService {
|
||||||
final INetworkRepository _repository;
|
final NetworkRepository _repository;
|
||||||
final IPermissionRepository _permissionRepository;
|
final IPermissionRepository _permissionRepository;
|
||||||
|
|
||||||
NetworkService(this._repository, this._permissionRepository);
|
NetworkService(this._repository, this._permissionRepository);
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
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/domain/models/user.model.dart';
|
||||||
import 'package:immich_mobile/interfaces/partner.interface.dart';
|
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
|
||||||
import 'package:immich_mobile/interfaces/partner_api.interface.dart';
|
|
||||||
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/partner.repository.dart';
|
import 'package:immich_mobile/repositories/partner.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/partner_api.repository.dart';
|
import 'package:immich_mobile/repositories/partner_api.repository.dart';
|
||||||
@@ -17,14 +15,14 @@ final partnerServiceProvider = Provider(
|
|||||||
);
|
);
|
||||||
|
|
||||||
class PartnerService {
|
class PartnerService {
|
||||||
final IPartnerApiRepository _partnerApiRepository;
|
final PartnerApiRepository _partnerApiRepository;
|
||||||
final IPartnerRepository _partnerRepository;
|
final PartnerRepository _partnerRepository;
|
||||||
final IUserRepository _userRepository;
|
final IsarUserRepository _isarUserRepository;
|
||||||
final Logger _log = Logger("PartnerService");
|
final Logger _log = Logger("PartnerService");
|
||||||
|
|
||||||
PartnerService(
|
PartnerService(
|
||||||
this._partnerApiRepository,
|
this._partnerApiRepository,
|
||||||
this._userRepository,
|
this._isarUserRepository,
|
||||||
this._partnerRepository,
|
this._partnerRepository,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -47,7 +45,8 @@ class PartnerService {
|
|||||||
Future<bool> removePartner(UserDto partner) async {
|
Future<bool> removePartner(UserDto partner) async {
|
||||||
try {
|
try {
|
||||||
await _partnerApiRepository.delete(partner.id);
|
await _partnerApiRepository.delete(partner.id);
|
||||||
await _userRepository.update(partner.copyWith(isPartnerSharedBy: false));
|
await _isarUserRepository
|
||||||
|
.update(partner.copyWith(isPartnerSharedBy: false));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_log.warning("Failed to remove partner ${partner.id}", e);
|
_log.warning("Failed to remove partner ${partner.id}", e);
|
||||||
return false;
|
return false;
|
||||||
@@ -58,7 +57,8 @@ class PartnerService {
|
|||||||
Future<bool> addPartner(UserDto partner) async {
|
Future<bool> addPartner(UserDto partner) async {
|
||||||
try {
|
try {
|
||||||
await _partnerApiRepository.create(partner.id);
|
await _partnerApiRepository.create(partner.id);
|
||||||
await _userRepository.update(partner.copyWith(isPartnerSharedBy: true));
|
await _isarUserRepository
|
||||||
|
.update(partner.copyWith(isPartnerSharedBy: true));
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_log.warning("Failed to add partner ${partner.id}", e);
|
_log.warning("Failed to add partner ${partner.id}", e);
|
||||||
@@ -75,7 +75,7 @@ class PartnerService {
|
|||||||
partner.id,
|
partner.id,
|
||||||
inTimeline: inTimeline,
|
inTimeline: inTimeline,
|
||||||
);
|
);
|
||||||
await _userRepository
|
await _isarUserRepository
|
||||||
.update(partner.copyWith(inTimeline: dto.inTimeline));
|
.update(partner.copyWith(inTimeline: dto.inTimeline));
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
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/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/interfaces/asset.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_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.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/asset_api.repository.dart';
|
import 'package:immich_mobile/repositories/asset_api.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/person_api.repository.dart';
|
import 'package:immich_mobile/repositories/person_api.repository.dart';
|
||||||
@@ -20,7 +20,7 @@ PersonService personService(Ref ref) => PersonService(
|
|||||||
|
|
||||||
class PersonService {
|
class PersonService {
|
||||||
final Logger _log = Logger("PersonService");
|
final Logger _log = Logger("PersonService");
|
||||||
final IPersonApiRepository _personApiRepository;
|
final PersonApiRepository _personApiRepository;
|
||||||
final IAssetApiRepository _assetApiRepository;
|
final IAssetApiRepository _assetApiRepository;
|
||||||
final IAssetRepository _assetRepository;
|
final IAssetRepository _assetRepository;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
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';
|
import 'package:immich_mobile/repositories/secure_storage.repository.dart';
|
||||||
|
|
||||||
final secureStorageServiceProvider = Provider(
|
final secureStorageServiceProvider = Provider(
|
||||||
@@ -9,9 +8,7 @@ final secureStorageServiceProvider = Provider(
|
|||||||
);
|
);
|
||||||
|
|
||||||
class SecureStorageService {
|
class SecureStorageService {
|
||||||
// final _log = Logger("LocalAuthService");
|
final SecureStorageRepository _secureStorageRepository;
|
||||||
|
|
||||||
final ISecureStorageRepository _secureStorageRepository;
|
|
||||||
|
|
||||||
SecureStorageService(this._secureStorageRepository);
|
SecureStorageService(this._secureStorageRepository);
|
||||||
|
|
||||||
|
|||||||
@@ -3,23 +3,20 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/constants/enums.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/exif.interface.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/models/user.model.dart';
|
||||||
import 'package:immich_mobile/domain/services/user.service.dart';
|
import 'package:immich_mobile/domain/services/user.service.dart';
|
||||||
import 'package:immich_mobile/entities/album.entity.dart';
|
import 'package:immich_mobile/entities/album.entity.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/etag.entity.dart';
|
import 'package:immich_mobile/entities/etag.entity.dart';
|
||||||
import 'package:immich_mobile/extensions/collection_extensions.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.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.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/etag.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/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/app_settings.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/exif.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/exif.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
|
||||||
@@ -63,17 +60,17 @@ final syncServiceProvider = Provider(
|
|||||||
class SyncService {
|
class SyncService {
|
||||||
final HashService _hashService;
|
final HashService _hashService;
|
||||||
final EntityService _entityService;
|
final EntityService _entityService;
|
||||||
final IAlbumMediaRepository _albumMediaRepository;
|
final AlbumMediaRepository _albumMediaRepository;
|
||||||
final IAlbumApiRepository _albumApiRepository;
|
final AlbumApiRepository _albumApiRepository;
|
||||||
final IAlbumRepository _albumRepository;
|
final IAlbumRepository _albumRepository;
|
||||||
final IAssetRepository _assetRepository;
|
final IAssetRepository _assetRepository;
|
||||||
final IExifInfoRepository _exifInfoRepository;
|
final IExifInfoRepository _exifInfoRepository;
|
||||||
final IUserRepository _userRepository;
|
final IsarUserRepository _isarUserRepository;
|
||||||
final UserService _userService;
|
final UserService _userService;
|
||||||
final IPartnerRepository _partnerRepository;
|
final PartnerRepository _partnerRepository;
|
||||||
final IETagRepository _eTagRepository;
|
final IETagRepository _eTagRepository;
|
||||||
final IPartnerApiRepository _partnerApiRepository;
|
final PartnerApiRepository _partnerApiRepository;
|
||||||
final IUserApiRepository _userApiRepository;
|
final UserApiRepository _userApiRepository;
|
||||||
final AsyncMutex _lock = AsyncMutex();
|
final AsyncMutex _lock = AsyncMutex();
|
||||||
final Logger _log = Logger('SyncService');
|
final Logger _log = Logger('SyncService');
|
||||||
final AppSettingsService _appSettingsService;
|
final AppSettingsService _appSettingsService;
|
||||||
@@ -88,7 +85,7 @@ class SyncService {
|
|||||||
this._assetRepository,
|
this._assetRepository,
|
||||||
this._exifInfoRepository,
|
this._exifInfoRepository,
|
||||||
this._partnerRepository,
|
this._partnerRepository,
|
||||||
this._userRepository,
|
this._isarUserRepository,
|
||||||
this._userService,
|
this._userService,
|
||||||
this._eTagRepository,
|
this._eTagRepository,
|
||||||
this._appSettingsService,
|
this._appSettingsService,
|
||||||
@@ -165,7 +162,7 @@ class SyncService {
|
|||||||
/// Returns `true`if there were any changes
|
/// Returns `true`if there were any changes
|
||||||
Future<bool> _syncUsersFromServer(List<UserDto> users) async {
|
Future<bool> _syncUsersFromServer(List<UserDto> users) async {
|
||||||
users.sortBy((u) => u.id);
|
users.sortBy((u) => u.id);
|
||||||
final dbUsers = await _userRepository.getAll(sortBy: SortUserBy.id);
|
final dbUsers = await _isarUserRepository.getAll(sortBy: SortUserBy.id);
|
||||||
final List<String> toDelete = [];
|
final List<String> toDelete = [];
|
||||||
final List<UserDto> toUpsert = [];
|
final List<UserDto> toUpsert = [];
|
||||||
final changes = diffSortedListsSync(
|
final changes = diffSortedListsSync(
|
||||||
@@ -186,9 +183,9 @@ class SyncService {
|
|||||||
onlySecond: (UserDto b) => toDelete.add(b.id),
|
onlySecond: (UserDto b) => toDelete.add(b.id),
|
||||||
);
|
);
|
||||||
if (changes) {
|
if (changes) {
|
||||||
await _userRepository.transaction(() async {
|
await _isarUserRepository.transaction(() async {
|
||||||
await _userRepository.delete(toDelete);
|
await _isarUserRepository.delete(toDelete);
|
||||||
await _userRepository.updateAll(toUpsert);
|
await _isarUserRepository.updateAll(toUpsert);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return changes;
|
return changes;
|
||||||
@@ -448,7 +445,7 @@ class SyncService {
|
|||||||
final (existingInDb, updated) = await _linkWithExistingFromDb(toAdd);
|
final (existingInDb, updated) = await _linkWithExistingFromDb(toAdd);
|
||||||
await upsertAssetsWithExif(updated);
|
await upsertAssetsWithExif(updated);
|
||||||
final assetsToLink = existingInDb + updated;
|
final assetsToLink = existingInDb + updated;
|
||||||
final usersToLink = await _userRepository.getByUserIds(userIdsToAdd);
|
final usersToLink = await _isarUserRepository.getByUserIds(userIdsToAdd);
|
||||||
|
|
||||||
album.name = dto.name;
|
album.name = dto.name;
|
||||||
album.description = dto.description;
|
album.description = dto.description;
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:immich_mobile/domain/services/user.service.dart';
|
import 'package:immich_mobile/domain/services/user.service.dart';
|
||||||
import 'package:immich_mobile/entities/album.entity.dart';
|
import 'package:immich_mobile/entities/album.entity.dart';
|
||||||
import 'package:immich_mobile/entities/asset.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/app_settings.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/timeline.repository.dart';
|
import 'package:immich_mobile/repositories/timeline.repository.dart';
|
||||||
@@ -18,7 +17,7 @@ final timelineServiceProvider = Provider<TimelineService>((ref) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
class TimelineService {
|
class TimelineService {
|
||||||
final ITimelineRepository _timelineRepository;
|
final TimelineRepository _timelineRepository;
|
||||||
final AppSettingsService _appSettingsService;
|
final AppSettingsService _appSettingsService;
|
||||||
final UserService _userService;
|
final UserService _userService;
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,13 @@ Future<void> migrateDatabaseIfNeeded(Isar db) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final shouldTruncate = version < 8 || version < targetVersion;
|
final shouldTruncate = version < 8 || version < targetVersion;
|
||||||
|
|
||||||
if (shouldTruncate) {
|
if (shouldTruncate) {
|
||||||
|
if (targetVersion == 12) {
|
||||||
|
await Store.put(StoreKey.version, targetVersion);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await _migrateTo(db, targetVersion);
|
await _migrateTo(db, targetVersion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/person.model.dart';
|
||||||
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_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/pages/common/large_leading_tile.dart';
|
||||||
import 'package:immich_mobile/providers/search/people.provider.dart';
|
import 'package:immich_mobile/providers/search/people.provider.dart';
|
||||||
import 'package:immich_mobile/services/api.service.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:
|
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
|
||||||
|
|
||||||
- API version: 1.135.0
|
- API version: 1.135.2
|
||||||
- Generator version: 7.8.0
|
- Generator version: 7.8.0
|
||||||
- Build package: org.openapitools.codegen.languages.DartClientCodegen
|
- Build package: org.openapitools.codegen.languages.DartClientCodegen
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ name: immich_mobile
|
|||||||
description: Immich - selfhosted backup media file on mobile phone
|
description: Immich - selfhosted backup media file on mobile phone
|
||||||
|
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 1.135.0+201
|
version: 1.135.2+203
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.3.0 <4.0.0'
|
sdk: '>=3.3.0 <4.0.0'
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/sync_api.interface.dart';
|
import 'package:immich_mobile/domain/interfaces/sync_api.interface.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/sync_stream.interface.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/sync_event.model.dart';
|
import 'package:immich_mobile/domain/models/sync_event.model.dart';
|
||||||
import 'package:immich_mobile/domain/services/sync_stream.service.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 'package:mocktail/mocktail.dart';
|
||||||
|
|
||||||
import '../../fixtures/sync_stream.stub.dart';
|
import '../../fixtures/sync_stream.stub.dart';
|
||||||
@@ -30,7 +30,7 @@ class _MockCancellationWrapper extends Mock implements _CancellationWrapper {}
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
late SyncStreamService sut;
|
late SyncStreamService sut;
|
||||||
late ISyncStreamRepository mockSyncStreamRepo;
|
late SyncStreamRepository mockSyncStreamRepo;
|
||||||
late ISyncApiRepository mockSyncApiRepo;
|
late ISyncApiRepository mockSyncApiRepo;
|
||||||
late Function(List<SyncEvent>, Function()) handleEventsCallback;
|
late Function(List<SyncEvent>, Function()) handleEventsCallback;
|
||||||
late _MockAbortCallbackWrapper mockAbortCallbackWrapper;
|
late _MockAbortCallbackWrapper mockAbortCallbackWrapper;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
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/models/store.model.dart';
|
||||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||||
import 'package:immich_mobile/domain/services/user.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 'package:mocktail/mocktail.dart';
|
||||||
|
|
||||||
import '../../fixtures/user.stub.dart';
|
import '../../fixtures/user.stub.dart';
|
||||||
@@ -14,16 +14,16 @@ import '../service.mock.dart';
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
late UserService sut;
|
late UserService sut;
|
||||||
late IUserRepository mockUserRepo;
|
late IsarUserRepository mockUserRepo;
|
||||||
late IUserApiRepository mockUserApiRepo;
|
late UserApiRepository mockUserApiRepo;
|
||||||
late StoreService mockStoreService;
|
late StoreService mockStoreService;
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
mockUserRepo = MockUserRepository();
|
mockUserRepo = MockIsarUserRepository();
|
||||||
mockUserApiRepo = MockUserApiRepository();
|
mockUserApiRepo = MockUserApiRepository();
|
||||||
mockStoreService = MockStoreService();
|
mockStoreService = MockStoreService();
|
||||||
sut = UserService(
|
sut = UserService(
|
||||||
userRepository: mockUserRepo,
|
isarUserRepository: mockUserRepo,
|
||||||
userApiRepository: mockUserApiRepo,
|
userApiRepository: mockUserApiRepo,
|
||||||
storeService: mockStoreService,
|
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/storage.interface.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/store.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/domain/interfaces/sync_api.interface.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/sync_stream.interface.dart';
|
import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/user_api.interface.dart';
|
import 'package:immich_mobile/infrastructure/repositories/user_api.repository.dart';
|
||||||
import 'package:mocktail/mocktail.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
|
||||||
class MockStoreRepository extends Mock implements IStoreRepository {}
|
class MockStoreRepository extends Mock implements IStoreRepository {}
|
||||||
|
|
||||||
class MockLogRepository extends Mock implements ILogRepository {}
|
class MockLogRepository extends Mock implements ILogRepository {}
|
||||||
|
|
||||||
class MockUserRepository extends Mock implements IUserRepository {}
|
class MockIsarUserRepository extends Mock implements IsarUserRepository {}
|
||||||
|
|
||||||
class MockDeviceAssetRepository extends Mock
|
class MockDeviceAssetRepository extends Mock
|
||||||
implements IDeviceAssetRepository {}
|
implements IDeviceAssetRepository {}
|
||||||
|
|
||||||
class MockSyncStreamRepository extends Mock implements ISyncStreamRepository {}
|
class MockSyncStreamRepository extends Mock implements SyncStreamRepository {}
|
||||||
|
|
||||||
class MockLocalAlbumRepository extends Mock implements ILocalAlbumRepository {}
|
class MockLocalAlbumRepository extends Mock implements ILocalAlbumRepository {}
|
||||||
|
|
||||||
@@ -28,6 +28,6 @@ class MockLocalAssetRepository extends Mock implements ILocalAssetRepository {}
|
|||||||
class MockStorageRepository extends Mock implements IStorageRepository {}
|
class MockStorageRepository extends Mock implements IStorageRepository {}
|
||||||
|
|
||||||
// API Repos
|
// API Repos
|
||||||
class MockUserApiRepository extends Mock implements IUserApiRepository {}
|
class MockUserApiRepository extends Mock implements UserApiRepository {}
|
||||||
|
|
||||||
class MockSyncApiRepository extends Mock implements ISyncApiRepository {}
|
class MockSyncApiRepository extends Mock implements ISyncApiRepository {}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
import 'package:immich_mobile/constants/enums.dart';
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||||
import 'package:immich_mobile/domain/services/log.service.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/log.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
||||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/partner_api.interface.dart';
|
import 'package:immich_mobile/repositories/partner_api.repository.dart';
|
||||||
import 'package:immich_mobile/services/sync.service.dart';
|
import 'package:immich_mobile/services/sync.service.dart';
|
||||||
import 'package:mocktail/mocktail.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ void main() {
|
|||||||
final MockAlbumRepository albumRepository = MockAlbumRepository();
|
final MockAlbumRepository albumRepository = MockAlbumRepository();
|
||||||
final MockAssetRepository assetRepository = MockAssetRepository();
|
final MockAssetRepository assetRepository = MockAssetRepository();
|
||||||
final MockExifInfoRepository exifInfoRepository = MockExifInfoRepository();
|
final MockExifInfoRepository exifInfoRepository = MockExifInfoRepository();
|
||||||
final MockUserRepository userRepository = MockUserRepository();
|
final MockIsarUserRepository userRepository = MockIsarUserRepository();
|
||||||
final MockETagRepository eTagRepository = MockETagRepository();
|
final MockETagRepository eTagRepository = MockETagRepository();
|
||||||
final MockAlbumMediaRepository albumMediaRepository =
|
final MockAlbumMediaRepository albumMediaRepository =
|
||||||
MockAlbumMediaRepository();
|
MockAlbumMediaRepository();
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
|
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/album.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.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/asset_api.interface.dart';
|
import 'package:immich_mobile/interfaces/asset_api.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/asset_media.interface.dart';
|
import 'package:immich_mobile/interfaces/asset_media.interface.dart';
|
||||||
@@ -11,8 +9,10 @@ import 'package:immich_mobile/interfaces/backup_album.interface.dart';
|
|||||||
import 'package:immich_mobile/interfaces/etag.interface.dart';
|
import 'package:immich_mobile/interfaces/etag.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/file_media.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/interfaces/local_files_manager.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/partner.interface.dart';
|
import 'package:immich_mobile/repositories/partner_api.repository.dart';
|
||||||
import 'package:immich_mobile/interfaces/partner_api.interface.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:mocktail/mocktail.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
|
||||||
class MockAlbumRepository extends Mock implements IAlbumRepository {}
|
class MockAlbumRepository extends Mock implements IAlbumRepository {}
|
||||||
@@ -25,7 +25,7 @@ class MockExifInfoRepository extends Mock implements IExifInfoRepository {}
|
|||||||
|
|
||||||
class MockETagRepository extends Mock implements IETagRepository {}
|
class MockETagRepository extends Mock implements IETagRepository {}
|
||||||
|
|
||||||
class MockAlbumMediaRepository extends Mock implements IAlbumMediaRepository {}
|
class MockAlbumMediaRepository extends Mock implements AlbumMediaRepository {}
|
||||||
|
|
||||||
class MockBackupAlbumRepository extends Mock
|
class MockBackupAlbumRepository extends Mock
|
||||||
implements IBackupAlbumRepository {}
|
implements IBackupAlbumRepository {}
|
||||||
@@ -36,15 +36,15 @@ class MockAssetMediaRepository extends Mock implements IAssetMediaRepository {}
|
|||||||
|
|
||||||
class MockFileMediaRepository extends Mock implements IFileMediaRepository {}
|
class MockFileMediaRepository extends Mock implements IFileMediaRepository {}
|
||||||
|
|
||||||
class MockAlbumApiRepository extends Mock implements IAlbumApiRepository {}
|
class MockAlbumApiRepository extends Mock implements AlbumApiRepository {}
|
||||||
|
|
||||||
class MockAuthApiRepository extends Mock implements IAuthApiRepository {}
|
class MockAuthApiRepository extends Mock implements IAuthApiRepository {}
|
||||||
|
|
||||||
class MockAuthRepository extends Mock implements IAuthRepository {}
|
class MockAuthRepository extends Mock implements IAuthRepository {}
|
||||||
|
|
||||||
class MockPartnerRepository extends Mock implements IPartnerRepository {}
|
class MockPartnerRepository extends Mock implements PartnerRepository {}
|
||||||
|
|
||||||
class MockPartnerApiRepository extends Mock implements IPartnerApiRepository {}
|
class MockPartnerApiRepository extends Mock implements PartnerApiRepository {}
|
||||||
|
|
||||||
class MockLocalFilesManagerRepository extends Mock
|
class MockLocalFilesManagerRepository extends Mock
|
||||||
implements ILocalFilesManager {}
|
implements ILocalFilesManager {}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ void main() {
|
|||||||
late MockExifInfoRepository exifInfoRepository;
|
late MockExifInfoRepository exifInfoRepository;
|
||||||
late MockETagRepository eTagRepository;
|
late MockETagRepository eTagRepository;
|
||||||
late MockBackupAlbumRepository backupAlbumRepository;
|
late MockBackupAlbumRepository backupAlbumRepository;
|
||||||
late MockUserRepository userRepository;
|
late MockIsarUserRepository userRepository;
|
||||||
late MockAssetMediaRepository assetMediaRepository;
|
late MockAssetMediaRepository assetMediaRepository;
|
||||||
late MockApiService apiService;
|
late MockApiService apiService;
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ void main() {
|
|||||||
assetRepository = MockAssetRepository();
|
assetRepository = MockAssetRepository();
|
||||||
assetApiRepository = MockAssetApiRepository();
|
assetApiRepository = MockAssetApiRepository();
|
||||||
exifInfoRepository = MockExifInfoRepository();
|
exifInfoRepository = MockExifInfoRepository();
|
||||||
userRepository = MockUserRepository();
|
userRepository = MockIsarUserRepository();
|
||||||
eTagRepository = MockETagRepository();
|
eTagRepository = MockETagRepository();
|
||||||
backupAlbumRepository = MockBackupAlbumRepository();
|
backupAlbumRepository = MockBackupAlbumRepository();
|
||||||
apiService = MockApiService();
|
apiService = MockApiService();
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ import '../repository.mocks.dart';
|
|||||||
void main() {
|
void main() {
|
||||||
late EntityService sut;
|
late EntityService sut;
|
||||||
late MockAssetRepository assetRepository;
|
late MockAssetRepository assetRepository;
|
||||||
late MockUserRepository userRepository;
|
late MockIsarUserRepository userRepository;
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
assetRepository = MockAssetRepository();
|
assetRepository = MockAssetRepository();
|
||||||
userRepository = MockUserRepository();
|
userRepository = MockIsarUserRepository();
|
||||||
sut = EntityService(assetRepository, userRepository);
|
sut = EntityService(assetRepository, userRepository);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -8503,7 +8503,7 @@
|
|||||||
"info": {
|
"info": {
|
||||||
"title": "Immich",
|
"title": "Immich",
|
||||||
"description": "Immich API",
|
"description": "Immich API",
|
||||||
"version": "1.135.0",
|
"version": "1.135.2",
|
||||||
"contact": {}
|
"contact": {}
|
||||||
},
|
},
|
||||||
"tags": [],
|
"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",
|
"name": "@immich/sdk",
|
||||||
"version": "1.135.0",
|
"version": "1.135.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "1.135.0",
|
"version": "1.135.2",
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oazapfts/runtime": "^1.0.2"
|
"@oazapfts/runtime": "^1.0.2"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "1.135.0",
|
"version": "1.135.2",
|
||||||
"description": "Auto-generated TypeScript SDK for the Immich API",
|
"description": "Auto-generated TypeScript SDK for the Immich API",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./build/index.js",
|
"main": "./build/index.js",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Immich
|
* Immich
|
||||||
* 1.135.0
|
* 1.135.2
|
||||||
* DO NOT MODIFY - This file has been generated using oazapfts.
|
* DO NOT MODIFY - This file has been generated using oazapfts.
|
||||||
* See https://www.npmjs.com/package/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",
|
"name": "immich",
|
||||||
"version": "1.135.0",
|
"version": "1.135.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "immich",
|
"name": "immich",
|
||||||
"version": "1.135.0",
|
"version": "1.135.2",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -42,7 +42,6 @@
|
|||||||
"handlebars": "^4.7.8",
|
"handlebars": "^4.7.8",
|
||||||
"i18n-iso-countries": "^7.6.0",
|
"i18n-iso-countries": "^7.6.0",
|
||||||
"ioredis": "^5.3.2",
|
"ioredis": "^5.3.2",
|
||||||
"joi": "^17.10.0",
|
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"kysely": "^0.28.0",
|
"kysely": "^0.28.0",
|
||||||
"kysely-postgres-js": "^2.0.0",
|
"kysely-postgres-js": "^2.0.0",
|
||||||
@@ -1349,21 +1348,6 @@
|
|||||||
"node": ">=6"
|
"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": {
|
"node_modules/@humanfs/core": {
|
||||||
"version": "0.19.1",
|
"version": "0.19.1",
|
||||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
||||||
@@ -5052,27 +5036,6 @@
|
|||||||
"url": "https://ko-fi.com/killymxi"
|
"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": {
|
"node_modules/@socket.io/component-emitter": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||||
@@ -11333,19 +11296,6 @@
|
|||||||
"jiti": "bin/jiti.js"
|
"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": {
|
"node_modules/jose": {
|
||||||
"version": "6.0.10",
|
"version": "6.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/jose/-/jose-6.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/jose/-/jose-6.0.10.tgz",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich",
|
"name": "immich",
|
||||||
"version": "1.135.0",
|
"version": "1.135.2",
|
||||||
"description": "",
|
"description": "",
|
||||||
"author": "",
|
"author": "",
|
||||||
"private": true,
|
"private": true,
|
||||||
@@ -68,7 +68,6 @@
|
|||||||
"handlebars": "^4.7.8",
|
"handlebars": "^4.7.8",
|
||||||
"i18n-iso-countries": "^7.6.0",
|
"i18n-iso-countries": "^7.6.0",
|
||||||
"ioredis": "^5.3.2",
|
"ioredis": "^5.3.2",
|
||||||
"joi": "^17.10.0",
|
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"kysely": "^0.28.0",
|
"kysely": "^0.28.0",
|
||||||
"kysely-postgres-js": "^2.0.0",
|
"kysely-postgres-js": "^2.0.0",
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ limit
|
|||||||
with
|
with
|
||||||
"assets" as (
|
"assets" as (
|
||||||
select
|
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
|
from
|
||||||
"assets"
|
"assets"
|
||||||
where
|
where
|
||||||
@@ -232,7 +232,7 @@ with
|
|||||||
and "assets"."visibility" in ('archive', 'timeline')
|
and "assets"."visibility" in ('archive', 'timeline')
|
||||||
)
|
)
|
||||||
select
|
select
|
||||||
"timeBucket"::date::text as "timeBucket",
|
("timeBucket" AT TIME ZONE 'UTC')::date::text as "timeBucket",
|
||||||
count(*) as "count"
|
count(*) as "count"
|
||||||
from
|
from
|
||||||
"assets"
|
"assets"
|
||||||
@@ -300,7 +300,7 @@ with
|
|||||||
where
|
where
|
||||||
"assets"."deletedAt" is null
|
"assets"."deletedAt" is null
|
||||||
and "assets"."visibility" in ('archive', 'timeline')
|
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 (
|
and not exists (
|
||||||
select
|
select
|
||||||
from
|
from
|
||||||
|
|||||||
@@ -12,6 +12,38 @@ delete from "person"
|
|||||||
where
|
where
|
||||||
"person"."id" in ($1)
|
"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
|
-- PersonRepository.getAllWithoutFaces
|
||||||
select
|
select
|
||||||
"person".*
|
"person".*
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ from
|
|||||||
where
|
where
|
||||||
"ownerId" = $1
|
"ownerId" = $1
|
||||||
and "updatedAt" < now() - interval '1 millisecond'
|
and "updatedAt" < now() - interval '1 millisecond'
|
||||||
and "updateId" < $2
|
and "updateId" <= $2
|
||||||
and "updateId" >= $3
|
and "updateId" >= $3
|
||||||
order by
|
order by
|
||||||
"updateId" asc
|
"updateId" asc
|
||||||
@@ -274,7 +274,7 @@ from
|
|||||||
where
|
where
|
||||||
"assets"."ownerId" = $1
|
"assets"."ownerId" = $1
|
||||||
and "exif"."updatedAt" < now() - interval '1 millisecond'
|
and "exif"."updatedAt" < now() - interval '1 millisecond'
|
||||||
and "exif"."updateId" < $2
|
and "exif"."updateId" <= $2
|
||||||
and "exif"."updateId" >= $3
|
and "exif"."updateId" >= $3
|
||||||
order by
|
order by
|
||||||
"exif"."updateId" asc
|
"exif"."updateId" asc
|
||||||
@@ -418,7 +418,7 @@ from
|
|||||||
where
|
where
|
||||||
"albumsId" = $1
|
"albumsId" = $1
|
||||||
and "updatedAt" < now() - interval '1 millisecond'
|
and "updatedAt" < now() - interval '1 millisecond'
|
||||||
and "updateId" < $2
|
and "updateId" <= $2
|
||||||
and "updateId" >= $3
|
and "updateId" >= $3
|
||||||
order by
|
order by
|
||||||
"updateId" asc
|
"updateId" asc
|
||||||
|
|||||||
@@ -42,11 +42,6 @@ interface LivePhotoSearchOptions {
|
|||||||
type: AssetType;
|
type: AssetType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum TimeBucketSize {
|
|
||||||
DAY = 'DAY',
|
|
||||||
MONTH = 'MONTH',
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AssetBuilderOptions {
|
interface AssetBuilderOptions {
|
||||||
isFavorite?: boolean;
|
isFavorite?: boolean;
|
||||||
isTrashed?: boolean;
|
isTrashed?: boolean;
|
||||||
@@ -490,13 +485,13 @@ export class AssetRepository {
|
|||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [{ size: TimeBucketSize.MONTH }] })
|
@GenerateSql({ params: [{}] })
|
||||||
async getTimeBuckets(options: TimeBucketOptions): Promise<TimeBucketItem[]> {
|
async getTimeBuckets(options: TimeBucketOptions): Promise<TimeBucketItem[]> {
|
||||||
return this.db
|
return this.db
|
||||||
.with('assets', (qb) =>
|
.with('assets', (qb) =>
|
||||||
qb
|
qb
|
||||||
.selectFrom('assets')
|
.selectFrom('assets')
|
||||||
.select(truncatedDate<Date>(TimeBucketSize.MONTH).as('timeBucket'))
|
.select(truncatedDate<Date>().as('timeBucket'))
|
||||||
.$if(!!options.isTrashed, (qb) => qb.where('assets.status', '!=', AssetStatus.DELETED))
|
.$if(!!options.isTrashed, (qb) => qb.where('assets.status', '!=', AssetStatus.DELETED))
|
||||||
.where('assets.deletedAt', options.isTrashed ? 'is not' : 'is', null)
|
.where('assets.deletedAt', options.isTrashed ? 'is not' : 'is', null)
|
||||||
.$if(options.visibility === undefined, withDefaultVisibility)
|
.$if(options.visibility === undefined, withDefaultVisibility)
|
||||||
@@ -525,7 +520,7 @@ export class AssetRepository {
|
|||||||
.$if(!!options.tagId, (qb) => withTagId(qb, options.tagId!)),
|
.$if(!!options.tagId, (qb) => withTagId(qb, options.tagId!)),
|
||||||
)
|
)
|
||||||
.selectFrom('assets')
|
.selectFrom('assets')
|
||||||
.select(sql<string>`"timeBucket"::date::text`.as('timeBucket'))
|
.select(sql<string>`("timeBucket" AT TIME ZONE 'UTC')::date::text`.as('timeBucket'))
|
||||||
.select((eb) => eb.fn.countAll<number>().as('count'))
|
.select((eb) => eb.fn.countAll<number>().as('count'))
|
||||||
.groupBy('timeBucket')
|
.groupBy('timeBucket')
|
||||||
.orderBy('timeBucket', options.order ?? 'desc')
|
.orderBy('timeBucket', options.order ?? 'desc')
|
||||||
@@ -576,7 +571,7 @@ export class AssetRepository {
|
|||||||
.where('assets.deletedAt', options.isTrashed ? 'is not' : 'is', null)
|
.where('assets.deletedAt', options.isTrashed ? 'is not' : 'is', null)
|
||||||
.$if(options.visibility == undefined, withDefaultVisibility)
|
.$if(options.visibility == undefined, withDefaultVisibility)
|
||||||
.$if(!!options.visibility, (qb) => qb.where('assets.visibility', '=', options.visibility!))
|
.$if(!!options.visibility, (qb) => qb.where('assets.visibility', '=', options.visibility!))
|
||||||
.where(truncatedDate(TimeBucketSize.MONTH), '=', timeBucket.replace(/^[+-]/, ''))
|
.where(truncatedDate(), '=', timeBucket.replace(/^[+-]/, ''))
|
||||||
.$if(!!options.albumId, (qb) =>
|
.$if(!!options.albumId, (qb) =>
|
||||||
qb.where((eb) =>
|
qb.where((eb) =>
|
||||||
eb.exists(
|
eb.exists(
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ export class PersonRepository {
|
|||||||
.stream();
|
.stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [{ take: 1, skip: 0 }, DummyValue.UUID] })
|
||||||
async getAllForUser(pagination: PaginationOptions, userId: string, options?: PersonSearchOptions) {
|
async getAllForUser(pagination: PaginationOptions, userId: string, options?: PersonSearchOptions) {
|
||||||
const items = await this.db
|
const items = await this.db
|
||||||
.selectFrom('person')
|
.selectFrom('person')
|
||||||
@@ -179,8 +180,9 @@ export class PersonRepository {
|
|||||||
)
|
)
|
||||||
.$if(!options?.closestFaceAssetId, (qb) =>
|
.$if(!options?.closestFaceAssetId, (qb) =>
|
||||||
qb
|
qb
|
||||||
|
.orderBy(sql`NULLIF(person.name, '') is null`, 'asc')
|
||||||
.orderBy((eb) => eb.fn.count('asset_faces.assetId'), 'desc')
|
.orderBy((eb) => eb.fn.count('asset_faces.assetId'), 'desc')
|
||||||
.orderBy(sql`NULLIF(person.name, '') asc nulls last`)
|
.orderBy(sql`NULLIF(person.name, '')`, (om) => om.asc().nullsLast())
|
||||||
.orderBy('person.createdAt'),
|
.orderBy('person.createdAt'),
|
||||||
)
|
)
|
||||||
.$if(!options?.withHidden, (qb) => qb.where('person.isHidden', '=', false))
|
.$if(!options?.withHidden, (qb) => qb.where('person.isHidden', '=', false))
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ export class SyncRepository {
|
|||||||
.select(columns.syncAsset)
|
.select(columns.syncAsset)
|
||||||
.where('ownerId', '=', partnerId)
|
.where('ownerId', '=', partnerId)
|
||||||
.where('updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
.where('updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||||
.where('updateId', '<', beforeUpdateId)
|
.where('updateId', '<=', beforeUpdateId)
|
||||||
.$if(!!afterUpdateId, (eb) => eb.where('updateId', '>=', afterUpdateId!))
|
.$if(!!afterUpdateId, (eb) => eb.where('updateId', '>=', afterUpdateId!))
|
||||||
.orderBy('updateId', 'asc')
|
.orderBy('updateId', 'asc')
|
||||||
.stream();
|
.stream();
|
||||||
@@ -169,7 +169,7 @@ export class SyncRepository {
|
|||||||
.innerJoin('assets', 'assets.id', 'exif.assetId')
|
.innerJoin('assets', 'assets.id', 'exif.assetId')
|
||||||
.where('assets.ownerId', '=', partnerId)
|
.where('assets.ownerId', '=', partnerId)
|
||||||
.where('exif.updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
.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!))
|
.$if(!!afterUpdateId, (eb) => eb.where('exif.updateId', '>=', afterUpdateId!))
|
||||||
.orderBy('exif.updateId', 'asc')
|
.orderBy('exif.updateId', 'asc')
|
||||||
.stream();
|
.stream();
|
||||||
@@ -273,7 +273,7 @@ export class SyncRepository {
|
|||||||
.select(columns.syncAlbumUser)
|
.select(columns.syncAlbumUser)
|
||||||
.where('albumsId', '=', albumId)
|
.where('albumsId', '=', albumId)
|
||||||
.where('updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
.where('updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||||
.where('updateId', '<', beforeUpdateId)
|
.where('updateId', '<=', beforeUpdateId)
|
||||||
.$if(!!afterUpdateId, (eb) => eb.where('updateId', '>=', afterUpdateId!))
|
.$if(!!afterUpdateId, (eb) => eb.where('updateId', '>=', afterUpdateId!))
|
||||||
.orderBy('updateId', 'asc')
|
.orderBy('updateId', 'asc')
|
||||||
.stream();
|
.stream();
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
import { Kysely, sql } from 'kysely';
|
import { Kysely, sql } from 'kysely';
|
||||||
|
|
||||||
export async function up(db: Kysely<any>): Promise<void> {
|
export async function up(qb: Kysely<any>): Promise<void> {
|
||||||
const { rows } = await sql<{ db: string }>`SELECT current_database() as db;`.execute(db);
|
type Conf = { db: string; guc: string[] };
|
||||||
const databaseName = rows[0].db;
|
const res = await sql<Conf>`select current_database() db, to_json(setconfig) guc from pg_db_role_setting`.execute(qb);
|
||||||
await sql.raw(`ALTER DATABASE "${databaseName}" RESET vchordrq.prewarm_dim;`).execute(db);
|
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(db: Kysely<any>): Promise<void> {
|
export async function down(): Promise<void> {}
|
||||||
const { rows } = await sql<{ db: string }>`SELECT current_database() as db;`.execute(db);
|
|
||||||
const databaseName = rows[0].db;
|
|
||||||
await sql
|
|
||||||
.raw(`ALTER DATABASE "${databaseName}" SET vchordrq.prewarm_dim = '512,640,768,1024,1152,1536';`)
|
|
||||||
.execute(db);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -151,11 +151,11 @@ describe(SearchService.name, () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const id = assetStub.livePhotoMotionAsset.id;
|
const id = assetStub.livePhotoMotionAsset.id;
|
||||||
mocks.asset.getById.mockResolvedValue(assetStub.livePhotoMotionAsset);
|
|
||||||
|
|
||||||
const result = await sut.handleSearchDuplicates({ id });
|
const result = await sut.handleSearchDuplicates({ id });
|
||||||
|
|
||||||
expect(result).toBe(JobStatus.SKIPPED);
|
expect(result).toBe(JobStatus.SKIPPED);
|
||||||
|
expect(mocks.assetJob.getForSearchDuplicatesJob).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should skip if duplicate detection is disabled', async () => {
|
it('should skip if duplicate detection is disabled', async () => {
|
||||||
@@ -168,11 +168,11 @@ describe(SearchService.name, () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const id = assetStub.livePhotoMotionAsset.id;
|
const id = assetStub.livePhotoMotionAsset.id;
|
||||||
mocks.asset.getById.mockResolvedValue(assetStub.livePhotoMotionAsset);
|
|
||||||
|
|
||||||
const result = await sut.handleSearchDuplicates({ id });
|
const result = await sut.handleSearchDuplicates({ id });
|
||||||
|
|
||||||
expect(result).toBe(JobStatus.SKIPPED);
|
expect(result).toBe(JobStatus.SKIPPED);
|
||||||
|
expect(mocks.assetJob.getForSearchDuplicatesJob).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail if asset is not found', async () => {
|
it('should fail if asset is not found', async () => {
|
||||||
|
|||||||
@@ -1113,8 +1113,6 @@ describe(LibraryService.name, () => {
|
|||||||
mocks.library.get.mockResolvedValue(library);
|
mocks.library.get.mockResolvedValue(library);
|
||||||
mocks.library.streamAssetIds.mockReturnValue(makeStream([assetStub.image1]));
|
mocks.library.streamAssetIds.mockReturnValue(makeStream([assetStub.image1]));
|
||||||
|
|
||||||
mocks.asset.getById.mockResolvedValue(assetStub.image1);
|
|
||||||
|
|
||||||
await expect(sut.handleDeleteLibrary({ id: library.id })).resolves.toBe(JobStatus.SUCCESS);
|
await expect(sut.handleDeleteLibrary({ id: library.id })).resolves.toBe(JobStatus.SUCCESS);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ describe(NotificationService.name, () => {
|
|||||||
mocks.album.getById.mockResolvedValue(albumStub.empty);
|
mocks.album.getById.mockResolvedValue(albumStub.empty);
|
||||||
|
|
||||||
await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.SKIPPED);
|
await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.SKIPPED);
|
||||||
expect(mocks.asset.getById).not.toHaveBeenCalled();
|
expect(mocks.job.queue).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should skip if the recipient has email notifications disabled', async () => {
|
it('should skip if the recipient has email notifications disabled', async () => {
|
||||||
|
|||||||
@@ -38,11 +38,11 @@ const mapSyncAssetV1 = ({ checksum, thumbhash, ...data }: AssetLike): SyncAssetV
|
|||||||
thumbhash: thumbhash ? hexOrBufferToBase64(thumbhash) : null,
|
thumbhash: thumbhash ? hexOrBufferToBase64(thumbhash) : null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const isEntityBackfillComplete = (entity: { createId: string }, checkpoint: SyncAck | undefined): boolean =>
|
const isEntityBackfillComplete = (createId: string, checkpoint: SyncAck | undefined): boolean =>
|
||||||
entity.createId === checkpoint?.updateId && checkpoint.extraId === COMPLETE_ID;
|
createId === checkpoint?.updateId && checkpoint.extraId === COMPLETE_ID;
|
||||||
|
|
||||||
const getStartId = (entity: { createId: string }, checkpoint: SyncAck | undefined): string | undefined =>
|
const getStartId = (createId: string, checkpoint: SyncAck | undefined): string | undefined =>
|
||||||
checkpoint?.updateId === entity.createId ? checkpoint?.extraId : undefined;
|
createId === checkpoint?.updateId ? checkpoint?.extraId : undefined;
|
||||||
|
|
||||||
const send = <T extends keyof SyncItem, D extends SyncItem[T]>(response: Writable, item: SerializeOptions<T, D>) => {
|
const send = <T extends keyof SyncItem, D extends SyncItem[T]>(response: Writable, item: SerializeOptions<T, D>) => {
|
||||||
response.write(serialize(item));
|
response.write(serialize(item));
|
||||||
@@ -235,22 +235,23 @@ export class SyncService extends BaseService {
|
|||||||
const endId = upsertCheckpoint.updateId;
|
const endId = upsertCheckpoint.updateId;
|
||||||
|
|
||||||
for (const partner of partners) {
|
for (const partner of partners) {
|
||||||
if (isEntityBackfillComplete(partner, backfillCheckpoint)) {
|
const createId = partner.createId;
|
||||||
|
if (isEntityBackfillComplete(createId, backfillCheckpoint)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const startId = getStartId(partner, backfillCheckpoint);
|
const startId = getStartId(createId, backfillCheckpoint);
|
||||||
const backfill = this.syncRepository.getPartnerAssetsBackfill(partner.sharedById, startId, endId);
|
const backfill = this.syncRepository.getPartnerAssetsBackfill(partner.sharedById, startId, endId);
|
||||||
|
|
||||||
for await (const { updateId, ...data } of backfill) {
|
for await (const { updateId, ...data } of backfill) {
|
||||||
send(response, {
|
send(response, {
|
||||||
type: backfillType,
|
type: backfillType,
|
||||||
ids: [updateId],
|
ids: [createId, updateId],
|
||||||
data: mapSyncAssetV1(data),
|
data: mapSyncAssetV1(data),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
sendEntityBackfillCompleteAck(response, backfillType, partner.sharedById);
|
sendEntityBackfillCompleteAck(response, backfillType, createId);
|
||||||
}
|
}
|
||||||
} else if (partners.length > 0) {
|
} else if (partners.length > 0) {
|
||||||
await this.upsertBackfillCheckpoint({
|
await this.upsertBackfillCheckpoint({
|
||||||
@@ -291,18 +292,19 @@ export class SyncService extends BaseService {
|
|||||||
const endId = upsertCheckpoint.updateId;
|
const endId = upsertCheckpoint.updateId;
|
||||||
|
|
||||||
for (const partner of partners) {
|
for (const partner of partners) {
|
||||||
if (isEntityBackfillComplete(partner, backfillCheckpoint)) {
|
const createId = partner.createId;
|
||||||
|
if (isEntityBackfillComplete(createId, backfillCheckpoint)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const startId = getStartId(partner, backfillCheckpoint);
|
const startId = getStartId(createId, backfillCheckpoint);
|
||||||
const backfill = this.syncRepository.getPartnerAssetExifsBackfill(partner.sharedById, startId, endId);
|
const backfill = this.syncRepository.getPartnerAssetExifsBackfill(partner.sharedById, startId, endId);
|
||||||
|
|
||||||
for await (const { updateId, ...data } of backfill) {
|
for await (const { updateId, ...data } of backfill) {
|
||||||
send(response, { type: backfillType, ids: [updateId], data });
|
send(response, { type: backfillType, ids: [partner.createId, updateId], data });
|
||||||
}
|
}
|
||||||
|
|
||||||
sendEntityBackfillCompleteAck(response, backfillType, partner.sharedById);
|
sendEntityBackfillCompleteAck(response, backfillType, partner.createId);
|
||||||
}
|
}
|
||||||
} else if (partners.length > 0) {
|
} else if (partners.length > 0) {
|
||||||
await this.upsertBackfillCheckpoint({
|
await this.upsertBackfillCheckpoint({
|
||||||
@@ -350,18 +352,19 @@ export class SyncService extends BaseService {
|
|||||||
const endId = upsertCheckpoint.updateId;
|
const endId = upsertCheckpoint.updateId;
|
||||||
|
|
||||||
for (const album of albums) {
|
for (const album of albums) {
|
||||||
if (isEntityBackfillComplete(album, backfillCheckpoint)) {
|
const createId = album.createId;
|
||||||
|
if (isEntityBackfillComplete(createId, backfillCheckpoint)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const startId = getStartId(album, backfillCheckpoint);
|
const startId = getStartId(createId, backfillCheckpoint);
|
||||||
const backfill = this.syncRepository.getAlbumUsersBackfill(album.id, startId, endId);
|
const backfill = this.syncRepository.getAlbumUsersBackfill(album.id, startId, endId);
|
||||||
|
|
||||||
for await (const { updateId, ...data } of backfill) {
|
for await (const { updateId, ...data } of backfill) {
|
||||||
send(response, { type: backfillType, ids: [updateId], data });
|
send(response, { type: backfillType, ids: [createId, updateId], data });
|
||||||
}
|
}
|
||||||
|
|
||||||
sendEntityBackfillCompleteAck(response, backfillType, album.id);
|
sendEntityBackfillCompleteAck(response, backfillType, createId);
|
||||||
}
|
}
|
||||||
} else if (albums.length > 0) {
|
} else if (albums.length > 0) {
|
||||||
await this.upsertBackfillCheckpoint({
|
await this.upsertBackfillCheckpoint({
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import postgres, { Notice } from 'postgres';
|
|||||||
import { columns, Exif, Person } from 'src/database';
|
import { columns, Exif, Person } from 'src/database';
|
||||||
import { DB } from 'src/db';
|
import { DB } from 'src/db';
|
||||||
import { AssetFileType, AssetVisibility, DatabaseExtension, DatabaseSslMode } from 'src/enum';
|
import { AssetFileType, AssetVisibility, DatabaseExtension, DatabaseSslMode } from 'src/enum';
|
||||||
import { TimeBucketSize } from 'src/repositories/asset.repository';
|
|
||||||
import { AssetSearchBuilderOptions } from 'src/repositories/search.repository';
|
import { AssetSearchBuilderOptions } from 'src/repositories/search.repository';
|
||||||
import { DatabaseConnectionParams, VectorExtension } from 'src/types';
|
import { DatabaseConnectionParams, VectorExtension } from 'src/types';
|
||||||
|
|
||||||
@@ -279,8 +278,8 @@ export function withTags(eb: ExpressionBuilder<DB, 'assets'>) {
|
|||||||
).as('tags');
|
).as('tags');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function truncatedDate<O>(size: TimeBucketSize) {
|
export function truncatedDate<O>() {
|
||||||
return sql<O>`date_trunc(${sql.lit(size)}, "localDateTime" at time zone 'UTC') at time zone 'UTC'`;
|
return sql<O>`date_trunc(${sql.lit('MONTH')}, "localDateTime" AT TIME ZONE 'UTC') AT TIME ZONE 'UTC'`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function withTagId<O>(qb: SelectQueryBuilder<DB, 'assets', O>, tagId: string) {
|
export function withTagId<O>(qb: SelectQueryBuilder<DB, 'assets', O>, tagId: string) {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user