Skip to content

Commit

Permalink
fix: memory lane assets in ascending order (#8309)
Browse files Browse the repository at this point in the history
* fix: memory lane asset order

* chore: deprecate title

* chore: open-api

* chore: rename years => yearsAgo
  • Loading branch information
jrasm91 committed Mar 27, 2024
1 parent 13b11a3 commit 9fe80c2
Show file tree
Hide file tree
Showing 12 changed files with 65 additions and 29 deletions.
1 change: 1 addition & 0 deletions mobile/openapi/doc/MemoryLaneResponseDto.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 11 additions & 3 deletions mobile/openapi/lib/model/memory_lane_response_dto.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions mobile/openapi/test/memory_lane_response_dto_test.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion open-api/immich-openapi-specs.json
Original file line number Diff line number Diff line change
Expand Up @@ -8427,12 +8427,17 @@
"type": "array"
},
"title": {
"deprecated": true,
"type": "string"
},
"yearsAgo": {
"type": "number"
}
},
"required": [
"assets",
"title"
"title",
"yearsAgo"
],
"type": "object"
},
Expand Down
1 change: 1 addition & 0 deletions open-api/typescript-sdk/src/fetch-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ export type MapMarkerResponseDto = {
export type MemoryLaneResponseDto = {
assets: AssetResponseDto[];
title: string;
yearsAgo: number;
};
export type UpdateStackParentDto = {
newParentId: string;
Expand Down
2 changes: 2 additions & 0 deletions server/src/dtos/asset-response.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,9 @@ export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): As
}

export class MemoryLaneResponseDto {
@ApiProperty({ deprecated: true })
title!: string;
yearsAgo!: number;
assets!: AssetResponseDto[];
}

Expand Down
2 changes: 1 addition & 1 deletion server/src/repositories/asset.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export class AssetRepository implements IAssetRepository {
},
)
.leftJoinAndSelect('entity.exifInfo', 'exifInfo')
.orderBy('entity.localDateTime', 'DESC')
.orderBy('entity.localDateTime', 'ASC')
.getMany();
}

Expand Down
13 changes: 9 additions & 4 deletions server/src/services/asset.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,20 +307,25 @@ describe(AssetService.name, () => {
jest.useRealTimers();
});

it('should set the title correctly', async () => {
it('should group the assets correctly', async () => {
const image1 = { ...assetStub.image, localDateTime: new Date(2023, 1, 15, 0, 0, 0) };
const image2 = { ...assetStub.image, localDateTime: new Date(2023, 1, 15, 1, 0, 0) };
const image3 = { ...assetStub.image, localDateTime: new Date(2015, 1, 15) };

partnerMock.getAll.mockResolvedValue([]);
assetMock.getByDayOfYear.mockResolvedValue([assetStub.image, assetStub.imageFrom2015]);
assetMock.getByDayOfYear.mockResolvedValue([image1, image2, image3]);

await expect(sut.getMemoryLane(authStub.admin, { day: 15, month: 1 })).resolves.toEqual([
{ title: '1 year since...', assets: [mapAsset(assetStub.image)] },
{ title: '9 years since...', assets: [mapAsset(assetStub.imageFrom2015)] },
{ yearsAgo: 1, title: '1 year since...', assets: [mapAsset(image1), mapAsset(image2)] },
{ yearsAgo: 9, title: '9 years since...', assets: [mapAsset(image3)] },
]);

expect(assetMock.getByDayOfYear.mock.calls).toEqual([[[authStub.admin.user.id], { day: 15, month: 1 }]]);
});

it('should get memories with partners with inTimeline enabled', async () => {
partnerMock.getAll.mockResolvedValue([partnerStub.user1ToAdmin1]);
assetMock.getByDayOfYear.mockResolvedValue([]);

await sut.getMemoryLane(authStub.admin, { day: 15, month: 1 });

Expand Down
31 changes: 18 additions & 13 deletions server/src/services/asset.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,20 +174,25 @@ export class AssetService {
userIds.push(...partnersIds);

const assets = await this.assetRepository.getByDayOfYear(userIds, dto);
const groups: Record<number, AssetEntity[]> = {};
for (const asset of assets) {
const yearsAgo = currentYear - asset.localDateTime.getFullYear();
if (!groups[yearsAgo]) {
groups[yearsAgo] = [];
}
groups[yearsAgo].push(asset);
}

return _.chain(assets)
.filter((asset) => asset.localDateTime.getFullYear() < currentYear)
.map((asset) => {
const years = currentYear - asset.localDateTime.getFullYear();

return {
title: `${years} year${years > 1 ? 's' : ''} since...`,
asset: mapAsset(asset, { auth }),
};
})
.groupBy((asset) => asset.title)
.map((items, title) => ({ title, assets: items.map(({ asset }) => asset) }))
.value();
return Object.keys(groups)
.map(Number)
.sort()
.filter((yearsAgo) => yearsAgo > 0)
.map((yearsAgo) => ({
yearsAgo,
// TODO move this to clients
title: `${yearsAgo} year${yearsAgo > 1 ? 's' : ''} since...`,
assets: groups[yearsAgo].map((asset) => mapAsset(asset, { auth })),
}));
}

private async timeBucketChecks(auth: AuthDto, dto: TimeBucketDto) {
Expand Down
8 changes: 4 additions & 4 deletions web/src/lib/components/memory-page/memory-viewer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import { AppRoute, QueryParameter } from '$lib/constants';
import type { Viewport } from '$lib/stores/assets.store';
import { memoryStore } from '$lib/stores/memory.store';
import { getAssetThumbnailUrl, handlePromiseError } from '$lib/utils';
import { getAssetThumbnailUrl, handlePromiseError, memoryLaneTitle } from '$lib/utils';
import { shortcuts } from '$lib/utils/shortcut';
import { fromLocalDateTime } from '$lib/utils/timeline-util';
import { ThumbnailFormat, getMemoryLane } from '@immich/sdk';
Expand Down Expand Up @@ -102,7 +102,7 @@
<ControlAppBar on:close={() => goto(AppRoute.PHOTOS)} forceDark>
<svelte:fragment slot="leading">
<p class="text-lg">
{currentMemory.title}
{memoryLaneTitle(currentMemory.yearsAgo)}
</p>
</svelte:fragment>

Expand Down Expand Up @@ -181,7 +181,7 @@
{#if previousMemory}
<div class="absolute bottom-4 right-4 text-left text-white">
<p class="text-xs font-semibold text-gray-200">PREVIOUS</p>
<p class="text-xl">{previousMemory.title}</p>
<p class="text-xl">{memoryLaneTitle(previousMemory.yearsAgo)}</p>
</div>
{/if}
</button>
Expand Down Expand Up @@ -254,7 +254,7 @@
{#if nextMemory}
<div class="absolute bottom-4 left-4 text-left text-white">
<p class="text-xs font-semibold text-gray-200">UP NEXT</p>
<p class="text-xl">{nextMemory.title}</p>
<p class="text-xl">{memoryLaneTitle(nextMemory.yearsAgo)}</p>
</div>
{/if}
</button>
Expand Down
8 changes: 5 additions & 3 deletions web/src/lib/components/photos-page/memory-lane.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import Icon from '$lib/components/elements/icon.svelte';
import { AppRoute, QueryParameter } from '$lib/constants';
import { memoryStore } from '$lib/stores/memory.store';
import { getAssetThumbnailUrl } from '$lib/utils';
import { getAssetThumbnailUrl, memoryLaneTitle } from '$lib/utils';
import { getAltText } from '$lib/utils/thumbnail-util';
import { ThumbnailFormat, getMemoryLane } from '@immich/sdk';
import { mdiChevronLeft, mdiChevronRight } from '@mdi/js';
Expand Down Expand Up @@ -66,7 +66,7 @@
</div>
{/if}
<div class="inline-block" bind:offsetWidth={innerWidth}>
{#each $memoryStore as memory, index (memory.title)}
{#each $memoryStore as memory, index (memory.yearsAgo)}
<button
class="memory-card relative mr-8 inline-block aspect-video h-[215px] rounded-xl"
on:click={() => goto(`${AppRoute.MEMORY}?${QueryParameter.MEMORY_INDEX}=${index}`)}
Expand All @@ -77,7 +77,9 @@
alt={`Memory Lane ${getAltText(memory.assets[0])}`}
draggable="false"
/>
<p class="absolute bottom-2 left-4 z-10 text-lg text-white">{memory.title}</p>
<p class="absolute bottom-2 left-4 z-10 text-lg text-white">
{memoryLaneTitle(memory.yearsAgo)}
</p>
<div
class="absolute left-0 top-0 z-0 h-full w-full rounded-xl bg-gradient-to-t from-black/40 via-transparent to-transparent transition-all hover:bg-black/20"
/>
Expand Down
2 changes: 2 additions & 0 deletions web/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,3 +277,5 @@ export const asyncTimeout = (ms: number) => {
export const handlePromiseError = <T>(promise: Promise<T>): void => {
promise.catch((error) => console.error(`[utils.ts]:handlePromiseError ${error}`, error));
};

export const memoryLaneTitle = (yearsAgo: number) => `${yearsAgo} ${yearsAgo ? 'years' : 'year'} since...`;

0 comments on commit 9fe80c2

Please sign in to comment.