mirror of
https://github.com/immich-app/immich.git
synced 2025-05-31 04:05:39 -04:00
fix(server): stack info in asset response for mobile (#7346)
* fix(server): stack info in asset response for mobile * fix(server): getAllAssets - do not filter by stack ID * tet(server): GET /assets stack e2e * chore(server): fix checks * stack asset height --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
parent
4c0bb2308c
commit
52dfe5fc92
@ -180,4 +180,4 @@ SPEC CHECKSUMS:
|
|||||||
|
|
||||||
PODFILE CHECKSUM: 64c9b5291666c0ca3caabdfe9865c141ac40321d
|
PODFILE CHECKSUM: 64c9b5291666c0ca3caabdfe9865c141ac40321d
|
||||||
|
|
||||||
COCOAPODS: 1.12.1
|
COCOAPODS: 1.11.3
|
||||||
|
@ -218,18 +218,19 @@ class GalleryViewerPage extends HookConsumerWidget {
|
|||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
itemCount: stackElements.length,
|
itemCount: stackElements.length,
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
left: 10,
|
left: 5,
|
||||||
right: 10,
|
right: 5,
|
||||||
bottom: 30,
|
bottom: 30,
|
||||||
),
|
),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final assetId = stackElements.elementAt(index).remoteId;
|
final assetId = stackElements.elementAt(index).remoteId;
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(right: 10),
|
padding: const EdgeInsets.only(right: 5),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () => stackIndex.value = index,
|
onTap: () => stackIndex.value = index,
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 40,
|
width: 60,
|
||||||
|
height: 60,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(6),
|
||||||
@ -391,7 +392,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
|||||||
Visibility(
|
Visibility(
|
||||||
visible: stack.isNotEmpty,
|
visible: stack.isNotEmpty,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 80,
|
||||||
child: buildStackedChildren(),
|
child: buildStackedChildren(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -14,6 +14,7 @@ import { AssetEntity, AssetStackEntity, AssetType, SharedLinkType } from '@app/i
|
|||||||
import { AssetRepository } from '@app/infra/repositories';
|
import { AssetRepository } from '@app/infra/repositories';
|
||||||
import { INestApplication } from '@nestjs/common';
|
import { INestApplication } from '@nestjs/common';
|
||||||
import { errorStub, userDto, uuidStub } from '@test/fixtures';
|
import { errorStub, userDto, uuidStub } from '@test/fixtures';
|
||||||
|
import { assetApi } from 'e2e/client/asset-api';
|
||||||
import { randomBytes } from 'node:crypto';
|
import { randomBytes } from 'node:crypto';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { api } from '../../client';
|
import { api } from '../../client';
|
||||||
@ -532,6 +533,23 @@ describe(`${AssetController.name} (e2e)`, () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it('should return stack data', async () => {
|
||||||
|
const parentId = asset1.id;
|
||||||
|
const childIds = [asset2.id, asset3.id];
|
||||||
|
await request(server)
|
||||||
|
.put('/asset')
|
||||||
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
|
.send({ stackParentId: parentId, ids: childIds });
|
||||||
|
|
||||||
|
const body = await assetApi.getAllAssets(server, user1.accessToken);
|
||||||
|
// Response includes parent with stack children count
|
||||||
|
const parentDto = body.find((a) => a.id == parentId);
|
||||||
|
expect(parentDto?.stackCount).toEqual(3);
|
||||||
|
|
||||||
|
// Response includes children at the root level
|
||||||
|
expect.arrayContaining([expect.objectContaining({ id: asset1.id }), expect.objectContaining({ id: asset2.id })]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /asset/upload', () => {
|
describe('POST /asset/upload', () => {
|
||||||
|
@ -92,12 +92,12 @@ export interface SearchStatusOptions {
|
|||||||
export interface SearchOneToOneRelationOptions {
|
export interface SearchOneToOneRelationOptions {
|
||||||
withExif?: boolean;
|
withExif?: boolean;
|
||||||
withSmartInfo?: boolean;
|
withSmartInfo?: boolean;
|
||||||
|
withStacked?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SearchRelationOptions extends SearchOneToOneRelationOptions {
|
export interface SearchRelationOptions extends SearchOneToOneRelationOptions {
|
||||||
withFaces?: boolean;
|
withFaces?: boolean;
|
||||||
withPeople?: boolean;
|
withPeople?: boolean;
|
||||||
withStacked?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SearchDateOptions {
|
export interface SearchDateOptions {
|
||||||
|
@ -116,9 +116,17 @@ export class AssetService {
|
|||||||
await this.access.requirePermission(auth, Permission.TIMELINE_READ, userId);
|
await this.access.requirePermission(auth, Permission.TIMELINE_READ, userId);
|
||||||
const assets = await this.assetRepository.getAllByFileCreationDate(
|
const assets = await this.assetRepository.getAllByFileCreationDate(
|
||||||
{ take: dto.take ?? 1000, skip: dto.skip },
|
{ take: dto.take ?? 1000, skip: dto.skip },
|
||||||
{ ...dto, userIds: [userId], withDeleted: true, orderDirection: 'DESC', withExif: true, isVisible: true },
|
{
|
||||||
|
...dto,
|
||||||
|
userIds: [userId],
|
||||||
|
withDeleted: true,
|
||||||
|
orderDirection: 'DESC',
|
||||||
|
withExif: true,
|
||||||
|
isVisible: true,
|
||||||
|
withStacked: true,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
return assets.items.map((asset) => mapAsset(asset));
|
return assets.items.map((asset) => mapAsset(asset, { withStack: true }));
|
||||||
}
|
}
|
||||||
|
|
||||||
async serveThumbnail(auth: AuthDto, assetId: string, dto: GetAssetThumbnailDto): Promise<ImmichFileResponse> {
|
async serveThumbnail(auth: AuthDto, assetId: string, dto: GetAssetThumbnailDto): Promise<ImmichFileResponse> {
|
||||||
|
@ -2,7 +2,6 @@ import { AssetSearchBuilderOptions, Paginated, PaginationOptions } from '@app/do
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import {
|
import {
|
||||||
Between,
|
Between,
|
||||||
Brackets,
|
|
||||||
FindManyOptions,
|
FindManyOptions,
|
||||||
IsNull,
|
IsNull,
|
||||||
LessThanOrEqual,
|
LessThanOrEqual,
|
||||||
@ -229,12 +228,7 @@ export function searchAssetBuilder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (withStacked) {
|
if (withStacked) {
|
||||||
builder
|
builder.leftJoinAndSelect(`${builder.alias}.stack`, 'stack').leftJoinAndSelect('stack.assets', 'stackedAssets');
|
||||||
.leftJoinAndSelect(`${builder.alias}.stack`, 'stack')
|
|
||||||
.leftJoinAndSelect('stack.assets', 'stackedAssets')
|
|
||||||
.andWhere(
|
|
||||||
new Brackets((qb) => qb.where(`stack.primaryAssetId = ${builder.alias}.id`).orWhere('asset.stackId IS NULL')),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const withDeleted = options.withDeleted ?? (trashedAfter !== undefined || trashedBefore !== undefined);
|
const withDeleted = options.withDeleted ?? (trashedAfter !== undefined || trashedBefore !== undefined);
|
||||||
|
@ -83,10 +83,6 @@ FROM
|
|||||||
"asset"."isFavorite" = $3
|
"asset"."isFavorite" = $3
|
||||||
AND "asset"."isArchived" = $4
|
AND "asset"."isArchived" = $4
|
||||||
)
|
)
|
||||||
AND (
|
|
||||||
"stack"."primaryAssetId" = "asset"."id"
|
|
||||||
OR "asset"."stackId" IS NULL
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
AND ("asset"."deletedAt" IS NULL)
|
AND ("asset"."deletedAt" IS NULL)
|
||||||
) "distinctAlias"
|
) "distinctAlias"
|
||||||
@ -184,10 +180,6 @@ WHERE
|
|||||||
"asset"."isFavorite" = $3
|
"asset"."isFavorite" = $3
|
||||||
AND "asset"."isArchived" = $4
|
AND "asset"."isArchived" = $4
|
||||||
)
|
)
|
||||||
AND (
|
|
||||||
"stack"."primaryAssetId" = "asset"."id"
|
|
||||||
OR "asset"."stackId" IS NULL
|
|
||||||
)
|
|
||||||
AND "asset"."ownerId" IN ($5)
|
AND "asset"."ownerId" IN ($5)
|
||||||
)
|
)
|
||||||
AND ("asset"."deletedAt" IS NULL)
|
AND ("asset"."deletedAt" IS NULL)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user