mirror of
https://github.com/immich-app/immich.git
synced 2026-04-30 04:58:48 -07:00
Compare commits
39 Commits
feature/lo
...
chore/dura
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
390da8a314 | ||
|
|
5c0bff2929 | ||
|
|
cc54f7e809 | ||
|
|
2916705a8c | ||
|
|
010ceea49b | ||
|
|
a861fad80e | ||
|
|
d250bac5f0 | ||
|
|
9dc9ee0726 | ||
|
|
3747a67d2d | ||
|
|
7ef7ecec5b | ||
|
|
bc4abd18e4 | ||
|
|
b74cfd4424 | ||
|
|
7dc84f56c0 | ||
|
|
92634f923b | ||
|
|
96b6165bd3 | ||
|
|
2624f3884f | ||
|
|
f9b7ce9407 | ||
|
|
013ea37a0d | ||
|
|
b2b4385271 | ||
|
|
081c75bb21 | ||
|
|
da337578fb | ||
|
|
acf4109171 | ||
|
|
66601a1fdc | ||
|
|
02ff077367 | ||
|
|
94bb6c1a5e | ||
|
|
fe9e5afcf4 | ||
|
|
5e89efba64 | ||
|
|
5a457d72c9 | ||
|
|
45ccdb37fb | ||
|
|
9263e2f2e1 | ||
|
|
a3ee615c5b | ||
|
|
39cfad7136 | ||
|
|
350056dd1a | ||
|
|
f0835d06f8 | ||
|
|
03b70cf029 | ||
|
|
4bfb8b36c2 | ||
|
|
dfacde5af8 | ||
|
|
317afe9e3b | ||
|
|
1fb5f13237 |
@@ -1,5 +1,5 @@
|
||||
[tools]
|
||||
terragrunt = "1.0.1"
|
||||
terragrunt = "1.0.2"
|
||||
opentofu = "1.11.6"
|
||||
|
||||
[tasks."tg:fmt"]
|
||||
|
||||
@@ -85,7 +85,7 @@ services:
|
||||
container_name: immich_prometheus
|
||||
ports:
|
||||
- 9090:9090
|
||||
image: prom/prometheus@sha256:5550dc63da361dc30f6fe02ac0e4dfc736ededfef3c8d12a634db04a67824d78
|
||||
image: prom/prometheus@sha256:e4254400b85610324913f0dc4acf92603d9984e7519414c5a12811aa6146acc3
|
||||
volumes:
|
||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
- prometheus-data:/prometheus
|
||||
|
||||
@@ -2,82 +2,43 @@ import { expect } from 'vitest';
|
||||
|
||||
export const errorDto = {
|
||||
unauthorized: {
|
||||
error: 'Unauthorized',
|
||||
statusCode: 401,
|
||||
message: 'Authentication required',
|
||||
correlationId: expect.any(String),
|
||||
},
|
||||
unauthorizedWithMessage: (message: string) => ({
|
||||
error: 'Unauthorized',
|
||||
statusCode: 401,
|
||||
message,
|
||||
correlationId: expect.any(String),
|
||||
}),
|
||||
forbidden: {
|
||||
error: 'Forbidden',
|
||||
statusCode: 403,
|
||||
message: expect.any(String),
|
||||
correlationId: expect.any(String),
|
||||
},
|
||||
missingPermission: (permission: string) => ({
|
||||
error: 'Forbidden',
|
||||
statusCode: 403,
|
||||
message: `Missing required permission: ${permission}`,
|
||||
correlationId: expect.any(String),
|
||||
}),
|
||||
wrongPassword: {
|
||||
error: 'Bad Request',
|
||||
statusCode: 400,
|
||||
message: 'Wrong password',
|
||||
correlationId: expect.any(String),
|
||||
},
|
||||
invalidToken: {
|
||||
error: 'Unauthorized',
|
||||
statusCode: 401,
|
||||
message: 'Invalid user token',
|
||||
correlationId: expect.any(String),
|
||||
},
|
||||
invalidShareKey: {
|
||||
error: 'Unauthorized',
|
||||
statusCode: 401,
|
||||
message: 'Invalid share key',
|
||||
correlationId: expect.any(String),
|
||||
},
|
||||
passwordRequired: {
|
||||
error: 'Unauthorized',
|
||||
statusCode: 401,
|
||||
message: 'Password required',
|
||||
correlationId: expect.any(String),
|
||||
},
|
||||
badRequest: (message: any = null) => ({
|
||||
error: 'Bad Request',
|
||||
statusCode: 400,
|
||||
message: message ?? expect.anything(),
|
||||
correlationId: expect.any(String),
|
||||
}),
|
||||
noPermission: {
|
||||
error: 'Bad Request',
|
||||
statusCode: 400,
|
||||
message: expect.stringContaining('Not found or no'),
|
||||
correlationId: expect.any(String),
|
||||
},
|
||||
incorrectLogin: {
|
||||
error: 'Unauthorized',
|
||||
statusCode: 401,
|
||||
message: 'Incorrect email or password',
|
||||
correlationId: expect.any(String),
|
||||
},
|
||||
alreadyHasAdmin: {
|
||||
error: 'Bad Request',
|
||||
statusCode: 400,
|
||||
message: 'The server already has an admin',
|
||||
correlationId: expect.any(String),
|
||||
},
|
||||
invalidEmail: {
|
||||
error: 'Bad Request',
|
||||
statusCode: 400,
|
||||
message: ['email must be an email'],
|
||||
correlationId: expect.any(String),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -154,23 +154,31 @@ describe('/albums', () => {
|
||||
expect(body).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
ownerId: user1.userId,
|
||||
albumName: user1SharedLink,
|
||||
albumUsers: expect.arrayContaining([
|
||||
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
|
||||
]),
|
||||
shared: true,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
ownerId: user1.userId,
|
||||
albumName: user1SharedEditorUser,
|
||||
albumUsers: expect.arrayContaining([
|
||||
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
|
||||
]),
|
||||
shared: true,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
ownerId: user1.userId,
|
||||
albumName: user1SharedViewerUser,
|
||||
albumUsers: expect.arrayContaining([
|
||||
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
|
||||
]),
|
||||
shared: true,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
ownerId: user2.userId,
|
||||
albumName: user2SharedUser,
|
||||
albumUsers: expect.arrayContaining([
|
||||
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user2.userId }) },
|
||||
]),
|
||||
shared: true,
|
||||
}),
|
||||
]),
|
||||
@@ -184,23 +192,31 @@ describe('/albums', () => {
|
||||
expect(body).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
ownerId: user1.userId,
|
||||
albumName: user1SharedEditorUser,
|
||||
albumUsers: expect.arrayContaining([
|
||||
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
|
||||
]),
|
||||
shared: true,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
ownerId: user1.userId,
|
||||
albumName: user1SharedViewerUser,
|
||||
albumUsers: expect.arrayContaining([
|
||||
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
|
||||
]),
|
||||
shared: true,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
ownerId: user1.userId,
|
||||
albumName: user1SharedLink,
|
||||
albumUsers: expect.arrayContaining([
|
||||
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
|
||||
]),
|
||||
shared: true,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
ownerId: user1.userId,
|
||||
albumName: user1NotShared,
|
||||
albumUsers: expect.arrayContaining([
|
||||
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
|
||||
]),
|
||||
shared: false,
|
||||
}),
|
||||
]),
|
||||
@@ -216,23 +232,31 @@ describe('/albums', () => {
|
||||
expect(body).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
ownerId: user1.userId,
|
||||
albumName: user1SharedEditorUser,
|
||||
albumUsers: expect.arrayContaining([
|
||||
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
|
||||
]),
|
||||
shared: true,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
ownerId: user1.userId,
|
||||
albumName: user1SharedViewerUser,
|
||||
albumUsers: expect.arrayContaining([
|
||||
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
|
||||
]),
|
||||
shared: true,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
ownerId: user1.userId,
|
||||
albumName: user1SharedLink,
|
||||
albumUsers: expect.arrayContaining([
|
||||
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
|
||||
]),
|
||||
shared: true,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
ownerId: user2.userId,
|
||||
albumName: user2SharedUser,
|
||||
albumUsers: expect.arrayContaining([
|
||||
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user2.userId }) },
|
||||
]),
|
||||
shared: true,
|
||||
}),
|
||||
]),
|
||||
@@ -248,8 +272,10 @@ describe('/albums', () => {
|
||||
expect(body).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
ownerId: user1.userId,
|
||||
albumName: user1NotShared,
|
||||
albumUsers: expect.arrayContaining([
|
||||
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
|
||||
]),
|
||||
shared: false,
|
||||
}),
|
||||
]),
|
||||
@@ -286,13 +312,17 @@ describe('/albums', () => {
|
||||
expect(body).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
ownerId: user4.userId,
|
||||
albumName: user4DeletedAsset,
|
||||
albumUsers: expect.arrayContaining([
|
||||
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user4.userId }) },
|
||||
]),
|
||||
shared: false,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
ownerId: user4.userId,
|
||||
albumName: user4Empty,
|
||||
albumUsers: expect.arrayContaining([
|
||||
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user4.userId }) },
|
||||
]),
|
||||
shared: false,
|
||||
}),
|
||||
]),
|
||||
@@ -362,16 +392,17 @@ describe('/albums', () => {
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual({
|
||||
...user2Albums[0],
|
||||
contributorCounts: [{ userId: user1.userId, assetCount: 1 }],
|
||||
assetCount: 1,
|
||||
lastModifiedAssetTimestamp: expect.any(String),
|
||||
endDate: expect.any(String),
|
||||
startDate: expect.any(String),
|
||||
albumUsers: expect.any(Array),
|
||||
shared: true,
|
||||
});
|
||||
expect(body).toEqual(
|
||||
expect.objectContaining({
|
||||
contributorCounts: [{ userId: user1.userId, assetCount: 1 }],
|
||||
assetCount: 1,
|
||||
lastModifiedAssetTimestamp: expect.any(String),
|
||||
endDate: expect.any(String),
|
||||
startDate: expect.any(String),
|
||||
albumUsers: expect.any(Array),
|
||||
shared: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -397,15 +428,13 @@ describe('/albums', () => {
|
||||
id: expect.any(String),
|
||||
createdAt: expect.any(String),
|
||||
updatedAt: expect.any(String),
|
||||
ownerId: user1.userId,
|
||||
albumName: 'New album',
|
||||
description: '',
|
||||
albumThumbnailAssetId: null,
|
||||
shared: false,
|
||||
albumUsers: [],
|
||||
albumUsers: [{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) }],
|
||||
hasSharedLink: false,
|
||||
assetCount: 0,
|
||||
owner: expect.objectContaining({ email: user1.userEmail }),
|
||||
isActivityEnabled: true,
|
||||
order: AssetOrder.Desc,
|
||||
});
|
||||
@@ -621,11 +650,11 @@ describe('/albums', () => {
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual(
|
||||
expect.objectContaining({
|
||||
albumUsers: [
|
||||
albumUsers: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
user: expect.objectContaining({ id: user2.userId }),
|
||||
}),
|
||||
],
|
||||
]),
|
||||
}),
|
||||
);
|
||||
});
|
||||
@@ -637,7 +666,7 @@ describe('/albums', () => {
|
||||
.send({ albumUsers: [{ userId: user1.userId, role: AlbumUserRole.Editor }] });
|
||||
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest('Cannot be shared with owner'));
|
||||
expect(body).toEqual(errorDto.badRequest('User already added'));
|
||||
});
|
||||
|
||||
it('should not be able to add existing user to shared album', async () => {
|
||||
@@ -663,7 +692,7 @@ describe('/albums', () => {
|
||||
albumUsers: [{ userId: user2.userId, role: AlbumUserRole.Viewer }],
|
||||
});
|
||||
|
||||
expect(album.albumUsers[0].role).toEqual(AlbumUserRole.Viewer);
|
||||
expect(album.albumUsers[1].role).toEqual(AlbumUserRole.Viewer);
|
||||
|
||||
const { status } = await request(app)
|
||||
.put(`/albums/${album.id}/user/${user2.userId}`)
|
||||
@@ -678,7 +707,10 @@ describe('/albums', () => {
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||
expect(body).toEqual(
|
||||
expect.objectContaining({
|
||||
albumUsers: [expect.objectContaining({ role: AlbumUserRole.Editor })],
|
||||
albumUsers: [
|
||||
expect.objectContaining({ role: AlbumUserRole.Owner }),
|
||||
expect.objectContaining({ role: AlbumUserRole.Editor }),
|
||||
],
|
||||
}),
|
||||
);
|
||||
});
|
||||
@@ -689,7 +721,7 @@ describe('/albums', () => {
|
||||
albumUsers: [{ userId: user2.userId, role: AlbumUserRole.Viewer }],
|
||||
});
|
||||
|
||||
expect(album.albumUsers[0].role).toEqual(AlbumUserRole.Viewer);
|
||||
expect(album.albumUsers[1].role).toEqual(AlbumUserRole.Viewer);
|
||||
|
||||
const { status, body } = await request(app)
|
||||
.put(`/albums/${album.id}/user/${user2.userId}`)
|
||||
|
||||
@@ -332,9 +332,7 @@ describe(`/oauth`, () => {
|
||||
const { status, body } = await request(app).post('/oauth/callback').send(callbackParams);
|
||||
expect(status).toBe(500);
|
||||
expect(body).toMatchObject({
|
||||
error: 'Internal Server Error',
|
||||
message: 'Failed to finish oauth',
|
||||
statusCode: 500,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -495,11 +493,10 @@ describe(`/oauth`, () => {
|
||||
});
|
||||
|
||||
it('should reject OAuth discovery over HTTP', async () => {
|
||||
const { status, body } = await request(app)
|
||||
const { status } = await request(app)
|
||||
.post('/oauth/authorize')
|
||||
.send({ redirectUri: 'http://127.0.0.1:2285/auth/login' });
|
||||
expect(status).toBe(500);
|
||||
expect(body).toMatchObject({ statusCode: 500 });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -32,8 +32,12 @@ export function generateThumbhash(rng: SeededRandom): string {
|
||||
return Array.from({ length: 10 }, () => rng.nextInt(0, 256).toString(16).padStart(2, '0')).join('');
|
||||
}
|
||||
|
||||
export function generateDuration(rng: SeededRandom): string {
|
||||
return `${rng.nextInt(GENERATION_CONSTANTS.MIN_VIDEO_DURATION_SECONDS, GENERATION_CONSTANTS.MAX_VIDEO_DURATION_SECONDS)}.${rng.nextInt(0, 1000).toString().padStart(3, '0')}`;
|
||||
export function generateDuration(rng: SeededRandom): number {
|
||||
return (
|
||||
rng.nextInt(GENERATION_CONSTANTS.MIN_VIDEO_DURATION_SECONDS, GENERATION_CONSTANTS.MAX_VIDEO_DURATION_SECONDS) *
|
||||
1000 +
|
||||
rng.nextInt(0, 1000)
|
||||
);
|
||||
}
|
||||
|
||||
export function generateUUID(): string {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
AlbumUserRole,
|
||||
AssetTypeEnum,
|
||||
AssetVisibility,
|
||||
UserAvatarColor,
|
||||
@@ -420,9 +421,7 @@ export function getAlbum(
|
||||
albumThumbnailAssetId: album.thumbnailAssetId,
|
||||
createdAt: album.createdAt,
|
||||
updatedAt: album.updatedAt,
|
||||
ownerId: albumOwner.id,
|
||||
owner: albumOwner,
|
||||
albumUsers: [], // Empty array for non-shared album
|
||||
albumUsers: [{ user: albumOwner, role: AlbumUserRole.Owner }],
|
||||
shared: false,
|
||||
hasSharedLink: false,
|
||||
isActivityEnabled: true,
|
||||
|
||||
@@ -43,7 +43,7 @@ export type MockTimelineAsset = {
|
||||
isTrashed: boolean;
|
||||
isVideo: boolean;
|
||||
isImage: boolean;
|
||||
duration: string | null;
|
||||
duration: number | null;
|
||||
projectionType: string | null;
|
||||
livePhotoVideoId: string | null;
|
||||
city: string | null;
|
||||
|
||||
@@ -48,14 +48,14 @@ FROM python:3.13-slim-trixie@sha256:d168b8d9eb761f4d3fe305ebd04aeb7e7f2de0297cec
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install --no-install-recommends -yqq ocl-icd-libopencl1 wget && \
|
||||
wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/v2.28.4/intel-igc-core-2_2.28.4+20760_amd64.deb && \
|
||||
wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/v2.28.4/intel-igc-opencl-2_2.28.4+20760_amd64.deb && \
|
||||
wget -nv https://github.com/intel/compute-runtime/releases/download/26.05.37020.3/intel-opencl-icd_26.05.37020.3-0_amd64.deb && \
|
||||
wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/v2.32.7/intel-igc-core-2_2.32.7+21184_amd64.deb && \
|
||||
wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/v2.32.7/intel-igc-opencl-2_2.32.7+21184_amd64.deb && \
|
||||
wget -nv https://github.com/intel/compute-runtime/releases/download/26.14.37833.4/intel-opencl-icd_26.14.37833.4-0_amd64.deb && \
|
||||
wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17537.24/intel-igc-core_1.0.17537.24_amd64.deb && \
|
||||
wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17537.24/intel-igc-opencl_1.0.17537.24_amd64.deb && \
|
||||
wget -nv https://github.com/intel/compute-runtime/releases/download/24.35.30872.36/intel-opencl-icd-legacy1_24.35.30872.36_amd64.deb && \
|
||||
# TODO: Figure out how to get renovate to manage this differently versioned libigdgmm file
|
||||
wget -nv https://github.com/intel/compute-runtime/releases/download/26.05.37020.3/libigdgmm12_22.9.0_amd64.deb && \
|
||||
wget -nv https://github.com/intel/compute-runtime/releases/download/26.14.37833.4/libigdgmm12_22.9.0_amd64.deb && \
|
||||
dpkg -i *.deb && \
|
||||
rm *.deb && \
|
||||
apt-get remove wget -yqq && \
|
||||
|
||||
@@ -183,7 +183,10 @@ async def predict(
|
||||
text: str | None = Form(default=None),
|
||||
) -> Any:
|
||||
if image is not None:
|
||||
inputs: Image | str = await run(lambda: decode_pil(image))
|
||||
decoded = await run(lambda: decode_pil(image))
|
||||
if decoded.width == 0 or decoded.height == 0:
|
||||
raise HTTPException(400, "Image has zero width or height")
|
||||
inputs: Image | str = decoded
|
||||
elif text is not None:
|
||||
inputs = text
|
||||
else:
|
||||
|
||||
@@ -9,12 +9,12 @@ dependencies = [
|
||||
"aiocache>=0.12.1,<1.0",
|
||||
"fastapi>=0.95.2,<1.0",
|
||||
"gunicorn>=21.1.0",
|
||||
"huggingface-hub>=0.20.1,<1.0",
|
||||
"huggingface-hub>=1.0,<2.0",
|
||||
"insightface>=0.7.3,<1.0",
|
||||
"numpy<2.4.0",
|
||||
"opencv-python-headless>=4.7.0.72,<5.0",
|
||||
"orjson>=3.9.5",
|
||||
"pillow>=12.2,<12.3",
|
||||
"pillow>=12.2,<13",
|
||||
"pydantic>=2.0.0,<3",
|
||||
"pydantic-settings>=2.5.2,<3",
|
||||
"python-multipart>=0.0.6,<1.0",
|
||||
|
||||
@@ -1198,6 +1198,19 @@ class TestLoad:
|
||||
mock_model.model_format = ModelFormat.ONNX
|
||||
|
||||
|
||||
@pytest.mark.parametrize("size", [(0, 100), (100, 0), (0, 0)])
|
||||
def test_predict_rejects_empty_image(size: tuple[int, int], deployed_app: TestClient) -> None:
|
||||
with mock.patch("immich_ml.main.decode_pil", return_value=Image.new("RGB", size)):
|
||||
response = deployed_app.post(
|
||||
"http://localhost:3003/predict",
|
||||
data={"entries": json.dumps({"clip": {"visual": {"modelName": "ViT-B-32__openai"}}})},
|
||||
files={"image": b"fake image bytes"},
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
assert "zero" in response.json()["detail"].lower()
|
||||
|
||||
|
||||
def test_root_endpoint(deployed_app: TestClient) -> None:
|
||||
response = deployed_app.get("http://localhost:3003")
|
||||
|
||||
|
||||
@@ -15,9 +15,9 @@ config_roots = [
|
||||
|
||||
[tools]
|
||||
node = "24.15.0"
|
||||
flutter = "3.41.6"
|
||||
pnpm = "10.33.0"
|
||||
terragrunt = "1.0.1"
|
||||
flutter = "3.41.7"
|
||||
pnpm = "10.33.1"
|
||||
terragrunt = "1.0.2"
|
||||
opentofu = "1.11.6"
|
||||
java = "21.0.2"
|
||||
|
||||
|
||||
3301
mobile/drift_schemas/main/drift_schema_v24.json
generated
Normal file
3301
mobile/drift_schemas/main/drift_schema_v24.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -751,7 +751,7 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 240;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
DEVELOPMENT_TEAM = 2W7AC6T8T5;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
@@ -760,7 +760,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.121.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.profile;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.profile;
|
||||
PRODUCT_NAME = "Immich-Profile";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@@ -895,7 +895,7 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 240;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
DEVELOPMENT_TEAM = 2W7AC6T8T5;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
@@ -904,7 +904,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.121.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.vdebug;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.debug;
|
||||
PRODUCT_NAME = "Immich-Debug";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@@ -925,7 +925,7 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 240;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
DEVELOPMENT_TEAM = 2W7AC6T8T5;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
@@ -958,7 +958,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 240;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
DEVELOPMENT_TEAM = 2W7AC6T8T5;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -975,7 +975,7 @@
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.vdebug.Widget;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.debug.Widget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||
@@ -1001,7 +1001,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 240;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
DEVELOPMENT_TEAM = 2W7AC6T8T5;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1041,7 +1041,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 240;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
DEVELOPMENT_TEAM = 2W7AC6T8T5;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1057,7 +1057,7 @@
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.profile.Widget;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.profile.Widget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
@@ -1081,7 +1081,7 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 240;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
DEVELOPMENT_TEAM = 2W7AC6T8T5;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1098,7 +1098,7 @@
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.vdebug.ShareExtension;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.debug.ShareExtension;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -1125,7 +1125,7 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 240;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
DEVELOPMENT_TEAM = 2W7AC6T8T5;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1166,7 +1166,7 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 240;
|
||||
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
DEVELOPMENT_TEAM = 2W7AC6T8T5;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1182,7 +1182,7 @@
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.profile.ShareExtension;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.profile.ShareExtension;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>AppGroupId</key>
|
||||
<string>$(CUSTOM_GROUP_ID)</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionAttributes</key>
|
||||
<dict>
|
||||
<key>IntentsSupported</key>
|
||||
<array>
|
||||
<string>INSendMessageIntent</string>
|
||||
</array>
|
||||
<key>NSExtensionActivationRule</key>
|
||||
<string>SUBQUERY ( extensionItems, $extensionItem, SUBQUERY ( $extensionItem.attachments,
|
||||
<dict>
|
||||
<key>AppGroupId</key>
|
||||
<string>$(CUSTOM_GROUP_ID)</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionAttributes</key>
|
||||
<dict>
|
||||
<key>IntentsSupported</key>
|
||||
<array>
|
||||
<string>INSendMessageIntent</string>
|
||||
</array>
|
||||
<key>NSExtensionActivationRule</key>
|
||||
<string>SUBQUERY ( extensionItems, $extensionItem, SUBQUERY ( $extensionItem.attachments,
|
||||
$attachment, ( ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.file-url"
|
||||
|| ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.image" || ANY
|
||||
$attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.text" || ANY
|
||||
$attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.movie" || ANY
|
||||
$attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url" ) ).@count > 0
|
||||
).@count > 0 </string>
|
||||
<key>PHSupportedMediaTypes</key>
|
||||
<array>
|
||||
<string>Video</string>
|
||||
<string>Image</string>
|
||||
</array>
|
||||
</dict>
|
||||
<key>NSExtensionMainStoryboard</key>
|
||||
<string>MainInterface</string>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.share-services</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
<key>PHSupportedMediaTypes</key>
|
||||
<array>
|
||||
<string>Video</string>
|
||||
<string>Image</string>
|
||||
</array>
|
||||
</dict>
|
||||
<key>NSExtensionMainStoryboard</key>
|
||||
<string>MainInterface</string>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.share-services</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.app.immich.share</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
<dict>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.app.immich.share</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,10 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.app.immich.share</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
<dict>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.app.immich.share</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,5 +1,5 @@
|
||||
app_identifier "app.alextran.immich" # The bundle identifier of your app
|
||||
apple_id "alex.tran1502@gmail.com" # Your Apple email address
|
||||
apple_id "altran@futo.org" # Your Apple email address
|
||||
|
||||
|
||||
# For more information about the Appfile, see:
|
||||
|
||||
@@ -17,10 +17,11 @@ default_platform(:ios)
|
||||
|
||||
platform :ios do
|
||||
# Constants
|
||||
TEAM_ID = "2F67MQ8R79"
|
||||
CODE_SIGN_IDENTITY = "Apple Distribution: Hau Tran (#{TEAM_ID})"
|
||||
TEAM_ID = "2W7AC6T8T5"
|
||||
CODE_SIGN_IDENTITY = "Apple Distribution: FUTO Holdings, Inc. (#{TEAM_ID})"
|
||||
BASE_BUNDLE_ID = "app.alextran.immich"
|
||||
|
||||
DEV_BUNDLE_ID = "tech.futo.immich.testflight"
|
||||
|
||||
# Helper method to get App Store Connect API key
|
||||
def get_api_key
|
||||
app_store_connect_api_key(
|
||||
@@ -44,47 +45,45 @@ def get_version_from_pubspec
|
||||
end
|
||||
|
||||
# Helper method to configure code signing for all targets
|
||||
def configure_code_signing(bundle_id_suffix: "", profile_name_main:, profile_name_share:, profile_name_widget:)
|
||||
bundle_suffix = bundle_id_suffix.empty? ? "" : ".#{bundle_id_suffix}"
|
||||
|
||||
def configure_code_signing(base_bundle_id:, profile_name_main:, profile_name_share:, profile_name_widget:)
|
||||
# Runner (main app)
|
||||
update_code_signing_settings(
|
||||
use_automatic_signing: false,
|
||||
path: "./Runner.xcodeproj",
|
||||
team_id: ENV["FASTLANE_TEAM_ID"] || TEAM_ID,
|
||||
code_sign_identity: CODE_SIGN_IDENTITY,
|
||||
bundle_identifier: "#{BASE_BUNDLE_ID}#{bundle_suffix}",
|
||||
bundle_identifier: base_bundle_id,
|
||||
profile_name: profile_name_main,
|
||||
targets: ["Runner"]
|
||||
)
|
||||
|
||||
|
||||
# ShareExtension
|
||||
update_code_signing_settings(
|
||||
use_automatic_signing: false,
|
||||
path: "./Runner.xcodeproj",
|
||||
team_id: ENV["FASTLANE_TEAM_ID"] || TEAM_ID,
|
||||
code_sign_identity: CODE_SIGN_IDENTITY,
|
||||
bundle_identifier: "#{BASE_BUNDLE_ID}#{bundle_suffix}.ShareExtension",
|
||||
bundle_identifier: "#{base_bundle_id}.ShareExtension",
|
||||
profile_name: profile_name_share,
|
||||
targets: ["ShareExtension"]
|
||||
)
|
||||
|
||||
|
||||
# WidgetExtension
|
||||
update_code_signing_settings(
|
||||
use_automatic_signing: false,
|
||||
path: "./Runner.xcodeproj",
|
||||
team_id: ENV["FASTLANE_TEAM_ID"] || TEAM_ID,
|
||||
code_sign_identity: CODE_SIGN_IDENTITY,
|
||||
bundle_identifier: "#{BASE_BUNDLE_ID}#{bundle_suffix}.Widget",
|
||||
bundle_identifier: "#{base_bundle_id}.Widget",
|
||||
profile_name: profile_name_widget,
|
||||
targets: ["WidgetExtension"]
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
# Helper method to build and upload to TestFlight
|
||||
def build_and_upload(
|
||||
api_key:,
|
||||
bundle_id_suffix: "",
|
||||
base_bundle_id:,
|
||||
configuration: "Release",
|
||||
distribute_external: true,
|
||||
version_number: nil,
|
||||
@@ -92,9 +91,8 @@ end
|
||||
profile_name_share:,
|
||||
profile_name_widget:
|
||||
)
|
||||
bundle_suffix = bundle_id_suffix.empty? ? "" : ".#{bundle_id_suffix}"
|
||||
app_identifier = "#{BASE_BUNDLE_ID}#{bundle_suffix}"
|
||||
|
||||
app_identifier = base_bundle_id
|
||||
|
||||
# Set version number if provided
|
||||
if version_number
|
||||
increment_version_number(version_number: version_number)
|
||||
@@ -138,31 +136,31 @@ end
|
||||
desc "iOS Development Build to TestFlight (requires separate bundle ID)"
|
||||
lane :gha_testflight_dev do
|
||||
api_key = get_api_key
|
||||
|
||||
|
||||
# Download and install provisioning profiles from App Store Connect
|
||||
# Certificate is imported by GHA workflow into build.keychain
|
||||
# Capture profile names after each sigh call
|
||||
sigh(api_key: api_key, app_identifier: "#{BASE_BUNDLE_ID}.development", force: true)
|
||||
sigh(api_key: api_key, app_identifier: DEV_BUNDLE_ID, force: true)
|
||||
main_profile_name = lane_context[SharedValues::SIGH_NAME]
|
||||
|
||||
sigh(api_key: api_key, app_identifier: "#{BASE_BUNDLE_ID}.development.ShareExtension", force: true)
|
||||
|
||||
sigh(api_key: api_key, app_identifier: "#{DEV_BUNDLE_ID}.ShareExtension", force: true)
|
||||
share_profile_name = lane_context[SharedValues::SIGH_NAME]
|
||||
|
||||
sigh(api_key: api_key, app_identifier: "#{BASE_BUNDLE_ID}.development.Widget", force: true)
|
||||
|
||||
sigh(api_key: api_key, app_identifier: "#{DEV_BUNDLE_ID}.Widget", force: true)
|
||||
widget_profile_name = lane_context[SharedValues::SIGH_NAME]
|
||||
|
||||
|
||||
# Configure code signing for dev bundle IDs using the downloaded profile names
|
||||
configure_code_signing(
|
||||
bundle_id_suffix: "development",
|
||||
base_bundle_id: DEV_BUNDLE_ID,
|
||||
profile_name_main: main_profile_name,
|
||||
profile_name_share: share_profile_name,
|
||||
profile_name_widget: widget_profile_name
|
||||
)
|
||||
|
||||
|
||||
# Build and upload
|
||||
build_and_upload(
|
||||
api_key: api_key,
|
||||
bundle_id_suffix: "development",
|
||||
base_bundle_id: DEV_BUNDLE_ID,
|
||||
configuration: "Profile",
|
||||
distribute_external: false,
|
||||
profile_name_main: main_profile_name,
|
||||
@@ -189,6 +187,7 @@ end
|
||||
|
||||
# Configure code signing for production bundle IDs
|
||||
configure_code_signing(
|
||||
base_bundle_id: BASE_BUNDLE_ID,
|
||||
profile_name_main: main_profile_name,
|
||||
profile_name_share: share_profile_name,
|
||||
profile_name_widget: widget_profile_name
|
||||
@@ -197,6 +196,7 @@ end
|
||||
# Build and upload with version number
|
||||
build_and_upload(
|
||||
api_key: api_key,
|
||||
base_bundle_id: BASE_BUNDLE_ID,
|
||||
version_number: get_version_from_pubspec,
|
||||
distribute_external: false,
|
||||
profile_name_main: main_profile_name,
|
||||
@@ -243,30 +243,30 @@ end
|
||||
|
||||
desc "iOS Build Only (no TestFlight upload)"
|
||||
lane :gha_build_only do
|
||||
# Use the same build process as production, just skip the upload
|
||||
# This ensures PR builds validate the same way as production builds
|
||||
|
||||
# Use the same build process as the dev TestFlight lane, just skip the upload
|
||||
# This ensures PR builds validate the same way as dev TestFlight builds
|
||||
|
||||
api_key = get_api_key
|
||||
|
||||
|
||||
# Download and install provisioning profiles from App Store Connect
|
||||
# Certificate is imported by GHA workflow into build.keychain
|
||||
sigh(api_key: api_key, app_identifier: "#{BASE_BUNDLE_ID}.development", force: true)
|
||||
sigh(api_key: api_key, app_identifier: DEV_BUNDLE_ID, force: true)
|
||||
main_profile_name = lane_context[SharedValues::SIGH_NAME]
|
||||
|
||||
sigh(api_key: api_key, app_identifier: "#{BASE_BUNDLE_ID}.development.ShareExtension", force: true)
|
||||
|
||||
sigh(api_key: api_key, app_identifier: "#{DEV_BUNDLE_ID}.ShareExtension", force: true)
|
||||
share_profile_name = lane_context[SharedValues::SIGH_NAME]
|
||||
|
||||
sigh(api_key: api_key, app_identifier: "#{BASE_BUNDLE_ID}.development.Widget", force: true)
|
||||
|
||||
sigh(api_key: api_key, app_identifier: "#{DEV_BUNDLE_ID}.Widget", force: true)
|
||||
widget_profile_name = lane_context[SharedValues::SIGH_NAME]
|
||||
|
||||
|
||||
# Configure code signing for dev bundle IDs
|
||||
configure_code_signing(
|
||||
bundle_id_suffix: "development",
|
||||
base_bundle_id: DEV_BUNDLE_ID,
|
||||
profile_name_main: main_profile_name,
|
||||
profile_name_share: share_profile_name,
|
||||
profile_name_widget: widget_profile_name
|
||||
)
|
||||
|
||||
|
||||
# Build the app (same as gha_testflight_dev but without upload)
|
||||
build_app(
|
||||
scheme: "Runner",
|
||||
@@ -277,9 +277,9 @@ end
|
||||
xcargs: "-skipMacroValidation CODE_SIGN_IDENTITY='#{CODE_SIGN_IDENTITY}' CODE_SIGN_STYLE=Manual",
|
||||
export_options: {
|
||||
provisioningProfiles: {
|
||||
"#{BASE_BUNDLE_ID}.development" => main_profile_name,
|
||||
"#{BASE_BUNDLE_ID}.development.ShareExtension" => share_profile_name,
|
||||
"#{BASE_BUNDLE_ID}.development.Widget" => widget_profile_name
|
||||
DEV_BUNDLE_ID => main_profile_name,
|
||||
"#{DEV_BUNDLE_ID}.ShareExtension" => share_profile_name,
|
||||
"#{DEV_BUNDLE_ID}.Widget" => widget_profile_name
|
||||
},
|
||||
signingStyle: "manual",
|
||||
signingCertificate: CODE_SIGN_IDENTITY
|
||||
|
||||
@@ -8,6 +8,7 @@ enum AlbumUserRole {
|
||||
// do not change this order!
|
||||
editor,
|
||||
viewer,
|
||||
owner,
|
||||
}
|
||||
|
||||
// Model for an album stored in the server
|
||||
|
||||
@@ -7,8 +7,8 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart';
|
||||
import 'package:immich_mobile/models/albums/album_search.model.dart';
|
||||
import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
|
||||
import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart';
|
||||
import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
|
||||
|
||||
class RemoteAlbumService {
|
||||
final DriftRemoteAlbumRepository _repository;
|
||||
@@ -28,10 +28,6 @@ class RemoteAlbumService {
|
||||
return _repository.get(albumId);
|
||||
}
|
||||
|
||||
Future<RemoteAlbum?> getByName(String albumName, String ownerId) {
|
||||
return _repository.getByName(albumName, ownerId);
|
||||
}
|
||||
|
||||
Future<List<RemoteAlbum>> sortAlbums(
|
||||
List<RemoteAlbum> albums,
|
||||
AlbumSortMode sortMode, {
|
||||
@@ -86,8 +82,18 @@ class RemoteAlbumService {
|
||||
return filtered;
|
||||
}
|
||||
|
||||
Future<RemoteAlbum> createAlbum({required String title, required List<String> assetIds, String? description}) async {
|
||||
final album = await _albumApiRepository.createDriftAlbum(title, description: description, assetIds: assetIds);
|
||||
Future<RemoteAlbum> createAlbum({
|
||||
required String title,
|
||||
required UserDto owner,
|
||||
required List<String> assetIds,
|
||||
String? description,
|
||||
}) async {
|
||||
final album = await _albumApiRepository.createDriftAlbum(
|
||||
title,
|
||||
owner,
|
||||
description: description,
|
||||
assetIds: assetIds,
|
||||
);
|
||||
await _repository.create(album, assetIds);
|
||||
|
||||
return album;
|
||||
@@ -101,8 +107,10 @@ class RemoteAlbumService {
|
||||
bool? isActivityEnabled,
|
||||
AlbumAssetOrder? order,
|
||||
}) async {
|
||||
final owner = await _repository.getOwner(albumId);
|
||||
final updatedAlbum = await _albumApiRepository.updateAlbum(
|
||||
albumId,
|
||||
owner,
|
||||
name: name,
|
||||
description: description,
|
||||
thumbnailAssetId: thumbnailAssetId,
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/store.provider.dart';
|
||||
import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
|
||||
import 'package:immich_mobile/utils/debug_print.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
@@ -12,6 +15,7 @@ final syncLinkedAlbumServiceProvider = Provider(
|
||||
ref.watch(localAlbumRepository),
|
||||
ref.watch(remoteAlbumRepository),
|
||||
ref.watch(driftAlbumApiRepositoryProvider),
|
||||
ref.watch(storeServiceProvider),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -19,8 +23,14 @@ class SyncLinkedAlbumService {
|
||||
final DriftLocalAlbumRepository _localAlbumRepository;
|
||||
final DriftRemoteAlbumRepository _remoteAlbumRepository;
|
||||
final DriftAlbumApiRepository _albumApiRepository;
|
||||
final StoreService _storeService;
|
||||
|
||||
SyncLinkedAlbumService(this._localAlbumRepository, this._remoteAlbumRepository, this._albumApiRepository);
|
||||
SyncLinkedAlbumService(
|
||||
this._localAlbumRepository,
|
||||
this._remoteAlbumRepository,
|
||||
this._albumApiRepository,
|
||||
this._storeService,
|
||||
);
|
||||
|
||||
final _log = Logger("SyncLinkedAlbumService");
|
||||
|
||||
@@ -103,7 +113,11 @@ class SyncLinkedAlbumService {
|
||||
/// Creates a new remote album and links it to the local album
|
||||
Future<void> _createAndLinkNewRemoteAlbum(LocalAlbum localAlbum) async {
|
||||
dPrint(() => "Creating new remote album for local album: ${localAlbum.name}");
|
||||
final newRemoteAlbum = await _albumApiRepository.createDriftAlbum(localAlbum.name, assetIds: []);
|
||||
final newRemoteAlbum = await _albumApiRepository.createDriftAlbum(
|
||||
localAlbum.name,
|
||||
_storeService.get(StoreKey.currentUser),
|
||||
assetIds: [],
|
||||
);
|
||||
await _remoteAlbumRepository.create(newRemoteAlbum, []);
|
||||
return _localAlbumRepository.linkRemoteAlbum(localAlbum.id, newRemoteAlbum.id);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/models/sync_event.model.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
@@ -192,17 +193,22 @@ class SyncStreamService {
|
||||
final remoteSyncAssets = data.cast<SyncAssetV1>();
|
||||
await _syncStreamRepository.updateAssetsV1(remoteSyncAssets);
|
||||
if (CurrentPlatform.isAndroid && Store.get(StoreKey.manageLocalMediaAndroid, false)) {
|
||||
final hasPermission = await _localFilesManager.hasManageMediaPermission();
|
||||
if (hasPermission) {
|
||||
await _handleRemoteTrashed(remoteSyncAssets.where((e) => e.deletedAt != null).map((e) => e.checksum));
|
||||
await _applyRemoteRestoreToLocal();
|
||||
} else {
|
||||
_logger.warning("sync Trashed Assets cannot proceed because MANAGE_MEDIA permission is missing");
|
||||
}
|
||||
await _syncAssetTrashStatus(remoteSyncAssets.where((e) => e.deletedAt != null).map((e) => e.id).toList());
|
||||
}
|
||||
return;
|
||||
case SyncEntityType.assetV2:
|
||||
final remoteSyncAssets = data.cast<SyncAssetV2>();
|
||||
await _syncStreamRepository.updateAssetsV2(remoteSyncAssets);
|
||||
if (CurrentPlatform.isAndroid && Store.get(StoreKey.manageLocalMediaAndroid, false)) {
|
||||
await _syncAssetTrashStatus(remoteSyncAssets.where((e) => e.deletedAt != null).map((e) => e.id).toList());
|
||||
}
|
||||
return;
|
||||
case SyncEntityType.assetDeleteV1:
|
||||
return _syncStreamRepository.deleteAssetsV1(data.cast());
|
||||
final remoteSyncAssets = data.cast<SyncAssetDeleteV1>();
|
||||
if (CurrentPlatform.isAndroid && Store.get(StoreKey.manageLocalMediaAndroid, false)) {
|
||||
await _syncAssetDeletion(remoteSyncAssets.map((e) => e.assetId).toList());
|
||||
}
|
||||
return _syncStreamRepository.deleteAssetsV1(remoteSyncAssets);
|
||||
case SyncEntityType.assetExifV1:
|
||||
return _syncStreamRepository.updateAssetsExifV1(data.cast());
|
||||
case SyncEntityType.assetEditV1:
|
||||
@@ -215,8 +221,12 @@ class SyncStreamService {
|
||||
return _syncStreamRepository.deleteAssetsMetadataV1(data.cast());
|
||||
case SyncEntityType.partnerAssetV1:
|
||||
return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'partner');
|
||||
case SyncEntityType.partnerAssetV2:
|
||||
return _syncStreamRepository.updateAssetsV2(data.cast(), debugLabel: 'partner');
|
||||
case SyncEntityType.partnerAssetBackfillV1:
|
||||
return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'partner backfill');
|
||||
case SyncEntityType.partnerAssetBackfillV2:
|
||||
return _syncStreamRepository.updateAssetsV2(data.cast(), debugLabel: 'partner backfill');
|
||||
case SyncEntityType.partnerAssetDeleteV1:
|
||||
return _syncStreamRepository.deleteAssetsV1(data.cast(), debugLabel: "partner");
|
||||
case SyncEntityType.partnerAssetExifV1:
|
||||
@@ -225,6 +235,8 @@ class SyncStreamService {
|
||||
return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'partner backfill');
|
||||
case SyncEntityType.albumV1:
|
||||
return _syncStreamRepository.updateAlbumsV1(data.cast());
|
||||
case SyncEntityType.albumV2:
|
||||
return _syncStreamRepository.updateAlbumsV2(data.cast());
|
||||
case SyncEntityType.albumDeleteV1:
|
||||
return _syncStreamRepository.deleteAlbumsV1(data.cast());
|
||||
case SyncEntityType.albumUserV1:
|
||||
@@ -235,10 +247,16 @@ class SyncStreamService {
|
||||
return _syncStreamRepository.deleteAlbumUsersV1(data.cast());
|
||||
case SyncEntityType.albumAssetCreateV1:
|
||||
return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album asset create');
|
||||
case SyncEntityType.albumAssetCreateV2:
|
||||
return _syncStreamRepository.updateAssetsV2(data.cast(), debugLabel: 'album asset create');
|
||||
case SyncEntityType.albumAssetUpdateV1:
|
||||
return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album asset update');
|
||||
case SyncEntityType.albumAssetUpdateV2:
|
||||
return _syncStreamRepository.updateAssetsV2(data.cast(), debugLabel: 'album asset update');
|
||||
case SyncEntityType.albumAssetBackfillV1:
|
||||
return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album asset backfill');
|
||||
case SyncEntityType.albumAssetBackfillV2:
|
||||
return _syncStreamRepository.updateAssetsV2(data.cast(), debugLabel: 'album asset backfill');
|
||||
case SyncEntityType.albumAssetExifCreateV1:
|
||||
return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'album asset exif create');
|
||||
case SyncEntityType.albumAssetExifUpdateV1:
|
||||
@@ -340,6 +358,47 @@ class SyncStreamService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> handleWsAssetUploadReadyV2Batch(List<dynamic> batchData) async {
|
||||
if (batchData.isEmpty) return;
|
||||
|
||||
_logger.info('Processing batch of ${batchData.length} AssetUploadReadyV2 events');
|
||||
|
||||
final List<SyncAssetV2> assets = [];
|
||||
final List<SyncAssetExifV1> exifs = [];
|
||||
|
||||
try {
|
||||
for (final data in batchData) {
|
||||
if (data is! Map<String, dynamic>) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final payload = data;
|
||||
final assetData = payload['asset'];
|
||||
final exifData = payload['exif'];
|
||||
|
||||
if (assetData == null || exifData == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final asset = SyncAssetV2.fromJson(assetData);
|
||||
final exif = SyncAssetExifV1.fromJson(exifData);
|
||||
|
||||
if (asset != null && exif != null) {
|
||||
assets.add(asset);
|
||||
exifs.add(exif);
|
||||
}
|
||||
}
|
||||
|
||||
if (assets.isNotEmpty && exifs.isNotEmpty) {
|
||||
await _syncStreamRepository.updateAssetsV2(assets, debugLabel: 'websocket-batch');
|
||||
await _syncStreamRepository.updateAssetsExifV1(exifs, debugLabel: 'websocket-batch');
|
||||
_logger.info('Successfully processed ${assets.length} assets in batch');
|
||||
}
|
||||
} catch (error, stackTrace) {
|
||||
_logger.severe("Error processing AssetUploadReadyV2 websocket batch events", error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> handleWsAssetEditReadyV1(dynamic data) async {
|
||||
_logger.info('Processing AssetEditReadyV1 event');
|
||||
|
||||
@@ -380,28 +439,67 @@ class SyncStreamService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _handleRemoteTrashed(Iterable<String> checksums) async {
|
||||
if (checksums.isEmpty) {
|
||||
Future<void> handleWsAssetEditReadyV2(dynamic data) async {
|
||||
_logger.info('Processing AssetEditReadyV2 event');
|
||||
|
||||
try {
|
||||
if (data is! Map<String, dynamic>) {
|
||||
throw ArgumentError("Invalid data format for AssetEditReadyV2 event");
|
||||
}
|
||||
|
||||
final payload = data;
|
||||
|
||||
if (payload['asset'] == null) {
|
||||
throw ArgumentError("Missing 'asset' field in AssetEditReadyV2 event data");
|
||||
}
|
||||
|
||||
final asset = SyncAssetV2.fromJson(payload['asset']);
|
||||
if (asset == null) {
|
||||
throw ArgumentError("Failed to parse 'asset' field in AssetEditReadyV2 event data");
|
||||
}
|
||||
|
||||
final assetEdits = (payload['edit'] as List<dynamic>)
|
||||
.map((e) => SyncAssetEditV1.fromJson(e))
|
||||
.whereType<SyncAssetEditV1>()
|
||||
.toList();
|
||||
|
||||
await _syncStreamRepository.updateAssetsV2([asset], debugLabel: 'websocket-edit');
|
||||
await _syncStreamRepository.replaceAssetEditsV1(asset.id, assetEdits, debugLabel: 'websocket-edit');
|
||||
|
||||
_logger.info(
|
||||
'Successfully processed AssetEditReadyV2 event for asset ${asset.id} with ${assetEdits.length} edits',
|
||||
);
|
||||
} catch (error, stackTrace) {
|
||||
_logger.severe("Error processing AssetEditReadyV2 websocket event", error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _handleRemoteDeleted(Iterable<String> remoteIds) async {
|
||||
if (remoteIds.isEmpty) {
|
||||
return Future.value();
|
||||
} else {
|
||||
final localAssetsToTrash = await _localAssetRepository.getAssetsFromBackupAlbums(checksums);
|
||||
final localAssetsToTrash = await _localAssetRepository.getAssetsFromBackupAlbums(remoteIds);
|
||||
if (localAssetsToTrash.isNotEmpty) {
|
||||
final mediaUrls = await Future.wait(
|
||||
localAssetsToTrash.values
|
||||
.expand((e) => e)
|
||||
.map((localAsset) => _storageRepository.getAssetEntityForAsset(localAsset).then((e) => e?.getMediaUrl())),
|
||||
);
|
||||
_logger.info("Moving to trash ${mediaUrls.join(", ")} assets");
|
||||
final result = await _localFilesManager.moveToTrash(mediaUrls.nonNulls.toList());
|
||||
if (result) {
|
||||
await _trashedLocalAssetRepository.trashLocalAsset(localAssetsToTrash);
|
||||
}
|
||||
await _trashLocalAssets(localAssetsToTrash);
|
||||
} else {
|
||||
_logger.info("No assets found in backup-enabled albums for assets: $checksums");
|
||||
_logger.info("No assets found in backup-enabled albums for remote assets: $remoteIds");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _trashLocalAssets(Map<String, List<LocalAsset>> localAssetsToTrash) async {
|
||||
final mediaUrls = await Future.wait(
|
||||
localAssetsToTrash.values
|
||||
.expand((e) => e)
|
||||
.map((localAsset) => _storageRepository.getAssetEntityForAsset(localAsset).then((e) => e?.getMediaUrl())),
|
||||
);
|
||||
_logger.info("Moving to trash ${mediaUrls.join(", ")} assets");
|
||||
final result = await _localFilesManager.moveToTrash(mediaUrls.nonNulls.toList());
|
||||
if (result) {
|
||||
await _trashedLocalAssetRepository.trashLocalAsset(localAssetsToTrash);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _applyRemoteRestoreToLocal() async {
|
||||
final assetsToRestore = await _trashedLocalAssetRepository.getToRestore();
|
||||
if (assetsToRestore.isNotEmpty) {
|
||||
@@ -411,4 +509,23 @@ class SyncStreamService {
|
||||
_logger.info("No remote assets found for restoration");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _syncAssetTrashStatus(List<String> remoteIds) async {
|
||||
if (!(await _localFilesManager.hasManageMediaPermission())) {
|
||||
_logger.warning("Syncing asset trash status cannot proceed because MANAGE_MEDIA permission is missing");
|
||||
return;
|
||||
}
|
||||
|
||||
await _handleRemoteDeleted(remoteIds);
|
||||
await _applyRemoteRestoreToLocal();
|
||||
}
|
||||
|
||||
Future<void> _syncAssetDeletion(List<String> remoteIds) async {
|
||||
if (!(await _localFilesManager.hasManageMediaPermission())) {
|
||||
_logger.warning("Syncing asset deletion cannot proceed because MANAGE_MEDIA permission is missing");
|
||||
return;
|
||||
}
|
||||
|
||||
await _handleRemoteDeleted(remoteIds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ class BackgroundSyncManager {
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> syncWebsocketBatch(List<dynamic> batchData) {
|
||||
Future<void> syncWebsocketBatchV1(List<dynamic> batchData) {
|
||||
if (_syncWebsocketTask != null) {
|
||||
return _syncWebsocketTask!.future;
|
||||
}
|
||||
@@ -196,7 +196,17 @@ class BackgroundSyncManager {
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> syncWebsocketEdit(dynamic data) {
|
||||
Future<void> syncWebsocketBatchV2(List<dynamic> batchData) {
|
||||
if (_syncWebsocketTask != null) {
|
||||
return _syncWebsocketTask!.future;
|
||||
}
|
||||
_syncWebsocketTask = _handleWsAssetUploadReadyV2Batch(batchData);
|
||||
return _syncWebsocketTask!.whenComplete(() {
|
||||
_syncWebsocketTask = null;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> syncWebsocketEditV1(dynamic data) {
|
||||
if (_syncWebsocketTask != null) {
|
||||
return _syncWebsocketTask!.future;
|
||||
}
|
||||
@@ -206,6 +216,16 @@ class BackgroundSyncManager {
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> syncWebsocketEditV2(dynamic data) {
|
||||
if (_syncWebsocketTask != null) {
|
||||
return _syncWebsocketTask!.future;
|
||||
}
|
||||
_syncWebsocketTask = _handleWsAssetEditReadyV2(data);
|
||||
return _syncWebsocketTask!.whenComplete(() {
|
||||
_syncWebsocketTask = null;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> syncLinkedAlbum() {
|
||||
if (_linkedAlbumSyncTask != null) {
|
||||
return _linkedAlbumSyncTask!.future;
|
||||
@@ -242,7 +262,17 @@ Cancelable<void> _handleWsAssetUploadReadyV1Batch(List<dynamic> batchData) => ru
|
||||
debugLabel: 'websocket-batch',
|
||||
);
|
||||
|
||||
Cancelable<void> _handleWsAssetUploadReadyV2Batch(List<dynamic> batchData) => runInIsolateGentle(
|
||||
computation: (ref) => ref.read(syncStreamServiceProvider).handleWsAssetUploadReadyV2Batch(batchData),
|
||||
debugLabel: 'websocket-batch',
|
||||
);
|
||||
|
||||
Cancelable<void> _handleWsAssetEditReadyV1(dynamic data) => runInIsolateGentle(
|
||||
computation: (ref) => ref.read(syncStreamServiceProvider).handleWsAssetEditReadyV1(data),
|
||||
debugLabel: 'websocket-edit',
|
||||
);
|
||||
|
||||
Cancelable<void> _handleWsAssetEditReadyV2(dynamic data) => runInIsolateGentle(
|
||||
computation: (ref) => ref.read(syncStreamServiceProvider).handleWsAssetEditReadyV2(data),
|
||||
debugLabel: 'websocket-edit',
|
||||
);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/exif.model.dart';
|
||||
import 'package:immich_mobile/extensions/string_extensions.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/exif.converter.dart';
|
||||
import 'package:openapi/api.dart' as api;
|
||||
|
||||
@@ -14,7 +13,7 @@ extension DTOToAsset on api.AssetResponseDto {
|
||||
updatedAt: updatedAt,
|
||||
ownerId: ownerId,
|
||||
visibility: visibility.toAssetVisibility(),
|
||||
durationMs: duration?.toDuration()?.inMilliseconds ?? 0,
|
||||
durationMs: duration,
|
||||
height: height?.toInt(),
|
||||
width: width?.toInt(),
|
||||
isFavorite: isFavorite,
|
||||
@@ -36,7 +35,7 @@ extension DTOToAsset on api.AssetResponseDto {
|
||||
updatedAt: updatedAt,
|
||||
ownerId: ownerId,
|
||||
visibility: visibility.toAssetVisibility(),
|
||||
durationMs: duration?.toDuration()?.inMilliseconds ?? 0,
|
||||
durationMs: duration,
|
||||
height: height?.toInt(),
|
||||
width: width?.toInt(),
|
||||
isFavorite: isFavorite,
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
||||
|
||||
@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_remote_album_owner_id ON remote_album_entity (owner_id)')
|
||||
class RemoteAlbumEntity extends Table with DriftDefaultsMixin {
|
||||
const RemoteAlbumEntity();
|
||||
|
||||
@@ -18,8 +16,6 @@ class RemoteAlbumEntity extends Table with DriftDefaultsMixin {
|
||||
|
||||
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
|
||||
|
||||
TextColumn get ownerId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
|
||||
|
||||
TextColumn get thumbnailAssetId =>
|
||||
text().references(RemoteAssetEntity, #id, onDelete: KeyAction.setNull).nullable()();
|
||||
|
||||
|
||||
@@ -7,11 +7,9 @@ import 'package:immich_mobile/domain/models/album/album.model.dart' as i2;
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart'
|
||||
as i3;
|
||||
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4;
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'
|
||||
as i5;
|
||||
import 'package:drift/internal/modular.dart' as i6;
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'
|
||||
as i7;
|
||||
|
||||
typedef $$RemoteAlbumEntityTableCreateCompanionBuilder =
|
||||
i1.RemoteAlbumEntityCompanion Function({
|
||||
@@ -20,7 +18,6 @@ typedef $$RemoteAlbumEntityTableCreateCompanionBuilder =
|
||||
i0.Value<String> description,
|
||||
i0.Value<DateTime> createdAt,
|
||||
i0.Value<DateTime> updatedAt,
|
||||
required String ownerId,
|
||||
i0.Value<String?> thumbnailAssetId,
|
||||
i0.Value<bool> isActivityEnabled,
|
||||
required i2.AlbumAssetOrder order,
|
||||
@@ -32,7 +29,6 @@ typedef $$RemoteAlbumEntityTableUpdateCompanionBuilder =
|
||||
i0.Value<String> description,
|
||||
i0.Value<DateTime> createdAt,
|
||||
i0.Value<DateTime> updatedAt,
|
||||
i0.Value<String> ownerId,
|
||||
i0.Value<String?> thumbnailAssetId,
|
||||
i0.Value<bool> isActivityEnabled,
|
||||
i0.Value<i2.AlbumAssetOrder> order,
|
||||
@@ -51,42 +47,10 @@ final class $$RemoteAlbumEntityTableReferences
|
||||
super.$_typedResult,
|
||||
);
|
||||
|
||||
static i5.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) =>
|
||||
i6.ReadDatabaseContainer(db)
|
||||
.resultSet<i5.$UserEntityTable>('user_entity')
|
||||
.createAlias(
|
||||
i0.$_aliasNameGenerator(
|
||||
i6.ReadDatabaseContainer(db)
|
||||
.resultSet<i1.$RemoteAlbumEntityTable>('remote_album_entity')
|
||||
.ownerId,
|
||||
i6.ReadDatabaseContainer(
|
||||
db,
|
||||
).resultSet<i5.$UserEntityTable>('user_entity').id,
|
||||
),
|
||||
);
|
||||
|
||||
i5.$$UserEntityTableProcessedTableManager get ownerId {
|
||||
final $_column = $_itemColumn<String>('owner_id')!;
|
||||
|
||||
final manager = i5
|
||||
.$$UserEntityTableTableManager(
|
||||
$_db,
|
||||
i6.ReadDatabaseContainer(
|
||||
$_db,
|
||||
).resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
)
|
||||
.filter((f) => f.id.sqlEquals($_column));
|
||||
final item = $_typedResult.readTableOrNull(_ownerIdTable($_db));
|
||||
if (item == null) return manager;
|
||||
return i0.ProcessedTableManager(
|
||||
manager.$state.copyWith(prefetchedData: [item]),
|
||||
);
|
||||
}
|
||||
|
||||
static i7.$RemoteAssetEntityTable _thumbnailAssetIdTable(
|
||||
static i5.$RemoteAssetEntityTable _thumbnailAssetIdTable(
|
||||
i0.GeneratedDatabase db,
|
||||
) => i6.ReadDatabaseContainer(db)
|
||||
.resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity')
|
||||
.resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity')
|
||||
.createAlias(
|
||||
i0.$_aliasNameGenerator(
|
||||
i6.ReadDatabaseContainer(db)
|
||||
@@ -94,19 +58,19 @@ final class $$RemoteAlbumEntityTableReferences
|
||||
.thumbnailAssetId,
|
||||
i6.ReadDatabaseContainer(
|
||||
db,
|
||||
).resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity').id,
|
||||
).resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity').id,
|
||||
),
|
||||
);
|
||||
|
||||
i7.$$RemoteAssetEntityTableProcessedTableManager? get thumbnailAssetId {
|
||||
i5.$$RemoteAssetEntityTableProcessedTableManager? get thumbnailAssetId {
|
||||
final $_column = $_itemColumn<String>('thumbnail_asset_id');
|
||||
if ($_column == null) return null;
|
||||
final manager = i7
|
||||
final manager = i5
|
||||
.$$RemoteAssetEntityTableTableManager(
|
||||
$_db,
|
||||
i6.ReadDatabaseContainer(
|
||||
$_db,
|
||||
).resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
).resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
)
|
||||
.filter((f) => f.id.sqlEquals($_column));
|
||||
final item = $_typedResult.readTableOrNull(_thumbnailAssetIdTable($_db));
|
||||
@@ -162,51 +126,24 @@ class $$RemoteAlbumEntityTableFilterComposer
|
||||
builder: (column) => i0.ColumnWithTypeConverterFilters(column),
|
||||
);
|
||||
|
||||
i5.$$UserEntityTableFilterComposer get ownerId {
|
||||
final i5.$$UserEntityTableFilterComposer composer = $composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.ownerId,
|
||||
referencedTable: i6.ReadDatabaseContainer(
|
||||
$db,
|
||||
).resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder:
|
||||
(
|
||||
joinBuilder, {
|
||||
$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
}) => i5.$$UserEntityTableFilterComposer(
|
||||
$db: $db,
|
||||
$table: i6.ReadDatabaseContainer(
|
||||
$db,
|
||||
).resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
),
|
||||
);
|
||||
return composer;
|
||||
}
|
||||
|
||||
i7.$$RemoteAssetEntityTableFilterComposer get thumbnailAssetId {
|
||||
final i7.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder(
|
||||
i5.$$RemoteAssetEntityTableFilterComposer get thumbnailAssetId {
|
||||
final i5.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.thumbnailAssetId,
|
||||
referencedTable: i6.ReadDatabaseContainer(
|
||||
$db,
|
||||
).resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
).resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder:
|
||||
(
|
||||
joinBuilder, {
|
||||
$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
}) => i7.$$RemoteAssetEntityTableFilterComposer(
|
||||
}) => i5.$$RemoteAssetEntityTableFilterComposer(
|
||||
$db: $db,
|
||||
$table: i6.ReadDatabaseContainer(
|
||||
$db,
|
||||
).resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
).resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
@@ -261,52 +198,25 @@ class $$RemoteAlbumEntityTableOrderingComposer
|
||||
builder: (column) => i0.ColumnOrderings(column),
|
||||
);
|
||||
|
||||
i5.$$UserEntityTableOrderingComposer get ownerId {
|
||||
final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.ownerId,
|
||||
referencedTable: i6.ReadDatabaseContainer(
|
||||
$db,
|
||||
).resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder:
|
||||
(
|
||||
joinBuilder, {
|
||||
$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
}) => i5.$$UserEntityTableOrderingComposer(
|
||||
$db: $db,
|
||||
$table: i6.ReadDatabaseContainer(
|
||||
$db,
|
||||
).resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
),
|
||||
);
|
||||
return composer;
|
||||
}
|
||||
|
||||
i7.$$RemoteAssetEntityTableOrderingComposer get thumbnailAssetId {
|
||||
final i7.$$RemoteAssetEntityTableOrderingComposer composer =
|
||||
i5.$$RemoteAssetEntityTableOrderingComposer get thumbnailAssetId {
|
||||
final i5.$$RemoteAssetEntityTableOrderingComposer composer =
|
||||
$composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.thumbnailAssetId,
|
||||
referencedTable: i6.ReadDatabaseContainer(
|
||||
$db,
|
||||
).resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
).resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder:
|
||||
(
|
||||
joinBuilder, {
|
||||
$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
}) => i7.$$RemoteAssetEntityTableOrderingComposer(
|
||||
}) => i5.$$RemoteAssetEntityTableOrderingComposer(
|
||||
$db: $db,
|
||||
$table: i6.ReadDatabaseContainer(
|
||||
$db,
|
||||
).resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
).resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
@@ -351,52 +261,25 @@ class $$RemoteAlbumEntityTableAnnotationComposer
|
||||
i0.GeneratedColumnWithTypeConverter<i2.AlbumAssetOrder, int> get order =>
|
||||
$composableBuilder(column: $table.order, builder: (column) => column);
|
||||
|
||||
i5.$$UserEntityTableAnnotationComposer get ownerId {
|
||||
final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.ownerId,
|
||||
referencedTable: i6.ReadDatabaseContainer(
|
||||
$db,
|
||||
).resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder:
|
||||
(
|
||||
joinBuilder, {
|
||||
$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
}) => i5.$$UserEntityTableAnnotationComposer(
|
||||
$db: $db,
|
||||
$table: i6.ReadDatabaseContainer(
|
||||
$db,
|
||||
).resultSet<i5.$UserEntityTable>('user_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
),
|
||||
);
|
||||
return composer;
|
||||
}
|
||||
|
||||
i7.$$RemoteAssetEntityTableAnnotationComposer get thumbnailAssetId {
|
||||
final i7.$$RemoteAssetEntityTableAnnotationComposer composer =
|
||||
i5.$$RemoteAssetEntityTableAnnotationComposer get thumbnailAssetId {
|
||||
final i5.$$RemoteAssetEntityTableAnnotationComposer composer =
|
||||
$composerBuilder(
|
||||
composer: this,
|
||||
getCurrentColumn: (t) => t.thumbnailAssetId,
|
||||
referencedTable: i6.ReadDatabaseContainer(
|
||||
$db,
|
||||
).resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
).resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
getReferencedColumn: (t) => t.id,
|
||||
builder:
|
||||
(
|
||||
joinBuilder, {
|
||||
$addJoinBuilderToRootComposer,
|
||||
$removeJoinBuilderFromRootComposer,
|
||||
}) => i7.$$RemoteAssetEntityTableAnnotationComposer(
|
||||
}) => i5.$$RemoteAssetEntityTableAnnotationComposer(
|
||||
$db: $db,
|
||||
$table: i6.ReadDatabaseContainer(
|
||||
$db,
|
||||
).resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
).resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||
joinBuilder: joinBuilder,
|
||||
$removeJoinBuilderFromRootComposer:
|
||||
@@ -420,7 +303,7 @@ class $$RemoteAlbumEntityTableTableManager
|
||||
$$RemoteAlbumEntityTableUpdateCompanionBuilder,
|
||||
(i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences),
|
||||
i1.RemoteAlbumEntityData,
|
||||
i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId})
|
||||
i0.PrefetchHooks Function({bool thumbnailAssetId})
|
||||
> {
|
||||
$$RemoteAlbumEntityTableTableManager(
|
||||
i0.GeneratedDatabase db,
|
||||
@@ -445,7 +328,6 @@ class $$RemoteAlbumEntityTableTableManager
|
||||
i0.Value<String> description = const i0.Value.absent(),
|
||||
i0.Value<DateTime> createdAt = const i0.Value.absent(),
|
||||
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
||||
i0.Value<String> ownerId = const i0.Value.absent(),
|
||||
i0.Value<String?> thumbnailAssetId = const i0.Value.absent(),
|
||||
i0.Value<bool> isActivityEnabled = const i0.Value.absent(),
|
||||
i0.Value<i2.AlbumAssetOrder> order = const i0.Value.absent(),
|
||||
@@ -455,7 +337,6 @@ class $$RemoteAlbumEntityTableTableManager
|
||||
description: description,
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
ownerId: ownerId,
|
||||
thumbnailAssetId: thumbnailAssetId,
|
||||
isActivityEnabled: isActivityEnabled,
|
||||
order: order,
|
||||
@@ -467,7 +348,6 @@ class $$RemoteAlbumEntityTableTableManager
|
||||
i0.Value<String> description = const i0.Value.absent(),
|
||||
i0.Value<DateTime> createdAt = const i0.Value.absent(),
|
||||
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
||||
required String ownerId,
|
||||
i0.Value<String?> thumbnailAssetId = const i0.Value.absent(),
|
||||
i0.Value<bool> isActivityEnabled = const i0.Value.absent(),
|
||||
required i2.AlbumAssetOrder order,
|
||||
@@ -477,7 +357,6 @@ class $$RemoteAlbumEntityTableTableManager
|
||||
description: description,
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
ownerId: ownerId,
|
||||
thumbnailAssetId: thumbnailAssetId,
|
||||
isActivityEnabled: isActivityEnabled,
|
||||
order: order,
|
||||
@@ -490,7 +369,7 @@ class $$RemoteAlbumEntityTableTableManager
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
prefetchHooksCallback: ({ownerId = false, thumbnailAssetId = false}) {
|
||||
prefetchHooksCallback: ({thumbnailAssetId = false}) {
|
||||
return i0.PrefetchHooks(
|
||||
db: db,
|
||||
explicitlyWatchedTables: [],
|
||||
@@ -510,21 +389,6 @@ class $$RemoteAlbumEntityTableTableManager
|
||||
dynamic
|
||||
>
|
||||
>(state) {
|
||||
if (ownerId) {
|
||||
state =
|
||||
state.withJoin(
|
||||
currentTable: table,
|
||||
currentColumn: table.ownerId,
|
||||
referencedTable: i1
|
||||
.$$RemoteAlbumEntityTableReferences
|
||||
._ownerIdTable(db),
|
||||
referencedColumn: i1
|
||||
.$$RemoteAlbumEntityTableReferences
|
||||
._ownerIdTable(db)
|
||||
.id,
|
||||
)
|
||||
as T;
|
||||
}
|
||||
if (thumbnailAssetId) {
|
||||
state =
|
||||
state.withJoin(
|
||||
@@ -564,12 +428,8 @@ typedef $$RemoteAlbumEntityTableProcessedTableManager =
|
||||
$$RemoteAlbumEntityTableUpdateCompanionBuilder,
|
||||
(i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences),
|
||||
i1.RemoteAlbumEntityData,
|
||||
i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId})
|
||||
i0.PrefetchHooks Function({bool thumbnailAssetId})
|
||||
>;
|
||||
i0.Index get idxRemoteAlbumOwnerId => i0.Index(
|
||||
'idx_remote_album_owner_id',
|
||||
'CREATE INDEX IF NOT EXISTS idx_remote_album_owner_id ON remote_album_entity (owner_id)',
|
||||
);
|
||||
|
||||
class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity
|
||||
with i0.TableInfo<$RemoteAlbumEntityTable, i1.RemoteAlbumEntityData> {
|
||||
@@ -636,20 +496,6 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity
|
||||
requiredDuringInsert: false,
|
||||
defaultValue: i4.currentDateAndTime,
|
||||
);
|
||||
static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta(
|
||||
'ownerId',
|
||||
);
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> ownerId = i0.GeneratedColumn<String>(
|
||||
'owner_id',
|
||||
aliasedName,
|
||||
false,
|
||||
type: i0.DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||
'REFERENCES user_entity (id) ON DELETE CASCADE',
|
||||
),
|
||||
);
|
||||
static const i0.VerificationMeta _thumbnailAssetIdMeta =
|
||||
const i0.VerificationMeta('thumbnailAssetId');
|
||||
@override
|
||||
@@ -698,7 +544,6 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity
|
||||
description,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
ownerId,
|
||||
thumbnailAssetId,
|
||||
isActivityEnabled,
|
||||
order,
|
||||
@@ -749,14 +594,6 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity
|
||||
updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('owner_id')) {
|
||||
context.handle(
|
||||
_ownerIdMeta,
|
||||
ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta),
|
||||
);
|
||||
} else if (isInserting) {
|
||||
context.missing(_ownerIdMeta);
|
||||
}
|
||||
if (data.containsKey('thumbnail_asset_id')) {
|
||||
context.handle(
|
||||
_thumbnailAssetIdMeta,
|
||||
@@ -807,10 +644,6 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity
|
||||
i0.DriftSqlType.dateTime,
|
||||
data['${effectivePrefix}updated_at'],
|
||||
)!,
|
||||
ownerId: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.string,
|
||||
data['${effectivePrefix}owner_id'],
|
||||
)!,
|
||||
thumbnailAssetId: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.string,
|
||||
data['${effectivePrefix}thumbnail_asset_id'],
|
||||
@@ -850,7 +683,6 @@ class RemoteAlbumEntityData extends i0.DataClass
|
||||
final String description;
|
||||
final DateTime createdAt;
|
||||
final DateTime updatedAt;
|
||||
final String ownerId;
|
||||
final String? thumbnailAssetId;
|
||||
final bool isActivityEnabled;
|
||||
final i2.AlbumAssetOrder order;
|
||||
@@ -860,7 +692,6 @@ class RemoteAlbumEntityData extends i0.DataClass
|
||||
required this.description,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
required this.ownerId,
|
||||
this.thumbnailAssetId,
|
||||
required this.isActivityEnabled,
|
||||
required this.order,
|
||||
@@ -873,7 +704,6 @@ class RemoteAlbumEntityData extends i0.DataClass
|
||||
map['description'] = i0.Variable<String>(description);
|
||||
map['created_at'] = i0.Variable<DateTime>(createdAt);
|
||||
map['updated_at'] = i0.Variable<DateTime>(updatedAt);
|
||||
map['owner_id'] = i0.Variable<String>(ownerId);
|
||||
if (!nullToAbsent || thumbnailAssetId != null) {
|
||||
map['thumbnail_asset_id'] = i0.Variable<String>(thumbnailAssetId);
|
||||
}
|
||||
@@ -897,7 +727,6 @@ class RemoteAlbumEntityData extends i0.DataClass
|
||||
description: serializer.fromJson<String>(json['description']),
|
||||
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
|
||||
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
|
||||
ownerId: serializer.fromJson<String>(json['ownerId']),
|
||||
thumbnailAssetId: serializer.fromJson<String?>(json['thumbnailAssetId']),
|
||||
isActivityEnabled: serializer.fromJson<bool>(json['isActivityEnabled']),
|
||||
order: i1.$RemoteAlbumEntityTable.$converterorder.fromJson(
|
||||
@@ -914,7 +743,6 @@ class RemoteAlbumEntityData extends i0.DataClass
|
||||
'description': serializer.toJson<String>(description),
|
||||
'createdAt': serializer.toJson<DateTime>(createdAt),
|
||||
'updatedAt': serializer.toJson<DateTime>(updatedAt),
|
||||
'ownerId': serializer.toJson<String>(ownerId),
|
||||
'thumbnailAssetId': serializer.toJson<String?>(thumbnailAssetId),
|
||||
'isActivityEnabled': serializer.toJson<bool>(isActivityEnabled),
|
||||
'order': serializer.toJson<int>(
|
||||
@@ -929,7 +757,6 @@ class RemoteAlbumEntityData extends i0.DataClass
|
||||
String? description,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
String? ownerId,
|
||||
i0.Value<String?> thumbnailAssetId = const i0.Value.absent(),
|
||||
bool? isActivityEnabled,
|
||||
i2.AlbumAssetOrder? order,
|
||||
@@ -939,7 +766,6 @@ class RemoteAlbumEntityData extends i0.DataClass
|
||||
description: description ?? this.description,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
ownerId: ownerId ?? this.ownerId,
|
||||
thumbnailAssetId: thumbnailAssetId.present
|
||||
? thumbnailAssetId.value
|
||||
: this.thumbnailAssetId,
|
||||
@@ -955,7 +781,6 @@ class RemoteAlbumEntityData extends i0.DataClass
|
||||
: this.description,
|
||||
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
|
||||
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
|
||||
ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId,
|
||||
thumbnailAssetId: data.thumbnailAssetId.present
|
||||
? data.thumbnailAssetId.value
|
||||
: this.thumbnailAssetId,
|
||||
@@ -974,7 +799,6 @@ class RemoteAlbumEntityData extends i0.DataClass
|
||||
..write('description: $description, ')
|
||||
..write('createdAt: $createdAt, ')
|
||||
..write('updatedAt: $updatedAt, ')
|
||||
..write('ownerId: $ownerId, ')
|
||||
..write('thumbnailAssetId: $thumbnailAssetId, ')
|
||||
..write('isActivityEnabled: $isActivityEnabled, ')
|
||||
..write('order: $order')
|
||||
@@ -989,7 +813,6 @@ class RemoteAlbumEntityData extends i0.DataClass
|
||||
description,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
ownerId,
|
||||
thumbnailAssetId,
|
||||
isActivityEnabled,
|
||||
order,
|
||||
@@ -1003,7 +826,6 @@ class RemoteAlbumEntityData extends i0.DataClass
|
||||
other.description == this.description &&
|
||||
other.createdAt == this.createdAt &&
|
||||
other.updatedAt == this.updatedAt &&
|
||||
other.ownerId == this.ownerId &&
|
||||
other.thumbnailAssetId == this.thumbnailAssetId &&
|
||||
other.isActivityEnabled == this.isActivityEnabled &&
|
||||
other.order == this.order);
|
||||
@@ -1016,7 +838,6 @@ class RemoteAlbumEntityCompanion
|
||||
final i0.Value<String> description;
|
||||
final i0.Value<DateTime> createdAt;
|
||||
final i0.Value<DateTime> updatedAt;
|
||||
final i0.Value<String> ownerId;
|
||||
final i0.Value<String?> thumbnailAssetId;
|
||||
final i0.Value<bool> isActivityEnabled;
|
||||
final i0.Value<i2.AlbumAssetOrder> order;
|
||||
@@ -1026,7 +847,6 @@ class RemoteAlbumEntityCompanion
|
||||
this.description = const i0.Value.absent(),
|
||||
this.createdAt = const i0.Value.absent(),
|
||||
this.updatedAt = const i0.Value.absent(),
|
||||
this.ownerId = const i0.Value.absent(),
|
||||
this.thumbnailAssetId = const i0.Value.absent(),
|
||||
this.isActivityEnabled = const i0.Value.absent(),
|
||||
this.order = const i0.Value.absent(),
|
||||
@@ -1037,13 +857,11 @@ class RemoteAlbumEntityCompanion
|
||||
this.description = const i0.Value.absent(),
|
||||
this.createdAt = const i0.Value.absent(),
|
||||
this.updatedAt = const i0.Value.absent(),
|
||||
required String ownerId,
|
||||
this.thumbnailAssetId = const i0.Value.absent(),
|
||||
this.isActivityEnabled = const i0.Value.absent(),
|
||||
required i2.AlbumAssetOrder order,
|
||||
}) : id = i0.Value(id),
|
||||
name = i0.Value(name),
|
||||
ownerId = i0.Value(ownerId),
|
||||
order = i0.Value(order);
|
||||
static i0.Insertable<i1.RemoteAlbumEntityData> custom({
|
||||
i0.Expression<String>? id,
|
||||
@@ -1051,7 +869,6 @@ class RemoteAlbumEntityCompanion
|
||||
i0.Expression<String>? description,
|
||||
i0.Expression<DateTime>? createdAt,
|
||||
i0.Expression<DateTime>? updatedAt,
|
||||
i0.Expression<String>? ownerId,
|
||||
i0.Expression<String>? thumbnailAssetId,
|
||||
i0.Expression<bool>? isActivityEnabled,
|
||||
i0.Expression<int>? order,
|
||||
@@ -1062,7 +879,6 @@ class RemoteAlbumEntityCompanion
|
||||
if (description != null) 'description': description,
|
||||
if (createdAt != null) 'created_at': createdAt,
|
||||
if (updatedAt != null) 'updated_at': updatedAt,
|
||||
if (ownerId != null) 'owner_id': ownerId,
|
||||
if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId,
|
||||
if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled,
|
||||
if (order != null) 'order': order,
|
||||
@@ -1075,7 +891,6 @@ class RemoteAlbumEntityCompanion
|
||||
i0.Value<String>? description,
|
||||
i0.Value<DateTime>? createdAt,
|
||||
i0.Value<DateTime>? updatedAt,
|
||||
i0.Value<String>? ownerId,
|
||||
i0.Value<String?>? thumbnailAssetId,
|
||||
i0.Value<bool>? isActivityEnabled,
|
||||
i0.Value<i2.AlbumAssetOrder>? order,
|
||||
@@ -1086,7 +901,6 @@ class RemoteAlbumEntityCompanion
|
||||
description: description ?? this.description,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
ownerId: ownerId ?? this.ownerId,
|
||||
thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId,
|
||||
isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled,
|
||||
order: order ?? this.order,
|
||||
@@ -1111,9 +925,6 @@ class RemoteAlbumEntityCompanion
|
||||
if (updatedAt.present) {
|
||||
map['updated_at'] = i0.Variable<DateTime>(updatedAt.value);
|
||||
}
|
||||
if (ownerId.present) {
|
||||
map['owner_id'] = i0.Variable<String>(ownerId.value);
|
||||
}
|
||||
if (thumbnailAssetId.present) {
|
||||
map['thumbnail_asset_id'] = i0.Variable<String>(thumbnailAssetId.value);
|
||||
}
|
||||
@@ -1136,7 +947,6 @@ class RemoteAlbumEntityCompanion
|
||||
..write('description: $description, ')
|
||||
..write('createdAt: $createdAt, ')
|
||||
..write('updatedAt: $updatedAt, ')
|
||||
..write('ownerId: $ownerId, ')
|
||||
..write('thumbnailAssetId: $thumbnailAssetId, ')
|
||||
..write('isActivityEnabled: $isActivityEnabled, ')
|
||||
..write('order: $order')
|
||||
|
||||
@@ -84,7 +84,7 @@ class Drift extends $Drift {
|
||||
}
|
||||
|
||||
@override
|
||||
int get schemaVersion => 23;
|
||||
int get schemaVersion => 24;
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration => MigrationStrategy(
|
||||
@@ -246,6 +246,10 @@ class Drift extends $Drift {
|
||||
),
|
||||
);
|
||||
},
|
||||
from23To24: (m, v24) async {
|
||||
await customStatement('DROP INDEX IF EXISTS idx_remote_album_owner_id');
|
||||
await m.alterTable(TableMigration(v24.remoteAlbumEntity));
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -105,7 +105,6 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
||||
localAlbumEntity,
|
||||
localAlbumAssetEntity,
|
||||
i7.idxLocalAlbumAssetAlbumAsset,
|
||||
i5.idxRemoteAlbumOwnerId,
|
||||
i4.idxLocalAssetChecksum,
|
||||
i4.idxLocalAssetCloudId,
|
||||
i3.idxStackPrimaryAssetId,
|
||||
@@ -160,15 +159,6 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
||||
),
|
||||
result: [i0.TableUpdate('stack_entity', kind: i0.UpdateKind.delete)],
|
||||
),
|
||||
i0.WritePropagation(
|
||||
on: i0.TableUpdateQuery.onTableName(
|
||||
'user_entity',
|
||||
limitUpdateKind: i0.UpdateKind.delete,
|
||||
),
|
||||
result: [
|
||||
i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.delete),
|
||||
],
|
||||
),
|
||||
i0.WritePropagation(
|
||||
on: i0.TableUpdateQuery.onTableName(
|
||||
'remote_asset_entity',
|
||||
|
||||
@@ -11824,6 +11824,557 @@ i1.GeneratedColumn<int> _column_209(String aliasedName) =>
|
||||
type: i1.DriftSqlType.int,
|
||||
$customConstraints: 'NOT NULL',
|
||||
);
|
||||
|
||||
final class Schema24 extends i0.VersionedSchema {
|
||||
Schema24({required super.database}) : super(version: 24);
|
||||
@override
|
||||
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||
userEntity,
|
||||
remoteAssetEntity,
|
||||
stackEntity,
|
||||
localAssetEntity,
|
||||
remoteAlbumEntity,
|
||||
localAlbumEntity,
|
||||
localAlbumAssetEntity,
|
||||
idxLocalAlbumAssetAlbumAsset,
|
||||
idxLocalAssetChecksum,
|
||||
idxLocalAssetCloudId,
|
||||
idxStackPrimaryAssetId,
|
||||
idxRemoteAssetOwnerChecksum,
|
||||
uQRemoteAssetsOwnerChecksum,
|
||||
uQRemoteAssetsOwnerLibraryChecksum,
|
||||
idxRemoteAssetChecksum,
|
||||
idxRemoteAssetStackId,
|
||||
idxRemoteAssetLocalDateTimeDay,
|
||||
idxRemoteAssetLocalDateTimeMonth,
|
||||
authUserEntity,
|
||||
userMetadataEntity,
|
||||
partnerEntity,
|
||||
remoteExifEntity,
|
||||
remoteAlbumAssetEntity,
|
||||
remoteAlbumUserEntity,
|
||||
remoteAssetCloudIdEntity,
|
||||
memoryEntity,
|
||||
memoryAssetEntity,
|
||||
personEntity,
|
||||
assetFaceEntity,
|
||||
storeEntity,
|
||||
trashedLocalAssetEntity,
|
||||
assetEditEntity,
|
||||
idxPartnerSharedWithId,
|
||||
idxLatLng,
|
||||
idxRemoteAlbumAssetAlbumAsset,
|
||||
idxRemoteAssetCloudId,
|
||||
idxPersonOwnerId,
|
||||
idxAssetFacePersonId,
|
||||
idxAssetFaceAssetId,
|
||||
idxTrashedLocalAssetChecksum,
|
||||
idxTrashedLocalAssetAlbum,
|
||||
idxAssetEditAssetId,
|
||||
];
|
||||
late final Shape33 userEntity = Shape33(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'user_entity',
|
||||
withoutRowId: true,
|
||||
isStrict: true,
|
||||
tableConstraints: ['PRIMARY KEY(id)'],
|
||||
columns: [
|
||||
_column_107,
|
||||
_column_108,
|
||||
_column_109,
|
||||
_column_110,
|
||||
_column_111,
|
||||
_column_112,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape34 remoteAssetEntity = Shape34(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'remote_asset_entity',
|
||||
withoutRowId: true,
|
||||
isStrict: true,
|
||||
tableConstraints: ['PRIMARY KEY(id)'],
|
||||
columns: [
|
||||
_column_108,
|
||||
_column_113,
|
||||
_column_114,
|
||||
_column_115,
|
||||
_column_116,
|
||||
_column_117,
|
||||
_column_118,
|
||||
_column_107,
|
||||
_column_119,
|
||||
_column_120,
|
||||
_column_121,
|
||||
_column_122,
|
||||
_column_123,
|
||||
_column_124,
|
||||
_column_125,
|
||||
_column_126,
|
||||
_column_127,
|
||||
_column_128,
|
||||
_column_129,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape35 stackEntity = Shape35(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'stack_entity',
|
||||
withoutRowId: true,
|
||||
isStrict: true,
|
||||
tableConstraints: ['PRIMARY KEY(id)'],
|
||||
columns: [
|
||||
_column_107,
|
||||
_column_114,
|
||||
_column_115,
|
||||
_column_121,
|
||||
_column_130,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape36 localAssetEntity = Shape36(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'local_asset_entity',
|
||||
withoutRowId: true,
|
||||
isStrict: true,
|
||||
tableConstraints: ['PRIMARY KEY(id)'],
|
||||
columns: [
|
||||
_column_108,
|
||||
_column_113,
|
||||
_column_114,
|
||||
_column_115,
|
||||
_column_116,
|
||||
_column_117,
|
||||
_column_118,
|
||||
_column_107,
|
||||
_column_131,
|
||||
_column_120,
|
||||
_column_132,
|
||||
_column_133,
|
||||
_column_134,
|
||||
_column_135,
|
||||
_column_136,
|
||||
_column_137,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape48 remoteAlbumEntity = Shape48(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'remote_album_entity',
|
||||
withoutRowId: true,
|
||||
isStrict: true,
|
||||
tableConstraints: ['PRIMARY KEY(id)'],
|
||||
columns: [
|
||||
_column_107,
|
||||
_column_108,
|
||||
_column_138,
|
||||
_column_114,
|
||||
_column_115,
|
||||
_column_139,
|
||||
_column_140,
|
||||
_column_141,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape38 localAlbumEntity = Shape38(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'local_album_entity',
|
||||
withoutRowId: true,
|
||||
isStrict: true,
|
||||
tableConstraints: ['PRIMARY KEY(id)'],
|
||||
columns: [
|
||||
_column_107,
|
||||
_column_108,
|
||||
_column_115,
|
||||
_column_142,
|
||||
_column_143,
|
||||
_column_144,
|
||||
_column_145,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape39 localAlbumAssetEntity = Shape39(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'local_album_asset_entity',
|
||||
withoutRowId: true,
|
||||
isStrict: true,
|
||||
tableConstraints: ['PRIMARY KEY(asset_id, album_id)'],
|
||||
columns: [_column_146, _column_147, _column_145],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
final i1.Index idxLocalAlbumAssetAlbumAsset = i1.Index(
|
||||
'idx_local_album_asset_album_asset',
|
||||
'CREATE INDEX IF NOT EXISTS idx_local_album_asset_album_asset ON local_album_asset_entity (album_id, asset_id)',
|
||||
);
|
||||
final i1.Index idxLocalAssetChecksum = i1.Index(
|
||||
'idx_local_asset_checksum',
|
||||
'CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)',
|
||||
);
|
||||
final i1.Index idxLocalAssetCloudId = i1.Index(
|
||||
'idx_local_asset_cloud_id',
|
||||
'CREATE INDEX IF NOT EXISTS idx_local_asset_cloud_id ON local_asset_entity (i_cloud_id)',
|
||||
);
|
||||
final i1.Index idxStackPrimaryAssetId = i1.Index(
|
||||
'idx_stack_primary_asset_id',
|
||||
'CREATE INDEX IF NOT EXISTS idx_stack_primary_asset_id ON stack_entity (primary_asset_id)',
|
||||
);
|
||||
final i1.Index idxRemoteAssetOwnerChecksum = i1.Index(
|
||||
'idx_remote_asset_owner_checksum',
|
||||
'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)',
|
||||
);
|
||||
final i1.Index uQRemoteAssetsOwnerChecksum = i1.Index(
|
||||
'UQ_remote_assets_owner_checksum',
|
||||
'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)',
|
||||
);
|
||||
final i1.Index uQRemoteAssetsOwnerLibraryChecksum = i1.Index(
|
||||
'UQ_remote_assets_owner_library_checksum',
|
||||
'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)',
|
||||
);
|
||||
final i1.Index idxRemoteAssetChecksum = i1.Index(
|
||||
'idx_remote_asset_checksum',
|
||||
'CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)',
|
||||
);
|
||||
final i1.Index idxRemoteAssetStackId = i1.Index(
|
||||
'idx_remote_asset_stack_id',
|
||||
'CREATE INDEX IF NOT EXISTS idx_remote_asset_stack_id ON remote_asset_entity (stack_id)',
|
||||
);
|
||||
final i1.Index idxRemoteAssetLocalDateTimeDay = i1.Index(
|
||||
'idx_remote_asset_local_date_time_day',
|
||||
'CREATE INDEX IF NOT EXISTS idx_remote_asset_local_date_time_day ON remote_asset_entity (STRFTIME(\'%Y-%m-%d\', local_date_time))',
|
||||
);
|
||||
final i1.Index idxRemoteAssetLocalDateTimeMonth = i1.Index(
|
||||
'idx_remote_asset_local_date_time_month',
|
||||
'CREATE INDEX IF NOT EXISTS idx_remote_asset_local_date_time_month ON remote_asset_entity (STRFTIME(\'%Y-%m\', local_date_time))',
|
||||
);
|
||||
late final Shape40 authUserEntity = Shape40(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'auth_user_entity',
|
||||
withoutRowId: true,
|
||||
isStrict: true,
|
||||
tableConstraints: ['PRIMARY KEY(id)'],
|
||||
columns: [
|
||||
_column_107,
|
||||
_column_108,
|
||||
_column_109,
|
||||
_column_148,
|
||||
_column_110,
|
||||
_column_111,
|
||||
_column_149,
|
||||
_column_150,
|
||||
_column_151,
|
||||
_column_152,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape4 userMetadataEntity = Shape4(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'user_metadata_entity',
|
||||
withoutRowId: true,
|
||||
isStrict: true,
|
||||
tableConstraints: ['PRIMARY KEY(user_id, "key")'],
|
||||
columns: [_column_153, _column_154, _column_155],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape41 partnerEntity = Shape41(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'partner_entity',
|
||||
withoutRowId: true,
|
||||
isStrict: true,
|
||||
tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'],
|
||||
columns: [_column_156, _column_157, _column_158],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape42 remoteExifEntity = Shape42(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'remote_exif_entity',
|
||||
withoutRowId: true,
|
||||
isStrict: true,
|
||||
tableConstraints: ['PRIMARY KEY(asset_id)'],
|
||||
columns: [
|
||||
_column_159,
|
||||
_column_160,
|
||||
_column_161,
|
||||
_column_162,
|
||||
_column_163,
|
||||
_column_164,
|
||||
_column_117,
|
||||
_column_116,
|
||||
_column_165,
|
||||
_column_166,
|
||||
_column_167,
|
||||
_column_168,
|
||||
_column_135,
|
||||
_column_136,
|
||||
_column_169,
|
||||
_column_170,
|
||||
_column_171,
|
||||
_column_172,
|
||||
_column_173,
|
||||
_column_174,
|
||||
_column_175,
|
||||
_column_176,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape7 remoteAlbumAssetEntity = Shape7(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'remote_album_asset_entity',
|
||||
withoutRowId: true,
|
||||
isStrict: true,
|
||||
tableConstraints: ['PRIMARY KEY(asset_id, album_id)'],
|
||||
columns: [_column_159, _column_177],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape10 remoteAlbumUserEntity = Shape10(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'remote_album_user_entity',
|
||||
withoutRowId: true,
|
||||
isStrict: true,
|
||||
tableConstraints: ['PRIMARY KEY(album_id, user_id)'],
|
||||
columns: [_column_177, _column_153, _column_178],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape43 remoteAssetCloudIdEntity = Shape43(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'remote_asset_cloud_id_entity',
|
||||
withoutRowId: true,
|
||||
isStrict: true,
|
||||
tableConstraints: ['PRIMARY KEY(asset_id)'],
|
||||
columns: [
|
||||
_column_159,
|
||||
_column_179,
|
||||
_column_180,
|
||||
_column_134,
|
||||
_column_135,
|
||||
_column_136,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape44 memoryEntity = Shape44(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'memory_entity',
|
||||
withoutRowId: true,
|
||||
isStrict: true,
|
||||
tableConstraints: ['PRIMARY KEY(id)'],
|
||||
columns: [
|
||||
_column_107,
|
||||
_column_114,
|
||||
_column_115,
|
||||
_column_124,
|
||||
_column_121,
|
||||
_column_113,
|
||||
_column_181,
|
||||
_column_182,
|
||||
_column_183,
|
||||
_column_184,
|
||||
_column_185,
|
||||
_column_186,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape12 memoryAssetEntity = Shape12(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'memory_asset_entity',
|
||||
withoutRowId: true,
|
||||
isStrict: true,
|
||||
tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'],
|
||||
columns: [_column_159, _column_187],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape45 personEntity = Shape45(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'person_entity',
|
||||
withoutRowId: true,
|
||||
isStrict: true,
|
||||
tableConstraints: ['PRIMARY KEY(id)'],
|
||||
columns: [
|
||||
_column_107,
|
||||
_column_114,
|
||||
_column_115,
|
||||
_column_121,
|
||||
_column_108,
|
||||
_column_188,
|
||||
_column_189,
|
||||
_column_190,
|
||||
_column_191,
|
||||
_column_192,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape46 assetFaceEntity = Shape46(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'asset_face_entity',
|
||||
withoutRowId: true,
|
||||
isStrict: true,
|
||||
tableConstraints: ['PRIMARY KEY(id)'],
|
||||
columns: [
|
||||
_column_107,
|
||||
_column_159,
|
||||
_column_193,
|
||||
_column_194,
|
||||
_column_195,
|
||||
_column_196,
|
||||
_column_197,
|
||||
_column_198,
|
||||
_column_199,
|
||||
_column_200,
|
||||
_column_201,
|
||||
_column_124,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape18 storeEntity = Shape18(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'store_entity',
|
||||
withoutRowId: true,
|
||||
isStrict: true,
|
||||
tableConstraints: ['PRIMARY KEY(id)'],
|
||||
columns: [_column_202, _column_203, _column_204],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape47 trashedLocalAssetEntity = Shape47(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'trashed_local_asset_entity',
|
||||
withoutRowId: true,
|
||||
isStrict: true,
|
||||
tableConstraints: ['PRIMARY KEY(id, album_id)'],
|
||||
columns: [
|
||||
_column_108,
|
||||
_column_113,
|
||||
_column_114,
|
||||
_column_115,
|
||||
_column_116,
|
||||
_column_117,
|
||||
_column_118,
|
||||
_column_107,
|
||||
_column_205,
|
||||
_column_131,
|
||||
_column_120,
|
||||
_column_132,
|
||||
_column_206,
|
||||
_column_137,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape32 assetEditEntity = Shape32(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'asset_edit_entity',
|
||||
withoutRowId: true,
|
||||
isStrict: true,
|
||||
tableConstraints: ['PRIMARY KEY(id)'],
|
||||
columns: [
|
||||
_column_107,
|
||||
_column_159,
|
||||
_column_207,
|
||||
_column_208,
|
||||
_column_209,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
final i1.Index idxPartnerSharedWithId = i1.Index(
|
||||
'idx_partner_shared_with_id',
|
||||
'CREATE INDEX IF NOT EXISTS idx_partner_shared_with_id ON partner_entity (shared_with_id)',
|
||||
);
|
||||
final i1.Index idxLatLng = i1.Index(
|
||||
'idx_lat_lng',
|
||||
'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)',
|
||||
);
|
||||
final i1.Index idxRemoteAlbumAssetAlbumAsset = i1.Index(
|
||||
'idx_remote_album_asset_album_asset',
|
||||
'CREATE INDEX IF NOT EXISTS idx_remote_album_asset_album_asset ON remote_album_asset_entity (album_id, asset_id)',
|
||||
);
|
||||
final i1.Index idxRemoteAssetCloudId = i1.Index(
|
||||
'idx_remote_asset_cloud_id',
|
||||
'CREATE INDEX IF NOT EXISTS idx_remote_asset_cloud_id ON remote_asset_cloud_id_entity (cloud_id)',
|
||||
);
|
||||
final i1.Index idxPersonOwnerId = i1.Index(
|
||||
'idx_person_owner_id',
|
||||
'CREATE INDEX IF NOT EXISTS idx_person_owner_id ON person_entity (owner_id)',
|
||||
);
|
||||
final i1.Index idxAssetFacePersonId = i1.Index(
|
||||
'idx_asset_face_person_id',
|
||||
'CREATE INDEX IF NOT EXISTS idx_asset_face_person_id ON asset_face_entity (person_id)',
|
||||
);
|
||||
final i1.Index idxAssetFaceAssetId = i1.Index(
|
||||
'idx_asset_face_asset_id',
|
||||
'CREATE INDEX IF NOT EXISTS idx_asset_face_asset_id ON asset_face_entity (asset_id)',
|
||||
);
|
||||
final i1.Index idxTrashedLocalAssetChecksum = i1.Index(
|
||||
'idx_trashed_local_asset_checksum',
|
||||
'CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_checksum ON trashed_local_asset_entity (checksum)',
|
||||
);
|
||||
final i1.Index idxTrashedLocalAssetAlbum = i1.Index(
|
||||
'idx_trashed_local_asset_album',
|
||||
'CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_album ON trashed_local_asset_entity (album_id)',
|
||||
);
|
||||
final i1.Index idxAssetEditAssetId = i1.Index(
|
||||
'idx_asset_edit_asset_id',
|
||||
'CREATE INDEX IF NOT EXISTS idx_asset_edit_asset_id ON asset_edit_entity (asset_id)',
|
||||
);
|
||||
}
|
||||
|
||||
class Shape48 extends i0.VersionedTable {
|
||||
Shape48({required super.source, required super.alias}) : super.aliased();
|
||||
i1.GeneratedColumn<String> get id =>
|
||||
columnsByName['id']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get name =>
|
||||
columnsByName['name']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get description =>
|
||||
columnsByName['description']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get createdAt =>
|
||||
columnsByName['created_at']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get updatedAt =>
|
||||
columnsByName['updated_at']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get thumbnailAssetId =>
|
||||
columnsByName['thumbnail_asset_id']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<int> get isActivityEnabled =>
|
||||
columnsByName['is_activity_enabled']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get order =>
|
||||
columnsByName['order']! as i1.GeneratedColumn<int>;
|
||||
}
|
||||
|
||||
i0.MigrationStepWithVersion migrationSteps({
|
||||
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
|
||||
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
|
||||
@@ -11847,6 +12398,7 @@ i0.MigrationStepWithVersion migrationSteps({
|
||||
required Future<void> Function(i1.Migrator m, Schema21 schema) from20To21,
|
||||
required Future<void> Function(i1.Migrator m, Schema22 schema) from21To22,
|
||||
required Future<void> Function(i1.Migrator m, Schema23 schema) from22To23,
|
||||
required Future<void> Function(i1.Migrator m, Schema24 schema) from23To24,
|
||||
}) {
|
||||
return (currentVersion, database) async {
|
||||
switch (currentVersion) {
|
||||
@@ -11960,6 +12512,11 @@ i0.MigrationStepWithVersion migrationSteps({
|
||||
final migrator = i1.Migrator(database, schema);
|
||||
await from22To23(migrator, schema);
|
||||
return 23;
|
||||
case 23:
|
||||
final schema = Schema24(database: database);
|
||||
final migrator = i1.Migrator(database, schema);
|
||||
await from23To24(migrator, schema);
|
||||
return 24;
|
||||
default:
|
||||
throw ArgumentError.value('Unknown migration from $currentVersion');
|
||||
}
|
||||
@@ -11989,6 +12546,7 @@ i1.OnUpgrade stepByStep({
|
||||
required Future<void> Function(i1.Migrator m, Schema21 schema) from20To21,
|
||||
required Future<void> Function(i1.Migrator m, Schema22 schema) from21To22,
|
||||
required Future<void> Function(i1.Migrator m, Schema23 schema) from22To23,
|
||||
required Future<void> Function(i1.Migrator m, Schema24 schema) from23To24,
|
||||
}) => i0.VersionedSchema.stepByStepHelper(
|
||||
step: migrationSteps(
|
||||
from1To2: from1To2,
|
||||
@@ -12013,5 +12571,6 @@ i1.OnUpgrade stepByStep({
|
||||
from20To21: from20To21,
|
||||
from21To22: from21To22,
|
||||
from22To23: from22To23,
|
||||
from23To24: from23To24,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -109,31 +109,40 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository {
|
||||
return query.map((localAlbum) => localAlbum.toDto()).get();
|
||||
}
|
||||
|
||||
Future<Map<String, List<LocalAsset>>> getAssetsFromBackupAlbums(Iterable<String> checksums) async {
|
||||
if (checksums.isEmpty) {
|
||||
Future<Map<String, List<LocalAsset>>> getAssetsFromBackupAlbums(Iterable<String> remoteIds) async {
|
||||
if (remoteIds.isEmpty) {
|
||||
return {};
|
||||
}
|
||||
|
||||
final result = <String, List<LocalAsset>>{};
|
||||
|
||||
for (final slice in checksums.toSet().slices(kDriftMaxChunk)) {
|
||||
for (final slice in remoteIds.toSet().slices(kDriftMaxChunk)) {
|
||||
final rows =
|
||||
await (_db.select(_db.localAlbumAssetEntity).join([
|
||||
innerJoin(_db.localAlbumEntity, _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id)),
|
||||
innerJoin(
|
||||
_db.localAlbumEntity,
|
||||
_db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)),
|
||||
innerJoin(
|
||||
_db.remoteAssetEntity,
|
||||
_db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum),
|
||||
useColumns: false,
|
||||
),
|
||||
])..where(
|
||||
_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected) &
|
||||
_db.localAssetEntity.checksum.isIn(slice),
|
||||
_db.remoteAssetEntity.id.isIn(slice),
|
||||
))
|
||||
.get();
|
||||
|
||||
for (final row in rows) {
|
||||
final albumId = row.readTable(_db.localAlbumAssetEntity).albumId;
|
||||
final assetData = row.readTable(_db.localAssetEntity);
|
||||
final asset = assetData.toDto();
|
||||
final asset = row.readTable(_db.localAssetEntity).toDto();
|
||||
(result[albumId] ??= <LocalAsset>[]).add(asset);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,17 +32,23 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
||||
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), useColumns: false),
|
||||
leftOuterJoin(
|
||||
_db.remoteAlbumUserEntity,
|
||||
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
innerJoin(
|
||||
_db.userEntity,
|
||||
_db.userEntity.id.equalsExp(_db.remoteAlbumUserEntity.userId) &
|
||||
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id) &
|
||||
_db.remoteAlbumUserEntity.role.equalsValue(AlbumUserRole.owner),
|
||||
useColumns: false,
|
||||
),
|
||||
]);
|
||||
query
|
||||
..where(_db.remoteAssetEntity.deletedAt.isNull())
|
||||
..addColumns([assetCount])
|
||||
..addColumns([_db.userEntity.name])
|
||||
..addColumns([_db.userEntity.name, _db.userEntity.id])
|
||||
..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)])
|
||||
..groupBy([_db.remoteAlbumEntity.id]);
|
||||
|
||||
@@ -63,6 +69,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
||||
.readTable(_db.remoteAlbumEntity)
|
||||
.toDto(
|
||||
assetCount: row.read(assetCount) ?? 0,
|
||||
ownerId: row.read(_db.userEntity.id)!,
|
||||
ownerName: row.read(_db.userEntity.name)!,
|
||||
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0,
|
||||
),
|
||||
@@ -85,20 +92,22 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
||||
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.userEntity,
|
||||
_db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.remoteAlbumUserEntity,
|
||||
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
innerJoin(
|
||||
_db.userEntity,
|
||||
_db.userEntity.id.equalsExp(_db.remoteAlbumUserEntity.userId) &
|
||||
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id) &
|
||||
_db.remoteAlbumUserEntity.role.equalsValue(AlbumUserRole.owner),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(_db.remoteAlbumEntity.id.equals(albumId) & _db.remoteAssetEntity.deletedAt.isNull())
|
||||
..addColumns([assetCount])
|
||||
..addColumns([_db.userEntity.name])
|
||||
..addColumns([_db.userEntity.name, _db.userEntity.id])
|
||||
..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)])
|
||||
..groupBy([_db.remoteAlbumEntity.id]);
|
||||
|
||||
@@ -108,6 +117,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
||||
.readTable(_db.remoteAlbumEntity)
|
||||
.toDto(
|
||||
assetCount: row.read(assetCount) ?? 0,
|
||||
ownerId: row.read(_db.userEntity.id)!,
|
||||
ownerName: row.read(_db.userEntity.name)!,
|
||||
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0,
|
||||
),
|
||||
@@ -116,12 +126,29 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
||||
}
|
||||
|
||||
Future<RemoteAlbum?> getByName(String albumName, String ownerId) {
|
||||
final query = _db.remoteAlbumEntity.select()
|
||||
..where((row) => row.name.equals(albumName) & row.ownerId.equals(ownerId))
|
||||
..orderBy([(row) => OrderingTerm.desc(row.createdAt)])
|
||||
final query = _db.remoteAlbumEntity.select().join([
|
||||
innerJoin(
|
||||
_db.remoteAlbumUserEntity,
|
||||
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id) &
|
||||
_db.remoteAlbumUserEntity.userId.equals(ownerId) &
|
||||
_db.remoteAlbumUserEntity.role.equalsValue(AlbumUserRole.owner),
|
||||
useColumns: false,
|
||||
),
|
||||
]);
|
||||
|
||||
query
|
||||
..addColumns([_db.remoteAlbumUserEntity.userId])
|
||||
..where(_db.remoteAlbumEntity.name.equals(albumName))
|
||||
..orderBy([OrderingTerm.desc(_db.remoteAlbumEntity.createdAt)])
|
||||
..limit(1);
|
||||
|
||||
return query.map((row) => row.toDto(ownerName: '', isShared: false)).getSingleOrNull();
|
||||
return query
|
||||
.map(
|
||||
(row) => row
|
||||
.readTable(_db.remoteAlbumEntity)
|
||||
.toDto(ownerId: row.read(_db.remoteAlbumUserEntity.userId)!, ownerName: '', isShared: false),
|
||||
)
|
||||
.getSingleOrNull();
|
||||
}
|
||||
|
||||
Future<void> create(RemoteAlbum album, List<String> assetIds) async {
|
||||
@@ -129,7 +156,6 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
||||
final entity = RemoteAlbumEntityCompanion(
|
||||
id: Value(album.id),
|
||||
name: Value(album.name),
|
||||
ownerId: Value(album.ownerId),
|
||||
createdAt: Value(album.createdAt),
|
||||
updatedAt: Value(album.updatedAt),
|
||||
description: Value(album.description),
|
||||
@@ -140,6 +166,14 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
||||
|
||||
await _db.remoteAlbumEntity.insertOne(entity);
|
||||
|
||||
await _db.remoteAlbumUserEntity.insertOne(
|
||||
RemoteAlbumUserEntityCompanion(
|
||||
albumId: Value(album.id),
|
||||
userId: Value(album.ownerId),
|
||||
role: const Value(AlbumUserRole.owner),
|
||||
),
|
||||
);
|
||||
|
||||
if (assetIds.isNotEmpty) {
|
||||
final albumAssets = assetIds.map(
|
||||
(assetId) => RemoteAlbumAssetEntityCompanion(albumId: Value(album.id), assetId: Value(assetId)),
|
||||
@@ -157,7 +191,6 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
||||
RemoteAlbumEntityCompanion(
|
||||
id: Value(album.id),
|
||||
name: Value(album.name),
|
||||
ownerId: Value(album.ownerId),
|
||||
createdAt: Value(album.createdAt),
|
||||
updatedAt: Value(album.updatedAt),
|
||||
description: Value(album.description),
|
||||
@@ -197,7 +230,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
||||
Future<List<UserDto>> getSharedUsers(String albumId) async {
|
||||
final albumUserRows = await (_db.select(
|
||||
_db.remoteAlbumUserEntity,
|
||||
)..where((row) => row.albumId.equals(albumId))).get();
|
||||
)..where((row) => row.albumId.equals(albumId) & row.role.isNotValue(AlbumUserRole.owner.index))).get();
|
||||
|
||||
if (albumUserRows.isEmpty) {
|
||||
return [];
|
||||
@@ -295,19 +328,21 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
||||
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.userEntity,
|
||||
_db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.remoteAlbumUserEntity,
|
||||
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
innerJoin(
|
||||
_db.userEntity,
|
||||
_db.userEntity.id.equalsExp(_db.remoteAlbumUserEntity.userId) &
|
||||
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id) &
|
||||
_db.remoteAlbumUserEntity.role.equalsValue(AlbumUserRole.owner),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(_db.remoteAlbumEntity.id.equals(albumId))
|
||||
..addColumns([_db.userEntity.name])
|
||||
..addColumns([_db.userEntity.name, _db.userEntity.id])
|
||||
..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)])
|
||||
..groupBy([_db.remoteAlbumEntity.id]);
|
||||
|
||||
@@ -315,6 +350,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
||||
final album = row
|
||||
.readTable(_db.remoteAlbumEntity)
|
||||
.toDto(
|
||||
ownerId: row.read(_db.userEntity.id)!,
|
||||
ownerName: row.read(_db.userEntity.name)!,
|
||||
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0,
|
||||
);
|
||||
@@ -355,6 +391,37 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
||||
return _db.managers.remoteAlbumEntity.count();
|
||||
}
|
||||
|
||||
Future<UserDto> getOwner(String albumId) {
|
||||
final query =
|
||||
_db.userEntity.select().join([
|
||||
innerJoin(
|
||||
_db.remoteAlbumUserEntity,
|
||||
_db.userEntity.id.equalsExp(_db.remoteAlbumUserEntity.userId),
|
||||
useColumns: false,
|
||||
),
|
||||
])..where(
|
||||
_db.remoteAlbumUserEntity.albumId.equals(albumId) &
|
||||
_db.remoteAlbumUserEntity.role.equalsValue(AlbumUserRole.owner),
|
||||
);
|
||||
|
||||
return query
|
||||
.map(
|
||||
(row) => UserDto(
|
||||
id: row.read(_db.userEntity.id)!,
|
||||
email: row.read(_db.userEntity.email)!,
|
||||
name: row.read(_db.userEntity.name)!,
|
||||
memoryEnabled: true,
|
||||
inTimeline: false,
|
||||
isPartnerSharedBy: false,
|
||||
isPartnerSharedWith: false,
|
||||
profileChangedAt: row.read(_db.userEntity.profileChangedAt)!,
|
||||
hasProfileImage: row.read(_db.userEntity.hasProfileImage)!,
|
||||
avatarColor: AvatarColor.values[row.read(_db.userEntity.avatarColor)!],
|
||||
),
|
||||
)
|
||||
.getSingle();
|
||||
}
|
||||
|
||||
Future<List<String>> getLinkedAssetIds(String userId, String localAlbumId, String remoteAlbumId) async {
|
||||
// Find remote asset ids that:
|
||||
// 1. Belong to the provided local album (via local_album_asset_entity)
|
||||
@@ -416,21 +483,23 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
||||
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.userEntity,
|
||||
_db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.remoteAlbumUserEntity,
|
||||
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
innerJoin(
|
||||
_db.userEntity,
|
||||
_db.userEntity.id.equalsExp(_db.remoteAlbumUserEntity.userId) &
|
||||
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id) &
|
||||
_db.remoteAlbumUserEntity.role.equalsValue(AlbumUserRole.owner),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(_db.remoteAlbumEntity.id.isIn(albumIds) & _db.remoteAssetEntity.deletedAt.isNull())
|
||||
..addColumns([assetCount])
|
||||
..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)])
|
||||
..addColumns([_db.userEntity.name])
|
||||
..addColumns([_db.userEntity.name, _db.userEntity.id])
|
||||
..groupBy([_db.remoteAlbumEntity.id]);
|
||||
|
||||
return query
|
||||
@@ -438,6 +507,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
||||
(row) => row
|
||||
.readTable(_db.remoteAlbumEntity)
|
||||
.toDto(
|
||||
ownerId: row.read(_db.userEntity.id)!,
|
||||
ownerName: row.read(_db.userEntity.name) ?? '',
|
||||
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0,
|
||||
assetCount: row.read(assetCount) ?? 0,
|
||||
@@ -448,7 +518,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
||||
}
|
||||
|
||||
extension on RemoteAlbumEntityData {
|
||||
RemoteAlbum toDto({int assetCount = 0, required String ownerName, required bool isShared}) {
|
||||
RemoteAlbum toDto({int assetCount = 0, required String ownerName, required String ownerId, required bool isShared}) {
|
||||
return RemoteAlbum(
|
||||
id: id,
|
||||
name: name,
|
||||
|
||||
@@ -46,16 +46,25 @@ class SyncApiRepository {
|
||||
types: [
|
||||
SyncRequestType.authUsersV1,
|
||||
SyncRequestType.usersV1,
|
||||
SyncRequestType.assetsV1,
|
||||
serverVersion >= const SemVer(major: 3, minor: 0, patch: 0)
|
||||
? SyncRequestType.assetsV2
|
||||
: SyncRequestType.assetsV1,
|
||||
SyncRequestType.assetExifsV1,
|
||||
if (serverVersion >= const SemVer(major: 2, minor: 6, patch: 0)) SyncRequestType.assetEditsV1,
|
||||
SyncRequestType.assetMetadataV1,
|
||||
SyncRequestType.partnersV1,
|
||||
SyncRequestType.partnerAssetsV1,
|
||||
serverVersion >= const SemVer(major: 3, minor: 0, patch: 0)
|
||||
? SyncRequestType.partnerAssetsV2
|
||||
: SyncRequestType.partnerAssetsV1,
|
||||
SyncRequestType.partnerAssetExifsV1,
|
||||
SyncRequestType.albumsV1,
|
||||
if (serverVersion < const SemVer(major: 3, minor: 0, patch: 0))
|
||||
SyncRequestType.albumsV1
|
||||
else
|
||||
SyncRequestType.albumsV2,
|
||||
SyncRequestType.albumUsersV1,
|
||||
SyncRequestType.albumAssetsV1,
|
||||
serverVersion >= const SemVer(major: 3, minor: 0, patch: 0)
|
||||
? SyncRequestType.albumAssetsV2
|
||||
: SyncRequestType.albumAssetsV1,
|
||||
SyncRequestType.albumAssetExifsV1,
|
||||
SyncRequestType.albumToAssetsV1,
|
||||
SyncRequestType.memoriesV1,
|
||||
@@ -64,8 +73,9 @@ class SyncApiRepository {
|
||||
SyncRequestType.partnerStacksV1,
|
||||
SyncRequestType.userMetadataV1,
|
||||
SyncRequestType.peopleV1,
|
||||
if (serverVersion < const SemVer(major: 2, minor: 6, patch: 0)) SyncRequestType.assetFacesV1,
|
||||
if (serverVersion >= const SemVer(major: 2, minor: 6, patch: 0)) SyncRequestType.assetFacesV2,
|
||||
serverVersion >= const SemVer(major: 2, minor: 6, patch: 0)
|
||||
? SyncRequestType.assetFacesV2
|
||||
: SyncRequestType.assetFacesV1,
|
||||
],
|
||||
reset: shouldReset,
|
||||
).toJson(),
|
||||
@@ -150,6 +160,7 @@ const _kResponseMap = <SyncEntityType, Function(Object)>{
|
||||
SyncEntityType.partnerV1: SyncPartnerV1.fromJson,
|
||||
SyncEntityType.partnerDeleteV1: SyncPartnerDeleteV1.fromJson,
|
||||
SyncEntityType.assetV1: SyncAssetV1.fromJson,
|
||||
SyncEntityType.assetV2: SyncAssetV2.fromJson,
|
||||
SyncEntityType.assetDeleteV1: SyncAssetDeleteV1.fromJson,
|
||||
SyncEntityType.assetExifV1: SyncAssetExifV1.fromJson,
|
||||
SyncEntityType.assetEditV1: SyncAssetEditV1.fromJson,
|
||||
@@ -157,18 +168,24 @@ const _kResponseMap = <SyncEntityType, Function(Object)>{
|
||||
SyncEntityType.assetMetadataV1: SyncAssetMetadataV1.fromJson,
|
||||
SyncEntityType.assetMetadataDeleteV1: SyncAssetMetadataDeleteV1.fromJson,
|
||||
SyncEntityType.partnerAssetV1: SyncAssetV1.fromJson,
|
||||
SyncEntityType.partnerAssetV2: SyncAssetV2.fromJson,
|
||||
SyncEntityType.partnerAssetBackfillV1: SyncAssetV1.fromJson,
|
||||
SyncEntityType.partnerAssetBackfillV2: SyncAssetV2.fromJson,
|
||||
SyncEntityType.partnerAssetDeleteV1: SyncAssetDeleteV1.fromJson,
|
||||
SyncEntityType.partnerAssetExifV1: SyncAssetExifV1.fromJson,
|
||||
SyncEntityType.partnerAssetExifBackfillV1: SyncAssetExifV1.fromJson,
|
||||
SyncEntityType.albumV1: SyncAlbumV1.fromJson,
|
||||
SyncEntityType.albumV2: SyncAlbumV2.fromJson,
|
||||
SyncEntityType.albumDeleteV1: SyncAlbumDeleteV1.fromJson,
|
||||
SyncEntityType.albumUserV1: SyncAlbumUserV1.fromJson,
|
||||
SyncEntityType.albumUserBackfillV1: SyncAlbumUserV1.fromJson,
|
||||
SyncEntityType.albumUserDeleteV1: SyncAlbumUserDeleteV1.fromJson,
|
||||
SyncEntityType.albumAssetCreateV1: SyncAssetV1.fromJson,
|
||||
SyncEntityType.albumAssetCreateV2: SyncAssetV2.fromJson,
|
||||
SyncEntityType.albumAssetUpdateV1: SyncAssetV1.fromJson,
|
||||
SyncEntityType.albumAssetUpdateV2: SyncAssetV2.fromJson,
|
||||
SyncEntityType.albumAssetBackfillV1: SyncAssetV1.fromJson,
|
||||
SyncEntityType.albumAssetBackfillV2: SyncAssetV2.fromJson,
|
||||
SyncEntityType.albumAssetExifCreateV1: SyncAssetExifV1.fromJson,
|
||||
SyncEntityType.albumAssetExifUpdateV1: SyncAssetExifV1.fromJson,
|
||||
SyncEntityType.albumAssetExifBackfillV1: SyncAssetExifV1.fromJson,
|
||||
|
||||
@@ -30,7 +30,7 @@ import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/exif.converter.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:openapi/api.dart' as api show AssetVisibility, AlbumUserRole, UserMetadataKey, AssetEditAction;
|
||||
import 'package:openapi/api.dart' hide AlbumUserRole, UserMetadataKey, AssetEditAction, AssetVisibility;
|
||||
import 'package:openapi/api.dart' hide UserMetadataKey, AssetEditAction, AssetVisibility, AlbumUserRole;
|
||||
|
||||
class SyncStreamRepository extends DriftDatabaseRepository {
|
||||
final Logger _logger = Logger('DriftSyncStreamRepository');
|
||||
@@ -220,6 +220,44 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateAssetsV2(Iterable<SyncAssetV2> data, {String debugLabel = 'user'}) async {
|
||||
try {
|
||||
await _db.batch((batch) {
|
||||
for (final asset in data) {
|
||||
final companion = RemoteAssetEntityCompanion(
|
||||
name: Value(asset.originalFileName),
|
||||
type: Value(asset.type.toAssetType()),
|
||||
createdAt: Value.absentIfNull(asset.fileCreatedAt),
|
||||
updatedAt: Value.absentIfNull(asset.fileModifiedAt),
|
||||
durationMs: Value(asset.duration),
|
||||
checksum: Value(asset.checksum),
|
||||
isFavorite: Value(asset.isFavorite),
|
||||
ownerId: Value(asset.ownerId),
|
||||
localDateTime: Value(asset.localDateTime),
|
||||
thumbHash: Value(asset.thumbhash),
|
||||
deletedAt: Value(asset.deletedAt),
|
||||
visibility: Value(asset.visibility.toAssetVisibility()),
|
||||
livePhotoVideoId: Value(asset.livePhotoVideoId),
|
||||
stackId: Value(asset.stackId),
|
||||
libraryId: Value(asset.libraryId),
|
||||
width: Value(asset.width),
|
||||
height: Value(asset.height),
|
||||
isEdited: Value(asset.isEdited),
|
||||
);
|
||||
|
||||
batch.insert(
|
||||
_db.remoteAssetEntity,
|
||||
companion.copyWith(id: Value(asset.id)),
|
||||
onConflict: DoUpdate((_) => companion),
|
||||
);
|
||||
}
|
||||
});
|
||||
} catch (error, stack) {
|
||||
_logger.severe('Error: updateAssetsV2 - $debugLabel', error, stack);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateAssetsExifV1(Iterable<SyncAssetExifV1> data, {String debugLabel = 'user'}) async {
|
||||
try {
|
||||
await _db.batch((batch) {
|
||||
@@ -397,6 +435,47 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
||||
}
|
||||
|
||||
Future<void> updateAlbumsV1(Iterable<SyncAlbumV1> data) async {
|
||||
try {
|
||||
await _db.transaction(() async {
|
||||
await _db.batch((batch) {
|
||||
for (final album in data) {
|
||||
final companion = RemoteAlbumEntityCompanion(
|
||||
name: Value(album.name),
|
||||
description: Value(album.description),
|
||||
isActivityEnabled: Value(album.isActivityEnabled),
|
||||
order: Value(album.order.toAlbumAssetOrder()),
|
||||
thumbnailAssetId: Value(album.thumbnailAssetId),
|
||||
createdAt: Value(album.createdAt),
|
||||
updatedAt: Value(album.updatedAt),
|
||||
);
|
||||
|
||||
batch.insert(
|
||||
_db.remoteAlbumEntity,
|
||||
companion.copyWith(id: Value(album.id)),
|
||||
onConflict: DoUpdate((_) => companion),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
await _db.batch((batch) {
|
||||
for (final album in data) {
|
||||
final companion = RemoteAlbumUserEntityCompanion(
|
||||
albumId: Value(album.id),
|
||||
userId: Value(album.ownerId),
|
||||
role: const Value(AlbumUserRole.owner),
|
||||
);
|
||||
|
||||
batch.insert(_db.remoteAlbumUserEntity, companion, onConflict: DoUpdate((_) => companion));
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (error, stack) {
|
||||
_logger.severe('Error: updateAlbumsV1', error, stack);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateAlbumsV2(Iterable<SyncAlbumV2> data) async {
|
||||
try {
|
||||
await _db.batch((batch) {
|
||||
for (final album in data) {
|
||||
@@ -406,7 +485,6 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
||||
isActivityEnabled: Value(album.isActivityEnabled),
|
||||
order: Value(album.order.toAlbumAssetOrder()),
|
||||
thumbnailAssetId: Value(album.thumbnailAssetId),
|
||||
ownerId: Value(album.ownerId),
|
||||
createdAt: Value(album.createdAt),
|
||||
updatedAt: Value(album.updatedAt),
|
||||
);
|
||||
@@ -419,7 +497,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
||||
}
|
||||
});
|
||||
} catch (error, stack) {
|
||||
_logger.severe('Error: updateAlbumsV1', error, stack);
|
||||
_logger.severe('Error: updateAlbumsV2', error, stack);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
@@ -821,6 +899,7 @@ extension on api.AlbumUserRole {
|
||||
AlbumUserRole toAlbumUserRole() => switch (this) {
|
||||
api.AlbumUserRole.editor => AlbumUserRole.editor,
|
||||
api.AlbumUserRole.viewer => AlbumUserRole.viewer,
|
||||
api.AlbumUserRole.owner => AlbumUserRole.owner,
|
||||
_ => throw Exception('Unknown AlbumUserRole value: $this'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/events.model.dart';
|
||||
import 'package:immich_mobile/domain/services/timeline.service.dart';
|
||||
import 'package:immich_mobile/domain/utils/event_stream.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/scroll_extensions.dart';
|
||||
@@ -363,7 +364,8 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
||||
}
|
||||
|
||||
BaseAsset displayAsset = asset;
|
||||
final stackChildren = ref.watch(stackChildrenNotifier(asset)).valueOrNull;
|
||||
final showAssetStack = ref.watch(timelineServiceProvider.select((s) => s.origin != TimelineOrigin.trash));
|
||||
final stackChildren = showAssetStack ? ref.watch(stackChildrenNotifier(asset)).valueOrNull : null;
|
||||
if (stackChildren != null && stackChildren.isNotEmpty) {
|
||||
displayAsset = stackChildren.elementAt(stackIndex);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/services/timeline.service.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||
|
||||
class AssetStackRow extends ConsumerWidget {
|
||||
final List<RemoteAsset> stack;
|
||||
@@ -15,6 +17,11 @@ class AssetStackRow extends ConsumerWidget {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final hideAssetStack = ref.read(timelineServiceProvider).origin == TimelineOrigin.trash;
|
||||
if (hideAssetStack) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final showingControls = ref.watch(assetViewerProvider.select((s) => s.showingControls));
|
||||
double opacity = ref.watch(assetViewerProvider.select((s) => s.backgroundOpacity)) * (showingControls ? 1 : 0);
|
||||
|
||||
|
||||
@@ -2,17 +2,21 @@ import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/map/map.state.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
|
||||
class MapBottomSheet extends StatelessWidget {
|
||||
const MapBottomSheet({super.key});
|
||||
final Key? sheetKey;
|
||||
|
||||
const MapBottomSheet({super.key, this.sheetKey});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BaseBottomSheet(
|
||||
key: sheetKey,
|
||||
initialChildSize: 0.25,
|
||||
maxChildSize: 0.75,
|
||||
shouldCloseOnMinExtent: false,
|
||||
@@ -49,7 +53,7 @@ class _ScopedMapTimeline extends StatelessWidget {
|
||||
return timelineService;
|
||||
}),
|
||||
],
|
||||
child: const Timeline(appBar: null, bottomSheet: null, withScrubber: false),
|
||||
child: const Timeline(appBar: null, bottomSheet: GeneralBottomSheet(minChildSize: 0.23), withScrubber: false),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ class ThumbnailTile extends ConsumerStatefulWidget {
|
||||
this.showStorageIndicator = false,
|
||||
this.lockSelection = false,
|
||||
this.heroOffset,
|
||||
this.showStackIndicator = false,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@@ -30,6 +31,7 @@ class ThumbnailTile extends ConsumerStatefulWidget {
|
||||
final bool showStorageIndicator;
|
||||
final bool lockSelection;
|
||||
final int? heroOffset;
|
||||
final bool showStackIndicator;
|
||||
|
||||
@override
|
||||
ConsumerState<ThumbnailTile> createState() => _ThumbnailTileState();
|
||||
@@ -139,7 +141,14 @@ class _ThumbnailTileState extends ConsumerState<ThumbnailTile> {
|
||||
duration: Durations.short4,
|
||||
child: Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: _AssetTypeIcons(asset: asset),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
_AssetTypeIcons(asset: asset),
|
||||
if (widget.showStackIndicator) _StackIndicator(asset: asset),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (storageIndicator && asset != null)
|
||||
@@ -286,8 +295,8 @@ class _AssetTypeIcons extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final hasStack = asset is RemoteAsset && (asset as RemoteAsset).stackId != null;
|
||||
final isLivePhoto = asset is RemoteAsset && asset.livePhotoVideoId != null;
|
||||
final remoteAsset = asset is RemoteAsset ? asset as RemoteAsset : null;
|
||||
final isLivePhoto = remoteAsset?.livePhotoVideoId != null;
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -295,11 +304,6 @@ class _AssetTypeIcons extends StatelessWidget {
|
||||
children: [
|
||||
if (asset.isVideo)
|
||||
Padding(padding: const EdgeInsets.only(right: 10.0, top: 6.0), child: _VideoIndicator(asset.duration)),
|
||||
if (hasStack)
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(right: 10.0, top: 6.0),
|
||||
child: _TileOverlayIcon(Icons.burst_mode_rounded),
|
||||
),
|
||||
if (isLivePhoto)
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(right: 10.0, top: 6.0),
|
||||
@@ -312,6 +316,24 @@ class _AssetTypeIcons extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _StackIndicator extends StatelessWidget {
|
||||
final BaseAsset asset;
|
||||
|
||||
const _StackIndicator({required this.asset});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (asset is! RemoteAsset || (asset as RemoteAsset).stackId == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return const Padding(
|
||||
padding: EdgeInsets.only(right: 10.0, top: 6.0),
|
||||
child: _TileOverlayIcon(Icons.burst_mode_rounded),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _UploadProgressOverlay extends StatelessWidget {
|
||||
final double progress;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import 'package:immich_mobile/domain/utils/event_stream.dart';
|
||||
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/map/map.state.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/map/map_utils.dart';
|
||||
@@ -53,6 +54,7 @@ class _DriftMapState extends ConsumerState<DriftMap> {
|
||||
final _reloadMutex = AsyncMutex();
|
||||
final _debouncer = Debouncer(interval: const Duration(milliseconds: 500), maxWaitTime: const Duration(seconds: 2));
|
||||
final ValueNotifier<double> bottomSheetOffset = ValueNotifier(0.25);
|
||||
final GlobalKey _bottomSheetKey = GlobalKey();
|
||||
StreamSubscription? _eventSubscription;
|
||||
|
||||
@override
|
||||
@@ -184,7 +186,7 @@ class _DriftMapState extends ConsumerState<DriftMap> {
|
||||
return Stack(
|
||||
children: [
|
||||
_Map(initialLocation: widget.initialLocation, onMapCreated: onMapCreated, onMapReady: onMapReady),
|
||||
_DynamicBottomSheet(bottomSheetOffset: bottomSheetOffset),
|
||||
_DynamicBottomSheet(bottomSheetOffset: bottomSheetOffset, sheetKey: _bottomSheetKey),
|
||||
_DynamicMyLocationButton(onZoomToLocation: onZoomToLocation, bottomSheetOffset: bottomSheetOffset),
|
||||
],
|
||||
);
|
||||
@@ -224,8 +226,9 @@ class _Map extends StatelessWidget {
|
||||
|
||||
class _DynamicBottomSheet extends StatefulWidget {
|
||||
final ValueNotifier<double> bottomSheetOffset;
|
||||
final GlobalKey sheetKey;
|
||||
|
||||
const _DynamicBottomSheet({required this.bottomSheetOffset});
|
||||
const _DynamicBottomSheet({required this.bottomSheetOffset, required this.sheetKey});
|
||||
|
||||
@override
|
||||
State<_DynamicBottomSheet> createState() => _DynamicBottomSheetState();
|
||||
@@ -236,10 +239,13 @@ class _DynamicBottomSheetState extends State<_DynamicBottomSheet> {
|
||||
Widget build(BuildContext context) {
|
||||
return NotificationListener<DraggableScrollableNotification>(
|
||||
onNotification: (notification) {
|
||||
widget.bottomSheetOffset.value = notification.extent;
|
||||
return true;
|
||||
final sheet = notification.context.findAncestorWidgetOfExactType<BaseBottomSheet>();
|
||||
if (sheet?.key == widget.sheetKey) {
|
||||
widget.bottomSheetOffset.value = notification.extent;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
child: const MapBottomSheet(),
|
||||
child: MapBottomSheet(sheetKey: widget.sheetKey),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,7 @@ class DriftMemoryBottomInfo extends StatelessWidget {
|
||||
child: MaterialButton(
|
||||
minWidth: 0,
|
||||
onPressed: () async {
|
||||
await context.maybePop();
|
||||
await context.navigateTo(const TabShellRoute(children: [MainTimelineRoute()]));
|
||||
await context.router.navigate(const TabShellRoute(children: [MainTimelineRoute()]));
|
||||
EventStream.shared.emit(ScrollToDateEvent(fileCreatedDate));
|
||||
},
|
||||
shape: const CircleBorder(),
|
||||
|
||||
@@ -244,6 +244,7 @@ class _AssetTileWidget extends ConsumerWidget {
|
||||
final lockSelection = _getLockSelectionStatus(ref);
|
||||
final showStorageIndicator = ref.watch(timelineArgsProvider.select((args) => args.showStorageIndicator));
|
||||
final isReadonlyModeEnabled = ref.watch(readonlyModeProvider);
|
||||
final showStackIndicator = ref.read(timelineServiceProvider).origin != TimelineOrigin.trash;
|
||||
|
||||
return RepaintBoundary(
|
||||
child: GestureDetector(
|
||||
@@ -253,6 +254,7 @@ class _AssetTileWidget extends ConsumerWidget {
|
||||
asset,
|
||||
lockSelection: lockSelection,
|
||||
showStorageIndicator: showStorageIndicator,
|
||||
showStackIndicator: showStackIndicator,
|
||||
heroOffset: heroOffset,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -469,6 +469,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
||||
ref.read(timelineStateProvider.notifier).setScrolling(true);
|
||||
},
|
||||
child: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
timeline,
|
||||
if (isBottomWidgetVisible)
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:immich_mobile/domain/services/remote_album.service.dart';
|
||||
import 'package:immich_mobile/models/albums/album_search.model.dart';
|
||||
import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
||||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
class RemoteAlbumState {
|
||||
@@ -81,7 +82,17 @@ class RemoteAlbumNotifier extends Notifier<RemoteAlbumState> {
|
||||
List<String> assetIds = const [],
|
||||
}) async {
|
||||
try {
|
||||
final album = await _remoteAlbumService.createAlbum(title: title, description: description, assetIds: assetIds);
|
||||
final currentUser = ref.read(currentUserProvider);
|
||||
if (currentUser == null) {
|
||||
throw Exception('User not logged in');
|
||||
}
|
||||
|
||||
final album = await _remoteAlbumService.createAlbum(
|
||||
title: title,
|
||||
owner: currentUser,
|
||||
description: description,
|
||||
assetIds: assetIds,
|
||||
);
|
||||
|
||||
state = state.copyWith(albums: [...state.albums, album]);
|
||||
|
||||
|
||||
@@ -94,8 +94,10 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
|
||||
state = const WebsocketState(isConnected: false, socket: null);
|
||||
});
|
||||
|
||||
socket.on('AssetUploadReadyV1', _handleSyncAssetUploadReady);
|
||||
socket.on('AssetEditReadyV1', _handleSyncAssetEditReady);
|
||||
socket.on('AssetUploadReadyV1', _handleSyncAssetUploadReadyV1);
|
||||
socket.on('AssetUploadReadyV2', _handleSyncAssetUploadReadyV2);
|
||||
socket.on('AssetEditReadyV1', _handleSyncAssetEditReadyV1);
|
||||
socket.on('AssetEditReadyV2', _handleSyncAssetEditReadyV2);
|
||||
socket.on('on_config_update', _handleOnConfigUpdate);
|
||||
socket.on('on_new_release', _handleReleaseUpdates);
|
||||
} catch (e) {
|
||||
@@ -163,16 +165,25 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
|
||||
_ref.read(serverInfoProvider.notifier).handleReleaseInfo(serverVersion, releaseVersion);
|
||||
}
|
||||
|
||||
void _handleSyncAssetUploadReady(dynamic data) {
|
||||
void _handleSyncAssetUploadReadyV1(dynamic data) {
|
||||
_batchedAssetUploadReady.add(data);
|
||||
_batchDebouncer.run(_processBatchedAssetUploadReady);
|
||||
_batchDebouncer.run(_processBatchedAssetUploadReadyV1);
|
||||
}
|
||||
|
||||
void _handleSyncAssetEditReady(dynamic data) {
|
||||
unawaited(_ref.read(backgroundSyncProvider).syncWebsocketEdit(data));
|
||||
void _handleSyncAssetUploadReadyV2(dynamic data) {
|
||||
_batchedAssetUploadReady.add(data);
|
||||
_batchDebouncer.run(_processBatchedAssetUploadReadyV2);
|
||||
}
|
||||
|
||||
void _processBatchedAssetUploadReady() {
|
||||
void _handleSyncAssetEditReadyV1(dynamic data) {
|
||||
unawaited(_ref.read(backgroundSyncProvider).syncWebsocketEditV1(data));
|
||||
}
|
||||
|
||||
void _handleSyncAssetEditReadyV2(dynamic data) {
|
||||
unawaited(_ref.read(backgroundSyncProvider).syncWebsocketEditV2(data));
|
||||
}
|
||||
|
||||
void _processBatchedAssetUploadReadyV1() {
|
||||
if (_batchedAssetUploadReady.isEmpty) {
|
||||
return;
|
||||
}
|
||||
@@ -180,7 +191,7 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
|
||||
final isSyncAlbumEnabled = Store.get(StoreKey.syncAlbums, false);
|
||||
try {
|
||||
unawaited(
|
||||
_ref.read(backgroundSyncProvider).syncWebsocketBatch(_batchedAssetUploadReady.toList()).then((_) {
|
||||
_ref.read(backgroundSyncProvider).syncWebsocketBatchV1(_batchedAssetUploadReady.toList()).then((_) {
|
||||
if (isSyncAlbumEnabled) {
|
||||
_ref.read(backgroundSyncProvider).syncLinkedAlbum();
|
||||
}
|
||||
@@ -192,6 +203,27 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
|
||||
|
||||
_batchedAssetUploadReady.clear();
|
||||
}
|
||||
|
||||
void _processBatchedAssetUploadReadyV2() {
|
||||
if (_batchedAssetUploadReady.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
final isSyncAlbumEnabled = Store.get(StoreKey.syncAlbums, false);
|
||||
try {
|
||||
unawaited(
|
||||
_ref.read(backgroundSyncProvider).syncWebsocketBatchV2(_batchedAssetUploadReady.toList()).then((_) {
|
||||
if (isSyncAlbumEnabled) {
|
||||
_ref.read(backgroundSyncProvider).syncLinkedAlbum();
|
||||
}
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
_log.severe("Error processing batched AssetUploadReadyV2 events: $error");
|
||||
}
|
||||
|
||||
_batchedAssetUploadReady.clear();
|
||||
}
|
||||
}
|
||||
|
||||
final websocketProvider = StateNotifierProvider<WebsocketNotifier, WebsocketState>((ref) {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/repositories/api.repository.dart';
|
||||
// ignore: import_rule_openapi
|
||||
import 'package:openapi/api.dart';
|
||||
import 'package:openapi/api.dart' hide AlbumUserRole;
|
||||
|
||||
final driftAlbumApiRepositoryProvider = Provider(
|
||||
(ref) => DriftAlbumApiRepository(ref.watch(apiServiceProvider).albumsApi),
|
||||
@@ -14,12 +15,17 @@ class DriftAlbumApiRepository extends ApiRepository {
|
||||
|
||||
DriftAlbumApiRepository(this._api);
|
||||
|
||||
Future<RemoteAlbum> createDriftAlbum(String name, {required Iterable<String> assetIds, String? description}) async {
|
||||
Future<RemoteAlbum> createDriftAlbum(
|
||||
String name,
|
||||
UserDto owner, {
|
||||
required Iterable<String> assetIds,
|
||||
String? description,
|
||||
}) async {
|
||||
final responseDto = await checkNull(
|
||||
_api.createAlbum(CreateAlbumDto(albumName: name, description: description, assetIds: assetIds.toList())),
|
||||
);
|
||||
|
||||
return responseDto.toRemoteAlbum();
|
||||
return responseDto.toRemoteAlbum(owner);
|
||||
}
|
||||
|
||||
Future<({List<String> removed, List<String> failed})> removeAssets(String albumId, Iterable<String> assetIds) async {
|
||||
@@ -50,7 +56,8 @@ class DriftAlbumApiRepository extends ApiRepository {
|
||||
}
|
||||
|
||||
Future<RemoteAlbum> updateAlbum(
|
||||
String albumId, {
|
||||
String albumId,
|
||||
UserDto owner, {
|
||||
String? name,
|
||||
String? description,
|
||||
String? thumbnailAssetId,
|
||||
@@ -75,17 +82,16 @@ class DriftAlbumApiRepository extends ApiRepository {
|
||||
),
|
||||
);
|
||||
|
||||
return responseDto.toRemoteAlbum();
|
||||
return responseDto.toRemoteAlbum(owner);
|
||||
}
|
||||
|
||||
Future<void> deleteAlbum(String albumId) {
|
||||
return _api.deleteAlbum(albumId);
|
||||
}
|
||||
|
||||
Future<RemoteAlbum> addUsers(String albumId, Iterable<String> userIds) async {
|
||||
Future<void> addUsers(String albumId, Iterable<String> userIds) async {
|
||||
final albumUsers = userIds.map((userId) => AlbumUserAddDto(userId: userId)).toList();
|
||||
final response = await checkNull(_api.addUsersToAlbum(albumId, AddUsersDto(albumUsers: albumUsers)));
|
||||
return response.toRemoteAlbum();
|
||||
await checkNull(_api.addUsersToAlbum(albumId, AddUsersDto(albumUsers: albumUsers)));
|
||||
}
|
||||
|
||||
Future<void> removeUser(String albumId, {required String userId}) async {
|
||||
@@ -99,11 +105,12 @@ class DriftAlbumApiRepository extends ApiRepository {
|
||||
}
|
||||
|
||||
extension on AlbumResponseDto {
|
||||
RemoteAlbum toRemoteAlbum() {
|
||||
RemoteAlbum toRemoteAlbum(final UserDto user) {
|
||||
return RemoteAlbum(
|
||||
id: id,
|
||||
name: albumName,
|
||||
ownerId: owner.id,
|
||||
ownerId: user.id,
|
||||
ownerName: user.name,
|
||||
description: description,
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
@@ -111,7 +118,6 @@ extension on AlbumResponseDto {
|
||||
isActivityEnabled: isActivityEnabled,
|
||||
order: order == AssetOrder.asc ? AlbumAssetOrder.asc : AlbumAssetOrder.desc,
|
||||
assetCount: assetCount,
|
||||
ownerName: owner.name,
|
||||
isShared: albumUsers.length > 2,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -241,7 +241,8 @@ class ActionService {
|
||||
}
|
||||
|
||||
Future<bool> setAlbumCover(String albumId, String assetId) async {
|
||||
final updatedAlbum = await _albumApiRepository.updateAlbum(albumId, thumbnailAssetId: assetId);
|
||||
final owner = await _remoteAlbumRepository.getOwner(albumId);
|
||||
final updatedAlbum = await _albumApiRepository.updateAlbum(albumId, owner, thumbnailAssetId: assetId);
|
||||
await _remoteAlbumRepository.update(updatedAlbum);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -148,6 +148,7 @@ enum ActionButtonType {
|
||||
context.selectedCount == 1,
|
||||
ActionButtonType.unstack =>
|
||||
context.isOwner && //
|
||||
context.timelineOrigin != TimelineOrigin.trash &&
|
||||
!context.isInLockedView && //
|
||||
context.isStacked,
|
||||
ActionButtonType.openInBrowser => context.asset.hasRemote && !context.isInLockedView,
|
||||
@@ -271,8 +272,7 @@ enum ActionButtonType {
|
||||
onPressed: buildContext == null
|
||||
? null
|
||||
: () async {
|
||||
await buildContext.maybePop();
|
||||
await buildContext.navigateTo(const TabShellRoute(children: [MainTimelineRoute()]));
|
||||
await buildContext.router.navigate(const TabShellRoute(children: [MainTimelineRoute()]));
|
||||
EventStream.shared.emit(ScrollToDateEvent(context.asset.createdAt));
|
||||
},
|
||||
),
|
||||
|
||||
2
mobile/openapi/README.md
generated
2
mobile/openapi/README.md
generated
@@ -568,6 +568,7 @@ Class | Method | HTTP request | Description
|
||||
- [SyncAlbumUserDeleteV1](doc//SyncAlbumUserDeleteV1.md)
|
||||
- [SyncAlbumUserV1](doc//SyncAlbumUserV1.md)
|
||||
- [SyncAlbumV1](doc//SyncAlbumV1.md)
|
||||
- [SyncAlbumV2](doc//SyncAlbumV2.md)
|
||||
- [SyncAssetDeleteV1](doc//SyncAssetDeleteV1.md)
|
||||
- [SyncAssetEditDeleteV1](doc//SyncAssetEditDeleteV1.md)
|
||||
- [SyncAssetEditV1](doc//SyncAssetEditV1.md)
|
||||
@@ -578,6 +579,7 @@ Class | Method | HTTP request | Description
|
||||
- [SyncAssetMetadataDeleteV1](doc//SyncAssetMetadataDeleteV1.md)
|
||||
- [SyncAssetMetadataV1](doc//SyncAssetMetadataV1.md)
|
||||
- [SyncAssetV1](doc//SyncAssetV1.md)
|
||||
- [SyncAssetV2](doc//SyncAssetV2.md)
|
||||
- [SyncAuthUserV1](doc//SyncAuthUserV1.md)
|
||||
- [SyncEntityType](doc//SyncEntityType.md)
|
||||
- [SyncMemoryAssetDeleteV1](doc//SyncMemoryAssetDeleteV1.md)
|
||||
|
||||
2
mobile/openapi/lib/api.dart
generated
2
mobile/openapi/lib/api.dart
generated
@@ -316,6 +316,7 @@ part 'model/sync_album_to_asset_v1.dart';
|
||||
part 'model/sync_album_user_delete_v1.dart';
|
||||
part 'model/sync_album_user_v1.dart';
|
||||
part 'model/sync_album_v1.dart';
|
||||
part 'model/sync_album_v2.dart';
|
||||
part 'model/sync_asset_delete_v1.dart';
|
||||
part 'model/sync_asset_edit_delete_v1.dart';
|
||||
part 'model/sync_asset_edit_v1.dart';
|
||||
@@ -326,6 +327,7 @@ part 'model/sync_asset_face_v2.dart';
|
||||
part 'model/sync_asset_metadata_delete_v1.dart';
|
||||
part 'model/sync_asset_metadata_v1.dart';
|
||||
part 'model/sync_asset_v1.dart';
|
||||
part 'model/sync_asset_v2.dart';
|
||||
part 'model/sync_auth_user_v1.dart';
|
||||
part 'model/sync_entity_type.dart';
|
||||
part 'model/sync_memory_asset_delete_v1.dart';
|
||||
|
||||
12
mobile/openapi/lib/api/assets_api.dart
generated
12
mobile/openapi/lib/api/assets_api.dart
generated
@@ -1234,8 +1234,8 @@ class AssetsApi {
|
||||
/// * [String] xImmichChecksum:
|
||||
/// sha1 checksum that can be used for duplicate detection before the file is uploaded
|
||||
///
|
||||
/// * [String] duration:
|
||||
/// Duration (for videos)
|
||||
/// * [int] duration:
|
||||
/// Duration in milliseconds (for videos)
|
||||
///
|
||||
/// * [String] filename:
|
||||
/// Filename
|
||||
@@ -1253,7 +1253,7 @@ class AssetsApi {
|
||||
/// Sidecar file data
|
||||
///
|
||||
/// * [AssetVisibility] visibility:
|
||||
Future<Response> uploadAssetWithHttpInfo(MultipartFile assetData, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, List<AssetMetadataUpsertItemDto>? metadata, MultipartFile? sidecarData, AssetVisibility? visibility, }) async {
|
||||
Future<Response> uploadAssetWithHttpInfo(MultipartFile assetData, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, int? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, List<AssetMetadataUpsertItemDto>? metadata, MultipartFile? sidecarData, AssetVisibility? visibility, }) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final apiPath = r'/assets';
|
||||
|
||||
@@ -1358,8 +1358,8 @@ class AssetsApi {
|
||||
/// * [String] xImmichChecksum:
|
||||
/// sha1 checksum that can be used for duplicate detection before the file is uploaded
|
||||
///
|
||||
/// * [String] duration:
|
||||
/// Duration (for videos)
|
||||
/// * [int] duration:
|
||||
/// Duration in milliseconds (for videos)
|
||||
///
|
||||
/// * [String] filename:
|
||||
/// Filename
|
||||
@@ -1377,7 +1377,7 @@ class AssetsApi {
|
||||
/// Sidecar file data
|
||||
///
|
||||
/// * [AssetVisibility] visibility:
|
||||
Future<AssetMediaResponseDto?> uploadAsset(MultipartFile assetData, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, List<AssetMetadataUpsertItemDto>? metadata, MultipartFile? sidecarData, AssetVisibility? visibility, }) async {
|
||||
Future<AssetMediaResponseDto?> uploadAsset(MultipartFile assetData, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, int? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, List<AssetMetadataUpsertItemDto>? metadata, MultipartFile? sidecarData, AssetVisibility? visibility, }) async {
|
||||
final response = await uploadAssetWithHttpInfo(assetData, fileCreatedAt, fileModifiedAt, key: key, slug: slug, xImmichChecksum: xImmichChecksum, duration: duration, filename: filename, isFavorite: isFavorite, livePhotoVideoId: livePhotoVideoId, metadata: metadata, sidecarData: sidecarData, visibility: visibility, );
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
|
||||
12
mobile/openapi/lib/api/people_api.dart
generated
12
mobile/openapi/lib/api/people_api.dart
generated
@@ -183,15 +183,15 @@ class PeopleApi {
|
||||
/// * [String] closestPersonId:
|
||||
/// Closest person ID for similarity search
|
||||
///
|
||||
/// * [num] page:
|
||||
/// * [int] page:
|
||||
/// Page number for pagination
|
||||
///
|
||||
/// * [num] size:
|
||||
/// * [int] size:
|
||||
/// Number of items per page
|
||||
///
|
||||
/// * [bool] withHidden:
|
||||
/// Include hidden people
|
||||
Future<Response> getAllPeopleWithHttpInfo({ String? closestAssetId, String? closestPersonId, num? page, num? size, bool? withHidden, }) async {
|
||||
Future<Response> getAllPeopleWithHttpInfo({ String? closestAssetId, String? closestPersonId, int? page, int? size, bool? withHidden, }) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final apiPath = r'/people';
|
||||
|
||||
@@ -244,15 +244,15 @@ class PeopleApi {
|
||||
/// * [String] closestPersonId:
|
||||
/// Closest person ID for similarity search
|
||||
///
|
||||
/// * [num] page:
|
||||
/// * [int] page:
|
||||
/// Page number for pagination
|
||||
///
|
||||
/// * [num] size:
|
||||
/// * [int] size:
|
||||
/// Number of items per page
|
||||
///
|
||||
/// * [bool] withHidden:
|
||||
/// Include hidden people
|
||||
Future<PeopleResponseDto?> getAllPeople({ String? closestAssetId, String? closestPersonId, num? page, num? size, bool? withHidden, }) async {
|
||||
Future<PeopleResponseDto?> getAllPeople({ String? closestAssetId, String? closestPersonId, int? page, int? size, bool? withHidden, }) async {
|
||||
final response = await getAllPeopleWithHttpInfo( closestAssetId: closestAssetId, closestPersonId: closestPersonId, page: page, size: size, withHidden: withHidden, );
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
|
||||
12
mobile/openapi/lib/api/search_api.dart
generated
12
mobile/openapi/lib/api/search_api.dart
generated
@@ -404,10 +404,10 @@ class SearchApi {
|
||||
/// * [List<String>] personIds:
|
||||
/// Filter by person IDs
|
||||
///
|
||||
/// * [num] rating:
|
||||
/// * [int] rating:
|
||||
/// Filter by rating [1-5], or null for unrated
|
||||
///
|
||||
/// * [num] size:
|
||||
/// * [int] size:
|
||||
/// Number of results to return
|
||||
///
|
||||
/// * [String] state:
|
||||
@@ -443,7 +443,7 @@ class SearchApi {
|
||||
///
|
||||
/// * [bool] withExif:
|
||||
/// Include EXIF data in response
|
||||
Future<Response> searchLargeAssetsWithHttpInfo({ List<String>? albumIds, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, bool? isEncoded, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, String? lensModel, String? libraryId, String? make, int? minFileSize, String? model, String? ocr, List<String>? personIds, num? rating, num? size, String? state, List<String>? tagIds, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, AssetVisibility? visibility, bool? withDeleted, bool? withExif, }) async {
|
||||
Future<Response> searchLargeAssetsWithHttpInfo({ List<String>? albumIds, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, bool? isEncoded, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, String? lensModel, String? libraryId, String? make, int? minFileSize, String? model, String? ocr, List<String>? personIds, int? rating, int? size, String? state, List<String>? tagIds, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, AssetVisibility? visibility, bool? withDeleted, bool? withExif, }) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final apiPath = r'/search/large-assets';
|
||||
|
||||
@@ -619,10 +619,10 @@ class SearchApi {
|
||||
/// * [List<String>] personIds:
|
||||
/// Filter by person IDs
|
||||
///
|
||||
/// * [num] rating:
|
||||
/// * [int] rating:
|
||||
/// Filter by rating [1-5], or null for unrated
|
||||
///
|
||||
/// * [num] size:
|
||||
/// * [int] size:
|
||||
/// Number of results to return
|
||||
///
|
||||
/// * [String] state:
|
||||
@@ -658,7 +658,7 @@ class SearchApi {
|
||||
///
|
||||
/// * [bool] withExif:
|
||||
/// Include EXIF data in response
|
||||
Future<List<AssetResponseDto>?> searchLargeAssets({ List<String>? albumIds, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, bool? isEncoded, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, String? lensModel, String? libraryId, String? make, int? minFileSize, String? model, String? ocr, List<String>? personIds, num? rating, num? size, String? state, List<String>? tagIds, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, AssetVisibility? visibility, bool? withDeleted, bool? withExif, }) async {
|
||||
Future<List<AssetResponseDto>?> searchLargeAssets({ List<String>? albumIds, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, bool? isEncoded, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, String? lensModel, String? libraryId, String? make, int? minFileSize, String? model, String? ocr, List<String>? personIds, int? rating, int? size, String? state, List<String>? tagIds, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, AssetVisibility? visibility, bool? withDeleted, bool? withExif, }) async {
|
||||
final response = await searchLargeAssetsWithHttpInfo( albumIds: albumIds, city: city, country: country, createdAfter: createdAfter, createdBefore: createdBefore, isEncoded: isEncoded, isFavorite: isFavorite, isMotion: isMotion, isNotInAlbum: isNotInAlbum, isOffline: isOffline, lensModel: lensModel, libraryId: libraryId, make: make, minFileSize: minFileSize, model: model, ocr: ocr, personIds: personIds, rating: rating, size: size, state: state, tagIds: tagIds, takenAfter: takenAfter, takenBefore: takenBefore, trashedAfter: trashedAfter, trashedBefore: trashedBefore, type: type, updatedAfter: updatedAfter, updatedBefore: updatedBefore, visibility: visibility, withDeleted: withDeleted, withExif: withExif, );
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
|
||||
4
mobile/openapi/lib/api_client.dart
generated
4
mobile/openapi/lib/api_client.dart
generated
@@ -678,6 +678,8 @@ class ApiClient {
|
||||
return SyncAlbumUserV1.fromJson(value);
|
||||
case 'SyncAlbumV1':
|
||||
return SyncAlbumV1.fromJson(value);
|
||||
case 'SyncAlbumV2':
|
||||
return SyncAlbumV2.fromJson(value);
|
||||
case 'SyncAssetDeleteV1':
|
||||
return SyncAssetDeleteV1.fromJson(value);
|
||||
case 'SyncAssetEditDeleteV1':
|
||||
@@ -698,6 +700,8 @@ class ApiClient {
|
||||
return SyncAssetMetadataV1.fromJson(value);
|
||||
case 'SyncAssetV1':
|
||||
return SyncAssetV1.fromJson(value);
|
||||
case 'SyncAssetV2':
|
||||
return SyncAssetV2.fromJson(value);
|
||||
case 'SyncAuthUserV1':
|
||||
return SyncAuthUserV1.fromJson(value);
|
||||
case 'SyncEntityType':
|
||||
|
||||
20
mobile/openapi/lib/model/album_response_dto.dart
generated
20
mobile/openapi/lib/model/album_response_dto.dart
generated
@@ -26,8 +26,6 @@ class AlbumResponseDto {
|
||||
required this.isActivityEnabled,
|
||||
this.lastModifiedAssetTimestamp,
|
||||
this.order,
|
||||
required this.owner,
|
||||
required this.ownerId,
|
||||
required this.shared,
|
||||
this.startDate,
|
||||
required this.updatedAt,
|
||||
@@ -39,6 +37,7 @@ class AlbumResponseDto {
|
||||
/// Thumbnail asset ID
|
||||
String? albumThumbnailAssetId;
|
||||
|
||||
/// First entry is always the album owner. Second entry is the auth user, if it differs from the owner. The rest are ordered alphabetically.
|
||||
List<AlbumUserResponseDto> albumUsers;
|
||||
|
||||
/// Number of assets
|
||||
@@ -90,11 +89,6 @@ class AlbumResponseDto {
|
||||
///
|
||||
AssetOrder? order;
|
||||
|
||||
UserResponseDto owner;
|
||||
|
||||
/// Owner user ID
|
||||
String ownerId;
|
||||
|
||||
/// Is shared album
|
||||
bool shared;
|
||||
|
||||
@@ -125,8 +119,6 @@ class AlbumResponseDto {
|
||||
other.isActivityEnabled == isActivityEnabled &&
|
||||
other.lastModifiedAssetTimestamp == lastModifiedAssetTimestamp &&
|
||||
other.order == order &&
|
||||
other.owner == owner &&
|
||||
other.ownerId == ownerId &&
|
||||
other.shared == shared &&
|
||||
other.startDate == startDate &&
|
||||
other.updatedAt == updatedAt;
|
||||
@@ -147,14 +139,12 @@ class AlbumResponseDto {
|
||||
(isActivityEnabled.hashCode) +
|
||||
(lastModifiedAssetTimestamp == null ? 0 : lastModifiedAssetTimestamp!.hashCode) +
|
||||
(order == null ? 0 : order!.hashCode) +
|
||||
(owner.hashCode) +
|
||||
(ownerId.hashCode) +
|
||||
(shared.hashCode) +
|
||||
(startDate == null ? 0 : startDate!.hashCode) +
|
||||
(updatedAt.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'AlbumResponseDto[albumName=$albumName, albumThumbnailAssetId=$albumThumbnailAssetId, albumUsers=$albumUsers, assetCount=$assetCount, contributorCounts=$contributorCounts, createdAt=$createdAt, description=$description, endDate=$endDate, hasSharedLink=$hasSharedLink, id=$id, isActivityEnabled=$isActivityEnabled, lastModifiedAssetTimestamp=$lastModifiedAssetTimestamp, order=$order, owner=$owner, ownerId=$ownerId, shared=$shared, startDate=$startDate, updatedAt=$updatedAt]';
|
||||
String toString() => 'AlbumResponseDto[albumName=$albumName, albumThumbnailAssetId=$albumThumbnailAssetId, albumUsers=$albumUsers, assetCount=$assetCount, contributorCounts=$contributorCounts, createdAt=$createdAt, description=$description, endDate=$endDate, hasSharedLink=$hasSharedLink, id=$id, isActivityEnabled=$isActivityEnabled, lastModifiedAssetTimestamp=$lastModifiedAssetTimestamp, order=$order, shared=$shared, startDate=$startDate, updatedAt=$updatedAt]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
@@ -187,8 +177,6 @@ class AlbumResponseDto {
|
||||
} else {
|
||||
// json[r'order'] = null;
|
||||
}
|
||||
json[r'owner'] = this.owner;
|
||||
json[r'ownerId'] = this.ownerId;
|
||||
json[r'shared'] = this.shared;
|
||||
if (this.startDate != null) {
|
||||
json[r'startDate'] = this.startDate!.toUtc().toIso8601String();
|
||||
@@ -221,8 +209,6 @@ class AlbumResponseDto {
|
||||
isActivityEnabled: mapValueOfType<bool>(json, r'isActivityEnabled')!,
|
||||
lastModifiedAssetTimestamp: mapDateTime(json, r'lastModifiedAssetTimestamp', r''),
|
||||
order: AssetOrder.fromJson(json[r'order']),
|
||||
owner: UserResponseDto.fromJson(json[r'owner'])!,
|
||||
ownerId: mapValueOfType<String>(json, r'ownerId')!,
|
||||
shared: mapValueOfType<bool>(json, r'shared')!,
|
||||
startDate: mapDateTime(json, r'startDate', r''),
|
||||
updatedAt: mapDateTime(json, r'updatedAt', r'')!,
|
||||
@@ -282,8 +268,6 @@ class AlbumResponseDto {
|
||||
'hasSharedLink',
|
||||
'id',
|
||||
'isActivityEnabled',
|
||||
'owner',
|
||||
'ownerId',
|
||||
'shared',
|
||||
'updatedAt',
|
||||
};
|
||||
|
||||
3
mobile/openapi/lib/model/album_user_role.dart
generated
3
mobile/openapi/lib/model/album_user_role.dart
generated
@@ -24,11 +24,13 @@ class AlbumUserRole {
|
||||
String toJson() => value;
|
||||
|
||||
static const editor = AlbumUserRole._(r'editor');
|
||||
static const owner = AlbumUserRole._(r'owner');
|
||||
static const viewer = AlbumUserRole._(r'viewer');
|
||||
|
||||
/// List of all possible values in this [enum][AlbumUserRole].
|
||||
static const values = <AlbumUserRole>[
|
||||
editor,
|
||||
owner,
|
||||
viewer,
|
||||
];
|
||||
|
||||
@@ -69,6 +71,7 @@ class AlbumUserRoleTypeTransformer {
|
||||
if (data != null) {
|
||||
switch (data) {
|
||||
case r'editor': return AlbumUserRole.editor;
|
||||
case r'owner': return AlbumUserRole.owner;
|
||||
case r'viewer': return AlbumUserRole.viewer;
|
||||
default:
|
||||
if (!allowNull) {
|
||||
|
||||
@@ -37,12 +37,15 @@ class AssetBulkUpdateDto {
|
||||
|
||||
/// Relative time offset in seconds
|
||||
///
|
||||
/// Minimum value: -9007199254740991
|
||||
/// Maximum value: 9007199254740991
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
num? dateTimeRelative;
|
||||
int? dateTimeRelative;
|
||||
|
||||
/// Asset description
|
||||
///
|
||||
@@ -213,7 +216,7 @@ class AssetBulkUpdateDto {
|
||||
|
||||
return AssetBulkUpdateDto(
|
||||
dateTimeOriginal: mapValueOfType<String>(json, r'dateTimeOriginal'),
|
||||
dateTimeRelative: num.parse('${json[r'dateTimeRelative']}'),
|
||||
dateTimeRelative: mapValueOfType<int>(json, r'dateTimeRelative'),
|
||||
description: mapValueOfType<String>(json, r'description'),
|
||||
duplicateId: mapValueOfType<String>(json, r'duplicateId'),
|
||||
ids: json[r'ids'] is Iterable
|
||||
|
||||
@@ -24,22 +24,26 @@ class AssetEditActionItemDtoParameters {
|
||||
/// Height of the crop
|
||||
///
|
||||
/// Minimum value: 1
|
||||
num height;
|
||||
/// Maximum value: 9007199254740991
|
||||
int height;
|
||||
|
||||
/// Width of the crop
|
||||
///
|
||||
/// Minimum value: 1
|
||||
num width;
|
||||
/// Maximum value: 9007199254740991
|
||||
int width;
|
||||
|
||||
/// Top-Left X coordinate of crop
|
||||
///
|
||||
/// Minimum value: 0
|
||||
num x;
|
||||
/// Maximum value: 9007199254740991
|
||||
int x;
|
||||
|
||||
/// Top-Left Y coordinate of crop
|
||||
///
|
||||
/// Minimum value: 0
|
||||
num y;
|
||||
/// Maximum value: 9007199254740991
|
||||
int y;
|
||||
|
||||
/// Rotation angle in degrees
|
||||
num angle;
|
||||
@@ -88,10 +92,10 @@ class AssetEditActionItemDtoParameters {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return AssetEditActionItemDtoParameters(
|
||||
height: num.parse('${json[r'height']}'),
|
||||
width: num.parse('${json[r'width']}'),
|
||||
x: num.parse('${json[r'x']}'),
|
||||
y: num.parse('${json[r'y']}'),
|
||||
height: mapValueOfType<int>(json, r'height')!,
|
||||
width: mapValueOfType<int>(json, r'width')!,
|
||||
x: mapValueOfType<int>(json, r'x')!,
|
||||
y: mapValueOfType<int>(json, r'y')!,
|
||||
angle: num.parse('${json[r'angle']}'),
|
||||
axis: MirrorAxis.fromJson(json[r'axis'])!,
|
||||
);
|
||||
|
||||
23
mobile/openapi/lib/model/asset_response_dto.dart
generated
23
mobile/openapi/lib/model/asset_response_dto.dart
generated
@@ -57,8 +57,11 @@ class AssetResponseDto {
|
||||
/// Duplicate group ID
|
||||
String? duplicateId;
|
||||
|
||||
/// Video/gif duration in hh:mm:ss.SSS format (null for static images)
|
||||
String? duration;
|
||||
/// Video/gif duration in milliseconds (null for static images)
|
||||
///
|
||||
/// Minimum value: 0
|
||||
/// Maximum value: 2147483647
|
||||
int? duration;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
@@ -80,7 +83,8 @@ class AssetResponseDto {
|
||||
/// Asset height
|
||||
///
|
||||
/// Minimum value: 0
|
||||
num? height;
|
||||
/// Maximum value: 9007199254740991
|
||||
int? height;
|
||||
|
||||
/// Asset ID
|
||||
String id;
|
||||
@@ -165,7 +169,8 @@ class AssetResponseDto {
|
||||
/// Asset width
|
||||
///
|
||||
/// Minimum value: 0
|
||||
num? width;
|
||||
/// Maximum value: 9007199254740991
|
||||
int? width;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is AssetResponseDto &&
|
||||
@@ -341,14 +346,12 @@ class AssetResponseDto {
|
||||
checksum: mapValueOfType<String>(json, r'checksum')!,
|
||||
createdAt: mapDateTime(json, r'createdAt', r'')!,
|
||||
duplicateId: mapValueOfType<String>(json, r'duplicateId'),
|
||||
duration: mapValueOfType<String>(json, r'duration'),
|
||||
duration: mapValueOfType<int>(json, r'duration'),
|
||||
exifInfo: ExifResponseDto.fromJson(json[r'exifInfo']),
|
||||
fileCreatedAt: mapDateTime(json, r'fileCreatedAt', r'')!,
|
||||
fileModifiedAt: mapDateTime(json, r'fileModifiedAt', r'')!,
|
||||
hasMetadata: mapValueOfType<bool>(json, r'hasMetadata')!,
|
||||
height: json[r'height'] == null
|
||||
? null
|
||||
: num.parse('${json[r'height']}'),
|
||||
height: mapValueOfType<int>(json, r'height'),
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
isArchived: mapValueOfType<bool>(json, r'isArchived')!,
|
||||
isEdited: mapValueOfType<bool>(json, r'isEdited')!,
|
||||
@@ -372,9 +375,7 @@ class AssetResponseDto {
|
||||
unassignedFaces: AssetFaceWithoutPersonResponseDto.listFromJson(json[r'unassignedFaces']),
|
||||
updatedAt: mapDateTime(json, r'updatedAt', r'')!,
|
||||
visibility: AssetVisibility.fromJson(json[r'visibility'])!,
|
||||
width: json[r'width'] == null
|
||||
? null
|
||||
: num.parse('${json[r'width']}'),
|
||||
width: mapValueOfType<int>(json, r'width'),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
20
mobile/openapi/lib/model/crop_parameters.dart
generated
20
mobile/openapi/lib/model/crop_parameters.dart
generated
@@ -22,22 +22,26 @@ class CropParameters {
|
||||
/// Height of the crop
|
||||
///
|
||||
/// Minimum value: 1
|
||||
num height;
|
||||
/// Maximum value: 9007199254740991
|
||||
int height;
|
||||
|
||||
/// Width of the crop
|
||||
///
|
||||
/// Minimum value: 1
|
||||
num width;
|
||||
/// Maximum value: 9007199254740991
|
||||
int width;
|
||||
|
||||
/// Top-Left X coordinate of crop
|
||||
///
|
||||
/// Minimum value: 0
|
||||
num x;
|
||||
/// Maximum value: 9007199254740991
|
||||
int x;
|
||||
|
||||
/// Top-Left Y coordinate of crop
|
||||
///
|
||||
/// Minimum value: 0
|
||||
num y;
|
||||
/// Maximum value: 9007199254740991
|
||||
int y;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is CropParameters &&
|
||||
@@ -75,10 +79,10 @@ class CropParameters {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return CropParameters(
|
||||
height: num.parse('${json[r'height']}'),
|
||||
width: num.parse('${json[r'width']}'),
|
||||
x: num.parse('${json[r'x']}'),
|
||||
y: num.parse('${json[r'y']}'),
|
||||
height: mapValueOfType<int>(json, r'height')!,
|
||||
width: mapValueOfType<int>(json, r'width')!,
|
||||
x: mapValueOfType<int>(json, r'x')!,
|
||||
y: mapValueOfType<int>(json, r'y')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -27,7 +27,8 @@ class DatabaseBackupConfig {
|
||||
/// Keep last amount
|
||||
///
|
||||
/// Minimum value: 1
|
||||
num keepLastAmount;
|
||||
/// Maximum value: 9007199254740991
|
||||
int keepLastAmount;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is DatabaseBackupConfig &&
|
||||
@@ -64,7 +65,7 @@ class DatabaseBackupConfig {
|
||||
return DatabaseBackupConfig(
|
||||
cronExpression: mapValueOfType<String>(json, r'cronExpression')!,
|
||||
enabled: mapValueOfType<bool>(json, r'enabled')!,
|
||||
keepLastAmount: num.parse('${json[r'keepLastAmount']}'),
|
||||
keepLastAmount: mapValueOfType<int>(json, r'keepLastAmount')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -22,7 +22,10 @@ class DatabaseBackupDto {
|
||||
String filename;
|
||||
|
||||
/// Backup file size
|
||||
num filesize;
|
||||
///
|
||||
/// Minimum value: -9007199254740991
|
||||
/// Maximum value: 9007199254740991
|
||||
int filesize;
|
||||
|
||||
/// Backup timezone
|
||||
String timezone;
|
||||
@@ -61,7 +64,7 @@ class DatabaseBackupDto {
|
||||
|
||||
return DatabaseBackupDto(
|
||||
filename: mapValueOfType<String>(json, r'filename')!,
|
||||
filesize: num.parse('${json[r'filesize']}'),
|
||||
filesize: mapValueOfType<int>(json, r'filesize')!,
|
||||
timezone: mapValueOfType<String>(json, r'timezone')!,
|
||||
);
|
||||
}
|
||||
|
||||
32
mobile/openapi/lib/model/exif_response_dto.dart
generated
32
mobile/openapi/lib/model/exif_response_dto.dart
generated
@@ -52,12 +52,14 @@ class ExifResponseDto {
|
||||
/// Image height in pixels
|
||||
///
|
||||
/// Minimum value: 0
|
||||
num? exifImageHeight;
|
||||
/// Maximum value: 9007199254740991
|
||||
int? exifImageHeight;
|
||||
|
||||
/// Image width in pixels
|
||||
///
|
||||
/// Minimum value: 0
|
||||
num? exifImageWidth;
|
||||
/// Maximum value: 9007199254740991
|
||||
int? exifImageWidth;
|
||||
|
||||
/// Exposure time
|
||||
String? exposureTime;
|
||||
@@ -75,7 +77,10 @@ class ExifResponseDto {
|
||||
num? focalLength;
|
||||
|
||||
/// ISO sensitivity
|
||||
num? iso;
|
||||
///
|
||||
/// Minimum value: -9007199254740991
|
||||
/// Maximum value: 9007199254740991
|
||||
int? iso;
|
||||
|
||||
/// GPS latitude
|
||||
num? latitude;
|
||||
@@ -102,7 +107,10 @@ class ExifResponseDto {
|
||||
String? projectionType;
|
||||
|
||||
/// Rating
|
||||
num? rating;
|
||||
///
|
||||
/// Minimum value: -9007199254740991
|
||||
/// Maximum value: 9007199254740991
|
||||
int? rating;
|
||||
|
||||
/// State/province name
|
||||
String? state;
|
||||
@@ -292,12 +300,8 @@ class ExifResponseDto {
|
||||
country: mapValueOfType<String>(json, r'country'),
|
||||
dateTimeOriginal: mapDateTime(json, r'dateTimeOriginal', r''),
|
||||
description: mapValueOfType<String>(json, r'description'),
|
||||
exifImageHeight: json[r'exifImageHeight'] == null
|
||||
? null
|
||||
: num.parse('${json[r'exifImageHeight']}'),
|
||||
exifImageWidth: json[r'exifImageWidth'] == null
|
||||
? null
|
||||
: num.parse('${json[r'exifImageWidth']}'),
|
||||
exifImageHeight: mapValueOfType<int>(json, r'exifImageHeight'),
|
||||
exifImageWidth: mapValueOfType<int>(json, r'exifImageWidth'),
|
||||
exposureTime: mapValueOfType<String>(json, r'exposureTime'),
|
||||
fNumber: json[r'fNumber'] == null
|
||||
? null
|
||||
@@ -306,9 +310,7 @@ class ExifResponseDto {
|
||||
focalLength: json[r'focalLength'] == null
|
||||
? null
|
||||
: num.parse('${json[r'focalLength']}'),
|
||||
iso: json[r'iso'] == null
|
||||
? null
|
||||
: num.parse('${json[r'iso']}'),
|
||||
iso: mapValueOfType<int>(json, r'iso'),
|
||||
latitude: json[r'latitude'] == null
|
||||
? null
|
||||
: num.parse('${json[r'latitude']}'),
|
||||
@@ -321,9 +323,7 @@ class ExifResponseDto {
|
||||
modifyDate: mapDateTime(json, r'modifyDate', r''),
|
||||
orientation: mapValueOfType<String>(json, r'orientation'),
|
||||
projectionType: mapValueOfType<String>(json, r'projectionType'),
|
||||
rating: json[r'rating'] == null
|
||||
? null
|
||||
: num.parse('${json[r'rating']}'),
|
||||
rating: mapValueOfType<int>(json, r'rating'),
|
||||
state: mapValueOfType<String>(json, r'state'),
|
||||
timeZone: mapValueOfType<String>(json, r'timeZone'),
|
||||
);
|
||||
|
||||
@@ -21,9 +21,13 @@ class MachineLearningAvailabilityChecksDto {
|
||||
/// Enabled
|
||||
bool enabled;
|
||||
|
||||
num interval;
|
||||
/// Minimum value: -9007199254740991
|
||||
/// Maximum value: 9007199254740991
|
||||
int interval;
|
||||
|
||||
num timeout;
|
||||
/// Minimum value: -9007199254740991
|
||||
/// Maximum value: 9007199254740991
|
||||
int timeout;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is MachineLearningAvailabilityChecksDto &&
|
||||
@@ -59,8 +63,8 @@ class MachineLearningAvailabilityChecksDto {
|
||||
|
||||
return MachineLearningAvailabilityChecksDto(
|
||||
enabled: mapValueOfType<bool>(json, r'enabled')!,
|
||||
interval: num.parse('${json[r'interval']}'),
|
||||
timeout: num.parse('${json[r'timeout']}'),
|
||||
interval: mapValueOfType<int>(json, r'interval')!,
|
||||
timeout: mapValueOfType<int>(json, r'timeout')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -20,7 +20,10 @@ class MaintenanceDetectInstallStorageFolderDto {
|
||||
});
|
||||
|
||||
/// Number of files in the folder
|
||||
num files;
|
||||
///
|
||||
/// Minimum value: -9007199254740991
|
||||
/// Maximum value: 9007199254740991
|
||||
int files;
|
||||
|
||||
StorageFolder folder;
|
||||
|
||||
@@ -66,7 +69,7 @@ class MaintenanceDetectInstallStorageFolderDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return MaintenanceDetectInstallStorageFolderDto(
|
||||
files: num.parse('${json[r'files']}'),
|
||||
files: mapValueOfType<int>(json, r'files')!,
|
||||
folder: StorageFolder.fromJson(json[r'folder'])!,
|
||||
readable: mapValueOfType<bool>(json, r'readable')!,
|
||||
writable: mapValueOfType<bool>(json, r'writable')!,
|
||||
|
||||
@@ -32,13 +32,15 @@ class MaintenanceStatusResponseDto {
|
||||
///
|
||||
String? error;
|
||||
|
||||
/// Minimum value: -9007199254740991
|
||||
/// Maximum value: 9007199254740991
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
num? progress;
|
||||
int? progress;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
@@ -102,7 +104,7 @@ class MaintenanceStatusResponseDto {
|
||||
action: MaintenanceAction.fromJson(json[r'action'])!,
|
||||
active: mapValueOfType<bool>(json, r'active')!,
|
||||
error: mapValueOfType<String>(json, r'error'),
|
||||
progress: num.parse('${json[r'progress']}'),
|
||||
progress: mapValueOfType<int>(json, r'progress'),
|
||||
task: mapValueOfType<String>(json, r'task'),
|
||||
);
|
||||
}
|
||||
|
||||
15
mobile/openapi/lib/model/metadata_search_dto.dart
generated
15
mobile/openapi/lib/model/metadata_search_dto.dart
generated
@@ -215,13 +215,14 @@ class MetadataSearchDto {
|
||||
/// Page number
|
||||
///
|
||||
/// Minimum value: 1
|
||||
/// Maximum value: 9007199254740991
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
num? page;
|
||||
int? page;
|
||||
|
||||
/// Filter by person IDs
|
||||
List<String> personIds;
|
||||
@@ -239,7 +240,7 @@ class MetadataSearchDto {
|
||||
///
|
||||
/// Minimum value: -1
|
||||
/// Maximum value: 5
|
||||
num? rating;
|
||||
int? rating;
|
||||
|
||||
/// Number of results to return
|
||||
///
|
||||
@@ -251,7 +252,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
num? size;
|
||||
int? size;
|
||||
|
||||
/// Filter by state/province name
|
||||
String? state;
|
||||
@@ -724,15 +725,13 @@ class MetadataSearchDto {
|
||||
order: AssetOrder.fromJson(json[r'order']),
|
||||
originalFileName: mapValueOfType<String>(json, r'originalFileName'),
|
||||
originalPath: mapValueOfType<String>(json, r'originalPath'),
|
||||
page: num.parse('${json[r'page']}'),
|
||||
page: mapValueOfType<int>(json, r'page'),
|
||||
personIds: json[r'personIds'] is Iterable
|
||||
? (json[r'personIds'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
previewPath: mapValueOfType<String>(json, r'previewPath'),
|
||||
rating: json[r'rating'] == null
|
||||
? null
|
||||
: num.parse('${json[r'rating']}'),
|
||||
size: num.parse('${json[r'size']}'),
|
||||
rating: mapValueOfType<int>(json, r'rating'),
|
||||
size: mapValueOfType<int>(json, r'size'),
|
||||
state: mapValueOfType<String>(json, r'state'),
|
||||
tagIds: json[r'tagIds'] is Iterable
|
||||
? (json[r'tagIds'] as Iterable).cast<String>().toList(growable: false)
|
||||
|
||||
10
mobile/openapi/lib/model/random_search_dto.dart
generated
10
mobile/openapi/lib/model/random_search_dto.dart
generated
@@ -147,7 +147,7 @@ class RandomSearchDto {
|
||||
///
|
||||
/// Minimum value: -1
|
||||
/// Maximum value: 5
|
||||
num? rating;
|
||||
int? rating;
|
||||
|
||||
/// Number of results to return
|
||||
///
|
||||
@@ -159,7 +159,7 @@ class RandomSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
num? size;
|
||||
int? size;
|
||||
|
||||
/// Filter by state/province name
|
||||
String? state;
|
||||
@@ -549,10 +549,8 @@ class RandomSearchDto {
|
||||
personIds: json[r'personIds'] is Iterable
|
||||
? (json[r'personIds'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
rating: json[r'rating'] == null
|
||||
? null
|
||||
: num.parse('${json[r'rating']}'),
|
||||
size: num.parse('${json[r'size']}'),
|
||||
rating: mapValueOfType<int>(json, r'rating'),
|
||||
size: mapValueOfType<int>(json, r'size'),
|
||||
state: mapValueOfType<String>(json, r'state'),
|
||||
tagIds: json[r'tagIds'] is Iterable
|
||||
? (json[r'tagIds'] as Iterable).cast<String>().toList(growable: false)
|
||||
|
||||
5
mobile/openapi/lib/model/session_create_dto.dart
generated
5
mobile/openapi/lib/model/session_create_dto.dart
generated
@@ -39,13 +39,14 @@ class SessionCreateDto {
|
||||
/// Session duration in seconds
|
||||
///
|
||||
/// Minimum value: 1
|
||||
/// Maximum value: 9007199254740991
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
num? duration;
|
||||
int? duration;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is SessionCreateDto &&
|
||||
@@ -94,7 +95,7 @@ class SessionCreateDto {
|
||||
return SessionCreateDto(
|
||||
deviceOS: mapValueOfType<String>(json, r'deviceOS'),
|
||||
deviceType: mapValueOfType<String>(json, r'deviceType'),
|
||||
duration: num.parse('${json[r'duration']}'),
|
||||
duration: mapValueOfType<int>(json, r'duration'),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
15
mobile/openapi/lib/model/smart_search_dto.dart
generated
15
mobile/openapi/lib/model/smart_search_dto.dart
generated
@@ -154,13 +154,14 @@ class SmartSearchDto {
|
||||
/// Page number
|
||||
///
|
||||
/// Minimum value: 1
|
||||
/// Maximum value: 9007199254740991
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
num? page;
|
||||
int? page;
|
||||
|
||||
/// Filter by person IDs
|
||||
List<String> personIds;
|
||||
@@ -187,7 +188,7 @@ class SmartSearchDto {
|
||||
///
|
||||
/// Minimum value: -1
|
||||
/// Maximum value: 5
|
||||
num? rating;
|
||||
int? rating;
|
||||
|
||||
/// Number of results to return
|
||||
///
|
||||
@@ -199,7 +200,7 @@ class SmartSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
num? size;
|
||||
int? size;
|
||||
|
||||
/// Filter by state/province name
|
||||
String? state;
|
||||
@@ -583,16 +584,14 @@ class SmartSearchDto {
|
||||
make: mapValueOfType<String>(json, r'make'),
|
||||
model: mapValueOfType<String>(json, r'model'),
|
||||
ocr: mapValueOfType<String>(json, r'ocr'),
|
||||
page: num.parse('${json[r'page']}'),
|
||||
page: mapValueOfType<int>(json, r'page'),
|
||||
personIds: json[r'personIds'] is Iterable
|
||||
? (json[r'personIds'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
query: mapValueOfType<String>(json, r'query'),
|
||||
queryAssetId: mapValueOfType<String>(json, r'queryAssetId'),
|
||||
rating: json[r'rating'] == null
|
||||
? null
|
||||
: num.parse('${json[r'rating']}'),
|
||||
size: num.parse('${json[r'size']}'),
|
||||
rating: mapValueOfType<int>(json, r'rating'),
|
||||
size: mapValueOfType<int>(json, r'size'),
|
||||
state: mapValueOfType<String>(json, r'state'),
|
||||
tagIds: json[r'tagIds'] is Iterable
|
||||
? (json[r'tagIds'] as Iterable).cast<String>().toList(growable: false)
|
||||
|
||||
@@ -152,7 +152,7 @@ class StatisticsSearchDto {
|
||||
///
|
||||
/// Minimum value: -1
|
||||
/// Maximum value: 5
|
||||
num? rating;
|
||||
int? rating;
|
||||
|
||||
/// Filter by state/province name
|
||||
String? state;
|
||||
@@ -479,9 +479,7 @@ class StatisticsSearchDto {
|
||||
personIds: json[r'personIds'] is Iterable
|
||||
? (json[r'personIds'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
rating: json[r'rating'] == null
|
||||
? null
|
||||
: num.parse('${json[r'rating']}'),
|
||||
rating: mapValueOfType<int>(json, r'rating'),
|
||||
state: mapValueOfType<String>(json, r'state'),
|
||||
tagIds: json[r'tagIds'] is Iterable
|
||||
? (json[r'tagIds'] as Iterable).cast<String>().toList(growable: false)
|
||||
|
||||
170
mobile/openapi/lib/model/sync_album_v2.dart
generated
Normal file
170
mobile/openapi/lib/model/sync_album_v2.dart
generated
Normal file
@@ -0,0 +1,170 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class SyncAlbumV2 {
|
||||
/// Returns a new [SyncAlbumV2] instance.
|
||||
SyncAlbumV2({
|
||||
required this.createdAt,
|
||||
required this.description,
|
||||
required this.id,
|
||||
required this.isActivityEnabled,
|
||||
required this.name,
|
||||
required this.order,
|
||||
required this.thumbnailAssetId,
|
||||
required this.updatedAt,
|
||||
});
|
||||
|
||||
/// Created at
|
||||
DateTime createdAt;
|
||||
|
||||
/// Album description
|
||||
String description;
|
||||
|
||||
/// Album ID
|
||||
String id;
|
||||
|
||||
/// Is activity enabled
|
||||
bool isActivityEnabled;
|
||||
|
||||
/// Album name
|
||||
String name;
|
||||
|
||||
AssetOrder order;
|
||||
|
||||
/// Thumbnail asset ID
|
||||
String? thumbnailAssetId;
|
||||
|
||||
/// Updated at
|
||||
DateTime updatedAt;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is SyncAlbumV2 &&
|
||||
other.createdAt == createdAt &&
|
||||
other.description == description &&
|
||||
other.id == id &&
|
||||
other.isActivityEnabled == isActivityEnabled &&
|
||||
other.name == name &&
|
||||
other.order == order &&
|
||||
other.thumbnailAssetId == thumbnailAssetId &&
|
||||
other.updatedAt == updatedAt;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(createdAt.hashCode) +
|
||||
(description.hashCode) +
|
||||
(id.hashCode) +
|
||||
(isActivityEnabled.hashCode) +
|
||||
(name.hashCode) +
|
||||
(order.hashCode) +
|
||||
(thumbnailAssetId == null ? 0 : thumbnailAssetId!.hashCode) +
|
||||
(updatedAt.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SyncAlbumV2[createdAt=$createdAt, description=$description, id=$id, isActivityEnabled=$isActivityEnabled, name=$name, order=$order, thumbnailAssetId=$thumbnailAssetId, updatedAt=$updatedAt]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'createdAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.createdAt.millisecondsSinceEpoch
|
||||
: this.createdAt.toUtc().toIso8601String();
|
||||
json[r'description'] = this.description;
|
||||
json[r'id'] = this.id;
|
||||
json[r'isActivityEnabled'] = this.isActivityEnabled;
|
||||
json[r'name'] = this.name;
|
||||
json[r'order'] = this.order;
|
||||
if (this.thumbnailAssetId != null) {
|
||||
json[r'thumbnailAssetId'] = this.thumbnailAssetId;
|
||||
} else {
|
||||
// json[r'thumbnailAssetId'] = null;
|
||||
}
|
||||
json[r'updatedAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.updatedAt.millisecondsSinceEpoch
|
||||
: this.updatedAt.toUtc().toIso8601String();
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [SyncAlbumV2] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static SyncAlbumV2? fromJson(dynamic value) {
|
||||
upgradeDto(value, "SyncAlbumV2");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return SyncAlbumV2(
|
||||
createdAt: mapDateTime(json, r'createdAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
|
||||
description: mapValueOfType<String>(json, r'description')!,
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
isActivityEnabled: mapValueOfType<bool>(json, r'isActivityEnabled')!,
|
||||
name: mapValueOfType<String>(json, r'name')!,
|
||||
order: AssetOrder.fromJson(json[r'order'])!,
|
||||
thumbnailAssetId: mapValueOfType<String>(json, r'thumbnailAssetId'),
|
||||
updatedAt: mapDateTime(json, r'updatedAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<SyncAlbumV2> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <SyncAlbumV2>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = SyncAlbumV2.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, SyncAlbumV2> mapFromJson(dynamic json) {
|
||||
final map = <String, SyncAlbumV2>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = SyncAlbumV2.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of SyncAlbumV2-objects as value to a dart map
|
||||
static Map<String, List<SyncAlbumV2>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<SyncAlbumV2>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = SyncAlbumV2.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'createdAt',
|
||||
'description',
|
||||
'id',
|
||||
'isActivityEnabled',
|
||||
'name',
|
||||
'order',
|
||||
'thumbnailAssetId',
|
||||
'updatedAt',
|
||||
};
|
||||
}
|
||||
|
||||
321
mobile/openapi/lib/model/sync_asset_v2.dart
generated
Normal file
321
mobile/openapi/lib/model/sync_asset_v2.dart
generated
Normal file
@@ -0,0 +1,321 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class SyncAssetV2 {
|
||||
/// Returns a new [SyncAssetV2] instance.
|
||||
SyncAssetV2({
|
||||
required this.checksum,
|
||||
required this.deletedAt,
|
||||
required this.duration,
|
||||
required this.fileCreatedAt,
|
||||
required this.fileModifiedAt,
|
||||
required this.height,
|
||||
required this.id,
|
||||
required this.isEdited,
|
||||
required this.isFavorite,
|
||||
required this.libraryId,
|
||||
required this.livePhotoVideoId,
|
||||
required this.localDateTime,
|
||||
required this.originalFileName,
|
||||
required this.ownerId,
|
||||
required this.stackId,
|
||||
required this.thumbhash,
|
||||
required this.type,
|
||||
required this.visibility,
|
||||
required this.width,
|
||||
});
|
||||
|
||||
/// Checksum
|
||||
String checksum;
|
||||
|
||||
/// Deleted at
|
||||
DateTime? deletedAt;
|
||||
|
||||
/// Duration
|
||||
///
|
||||
/// Minimum value: 0
|
||||
/// Maximum value: 2147483647
|
||||
int? duration;
|
||||
|
||||
/// File created at
|
||||
DateTime? fileCreatedAt;
|
||||
|
||||
/// File modified at
|
||||
DateTime? fileModifiedAt;
|
||||
|
||||
/// Asset height
|
||||
///
|
||||
/// Minimum value: -9007199254740991
|
||||
/// Maximum value: 9007199254740991
|
||||
int? height;
|
||||
|
||||
/// Asset ID
|
||||
String id;
|
||||
|
||||
/// Is edited
|
||||
bool isEdited;
|
||||
|
||||
/// Is favorite
|
||||
bool isFavorite;
|
||||
|
||||
/// Library ID
|
||||
String? libraryId;
|
||||
|
||||
/// Live photo video ID
|
||||
String? livePhotoVideoId;
|
||||
|
||||
/// Local date time
|
||||
DateTime? localDateTime;
|
||||
|
||||
/// Original file name
|
||||
String originalFileName;
|
||||
|
||||
/// Owner ID
|
||||
String ownerId;
|
||||
|
||||
/// Stack ID
|
||||
String? stackId;
|
||||
|
||||
/// Thumbhash
|
||||
String? thumbhash;
|
||||
|
||||
AssetTypeEnum type;
|
||||
|
||||
AssetVisibility visibility;
|
||||
|
||||
/// Asset width
|
||||
///
|
||||
/// Minimum value: -9007199254740991
|
||||
/// Maximum value: 9007199254740991
|
||||
int? width;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is SyncAssetV2 &&
|
||||
other.checksum == checksum &&
|
||||
other.deletedAt == deletedAt &&
|
||||
other.duration == duration &&
|
||||
other.fileCreatedAt == fileCreatedAt &&
|
||||
other.fileModifiedAt == fileModifiedAt &&
|
||||
other.height == height &&
|
||||
other.id == id &&
|
||||
other.isEdited == isEdited &&
|
||||
other.isFavorite == isFavorite &&
|
||||
other.libraryId == libraryId &&
|
||||
other.livePhotoVideoId == livePhotoVideoId &&
|
||||
other.localDateTime == localDateTime &&
|
||||
other.originalFileName == originalFileName &&
|
||||
other.ownerId == ownerId &&
|
||||
other.stackId == stackId &&
|
||||
other.thumbhash == thumbhash &&
|
||||
other.type == type &&
|
||||
other.visibility == visibility &&
|
||||
other.width == width;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(checksum.hashCode) +
|
||||
(deletedAt == null ? 0 : deletedAt!.hashCode) +
|
||||
(duration == null ? 0 : duration!.hashCode) +
|
||||
(fileCreatedAt == null ? 0 : fileCreatedAt!.hashCode) +
|
||||
(fileModifiedAt == null ? 0 : fileModifiedAt!.hashCode) +
|
||||
(height == null ? 0 : height!.hashCode) +
|
||||
(id.hashCode) +
|
||||
(isEdited.hashCode) +
|
||||
(isFavorite.hashCode) +
|
||||
(libraryId == null ? 0 : libraryId!.hashCode) +
|
||||
(livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode) +
|
||||
(localDateTime == null ? 0 : localDateTime!.hashCode) +
|
||||
(originalFileName.hashCode) +
|
||||
(ownerId.hashCode) +
|
||||
(stackId == null ? 0 : stackId!.hashCode) +
|
||||
(thumbhash == null ? 0 : thumbhash!.hashCode) +
|
||||
(type.hashCode) +
|
||||
(visibility.hashCode) +
|
||||
(width == null ? 0 : width!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SyncAssetV2[checksum=$checksum, deletedAt=$deletedAt, duration=$duration, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, height=$height, id=$id, isEdited=$isEdited, isFavorite=$isFavorite, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, ownerId=$ownerId, stackId=$stackId, thumbhash=$thumbhash, type=$type, visibility=$visibility, width=$width]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'checksum'] = this.checksum;
|
||||
if (this.deletedAt != null) {
|
||||
json[r'deletedAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.deletedAt!.millisecondsSinceEpoch
|
||||
: this.deletedAt!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'deletedAt'] = null;
|
||||
}
|
||||
if (this.duration != null) {
|
||||
json[r'duration'] = this.duration;
|
||||
} else {
|
||||
// json[r'duration'] = null;
|
||||
}
|
||||
if (this.fileCreatedAt != null) {
|
||||
json[r'fileCreatedAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.fileCreatedAt!.millisecondsSinceEpoch
|
||||
: this.fileCreatedAt!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'fileCreatedAt'] = null;
|
||||
}
|
||||
if (this.fileModifiedAt != null) {
|
||||
json[r'fileModifiedAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.fileModifiedAt!.millisecondsSinceEpoch
|
||||
: this.fileModifiedAt!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'fileModifiedAt'] = null;
|
||||
}
|
||||
if (this.height != null) {
|
||||
json[r'height'] = this.height;
|
||||
} else {
|
||||
// json[r'height'] = null;
|
||||
}
|
||||
json[r'id'] = this.id;
|
||||
json[r'isEdited'] = this.isEdited;
|
||||
json[r'isFavorite'] = this.isFavorite;
|
||||
if (this.libraryId != null) {
|
||||
json[r'libraryId'] = this.libraryId;
|
||||
} else {
|
||||
// json[r'libraryId'] = null;
|
||||
}
|
||||
if (this.livePhotoVideoId != null) {
|
||||
json[r'livePhotoVideoId'] = this.livePhotoVideoId;
|
||||
} else {
|
||||
// json[r'livePhotoVideoId'] = null;
|
||||
}
|
||||
if (this.localDateTime != null) {
|
||||
json[r'localDateTime'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
|
||||
? this.localDateTime!.millisecondsSinceEpoch
|
||||
: this.localDateTime!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'localDateTime'] = null;
|
||||
}
|
||||
json[r'originalFileName'] = this.originalFileName;
|
||||
json[r'ownerId'] = this.ownerId;
|
||||
if (this.stackId != null) {
|
||||
json[r'stackId'] = this.stackId;
|
||||
} else {
|
||||
// json[r'stackId'] = null;
|
||||
}
|
||||
if (this.thumbhash != null) {
|
||||
json[r'thumbhash'] = this.thumbhash;
|
||||
} else {
|
||||
// json[r'thumbhash'] = null;
|
||||
}
|
||||
json[r'type'] = this.type;
|
||||
json[r'visibility'] = this.visibility;
|
||||
if (this.width != null) {
|
||||
json[r'width'] = this.width;
|
||||
} else {
|
||||
// json[r'width'] = null;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [SyncAssetV2] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static SyncAssetV2? fromJson(dynamic value) {
|
||||
upgradeDto(value, "SyncAssetV2");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return SyncAssetV2(
|
||||
checksum: mapValueOfType<String>(json, r'checksum')!,
|
||||
deletedAt: mapDateTime(json, r'deletedAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
duration: mapValueOfType<int>(json, r'duration'),
|
||||
fileCreatedAt: mapDateTime(json, r'fileCreatedAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
fileModifiedAt: mapDateTime(json, r'fileModifiedAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
height: mapValueOfType<int>(json, r'height'),
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
isEdited: mapValueOfType<bool>(json, r'isEdited')!,
|
||||
isFavorite: mapValueOfType<bool>(json, r'isFavorite')!,
|
||||
libraryId: mapValueOfType<String>(json, r'libraryId'),
|
||||
livePhotoVideoId: mapValueOfType<String>(json, r'livePhotoVideoId'),
|
||||
localDateTime: mapDateTime(json, r'localDateTime', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/'),
|
||||
originalFileName: mapValueOfType<String>(json, r'originalFileName')!,
|
||||
ownerId: mapValueOfType<String>(json, r'ownerId')!,
|
||||
stackId: mapValueOfType<String>(json, r'stackId'),
|
||||
thumbhash: mapValueOfType<String>(json, r'thumbhash'),
|
||||
type: AssetTypeEnum.fromJson(json[r'type'])!,
|
||||
visibility: AssetVisibility.fromJson(json[r'visibility'])!,
|
||||
width: mapValueOfType<int>(json, r'width'),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<SyncAssetV2> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <SyncAssetV2>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = SyncAssetV2.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, SyncAssetV2> mapFromJson(dynamic json) {
|
||||
final map = <String, SyncAssetV2>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = SyncAssetV2.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of SyncAssetV2-objects as value to a dart map
|
||||
static Map<String, List<SyncAssetV2>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<SyncAssetV2>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = SyncAssetV2.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'checksum',
|
||||
'deletedAt',
|
||||
'duration',
|
||||
'fileCreatedAt',
|
||||
'fileModifiedAt',
|
||||
'height',
|
||||
'id',
|
||||
'isEdited',
|
||||
'isFavorite',
|
||||
'libraryId',
|
||||
'livePhotoVideoId',
|
||||
'localDateTime',
|
||||
'originalFileName',
|
||||
'ownerId',
|
||||
'stackId',
|
||||
'thumbhash',
|
||||
'type',
|
||||
'visibility',
|
||||
'width',
|
||||
};
|
||||
}
|
||||
|
||||
21
mobile/openapi/lib/model/sync_entity_type.dart
generated
21
mobile/openapi/lib/model/sync_entity_type.dart
generated
@@ -27,6 +27,7 @@ class SyncEntityType {
|
||||
static const userV1 = SyncEntityType._(r'UserV1');
|
||||
static const userDeleteV1 = SyncEntityType._(r'UserDeleteV1');
|
||||
static const assetV1 = SyncEntityType._(r'AssetV1');
|
||||
static const assetV2 = SyncEntityType._(r'AssetV2');
|
||||
static const assetDeleteV1 = SyncEntityType._(r'AssetDeleteV1');
|
||||
static const assetExifV1 = SyncEntityType._(r'AssetExifV1');
|
||||
static const assetEditV1 = SyncEntityType._(r'AssetEditV1');
|
||||
@@ -36,7 +37,9 @@ class SyncEntityType {
|
||||
static const partnerV1 = SyncEntityType._(r'PartnerV1');
|
||||
static const partnerDeleteV1 = SyncEntityType._(r'PartnerDeleteV1');
|
||||
static const partnerAssetV1 = SyncEntityType._(r'PartnerAssetV1');
|
||||
static const partnerAssetV2 = SyncEntityType._(r'PartnerAssetV2');
|
||||
static const partnerAssetBackfillV1 = SyncEntityType._(r'PartnerAssetBackfillV1');
|
||||
static const partnerAssetBackfillV2 = SyncEntityType._(r'PartnerAssetBackfillV2');
|
||||
static const partnerAssetDeleteV1 = SyncEntityType._(r'PartnerAssetDeleteV1');
|
||||
static const partnerAssetExifV1 = SyncEntityType._(r'PartnerAssetExifV1');
|
||||
static const partnerAssetExifBackfillV1 = SyncEntityType._(r'PartnerAssetExifBackfillV1');
|
||||
@@ -44,13 +47,17 @@ class SyncEntityType {
|
||||
static const partnerStackDeleteV1 = SyncEntityType._(r'PartnerStackDeleteV1');
|
||||
static const partnerStackV1 = SyncEntityType._(r'PartnerStackV1');
|
||||
static const albumV1 = SyncEntityType._(r'AlbumV1');
|
||||
static const albumV2 = SyncEntityType._(r'AlbumV2');
|
||||
static const albumDeleteV1 = SyncEntityType._(r'AlbumDeleteV1');
|
||||
static const albumUserV1 = SyncEntityType._(r'AlbumUserV1');
|
||||
static const albumUserBackfillV1 = SyncEntityType._(r'AlbumUserBackfillV1');
|
||||
static const albumUserDeleteV1 = SyncEntityType._(r'AlbumUserDeleteV1');
|
||||
static const albumAssetCreateV1 = SyncEntityType._(r'AlbumAssetCreateV1');
|
||||
static const albumAssetCreateV2 = SyncEntityType._(r'AlbumAssetCreateV2');
|
||||
static const albumAssetUpdateV1 = SyncEntityType._(r'AlbumAssetUpdateV1');
|
||||
static const albumAssetUpdateV2 = SyncEntityType._(r'AlbumAssetUpdateV2');
|
||||
static const albumAssetBackfillV1 = SyncEntityType._(r'AlbumAssetBackfillV1');
|
||||
static const albumAssetBackfillV2 = SyncEntityType._(r'AlbumAssetBackfillV2');
|
||||
static const albumAssetExifCreateV1 = SyncEntityType._(r'AlbumAssetExifCreateV1');
|
||||
static const albumAssetExifUpdateV1 = SyncEntityType._(r'AlbumAssetExifUpdateV1');
|
||||
static const albumAssetExifBackfillV1 = SyncEntityType._(r'AlbumAssetExifBackfillV1');
|
||||
@@ -80,6 +87,7 @@ class SyncEntityType {
|
||||
userV1,
|
||||
userDeleteV1,
|
||||
assetV1,
|
||||
assetV2,
|
||||
assetDeleteV1,
|
||||
assetExifV1,
|
||||
assetEditV1,
|
||||
@@ -89,7 +97,9 @@ class SyncEntityType {
|
||||
partnerV1,
|
||||
partnerDeleteV1,
|
||||
partnerAssetV1,
|
||||
partnerAssetV2,
|
||||
partnerAssetBackfillV1,
|
||||
partnerAssetBackfillV2,
|
||||
partnerAssetDeleteV1,
|
||||
partnerAssetExifV1,
|
||||
partnerAssetExifBackfillV1,
|
||||
@@ -97,13 +107,17 @@ class SyncEntityType {
|
||||
partnerStackDeleteV1,
|
||||
partnerStackV1,
|
||||
albumV1,
|
||||
albumV2,
|
||||
albumDeleteV1,
|
||||
albumUserV1,
|
||||
albumUserBackfillV1,
|
||||
albumUserDeleteV1,
|
||||
albumAssetCreateV1,
|
||||
albumAssetCreateV2,
|
||||
albumAssetUpdateV1,
|
||||
albumAssetUpdateV2,
|
||||
albumAssetBackfillV1,
|
||||
albumAssetBackfillV2,
|
||||
albumAssetExifCreateV1,
|
||||
albumAssetExifUpdateV1,
|
||||
albumAssetExifBackfillV1,
|
||||
@@ -168,6 +182,7 @@ class SyncEntityTypeTypeTransformer {
|
||||
case r'UserV1': return SyncEntityType.userV1;
|
||||
case r'UserDeleteV1': return SyncEntityType.userDeleteV1;
|
||||
case r'AssetV1': return SyncEntityType.assetV1;
|
||||
case r'AssetV2': return SyncEntityType.assetV2;
|
||||
case r'AssetDeleteV1': return SyncEntityType.assetDeleteV1;
|
||||
case r'AssetExifV1': return SyncEntityType.assetExifV1;
|
||||
case r'AssetEditV1': return SyncEntityType.assetEditV1;
|
||||
@@ -177,7 +192,9 @@ class SyncEntityTypeTypeTransformer {
|
||||
case r'PartnerV1': return SyncEntityType.partnerV1;
|
||||
case r'PartnerDeleteV1': return SyncEntityType.partnerDeleteV1;
|
||||
case r'PartnerAssetV1': return SyncEntityType.partnerAssetV1;
|
||||
case r'PartnerAssetV2': return SyncEntityType.partnerAssetV2;
|
||||
case r'PartnerAssetBackfillV1': return SyncEntityType.partnerAssetBackfillV1;
|
||||
case r'PartnerAssetBackfillV2': return SyncEntityType.partnerAssetBackfillV2;
|
||||
case r'PartnerAssetDeleteV1': return SyncEntityType.partnerAssetDeleteV1;
|
||||
case r'PartnerAssetExifV1': return SyncEntityType.partnerAssetExifV1;
|
||||
case r'PartnerAssetExifBackfillV1': return SyncEntityType.partnerAssetExifBackfillV1;
|
||||
@@ -185,13 +202,17 @@ class SyncEntityTypeTypeTransformer {
|
||||
case r'PartnerStackDeleteV1': return SyncEntityType.partnerStackDeleteV1;
|
||||
case r'PartnerStackV1': return SyncEntityType.partnerStackV1;
|
||||
case r'AlbumV1': return SyncEntityType.albumV1;
|
||||
case r'AlbumV2': return SyncEntityType.albumV2;
|
||||
case r'AlbumDeleteV1': return SyncEntityType.albumDeleteV1;
|
||||
case r'AlbumUserV1': return SyncEntityType.albumUserV1;
|
||||
case r'AlbumUserBackfillV1': return SyncEntityType.albumUserBackfillV1;
|
||||
case r'AlbumUserDeleteV1': return SyncEntityType.albumUserDeleteV1;
|
||||
case r'AlbumAssetCreateV1': return SyncEntityType.albumAssetCreateV1;
|
||||
case r'AlbumAssetCreateV2': return SyncEntityType.albumAssetCreateV2;
|
||||
case r'AlbumAssetUpdateV1': return SyncEntityType.albumAssetUpdateV1;
|
||||
case r'AlbumAssetUpdateV2': return SyncEntityType.albumAssetUpdateV2;
|
||||
case r'AlbumAssetBackfillV1': return SyncEntityType.albumAssetBackfillV1;
|
||||
case r'AlbumAssetBackfillV2': return SyncEntityType.albumAssetBackfillV2;
|
||||
case r'AlbumAssetExifCreateV1': return SyncEntityType.albumAssetExifCreateV1;
|
||||
case r'AlbumAssetExifUpdateV1': return SyncEntityType.albumAssetExifUpdateV1;
|
||||
case r'AlbumAssetExifBackfillV1': return SyncEntityType.albumAssetExifBackfillV1;
|
||||
|
||||
12
mobile/openapi/lib/model/sync_request_type.dart
generated
12
mobile/openapi/lib/model/sync_request_type.dart
generated
@@ -24,11 +24,14 @@ class SyncRequestType {
|
||||
String toJson() => value;
|
||||
|
||||
static const albumsV1 = SyncRequestType._(r'AlbumsV1');
|
||||
static const albumsV2 = SyncRequestType._(r'AlbumsV2');
|
||||
static const albumUsersV1 = SyncRequestType._(r'AlbumUsersV1');
|
||||
static const albumToAssetsV1 = SyncRequestType._(r'AlbumToAssetsV1');
|
||||
static const albumAssetsV1 = SyncRequestType._(r'AlbumAssetsV1');
|
||||
static const albumAssetsV2 = SyncRequestType._(r'AlbumAssetsV2');
|
||||
static const albumAssetExifsV1 = SyncRequestType._(r'AlbumAssetExifsV1');
|
||||
static const assetsV1 = SyncRequestType._(r'AssetsV1');
|
||||
static const assetsV2 = SyncRequestType._(r'AssetsV2');
|
||||
static const assetExifsV1 = SyncRequestType._(r'AssetExifsV1');
|
||||
static const assetEditsV1 = SyncRequestType._(r'AssetEditsV1');
|
||||
static const assetMetadataV1 = SyncRequestType._(r'AssetMetadataV1');
|
||||
@@ -37,6 +40,7 @@ class SyncRequestType {
|
||||
static const memoryToAssetsV1 = SyncRequestType._(r'MemoryToAssetsV1');
|
||||
static const partnersV1 = SyncRequestType._(r'PartnersV1');
|
||||
static const partnerAssetsV1 = SyncRequestType._(r'PartnerAssetsV1');
|
||||
static const partnerAssetsV2 = SyncRequestType._(r'PartnerAssetsV2');
|
||||
static const partnerAssetExifsV1 = SyncRequestType._(r'PartnerAssetExifsV1');
|
||||
static const partnerStacksV1 = SyncRequestType._(r'PartnerStacksV1');
|
||||
static const stacksV1 = SyncRequestType._(r'StacksV1');
|
||||
@@ -49,11 +53,14 @@ class SyncRequestType {
|
||||
/// List of all possible values in this [enum][SyncRequestType].
|
||||
static const values = <SyncRequestType>[
|
||||
albumsV1,
|
||||
albumsV2,
|
||||
albumUsersV1,
|
||||
albumToAssetsV1,
|
||||
albumAssetsV1,
|
||||
albumAssetsV2,
|
||||
albumAssetExifsV1,
|
||||
assetsV1,
|
||||
assetsV2,
|
||||
assetExifsV1,
|
||||
assetEditsV1,
|
||||
assetMetadataV1,
|
||||
@@ -62,6 +69,7 @@ class SyncRequestType {
|
||||
memoryToAssetsV1,
|
||||
partnersV1,
|
||||
partnerAssetsV1,
|
||||
partnerAssetsV2,
|
||||
partnerAssetExifsV1,
|
||||
partnerStacksV1,
|
||||
stacksV1,
|
||||
@@ -109,11 +117,14 @@ class SyncRequestTypeTypeTransformer {
|
||||
if (data != null) {
|
||||
switch (data) {
|
||||
case r'AlbumsV1': return SyncRequestType.albumsV1;
|
||||
case r'AlbumsV2': return SyncRequestType.albumsV2;
|
||||
case r'AlbumUsersV1': return SyncRequestType.albumUsersV1;
|
||||
case r'AlbumToAssetsV1': return SyncRequestType.albumToAssetsV1;
|
||||
case r'AlbumAssetsV1': return SyncRequestType.albumAssetsV1;
|
||||
case r'AlbumAssetsV2': return SyncRequestType.albumAssetsV2;
|
||||
case r'AlbumAssetExifsV1': return SyncRequestType.albumAssetExifsV1;
|
||||
case r'AssetsV1': return SyncRequestType.assetsV1;
|
||||
case r'AssetsV2': return SyncRequestType.assetsV2;
|
||||
case r'AssetExifsV1': return SyncRequestType.assetExifsV1;
|
||||
case r'AssetEditsV1': return SyncRequestType.assetEditsV1;
|
||||
case r'AssetMetadataV1': return SyncRequestType.assetMetadataV1;
|
||||
@@ -122,6 +133,7 @@ class SyncRequestTypeTypeTransformer {
|
||||
case r'MemoryToAssetsV1': return SyncRequestType.memoryToAssetsV1;
|
||||
case r'PartnersV1': return SyncRequestType.partnersV1;
|
||||
case r'PartnerAssetsV1': return SyncRequestType.partnerAssetsV1;
|
||||
case r'PartnerAssetsV2': return SyncRequestType.partnerAssetsV2;
|
||||
case r'PartnerAssetExifsV1': return SyncRequestType.partnerAssetExifsV1;
|
||||
case r'PartnerStacksV1': return SyncRequestType.partnerStacksV1;
|
||||
case r'StacksV1': return SyncRequestType.stacksV1;
|
||||
|
||||
@@ -57,7 +57,8 @@ class SystemConfigOAuthDto {
|
||||
/// Default storage quota
|
||||
///
|
||||
/// Minimum value: 0
|
||||
num? defaultStorageQuota;
|
||||
/// Maximum value: 9007199254740991
|
||||
int? defaultStorageQuota;
|
||||
|
||||
/// Enabled
|
||||
bool enabled;
|
||||
@@ -200,9 +201,7 @@ class SystemConfigOAuthDto {
|
||||
buttonText: mapValueOfType<String>(json, r'buttonText')!,
|
||||
clientId: mapValueOfType<String>(json, r'clientId')!,
|
||||
clientSecret: mapValueOfType<String>(json, r'clientSecret')!,
|
||||
defaultStorageQuota: json[r'defaultStorageQuota'] == null
|
||||
? null
|
||||
: num.parse('${json[r'defaultStorageQuota']}'),
|
||||
defaultStorageQuota: mapValueOfType<int>(json, r'defaultStorageQuota'),
|
||||
enabled: mapValueOfType<bool>(json, r'enabled')!,
|
||||
endSessionEndpoint: mapValueOfType<String>(json, r'endSessionEndpoint')!,
|
||||
issuerUrl: mapValueOfType<String>(json, r'issuerUrl')!,
|
||||
|
||||
@@ -34,7 +34,7 @@ class SystemConfigSmtpTransportDto {
|
||||
///
|
||||
/// Minimum value: 0
|
||||
/// Maximum value: 65535
|
||||
num port;
|
||||
int port;
|
||||
|
||||
/// Whether to use secure connection (TLS/SSL)
|
||||
bool secure;
|
||||
@@ -87,7 +87,7 @@ class SystemConfigSmtpTransportDto {
|
||||
host: mapValueOfType<String>(json, r'host')!,
|
||||
ignoreCert: mapValueOfType<bool>(json, r'ignoreCert')!,
|
||||
password: mapValueOfType<String>(json, r'password')!,
|
||||
port: num.parse('${json[r'port']}'),
|
||||
port: mapValueOfType<int>(json, r'port')!,
|
||||
secure: mapValueOfType<bool>(json, r'secure')!,
|
||||
username: mapValueOfType<String>(json, r'username')!,
|
||||
);
|
||||
|
||||
@@ -39,8 +39,8 @@ class TimeBucketAssetResponseDto {
|
||||
/// Array of country names extracted from EXIF GPS data
|
||||
List<String?> country;
|
||||
|
||||
/// Array of video/gif durations in hh:mm:ss.SSS format (null for static images)
|
||||
List<String?> duration;
|
||||
/// Array of video/gif durations in milliseconds (null for static images)
|
||||
List<int?> duration;
|
||||
|
||||
/// Array of file creation timestamps in UTC
|
||||
List<String> fileCreatedAt;
|
||||
@@ -172,7 +172,7 @@ class TimeBucketAssetResponseDto {
|
||||
? (json[r'country'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
duration: json[r'duration'] is Iterable
|
||||
? (json[r'duration'] as Iterable).cast<String>().toList(growable: false)
|
||||
? (json[r'duration'] as Iterable).cast<int>().toList(growable: false)
|
||||
: const [],
|
||||
fileCreatedAt: json[r'fileCreatedAt'] is Iterable
|
||||
? (json[r'fileCreatedAt'] as Iterable).cast<String>().toList(growable: false)
|
||||
|
||||
@@ -26,7 +26,10 @@ class WorkflowActionResponseDto {
|
||||
String id;
|
||||
|
||||
/// Action order
|
||||
num order;
|
||||
///
|
||||
/// Minimum value: -9007199254740991
|
||||
/// Maximum value: 9007199254740991
|
||||
int order;
|
||||
|
||||
/// Plugin action ID
|
||||
String pluginActionId;
|
||||
@@ -79,7 +82,7 @@ class WorkflowActionResponseDto {
|
||||
return WorkflowActionResponseDto(
|
||||
actionConfig: mapCastOfType<String, Object>(json, r'actionConfig'),
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
order: num.parse('${json[r'order']}'),
|
||||
order: mapValueOfType<int>(json, r'order')!,
|
||||
pluginActionId: mapValueOfType<String>(json, r'pluginActionId')!,
|
||||
workflowId: mapValueOfType<String>(json, r'workflowId')!,
|
||||
);
|
||||
|
||||
@@ -26,7 +26,10 @@ class WorkflowFilterResponseDto {
|
||||
String id;
|
||||
|
||||
/// Filter order
|
||||
num order;
|
||||
///
|
||||
/// Minimum value: -9007199254740991
|
||||
/// Maximum value: 9007199254740991
|
||||
int order;
|
||||
|
||||
/// Plugin filter ID
|
||||
String pluginFilterId;
|
||||
@@ -79,7 +82,7 @@ class WorkflowFilterResponseDto {
|
||||
return WorkflowFilterResponseDto(
|
||||
filterConfig: mapCastOfType<String, Object>(json, r'filterConfig'),
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
order: num.parse('${json[r'order']}'),
|
||||
order: mapValueOfType<int>(json, r'order')!,
|
||||
pluginFilterId: mapValueOfType<String>(json, r'pluginFilterId')!,
|
||||
workflowId: mapValueOfType<String>(json, r'workflowId')!,
|
||||
);
|
||||
|
||||
@@ -419,8 +419,8 @@ void main() {
|
||||
'album-b': [mergedAsset],
|
||||
};
|
||||
when(() => mockLocalAssetRepo.getAssetsFromBackupAlbums(any())).thenAnswer((invocation) async {
|
||||
final Iterable<String> requestedChecksums = invocation.positionalArguments.first as Iterable<String>;
|
||||
expect(requestedChecksums.toSet(), equals({'checksum-local', 'checksum-merged', 'checksum-remote-only'}));
|
||||
final Iterable<String> requestedRemoteIds = invocation.positionalArguments.first as Iterable<String>;
|
||||
expect(requestedRemoteIds.toSet(), equals({'remote-1', 'remote-2', 'remote-3'}));
|
||||
return assetsByAlbum;
|
||||
});
|
||||
|
||||
@@ -482,12 +482,18 @@ void main() {
|
||||
verifyNever(() => mockTrashedLocalAssetRepo.trashLocalAsset(any()));
|
||||
});
|
||||
|
||||
test("does not request local deletions for permanent remote delete events", () async {
|
||||
test("requests local deletions lookup by remote ids for permanent remote delete events", () async {
|
||||
when(() => mockLocalAssetRepo.getAssetsFromBackupAlbums(any())).thenAnswer((invocation) async {
|
||||
final Iterable<String> requestedRemoteIds = invocation.positionalArguments.first as Iterable<String>;
|
||||
expect(requestedRemoteIds.toSet(), equals({'remote-asset'}));
|
||||
return {};
|
||||
});
|
||||
|
||||
final events = [SyncStreamStub.assetDeleteV1];
|
||||
|
||||
await simulateEvents(events);
|
||||
|
||||
verifyNever(() => mockLocalAssetRepo.getAssetsFromBackupAlbums(any()));
|
||||
verify(() => mockLocalAssetRepo.getAssetsFromBackupAlbums(any())).called(1);
|
||||
verifyNever(() => mockLocalFilesManagerRepo.moveToTrash(any()));
|
||||
verify(() => mockSyncStreamRepo.deleteAssetsV1(any())).called(1);
|
||||
});
|
||||
|
||||
4
mobile/test/drift/main/generated/schema.dart
generated
4
mobile/test/drift/main/generated/schema.dart
generated
@@ -27,6 +27,7 @@ import 'schema_v20.dart' as v20;
|
||||
import 'schema_v21.dart' as v21;
|
||||
import 'schema_v22.dart' as v22;
|
||||
import 'schema_v23.dart' as v23;
|
||||
import 'schema_v24.dart' as v24;
|
||||
|
||||
class GeneratedHelper implements SchemaInstantiationHelper {
|
||||
@override
|
||||
@@ -78,6 +79,8 @@ class GeneratedHelper implements SchemaInstantiationHelper {
|
||||
return v22.DatabaseAtV22(db);
|
||||
case 23:
|
||||
return v23.DatabaseAtV23(db);
|
||||
case 24:
|
||||
return v24.DatabaseAtV24(db);
|
||||
default:
|
||||
throw MissingSchemaException(version, versions);
|
||||
}
|
||||
@@ -107,5 +110,6 @@ class GeneratedHelper implements SchemaInstantiationHelper {
|
||||
21,
|
||||
22,
|
||||
23,
|
||||
24,
|
||||
];
|
||||
}
|
||||
|
||||
9131
mobile/test/drift/main/generated/schema_v24.dart
generated
Normal file
9131
mobile/test/drift/main/generated/schema_v24.dart
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,7 @@ import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.d
|
||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_asset_cloud_id.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart';
|
||||
@@ -151,13 +152,13 @@ class MediumRepositoryContext {
|
||||
String? thumbnailAssetId,
|
||||
}) async {
|
||||
id = TestUtils.uuid(id);
|
||||
return db
|
||||
|
||||
final album = await db
|
||||
.into(db.remoteAlbumEntity)
|
||||
.insertReturning(
|
||||
RemoteAlbumEntityCompanion(
|
||||
id: Value(id),
|
||||
name: Value(name ?? 'remote_album_$id'),
|
||||
ownerId: Value(TestUtils.uuid(ownerId)),
|
||||
createdAt: Value(TestUtils.date(createdAt)),
|
||||
updatedAt: Value(TestUtils.date(updatedAt)),
|
||||
description: Value(description ?? 'Description for album $id'),
|
||||
@@ -166,6 +167,18 @@ class MediumRepositoryContext {
|
||||
thumbnailAssetId: Value(thumbnailAssetId),
|
||||
),
|
||||
);
|
||||
|
||||
await db
|
||||
.into(db.remoteAlbumUserEntity)
|
||||
.insert(
|
||||
RemoteAlbumUserEntityCompanion.insert(
|
||||
albumId: id,
|
||||
userId: ownerId ?? const Uuid().v4(),
|
||||
role: AlbumUserRole.owner,
|
||||
),
|
||||
);
|
||||
|
||||
return album;
|
||||
}
|
||||
|
||||
Future<void> insertRemoteAlbumAsset({required String albumId, required String assetId}) {
|
||||
|
||||
@@ -7964,8 +7964,9 @@
|
||||
"description": "Page number for pagination",
|
||||
"schema": {
|
||||
"minimum": 1,
|
||||
"maximum": 9007199254740991,
|
||||
"default": 1,
|
||||
"type": "number"
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -7977,7 +7978,7 @@
|
||||
"minimum": 1,
|
||||
"maximum": 1000,
|
||||
"default": 500,
|
||||
"type": "number"
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -9372,7 +9373,7 @@
|
||||
],
|
||||
"x-immich-state": "Stable",
|
||||
"schema": {
|
||||
"type": "number",
|
||||
"type": "integer",
|
||||
"minimum": -1,
|
||||
"maximum": 5,
|
||||
"nullable": true
|
||||
@@ -9386,7 +9387,7 @@
|
||||
"schema": {
|
||||
"minimum": 1,
|
||||
"maximum": 1000,
|
||||
"type": "number"
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -15279,9 +15280,11 @@
|
||||
"type": "string"
|
||||
},
|
||||
"albumUsers": {
|
||||
"description": "First entry is always the album owner. Second entry is the auth user, if it differs from the owner. The rest are ordered alphabetically.",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/AlbumUserResponseDto"
|
||||
},
|
||||
"minItems": 1,
|
||||
"type": "array"
|
||||
},
|
||||
"assetCount": {
|
||||
@@ -15330,13 +15333,6 @@
|
||||
"order": {
|
||||
"$ref": "#/components/schemas/AssetOrder"
|
||||
},
|
||||
"owner": {
|
||||
"$ref": "#/components/schemas/UserResponseDto"
|
||||
},
|
||||
"ownerId": {
|
||||
"description": "Owner user ID",
|
||||
"type": "string"
|
||||
},
|
||||
"shared": {
|
||||
"description": "Is shared album",
|
||||
"type": "boolean"
|
||||
@@ -15362,8 +15358,6 @@
|
||||
"hasSharedLink",
|
||||
"id",
|
||||
"isActivityEnabled",
|
||||
"owner",
|
||||
"ownerId",
|
||||
"shared",
|
||||
"updatedAt"
|
||||
],
|
||||
@@ -15453,6 +15447,7 @@
|
||||
"description": "Album user role",
|
||||
"enum": [
|
||||
"editor",
|
||||
"owner",
|
||||
"viewer"
|
||||
],
|
||||
"type": "string"
|
||||
@@ -15642,7 +15637,9 @@
|
||||
},
|
||||
"dateTimeRelative": {
|
||||
"description": "Relative time offset in seconds",
|
||||
"type": "number"
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": -9007199254740991,
|
||||
"type": "integer"
|
||||
},
|
||||
"description": {
|
||||
"description": "Asset description",
|
||||
@@ -16264,8 +16261,10 @@
|
||||
"type": "string"
|
||||
},
|
||||
"duration": {
|
||||
"description": "Duration (for videos)",
|
||||
"type": "string"
|
||||
"description": "Duration in milliseconds (for videos)",
|
||||
"maximum": 2147483647,
|
||||
"minimum": 0,
|
||||
"type": "integer"
|
||||
},
|
||||
"fileCreatedAt": {
|
||||
"description": "File creation date",
|
||||
@@ -16633,9 +16632,11 @@
|
||||
"type": "string"
|
||||
},
|
||||
"duration": {
|
||||
"description": "Video/gif duration in hh:mm:ss.SSS format (null for static images)",
|
||||
"description": "Video/gif duration in milliseconds (null for static images)",
|
||||
"maximum": 2147483647,
|
||||
"minimum": 0,
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
"type": "integer"
|
||||
},
|
||||
"exifInfo": {
|
||||
"$ref": "#/components/schemas/ExifResponseDto"
|
||||
@@ -16656,9 +16657,10 @@
|
||||
},
|
||||
"height": {
|
||||
"description": "Asset height",
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": 0,
|
||||
"nullable": true,
|
||||
"type": "number"
|
||||
"type": "integer"
|
||||
},
|
||||
"id": {
|
||||
"description": "Asset ID",
|
||||
@@ -16801,9 +16803,10 @@
|
||||
},
|
||||
"width": {
|
||||
"description": "Asset width",
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": 0,
|
||||
"nullable": true,
|
||||
"type": "number"
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -17220,23 +17223,27 @@
|
||||
"properties": {
|
||||
"height": {
|
||||
"description": "Height of the crop",
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
"type": "integer"
|
||||
},
|
||||
"width": {
|
||||
"description": "Width of the crop",
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
"type": "integer"
|
||||
},
|
||||
"x": {
|
||||
"description": "Top-Left X coordinate of crop",
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": 0,
|
||||
"type": "number"
|
||||
"type": "integer"
|
||||
},
|
||||
"y": {
|
||||
"description": "Top-Left Y coordinate of crop",
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": 0,
|
||||
"type": "number"
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -17260,8 +17267,9 @@
|
||||
},
|
||||
"keepLastAmount": {
|
||||
"description": "Keep last amount",
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -17294,7 +17302,9 @@
|
||||
},
|
||||
"filesize": {
|
||||
"description": "Backup file size",
|
||||
"type": "number"
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": -9007199254740991,
|
||||
"type": "integer"
|
||||
},
|
||||
"timezone": {
|
||||
"description": "Backup timezone",
|
||||
@@ -17633,16 +17643,18 @@
|
||||
"exifImageHeight": {
|
||||
"default": null,
|
||||
"description": "Image height in pixels",
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": 0,
|
||||
"nullable": true,
|
||||
"type": "number"
|
||||
"type": "integer"
|
||||
},
|
||||
"exifImageWidth": {
|
||||
"default": null,
|
||||
"description": "Image width in pixels",
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": 0,
|
||||
"nullable": true,
|
||||
"type": "number"
|
||||
"type": "integer"
|
||||
},
|
||||
"exposureTime": {
|
||||
"default": null,
|
||||
@@ -17673,8 +17685,10 @@
|
||||
"iso": {
|
||||
"default": null,
|
||||
"description": "ISO sensitivity",
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": -9007199254740991,
|
||||
"nullable": true,
|
||||
"type": "number"
|
||||
"type": "integer"
|
||||
},
|
||||
"latitude": {
|
||||
"default": null,
|
||||
@@ -17728,8 +17742,10 @@
|
||||
"rating": {
|
||||
"default": null,
|
||||
"description": "Rating",
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": -9007199254740991,
|
||||
"nullable": true,
|
||||
"type": "number"
|
||||
"type": "integer"
|
||||
},
|
||||
"state": {
|
||||
"default": null,
|
||||
@@ -18156,10 +18172,14 @@
|
||||
"type": "boolean"
|
||||
},
|
||||
"interval": {
|
||||
"type": "number"
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": -9007199254740991,
|
||||
"type": "integer"
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number"
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": -9007199254740991,
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -18209,7 +18229,9 @@
|
||||
"properties": {
|
||||
"files": {
|
||||
"description": "Number of files in the folder",
|
||||
"type": "number"
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": -9007199254740991,
|
||||
"type": "integer"
|
||||
},
|
||||
"folder": {
|
||||
"$ref": "#/components/schemas/StorageFolder"
|
||||
@@ -18252,7 +18274,9 @@
|
||||
"type": "string"
|
||||
},
|
||||
"progress": {
|
||||
"type": "number"
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": -9007199254740991,
|
||||
"type": "integer"
|
||||
},
|
||||
"task": {
|
||||
"type": "string"
|
||||
@@ -18729,8 +18753,9 @@
|
||||
},
|
||||
"page": {
|
||||
"description": "Page number",
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
"type": "integer"
|
||||
},
|
||||
"personIds": {
|
||||
"description": "Filter by person IDs",
|
||||
@@ -18750,7 +18775,7 @@
|
||||
"maximum": 5,
|
||||
"minimum": -1,
|
||||
"nullable": true,
|
||||
"type": "number",
|
||||
"type": "integer",
|
||||
"x-immich-history": [
|
||||
{
|
||||
"version": "v1",
|
||||
@@ -18772,7 +18797,7 @@
|
||||
"description": "Number of results to return",
|
||||
"maximum": 1000,
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
"type": "integer"
|
||||
},
|
||||
"state": {
|
||||
"description": "Filter by state/province name",
|
||||
@@ -20603,7 +20628,7 @@
|
||||
"maximum": 5,
|
||||
"minimum": -1,
|
||||
"nullable": true,
|
||||
"type": "number",
|
||||
"type": "integer",
|
||||
"x-immich-history": [
|
||||
{
|
||||
"version": "v1",
|
||||
@@ -20625,7 +20650,7 @@
|
||||
"description": "Number of results to return",
|
||||
"maximum": 1000,
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
"type": "integer"
|
||||
},
|
||||
"state": {
|
||||
"description": "Filter by state/province name",
|
||||
@@ -21443,8 +21468,9 @@
|
||||
},
|
||||
"duration": {
|
||||
"description": "Session duration in seconds",
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
@@ -21958,8 +21984,9 @@
|
||||
},
|
||||
"page": {
|
||||
"description": "Page number",
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
"type": "integer"
|
||||
},
|
||||
"personIds": {
|
||||
"description": "Filter by person IDs",
|
||||
@@ -21985,7 +22012,7 @@
|
||||
"maximum": 5,
|
||||
"minimum": -1,
|
||||
"nullable": true,
|
||||
"type": "number",
|
||||
"type": "integer",
|
||||
"x-immich-history": [
|
||||
{
|
||||
"version": "v1",
|
||||
@@ -22007,7 +22034,7 @@
|
||||
"description": "Number of results to return",
|
||||
"maximum": 1000,
|
||||
"minimum": 1,
|
||||
"type": "number"
|
||||
"type": "integer"
|
||||
},
|
||||
"state": {
|
||||
"description": "Filter by state/province name",
|
||||
@@ -22245,7 +22272,7 @@
|
||||
"maximum": 5,
|
||||
"minimum": -1,
|
||||
"nullable": true,
|
||||
"type": "number",
|
||||
"type": "integer",
|
||||
"x-immich-history": [
|
||||
{
|
||||
"version": "v1",
|
||||
@@ -22531,6 +22558,59 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"SyncAlbumV2": {
|
||||
"properties": {
|
||||
"createdAt": {
|
||||
"description": "Created at",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"description": "Album description",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"description": "Album ID",
|
||||
"type": "string"
|
||||
},
|
||||
"isActivityEnabled": {
|
||||
"description": "Is activity enabled",
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"description": "Album name",
|
||||
"type": "string"
|
||||
},
|
||||
"order": {
|
||||
"$ref": "#/components/schemas/AssetOrder"
|
||||
},
|
||||
"thumbnailAssetId": {
|
||||
"description": "Thumbnail asset ID",
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
},
|
||||
"updatedAt": {
|
||||
"description": "Updated at",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"createdAt",
|
||||
"description",
|
||||
"id",
|
||||
"isActivityEnabled",
|
||||
"name",
|
||||
"order",
|
||||
"thumbnailAssetId",
|
||||
"updatedAt"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"SyncAssetDeleteV1": {
|
||||
"properties": {
|
||||
"assetId": {
|
||||
@@ -23099,6 +23179,135 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"SyncAssetV2": {
|
||||
"properties": {
|
||||
"checksum": {
|
||||
"description": "Checksum",
|
||||
"type": "string"
|
||||
},
|
||||
"deletedAt": {
|
||||
"description": "Deleted at",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"nullable": true,
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
},
|
||||
"duration": {
|
||||
"description": "Duration",
|
||||
"maximum": 2147483647,
|
||||
"minimum": 0,
|
||||
"nullable": true,
|
||||
"type": "integer"
|
||||
},
|
||||
"fileCreatedAt": {
|
||||
"description": "File created at",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"nullable": true,
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
},
|
||||
"fileModifiedAt": {
|
||||
"description": "File modified at",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"nullable": true,
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
},
|
||||
"height": {
|
||||
"description": "Asset height",
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": -9007199254740991,
|
||||
"nullable": true,
|
||||
"type": "integer"
|
||||
},
|
||||
"id": {
|
||||
"description": "Asset ID",
|
||||
"type": "string"
|
||||
},
|
||||
"isEdited": {
|
||||
"description": "Is edited",
|
||||
"type": "boolean"
|
||||
},
|
||||
"isFavorite": {
|
||||
"description": "Is favorite",
|
||||
"type": "boolean"
|
||||
},
|
||||
"libraryId": {
|
||||
"description": "Library ID",
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
},
|
||||
"livePhotoVideoId": {
|
||||
"description": "Live photo video ID",
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
},
|
||||
"localDateTime": {
|
||||
"description": "Local date time",
|
||||
"example": "2024-01-01T00:00:00.000Z",
|
||||
"format": "date-time",
|
||||
"nullable": true,
|
||||
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
|
||||
"type": "string"
|
||||
},
|
||||
"originalFileName": {
|
||||
"description": "Original file name",
|
||||
"type": "string"
|
||||
},
|
||||
"ownerId": {
|
||||
"description": "Owner ID",
|
||||
"type": "string"
|
||||
},
|
||||
"stackId": {
|
||||
"description": "Stack ID",
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
},
|
||||
"thumbhash": {
|
||||
"description": "Thumbhash",
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"$ref": "#/components/schemas/AssetTypeEnum"
|
||||
},
|
||||
"visibility": {
|
||||
"$ref": "#/components/schemas/AssetVisibility"
|
||||
},
|
||||
"width": {
|
||||
"description": "Asset width",
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": -9007199254740991,
|
||||
"nullable": true,
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"checksum",
|
||||
"deletedAt",
|
||||
"duration",
|
||||
"fileCreatedAt",
|
||||
"fileModifiedAt",
|
||||
"height",
|
||||
"id",
|
||||
"isEdited",
|
||||
"isFavorite",
|
||||
"libraryId",
|
||||
"livePhotoVideoId",
|
||||
"localDateTime",
|
||||
"originalFileName",
|
||||
"ownerId",
|
||||
"stackId",
|
||||
"thumbhash",
|
||||
"type",
|
||||
"visibility",
|
||||
"width"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"SyncAuthUserV1": {
|
||||
"properties": {
|
||||
"avatarColor": {
|
||||
@@ -23199,6 +23408,7 @@
|
||||
"UserV1",
|
||||
"UserDeleteV1",
|
||||
"AssetV1",
|
||||
"AssetV2",
|
||||
"AssetDeleteV1",
|
||||
"AssetExifV1",
|
||||
"AssetEditV1",
|
||||
@@ -23208,7 +23418,9 @@
|
||||
"PartnerV1",
|
||||
"PartnerDeleteV1",
|
||||
"PartnerAssetV1",
|
||||
"PartnerAssetV2",
|
||||
"PartnerAssetBackfillV1",
|
||||
"PartnerAssetBackfillV2",
|
||||
"PartnerAssetDeleteV1",
|
||||
"PartnerAssetExifV1",
|
||||
"PartnerAssetExifBackfillV1",
|
||||
@@ -23216,13 +23428,17 @@
|
||||
"PartnerStackDeleteV1",
|
||||
"PartnerStackV1",
|
||||
"AlbumV1",
|
||||
"AlbumV2",
|
||||
"AlbumDeleteV1",
|
||||
"AlbumUserV1",
|
||||
"AlbumUserBackfillV1",
|
||||
"AlbumUserDeleteV1",
|
||||
"AlbumAssetCreateV1",
|
||||
"AlbumAssetCreateV2",
|
||||
"AlbumAssetUpdateV1",
|
||||
"AlbumAssetUpdateV2",
|
||||
"AlbumAssetBackfillV1",
|
||||
"AlbumAssetBackfillV2",
|
||||
"AlbumAssetExifCreateV1",
|
||||
"AlbumAssetExifUpdateV1",
|
||||
"AlbumAssetExifBackfillV1",
|
||||
@@ -23510,11 +23726,14 @@
|
||||
"description": "Sync request type",
|
||||
"enum": [
|
||||
"AlbumsV1",
|
||||
"AlbumsV2",
|
||||
"AlbumUsersV1",
|
||||
"AlbumToAssetsV1",
|
||||
"AlbumAssetsV1",
|
||||
"AlbumAssetsV2",
|
||||
"AlbumAssetExifsV1",
|
||||
"AssetsV1",
|
||||
"AssetsV2",
|
||||
"AssetExifsV1",
|
||||
"AssetEditsV1",
|
||||
"AssetMetadataV1",
|
||||
@@ -23523,6 +23742,7 @@
|
||||
"MemoryToAssetsV1",
|
||||
"PartnersV1",
|
||||
"PartnerAssetsV1",
|
||||
"PartnerAssetsV2",
|
||||
"PartnerAssetExifsV1",
|
||||
"PartnerStacksV1",
|
||||
"StacksV1",
|
||||
@@ -24322,9 +24542,10 @@
|
||||
},
|
||||
"defaultStorageQuota": {
|
||||
"description": "Default storage quota",
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": 0,
|
||||
"nullable": true,
|
||||
"type": "number"
|
||||
"type": "integer"
|
||||
},
|
||||
"enabled": {
|
||||
"description": "Enabled",
|
||||
@@ -24499,7 +24720,7 @@
|
||||
"description": "SMTP server port",
|
||||
"maximum": 65535,
|
||||
"minimum": 0,
|
||||
"type": "number"
|
||||
"type": "integer"
|
||||
},
|
||||
"secure": {
|
||||
"description": "Whether to use secure connection (TLS/SSL)",
|
||||
@@ -24915,10 +25136,12 @@
|
||||
"type": "array"
|
||||
},
|
||||
"duration": {
|
||||
"description": "Array of video/gif durations in hh:mm:ss.SSS format (null for static images)",
|
||||
"description": "Array of video/gif durations in milliseconds (null for static images)",
|
||||
"items": {
|
||||
"maximum": 2147483647,
|
||||
"minimum": 0,
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
"type": "integer"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
@@ -25917,7 +26140,9 @@
|
||||
},
|
||||
"order": {
|
||||
"description": "Action order",
|
||||
"type": "number"
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": -9007199254740991,
|
||||
"type": "integer"
|
||||
},
|
||||
"pluginActionId": {
|
||||
"description": "Plugin action ID",
|
||||
@@ -26016,7 +26241,9 @@
|
||||
},
|
||||
"order": {
|
||||
"description": "Filter order",
|
||||
"type": "number"
|
||||
"maximum": 9007199254740991,
|
||||
"minimum": -9007199254740991,
|
||||
"type": "integer"
|
||||
},
|
||||
"pluginFilterId": {
|
||||
"description": "Plugin filter ID",
|
||||
|
||||
@@ -457,6 +457,7 @@ export type AlbumResponseDto = {
|
||||
albumName: string;
|
||||
/** Thumbnail asset ID */
|
||||
albumThumbnailAssetId: string | null;
|
||||
/** First entry is always the album owner. Second entry is the auth user, if it differs from the owner. The rest are ordered alphabetically. */
|
||||
albumUsers: AlbumUserResponseDto[];
|
||||
/** Number of assets */
|
||||
assetCount: number;
|
||||
@@ -476,9 +477,6 @@ export type AlbumResponseDto = {
|
||||
/** Last modified asset timestamp */
|
||||
lastModifiedAssetTimestamp?: string;
|
||||
order?: AssetOrder;
|
||||
owner: UserResponseDto;
|
||||
/** Owner user ID */
|
||||
ownerId: string;
|
||||
/** Is shared album */
|
||||
shared: boolean;
|
||||
/** Start date (earliest asset) */
|
||||
@@ -616,8 +614,8 @@ export type AssetMetadataUpsertItemDto = {
|
||||
export type AssetMediaCreateDto = {
|
||||
/** Asset file data */
|
||||
assetData: Blob;
|
||||
/** Duration (for videos) */
|
||||
duration?: string;
|
||||
/** Duration in milliseconds (for videos) */
|
||||
duration?: number;
|
||||
/** File creation date */
|
||||
fileCreatedAt: string;
|
||||
/** File modification date */
|
||||
@@ -856,8 +854,8 @@ export type AssetResponseDto = {
|
||||
createdAt: string;
|
||||
/** Duplicate group ID */
|
||||
duplicateId?: string | null;
|
||||
/** Video/gif duration in hh:mm:ss.SSS format (null for static images) */
|
||||
duration: string | null;
|
||||
/** Video/gif duration in milliseconds (null for static images) */
|
||||
duration: number | null;
|
||||
exifInfo?: ExifResponseDto;
|
||||
/** The actual UTC timestamp when the file was created/captured, preserving timezone information. This is the authoritative timestamp for chronological sorting within timeline groups. Combined with timezone data, this can be used to determine the exact moment the photo was taken. */
|
||||
fileCreatedAt: string;
|
||||
@@ -2675,8 +2673,8 @@ export type TimeBucketAssetResponseDto = {
|
||||
city: (string | null)[];
|
||||
/** Array of country names extracted from EXIF GPS data */
|
||||
country: (string | null)[];
|
||||
/** Array of video/gif durations in hh:mm:ss.SSS format (null for static images) */
|
||||
duration: (string | null)[];
|
||||
/** Array of video/gif durations in milliseconds (null for static images) */
|
||||
duration: (number | null)[];
|
||||
/** Array of file creation timestamps in UTC */
|
||||
fileCreatedAt: string[];
|
||||
/** Array of asset IDs in the time bucket */
|
||||
@@ -2881,6 +2879,23 @@ export type SyncAlbumV1 = {
|
||||
/** Updated at */
|
||||
updatedAt: string;
|
||||
};
|
||||
export type SyncAlbumV2 = {
|
||||
/** Created at */
|
||||
createdAt: string;
|
||||
/** Album description */
|
||||
description: string;
|
||||
/** Album ID */
|
||||
id: string;
|
||||
/** Is activity enabled */
|
||||
isActivityEnabled: boolean;
|
||||
/** Album name */
|
||||
name: string;
|
||||
order: AssetOrder;
|
||||
/** Thumbnail asset ID */
|
||||
thumbnailAssetId: string | null;
|
||||
/** Updated at */
|
||||
updatedAt: string;
|
||||
};
|
||||
export type SyncAssetDeleteV1 = {
|
||||
/** Asset ID */
|
||||
assetId: string;
|
||||
@@ -3060,6 +3075,44 @@ export type SyncAssetV1 = {
|
||||
/** Asset width */
|
||||
width: number | null;
|
||||
};
|
||||
export type SyncAssetV2 = {
|
||||
/** Checksum */
|
||||
checksum: string;
|
||||
/** Deleted at */
|
||||
deletedAt: string | null;
|
||||
/** Duration */
|
||||
duration: number | null;
|
||||
/** File created at */
|
||||
fileCreatedAt: string | null;
|
||||
/** File modified at */
|
||||
fileModifiedAt: string | null;
|
||||
/** Asset height */
|
||||
height: number | null;
|
||||
/** Asset ID */
|
||||
id: string;
|
||||
/** Is edited */
|
||||
isEdited: boolean;
|
||||
/** Is favorite */
|
||||
isFavorite: boolean;
|
||||
/** Library ID */
|
||||
libraryId: string | null;
|
||||
/** Live photo video ID */
|
||||
livePhotoVideoId: string | null;
|
||||
/** Local date time */
|
||||
localDateTime: string | null;
|
||||
/** Original file name */
|
||||
originalFileName: string;
|
||||
/** Owner ID */
|
||||
ownerId: string;
|
||||
/** Stack ID */
|
||||
stackId: string | null;
|
||||
/** Thumbhash */
|
||||
thumbhash: string | null;
|
||||
"type": AssetTypeEnum;
|
||||
visibility: AssetVisibility;
|
||||
/** Asset width */
|
||||
width: number | null;
|
||||
};
|
||||
export type SyncAuthUserV1 = {
|
||||
avatarColor?: (UserAvatarColor) | null;
|
||||
/** User deleted at */
|
||||
@@ -6731,6 +6784,7 @@ export enum AssetVisibility {
|
||||
}
|
||||
export enum AlbumUserRole {
|
||||
Editor = "editor",
|
||||
Owner = "owner",
|
||||
Viewer = "viewer"
|
||||
}
|
||||
export enum BulkIdErrorReason {
|
||||
@@ -7093,6 +7147,7 @@ export enum SyncEntityType {
|
||||
UserV1 = "UserV1",
|
||||
UserDeleteV1 = "UserDeleteV1",
|
||||
AssetV1 = "AssetV1",
|
||||
AssetV2 = "AssetV2",
|
||||
AssetDeleteV1 = "AssetDeleteV1",
|
||||
AssetExifV1 = "AssetExifV1",
|
||||
AssetEditV1 = "AssetEditV1",
|
||||
@@ -7102,7 +7157,9 @@ export enum SyncEntityType {
|
||||
PartnerV1 = "PartnerV1",
|
||||
PartnerDeleteV1 = "PartnerDeleteV1",
|
||||
PartnerAssetV1 = "PartnerAssetV1",
|
||||
PartnerAssetV2 = "PartnerAssetV2",
|
||||
PartnerAssetBackfillV1 = "PartnerAssetBackfillV1",
|
||||
PartnerAssetBackfillV2 = "PartnerAssetBackfillV2",
|
||||
PartnerAssetDeleteV1 = "PartnerAssetDeleteV1",
|
||||
PartnerAssetExifV1 = "PartnerAssetExifV1",
|
||||
PartnerAssetExifBackfillV1 = "PartnerAssetExifBackfillV1",
|
||||
@@ -7110,13 +7167,17 @@ export enum SyncEntityType {
|
||||
PartnerStackDeleteV1 = "PartnerStackDeleteV1",
|
||||
PartnerStackV1 = "PartnerStackV1",
|
||||
AlbumV1 = "AlbumV1",
|
||||
AlbumV2 = "AlbumV2",
|
||||
AlbumDeleteV1 = "AlbumDeleteV1",
|
||||
AlbumUserV1 = "AlbumUserV1",
|
||||
AlbumUserBackfillV1 = "AlbumUserBackfillV1",
|
||||
AlbumUserDeleteV1 = "AlbumUserDeleteV1",
|
||||
AlbumAssetCreateV1 = "AlbumAssetCreateV1",
|
||||
AlbumAssetCreateV2 = "AlbumAssetCreateV2",
|
||||
AlbumAssetUpdateV1 = "AlbumAssetUpdateV1",
|
||||
AlbumAssetUpdateV2 = "AlbumAssetUpdateV2",
|
||||
AlbumAssetBackfillV1 = "AlbumAssetBackfillV1",
|
||||
AlbumAssetBackfillV2 = "AlbumAssetBackfillV2",
|
||||
AlbumAssetExifCreateV1 = "AlbumAssetExifCreateV1",
|
||||
AlbumAssetExifUpdateV1 = "AlbumAssetExifUpdateV1",
|
||||
AlbumAssetExifBackfillV1 = "AlbumAssetExifBackfillV1",
|
||||
@@ -7142,11 +7203,14 @@ export enum SyncEntityType {
|
||||
}
|
||||
export enum SyncRequestType {
|
||||
AlbumsV1 = "AlbumsV1",
|
||||
AlbumsV2 = "AlbumsV2",
|
||||
AlbumUsersV1 = "AlbumUsersV1",
|
||||
AlbumToAssetsV1 = "AlbumToAssetsV1",
|
||||
AlbumAssetsV1 = "AlbumAssetsV1",
|
||||
AlbumAssetsV2 = "AlbumAssetsV2",
|
||||
AlbumAssetExifsV1 = "AlbumAssetExifsV1",
|
||||
AssetsV1 = "AssetsV1",
|
||||
AssetsV2 = "AssetsV2",
|
||||
AssetExifsV1 = "AssetExifsV1",
|
||||
AssetEditsV1 = "AssetEditsV1",
|
||||
AssetMetadataV1 = "AssetMetadataV1",
|
||||
@@ -7155,6 +7219,7 @@ export enum SyncRequestType {
|
||||
MemoryToAssetsV1 = "MemoryToAssetsV1",
|
||||
PartnersV1 = "PartnersV1",
|
||||
PartnerAssetsV1 = "PartnerAssetsV1",
|
||||
PartnerAssetsV2 = "PartnerAssetsV2",
|
||||
PartnerAssetExifsV1 = "PartnerAssetExifsV1",
|
||||
PartnerStacksV1 = "PartnerStacksV1",
|
||||
StacksV1 = "StacksV1",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "2.7.5",
|
||||
"description": "Monorepo for Immich",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319",
|
||||
"packageManager": "pnpm@10.33.1+sha512.05ba3c1d5d1c18f68df06470d74055e62d41fc110a0c660db1b2dfb2785327f04cf0f68345d4609bc52089e7fa0343c31593b2f9594e2c5d5da426230acc9820",
|
||||
"engines": {
|
||||
"pnpm": ">=10.0.0"
|
||||
}
|
||||
|
||||
6
plugins/package-lock.json
generated
6
plugins/package-lock.json
generated
@@ -509,9 +509,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz",
|
||||
"integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==",
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
|
||||
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
|
||||
5710
pnpm-lock.yaml
generated
5710
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -46,18 +46,18 @@
|
||||
"@nestjs/platform-express": "^11.0.4",
|
||||
"@nestjs/platform-socket.io": "^11.0.4",
|
||||
"@nestjs/schedule": "^6.0.0",
|
||||
"@nestjs/swagger": "^11.0.2",
|
||||
"@nestjs/swagger": "^11.4.2",
|
||||
"@nestjs/websockets": "^11.0.4",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@opentelemetry/context-async-hooks": "^2.0.0",
|
||||
"@opentelemetry/exporter-prometheus": "^0.214.0",
|
||||
"@opentelemetry/instrumentation-http": "^0.214.0",
|
||||
"@opentelemetry/instrumentation-ioredis": "^0.62.0",
|
||||
"@opentelemetry/instrumentation-nestjs-core": "^0.60.0",
|
||||
"@opentelemetry/instrumentation-pg": "^0.66.0",
|
||||
"@opentelemetry/exporter-prometheus": "^0.215.0",
|
||||
"@opentelemetry/instrumentation-http": "^0.215.0",
|
||||
"@opentelemetry/instrumentation-ioredis": "^0.63.0",
|
||||
"@opentelemetry/instrumentation-nestjs-core": "^0.61.0",
|
||||
"@opentelemetry/instrumentation-pg": "^0.67.0",
|
||||
"@opentelemetry/resources": "^2.0.1",
|
||||
"@opentelemetry/sdk-metrics": "^2.0.1",
|
||||
"@opentelemetry/sdk-node": "^0.214.0",
|
||||
"@opentelemetry/sdk-node": "^0.215.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.34.0",
|
||||
"@react-email/components": "^1.0.0",
|
||||
"@react-email/render": "^2.0.0",
|
||||
@@ -114,7 +114,7 @@
|
||||
"thumbhash": "^0.1.1",
|
||||
"transformation-matrix": "^3.1.0",
|
||||
"ua-parser-js": "^2.0.0",
|
||||
"uuid": "^11.1.0",
|
||||
"uuid": "^14.0.0",
|
||||
"validator": "^13.12.0",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
|
||||
@@ -49,7 +49,7 @@ describe(SearchController.name, () => {
|
||||
});
|
||||
|
||||
it('should reject an invalid size', async () => {
|
||||
const { status, body } = await request(ctx.getHttpServer()).post('/search/metadata').send({ size: -1.5 });
|
||||
const { status, body } = await request(ctx.getHttpServer()).post('/search/metadata').send({ size: -1 });
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest(['[size] Too small: expected number to be >=1']));
|
||||
});
|
||||
|
||||
@@ -195,7 +195,6 @@ export type SharedLink = {
|
||||
};
|
||||
|
||||
export type Album = Selectable<AlbumTable> & {
|
||||
owner: ShallowDehydrateObject<User>;
|
||||
assets: ShallowDehydrateObject<Selectable<AssetTable>>[];
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { ShallowDehydrateObject } from 'kysely';
|
||||
import _ from 'lodash';
|
||||
import { createZodDto } from 'nestjs-zod';
|
||||
import { AlbumUser, AuthSharedLink, User } from 'src/database';
|
||||
import { AlbumUser, AuthSharedLink } from 'src/database';
|
||||
import { BulkIdErrorReasonSchema } from 'src/dtos/asset-ids.response.dto';
|
||||
import { MapAsset } from 'src/dtos/asset-response.dto';
|
||||
import { UserResponseSchema, mapUser } from 'src/dtos/user.dto';
|
||||
@@ -104,7 +103,6 @@ const ContributorCountResponseSchema = z
|
||||
export const AlbumResponseSchema = z
|
||||
.object({
|
||||
id: z.string().describe('Album ID'),
|
||||
ownerId: z.string().describe('Owner user ID'),
|
||||
albumName: z.string().describe('Album name'),
|
||||
description: z.string().describe('Album description'),
|
||||
// TODO: use `isoDatetimeToDate` when using `ZodSerializerDto` on the controllers.
|
||||
@@ -113,9 +111,13 @@ export const AlbumResponseSchema = z
|
||||
updatedAt: z.string().meta({ format: 'date-time' }).describe('Last update date'),
|
||||
albumThumbnailAssetId: z.string().nullable().describe('Thumbnail asset ID'),
|
||||
shared: z.boolean().describe('Is shared album'),
|
||||
albumUsers: z.array(AlbumUserResponseSchema),
|
||||
albumUsers: z
|
||||
.array(AlbumUserResponseSchema)
|
||||
.min(1)
|
||||
.describe(
|
||||
'First entry is always the album owner. Second entry is the auth user, if it differs from the owner. The rest are ordered alphabetically.',
|
||||
),
|
||||
hasSharedLink: z.boolean().describe('Has shared link'),
|
||||
owner: UserResponseSchema,
|
||||
assetCount: z.int().min(0).describe('Number of assets'),
|
||||
// TODO: use `isoDatetimeToDate` when using `ZodSerializerDto` on the controllers.
|
||||
lastModifiedAssetTimestamp: z
|
||||
@@ -155,8 +157,6 @@ export type MapAlbumDto = {
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
id: string;
|
||||
ownerId: string;
|
||||
owner: ShallowDehydrateObject<User>;
|
||||
isActivityEnabled: boolean;
|
||||
order: AssetOrder;
|
||||
};
|
||||
@@ -174,12 +174,10 @@ export const mapAlbum = (entity: MaybeDehydrated<MapAlbumDto>): AlbumResponseDto
|
||||
}
|
||||
}
|
||||
|
||||
const albumUsersSorted = _.orderBy(albumUsers, ['role', 'user.name']);
|
||||
|
||||
const assets = entity.assets || [];
|
||||
|
||||
const hasSharedLink = !!entity.sharedLinks && entity.sharedLinks.length > 0;
|
||||
const hasSharedUser = albumUsers.length > 0;
|
||||
const hasSharedUser = albumUsers.length > 1;
|
||||
|
||||
let startDate = assets.at(0)?.localDateTime;
|
||||
let endDate = assets.at(-1)?.localDateTime;
|
||||
@@ -195,9 +193,7 @@ export const mapAlbum = (entity: MaybeDehydrated<MapAlbumDto>): AlbumResponseDto
|
||||
createdAt: asDateString(entity.createdAt),
|
||||
updatedAt: asDateString(entity.updatedAt),
|
||||
id: entity.id,
|
||||
ownerId: entity.ownerId,
|
||||
owner: mapUser(entity.owner),
|
||||
albumUsers: albumUsersSorted,
|
||||
albumUsers,
|
||||
shared: hasSharedUser || hasSharedLink,
|
||||
hasSharedLink,
|
||||
startDate: asDateString(startDate),
|
||||
|
||||
@@ -38,7 +38,7 @@ export enum UploadFieldName {
|
||||
const AssetMediaBaseSchema = z.object({
|
||||
fileCreatedAt: isoDatetimeToDate.describe('File creation date'),
|
||||
fileModifiedAt: isoDatetimeToDate.describe('File modification date'),
|
||||
duration: z.string().optional().describe('Duration (for videos)'),
|
||||
duration: z.int32().min(0).optional().describe('Duration in milliseconds (for videos)'),
|
||||
filename: z.string().optional().describe('Filename'),
|
||||
/** The properties below are added to correctly generate the API docs and client SDKs. Validation should be handled in the controller. */
|
||||
[UploadFieldName.ASSET_DATA]: z.any().describe('Asset file data').meta({ type: 'string', format: 'binary' }),
|
||||
|
||||
@@ -47,11 +47,11 @@ const SanitizedAssetResponseSchema = z
|
||||
.describe(
|
||||
'The local date and time when the photo/video was taken, derived from EXIF metadata. This represents the photographer\'s local time regardless of timezone, stored as a timezone-agnostic timestamp. Used for timeline grouping by "local" days and months.',
|
||||
),
|
||||
duration: z.string().nullable().describe('Video/gif duration in hh:mm:ss.SSS format (null for static images)'),
|
||||
duration: z.int32().min(0).nullable().describe('Video/gif duration in milliseconds (null for static images)'),
|
||||
livePhotoVideoId: z.string().nullish().describe('Live photo video ID'),
|
||||
hasMetadata: z.boolean().describe('Whether asset has metadata'),
|
||||
width: z.number().min(0).nullable().describe('Asset width'),
|
||||
height: z.number().min(0).nullable().describe('Asset height'),
|
||||
width: z.int().min(0).nullable().describe('Asset width'),
|
||||
height: z.int().min(0).nullable().describe('Asset height'),
|
||||
})
|
||||
.meta({ id: 'SanitizedAssetResponseDto' });
|
||||
|
||||
@@ -136,7 +136,7 @@ export type MapAsset = {
|
||||
checksum: Buffer<ArrayBufferLike>;
|
||||
checksumAlgorithm: ChecksumAlgorithm;
|
||||
duplicateId: string | null;
|
||||
duration: string | null;
|
||||
duration: number | null;
|
||||
edits?: ShallowDehydrateObject<AssetEditActionItem>[];
|
||||
exifInfo?: ShallowDehydrateObject<Selectable<Exif>> | null;
|
||||
faces?: ShallowDehydrateObject<AssetFace>[];
|
||||
|
||||
@@ -40,7 +40,7 @@ const UpdateAssetBaseSchema = z
|
||||
const AssetBulkUpdateBaseSchema = UpdateAssetBaseSchema.extend({
|
||||
ids: z.array(z.uuidv4()).describe('Asset IDs to update'),
|
||||
duplicateId: z.string().nullish().describe('Duplicate ID'),
|
||||
dateTimeRelative: z.number().optional().describe('Relative time offset in seconds'),
|
||||
dateTimeRelative: z.int().optional().describe('Relative time offset in seconds'),
|
||||
timeZone: z.string().optional().describe('Time zone (IANA timezone)'),
|
||||
});
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import z from 'zod';
|
||||
const DatabaseBackupSchema = z
|
||||
.object({
|
||||
filename: z.string().describe('Backup filename'),
|
||||
filesize: z.number().describe('Backup file size'),
|
||||
filesize: z.int().describe('Backup file size'),
|
||||
timezone: z.string().describe('Backup timezone'),
|
||||
})
|
||||
.meta({ id: 'DatabaseBackupDto' });
|
||||
|
||||
@@ -21,10 +21,10 @@ const MirrorAxisSchema = z.enum(['horizontal', 'vertical']).describe('Axis to mi
|
||||
|
||||
const CropParametersSchema = z
|
||||
.object({
|
||||
x: z.number().min(0).describe('Top-Left X coordinate of crop'),
|
||||
y: z.number().min(0).describe('Top-Left Y coordinate of crop'),
|
||||
width: z.number().min(1).describe('Width of the crop'),
|
||||
height: z.number().min(1).describe('Height of the crop'),
|
||||
x: z.int().min(0).describe('Top-Left X coordinate of crop'),
|
||||
y: z.int().min(0).describe('Top-Left Y coordinate of crop'),
|
||||
width: z.int().min(1).describe('Width of the crop'),
|
||||
height: z.int().min(1).describe('Height of the crop'),
|
||||
})
|
||||
.meta({ id: 'CropParameters' });
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ export const ExifResponseSchema = z
|
||||
.object({
|
||||
make: z.string().nullish().default(null).describe('Camera make'),
|
||||
model: z.string().nullish().default(null).describe('Camera model'),
|
||||
exifImageWidth: z.number().min(0).nullish().default(null).describe('Image width in pixels'),
|
||||
exifImageHeight: z.number().min(0).nullish().default(null).describe('Image height in pixels'),
|
||||
exifImageWidth: z.int().min(0).nullish().default(null).describe('Image width in pixels'),
|
||||
exifImageHeight: z.int().min(0).nullish().default(null).describe('Image height in pixels'),
|
||||
fileSizeInByte: z.int().min(0).nullish().default(null).describe('File size in bytes'),
|
||||
orientation: z.string().nullish().default(null).describe('Image orientation'),
|
||||
// TODO: use `isoDatetimeToDate` when using `ZodSerializerDto` on the controllers.
|
||||
@@ -20,7 +20,7 @@ export const ExifResponseSchema = z
|
||||
lensModel: z.string().nullish().default(null).describe('Lens model'),
|
||||
fNumber: z.number().nullish().default(null).describe('F-number (aperture)'),
|
||||
focalLength: z.number().nullish().default(null).describe('Focal length in mm'),
|
||||
iso: z.number().nullish().default(null).describe('ISO sensitivity'),
|
||||
iso: z.int().nullish().default(null).describe('ISO sensitivity'),
|
||||
exposureTime: z.string().nullish().default(null).describe('Exposure time'),
|
||||
latitude: z.number().nullish().default(null).describe('GPS latitude'),
|
||||
longitude: z.number().nullish().default(null).describe('GPS longitude'),
|
||||
@@ -29,7 +29,7 @@ export const ExifResponseSchema = z
|
||||
country: z.string().nullish().default(null).describe('Country name'),
|
||||
description: z.string().nullish().default(null).describe('Image description'),
|
||||
projectionType: z.string().nullish().default(null).describe('Projection type'),
|
||||
rating: z.number().nullish().default(null).describe('Rating'),
|
||||
rating: z.int().nullish().default(null).describe('Rating'),
|
||||
})
|
||||
.describe('EXIF response')
|
||||
.meta({ id: 'ExifResponseDto' });
|
||||
|
||||
@@ -29,7 +29,7 @@ const MaintenanceStatusResponseSchema = z
|
||||
.object({
|
||||
active: z.boolean(),
|
||||
action: MaintenanceActionSchema,
|
||||
progress: z.number().optional(),
|
||||
progress: z.int().optional(),
|
||||
task: z.string().optional(),
|
||||
error: z.string().optional(),
|
||||
})
|
||||
@@ -40,7 +40,7 @@ const MaintenanceDetectInstallStorageFolderSchema = z
|
||||
folder: StorageFolderSchema,
|
||||
readable: z.boolean().describe('Whether the folder is readable'),
|
||||
writable: z.boolean().describe('Whether the folder is writable'),
|
||||
files: z.number().describe('Number of files in the folder'),
|
||||
files: z.int().describe('Number of files in the folder'),
|
||||
})
|
||||
.meta({ id: 'MaintenanceDetectInstallStorageFolderDto' });
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user