mirror of
https://github.com/immich-app/immich.git
synced 2024-12-29 15:11:58 +00:00
feat(server, web): album orders (#7819)
* feat: album orders * fix: tests * pr feedback * pr feedback * pr feedback * fix: tests * add comment * pr feedback * fix: rendering issue * wording * fix: order value doesn't change --------- Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
parent
1c4637cb43
commit
31f7e1aca3
32 changed files with 171 additions and 33 deletions
|
@ -1,6 +1,7 @@
|
||||||
import {
|
import {
|
||||||
AlbumResponseDto,
|
AlbumResponseDto,
|
||||||
AssetFileUploadResponseDto,
|
AssetFileUploadResponseDto,
|
||||||
|
AssetOrder,
|
||||||
LoginResponseDto,
|
LoginResponseDto,
|
||||||
SharedLinkType,
|
SharedLinkType,
|
||||||
deleteUser,
|
deleteUser,
|
||||||
|
@ -353,6 +354,7 @@ describe('/album', () => {
|
||||||
assetCount: 0,
|
assetCount: 0,
|
||||||
owner: expect.objectContaining({ email: user1.userEmail }),
|
owner: expect.objectContaining({ email: user1.userEmail }),
|
||||||
isActivityEnabled: true,
|
isActivityEnabled: true,
|
||||||
|
order: AssetOrder.Desc,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
BIN
mobile/openapi/doc/AlbumResponseDto.md
generated
BIN
mobile/openapi/doc/AlbumResponseDto.md
generated
Binary file not shown.
BIN
mobile/openapi/doc/AssetApi.md
generated
BIN
mobile/openapi/doc/AssetApi.md
generated
Binary file not shown.
BIN
mobile/openapi/doc/UpdateAlbumDto.md
generated
BIN
mobile/openapi/doc/UpdateAlbumDto.md
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/asset_api.dart
generated
BIN
mobile/openapi/lib/api/asset_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/model/album_response_dto.dart
generated
BIN
mobile/openapi/lib/model/album_response_dto.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/model/update_album_dto.dart
generated
BIN
mobile/openapi/lib/model/update_album_dto.dart
generated
Binary file not shown.
BIN
mobile/openapi/test/album_response_dto_test.dart
generated
BIN
mobile/openapi/test/album_response_dto_test.dart
generated
Binary file not shown.
BIN
mobile/openapi/test/asset_api_test.dart
generated
BIN
mobile/openapi/test/asset_api_test.dart
generated
Binary file not shown.
BIN
mobile/openapi/test/update_album_dto_test.dart
generated
BIN
mobile/openapi/test/update_album_dto_test.dart
generated
Binary file not shown.
|
@ -1765,6 +1765,14 @@
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "order",
|
||||||
|
"required": false,
|
||||||
|
"in": "query",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/AssetOrder"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "personId",
|
"name": "personId",
|
||||||
"required": false,
|
"required": false,
|
||||||
|
@ -1901,6 +1909,14 @@
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "order",
|
||||||
|
"required": false,
|
||||||
|
"in": "query",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/AssetOrder"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "personId",
|
"name": "personId",
|
||||||
"required": false,
|
"required": false,
|
||||||
|
@ -6722,6 +6738,9 @@
|
||||||
"format": "date-time",
|
"format": "date-time",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"order": {
|
||||||
|
"$ref": "#/components/schemas/AssetOrder"
|
||||||
|
},
|
||||||
"owner": {
|
"owner": {
|
||||||
"$ref": "#/components/schemas/UserResponseDto"
|
"$ref": "#/components/schemas/UserResponseDto"
|
||||||
},
|
},
|
||||||
|
@ -10335,6 +10354,9 @@
|
||||||
},
|
},
|
||||||
"isActivityEnabled": {
|
"isActivityEnabled": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"order": {
|
||||||
|
"$ref": "#/components/schemas/AssetOrder"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"type": "object"
|
"type": "object"
|
||||||
|
|
|
@ -153,6 +153,7 @@ export type AlbumResponseDto = {
|
||||||
id: string;
|
id: string;
|
||||||
isActivityEnabled: boolean;
|
isActivityEnabled: boolean;
|
||||||
lastModifiedAssetTimestamp?: string;
|
lastModifiedAssetTimestamp?: string;
|
||||||
|
order?: AssetOrder;
|
||||||
owner: UserResponseDto;
|
owner: UserResponseDto;
|
||||||
ownerId: string;
|
ownerId: string;
|
||||||
shared: boolean;
|
shared: boolean;
|
||||||
|
@ -176,6 +177,7 @@ export type UpdateAlbumDto = {
|
||||||
albumThumbnailAssetId?: string;
|
albumThumbnailAssetId?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
isActivityEnabled?: boolean;
|
isActivityEnabled?: boolean;
|
||||||
|
order?: AssetOrder;
|
||||||
};
|
};
|
||||||
export type BulkIdsDto = {
|
export type BulkIdsDto = {
|
||||||
ids: string[];
|
ids: string[];
|
||||||
|
@ -1453,12 +1455,13 @@ export function getAssetThumbnail({ format, id, key }: {
|
||||||
...opts
|
...opts
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
export function getTimeBucket({ albumId, isArchived, isFavorite, isTrashed, key, personId, size, timeBucket, userId, withPartners, withStacked }: {
|
export function getTimeBucket({ albumId, isArchived, isFavorite, isTrashed, key, order, personId, size, timeBucket, userId, withPartners, withStacked }: {
|
||||||
albumId?: string;
|
albumId?: string;
|
||||||
isArchived?: boolean;
|
isArchived?: boolean;
|
||||||
isFavorite?: boolean;
|
isFavorite?: boolean;
|
||||||
isTrashed?: boolean;
|
isTrashed?: boolean;
|
||||||
key?: string;
|
key?: string;
|
||||||
|
order?: AssetOrder;
|
||||||
personId?: string;
|
personId?: string;
|
||||||
size: TimeBucketSize;
|
size: TimeBucketSize;
|
||||||
timeBucket: string;
|
timeBucket: string;
|
||||||
|
@ -1475,6 +1478,7 @@ export function getTimeBucket({ albumId, isArchived, isFavorite, isTrashed, key,
|
||||||
isFavorite,
|
isFavorite,
|
||||||
isTrashed,
|
isTrashed,
|
||||||
key,
|
key,
|
||||||
|
order,
|
||||||
personId,
|
personId,
|
||||||
size,
|
size,
|
||||||
timeBucket,
|
timeBucket,
|
||||||
|
@ -1485,12 +1489,13 @@ export function getTimeBucket({ albumId, isArchived, isFavorite, isTrashed, key,
|
||||||
...opts
|
...opts
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
export function getTimeBuckets({ albumId, isArchived, isFavorite, isTrashed, key, personId, size, userId, withPartners, withStacked }: {
|
export function getTimeBuckets({ albumId, isArchived, isFavorite, isTrashed, key, order, personId, size, userId, withPartners, withStacked }: {
|
||||||
albumId?: string;
|
albumId?: string;
|
||||||
isArchived?: boolean;
|
isArchived?: boolean;
|
||||||
isFavorite?: boolean;
|
isFavorite?: boolean;
|
||||||
isTrashed?: boolean;
|
isTrashed?: boolean;
|
||||||
key?: string;
|
key?: string;
|
||||||
|
order?: AssetOrder;
|
||||||
personId?: string;
|
personId?: string;
|
||||||
size: TimeBucketSize;
|
size: TimeBucketSize;
|
||||||
userId?: string;
|
userId?: string;
|
||||||
|
@ -1506,6 +1511,7 @@ export function getTimeBuckets({ albumId, isArchived, isFavorite, isTrashed, key
|
||||||
isFavorite,
|
isFavorite,
|
||||||
isTrashed,
|
isTrashed,
|
||||||
key,
|
key,
|
||||||
|
order,
|
||||||
personId,
|
personId,
|
||||||
size,
|
size,
|
||||||
userId,
|
userId,
|
||||||
|
@ -2747,6 +2753,10 @@ export enum AssetTypeEnum {
|
||||||
Audio = "AUDIO",
|
Audio = "AUDIO",
|
||||||
Other = "OTHER"
|
Other = "OTHER"
|
||||||
}
|
}
|
||||||
|
export enum AssetOrder {
|
||||||
|
Asc = "asc",
|
||||||
|
Desc = "desc"
|
||||||
|
}
|
||||||
export enum Error {
|
export enum Error {
|
||||||
Duplicate = "duplicate",
|
Duplicate = "duplicate",
|
||||||
NoPermission = "no_permission",
|
NoPermission = "no_permission",
|
||||||
|
@ -2774,10 +2784,6 @@ export enum TimeBucketSize {
|
||||||
Day = "DAY",
|
Day = "DAY",
|
||||||
Month = "MONTH"
|
Month = "MONTH"
|
||||||
}
|
}
|
||||||
export enum AssetOrder {
|
|
||||||
Asc = "asc",
|
|
||||||
Desc = "desc"
|
|
||||||
}
|
|
||||||
export enum EntityType {
|
export enum EntityType {
|
||||||
Asset = "ASSET",
|
Asset = "ASSET",
|
||||||
Album = "ALBUM"
|
Album = "ALBUM"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { AlbumEntity } from '@app/infra/entities';
|
import { AlbumEntity, AssetOrder } from '@app/infra/entities';
|
||||||
|
import { Optional } from '@nestjs/common';
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { AssetResponseDto, mapAsset } from '../asset';
|
import { AssetResponseDto, mapAsset } from '../asset';
|
||||||
import { AuthDto } from '../auth/auth.dto';
|
import { AuthDto } from '../auth/auth.dto';
|
||||||
|
@ -23,6 +24,9 @@ export class AlbumResponseDto {
|
||||||
startDate?: Date;
|
startDate?: Date;
|
||||||
endDate?: Date;
|
endDate?: Date;
|
||||||
isActivityEnabled!: boolean;
|
isActivityEnabled!: boolean;
|
||||||
|
@Optional()
|
||||||
|
@ApiProperty({ enumName: 'AssetOrder', enum: AssetOrder })
|
||||||
|
order?: AssetOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mapAlbum = (entity: AlbumEntity, withAssets: boolean, auth?: AuthDto): AlbumResponseDto => {
|
export const mapAlbum = (entity: AlbumEntity, withAssets: boolean, auth?: AuthDto): AlbumResponseDto => {
|
||||||
|
@ -63,6 +67,7 @@ export const mapAlbum = (entity: AlbumEntity, withAssets: boolean, auth?: AuthDt
|
||||||
assets: (withAssets ? assets : []).map((asset) => mapAsset(asset, { auth })),
|
assets: (withAssets ? assets : []).map((asset) => mapAsset(asset, { auth })),
|
||||||
assetCount: entity.assets?.length || 0,
|
assetCount: entity.assets?.length || 0,
|
||||||
isActivityEnabled: entity.isActivityEnabled,
|
isActivityEnabled: entity.isActivityEnabled,
|
||||||
|
order: entity.order,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -148,6 +148,7 @@ export class AlbumService {
|
||||||
description: dto.description,
|
description: dto.description,
|
||||||
albumThumbnailAssetId: dto.albumThumbnailAssetId,
|
albumThumbnailAssetId: dto.albumThumbnailAssetId,
|
||||||
isActivityEnabled: dto.isActivityEnabled,
|
isActivityEnabled: dto.isActivityEnabled,
|
||||||
|
order: dto.order,
|
||||||
});
|
});
|
||||||
|
|
||||||
return mapAlbumWithoutAssets(updatedAlbum);
|
return mapAlbumWithoutAssets(updatedAlbum);
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import { IsString } from 'class-validator';
|
import { AssetOrder } from '@app/infra/entities';
|
||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsEnum, IsString } from 'class-validator';
|
||||||
import { Optional, ValidateBoolean, ValidateUUID } from '../../domain.util';
|
import { Optional, ValidateBoolean, ValidateUUID } from '../../domain.util';
|
||||||
|
|
||||||
export class UpdateAlbumDto {
|
export class UpdateAlbumDto {
|
||||||
|
@ -15,4 +17,9 @@ export class UpdateAlbumDto {
|
||||||
|
|
||||||
@ValidateBoolean({ optional: true })
|
@ValidateBoolean({ optional: true })
|
||||||
isActivityEnabled?: boolean;
|
isActivityEnabled?: boolean;
|
||||||
|
|
||||||
|
@IsEnum(AssetOrder)
|
||||||
|
@Optional()
|
||||||
|
@ApiProperty({ enum: AssetOrder, enumName: 'AssetOrder' })
|
||||||
|
order?: AssetOrder;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,11 +18,6 @@ export class DeviceIdDto {
|
||||||
deviceId!: string;
|
deviceId!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum AssetOrder {
|
|
||||||
ASC = 'asc',
|
|
||||||
DESC = 'desc',
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasGPS = (o: { latitude: undefined; longitude: undefined }) =>
|
const hasGPS = (o: { latitude: undefined; longitude: undefined }) =>
|
||||||
o.latitude !== undefined || o.longitude !== undefined;
|
o.latitude !== undefined || o.longitude !== undefined;
|
||||||
const ValidateGPS = () => ValidateIf(hasGPS);
|
const ValidateGPS = () => ValidateIf(hasGPS);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
import { AssetOrder } from '@app/infra/entities';
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
|
import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
|
||||||
import { ValidateBoolean, ValidateUUID } from '../../domain.util';
|
import { Optional, ValidateBoolean, ValidateUUID } from '../../domain.util';
|
||||||
import { TimeBucketSize } from '../../repositories';
|
import { TimeBucketSize } from '../../repositories';
|
||||||
|
|
||||||
export class TimeBucketDto {
|
export class TimeBucketDto {
|
||||||
|
@ -32,6 +33,11 @@ export class TimeBucketDto {
|
||||||
|
|
||||||
@ValidateBoolean({ optional: true })
|
@ValidateBoolean({ optional: true })
|
||||||
withPartners?: boolean;
|
withPartners?: boolean;
|
||||||
|
|
||||||
|
@IsEnum(AssetOrder)
|
||||||
|
@Optional()
|
||||||
|
@ApiProperty({ enum: AssetOrder, enumName: 'AssetOrder' })
|
||||||
|
order?: AssetOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TimeBucketAssetDto extends TimeBucketDto {
|
export class TimeBucketAssetDto extends TimeBucketDto {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { AssetSearchOptions, ReverseGeocodeResult, SearchExploreItem } from '@app/domain';
|
import { AssetSearchOptions, ReverseGeocodeResult, SearchExploreItem } from '@app/domain';
|
||||||
import { AssetEntity, AssetJobStatusEntity, AssetType, ExifEntity } from '@app/infra/entities';
|
import { AssetEntity, AssetJobStatusEntity, AssetOrder, AssetType, ExifEntity } from '@app/infra/entities';
|
||||||
import { FindOptionsRelations, FindOptionsSelect } from 'typeorm';
|
import { FindOptionsRelations, FindOptionsSelect } from 'typeorm';
|
||||||
import { Paginated, PaginationOptions } from '../domain.util';
|
import { Paginated, PaginationOptions } from '../domain.util';
|
||||||
|
|
||||||
|
@ -66,6 +66,7 @@ export interface AssetBuilderOptions {
|
||||||
|
|
||||||
export interface TimeBucketOptions extends AssetBuilderOptions {
|
export interface TimeBucketOptions extends AssetBuilderOptions {
|
||||||
size: TimeBucketSize;
|
size: TimeBucketSize;
|
||||||
|
order?: AssetOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TimeBucketItem {
|
export interface TimeBucketItem {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { AssetOrder } from '@app/domain/asset/dto/asset.dto';
|
import { AssetOrder, AssetType, GeodataPlacesEntity } from '@app/infra/entities';
|
||||||
import { AssetType, GeodataPlacesEntity } from '@app/infra/entities';
|
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
import { IsEnum, IsInt, IsNotEmpty, IsString, Max, Min } from 'class-validator';
|
import { IsEnum, IsInt, IsNotEmpty, IsString, Max, Min } from 'class-validator';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { AssetEntity } from '@app/infra/entities';
|
import { AssetEntity, AssetOrder } from '@app/infra/entities';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { AssetOrder, AssetResponseDto, mapAsset } from '../asset';
|
import { AssetResponseDto, mapAsset } from '../asset';
|
||||||
import { AuthDto } from '../auth';
|
import { AuthDto } from '../auth';
|
||||||
import { PersonResponseDto } from '../person';
|
import { PersonResponseDto } from '../person';
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -14,6 +14,12 @@ import { AssetEntity } from './asset.entity';
|
||||||
import { SharedLinkEntity } from './shared-link.entity';
|
import { SharedLinkEntity } from './shared-link.entity';
|
||||||
import { UserEntity } from './user.entity';
|
import { UserEntity } from './user.entity';
|
||||||
|
|
||||||
|
// ran into issues when importing the enum from `asset.dto.ts`
|
||||||
|
export enum AssetOrder {
|
||||||
|
ASC = 'asc',
|
||||||
|
DESC = 'desc',
|
||||||
|
}
|
||||||
|
|
||||||
@Entity('albums')
|
@Entity('albums')
|
||||||
export class AlbumEntity {
|
export class AlbumEntity {
|
||||||
@PrimaryGeneratedColumn('uuid')
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
@ -59,4 +65,7 @@ export class AlbumEntity {
|
||||||
|
|
||||||
@Column({ default: true })
|
@Column({ default: true })
|
||||||
isActivityEnabled!: boolean;
|
isActivityEnabled!: boolean;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', default: AssetOrder.DESC })
|
||||||
|
order!: AssetOrder;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class AscendingOrderAlbum1710182081326 implements MigrationInterface {
|
||||||
|
name = 'AscendingOrderAlbum1710182081326'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "albums" ADD "order" character varying NOT NULL DEFAULT 'desc'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "order"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -36,7 +36,7 @@ import {
|
||||||
Not,
|
Not,
|
||||||
Repository,
|
Repository,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
import { AssetEntity, AssetJobStatusEntity, AssetType, ExifEntity, SmartInfoEntity } from '../entities';
|
import { AssetEntity, AssetJobStatusEntity, AssetOrder, AssetType, ExifEntity, SmartInfoEntity } from '../entities';
|
||||||
import { DummyValue, GenerateSql } from '../infra.util';
|
import { DummyValue, GenerateSql } from '../infra.util';
|
||||||
import { Chunked, ChunkedArray, OptionalBetween, paginate, paginatedBuilder, searchAssetBuilder } from '../infra.utils';
|
import { Chunked, ChunkedArray, OptionalBetween, paginate, paginatedBuilder, searchAssetBuilder } from '../infra.utils';
|
||||||
import { Instrumentation } from '../instrumentation';
|
import { Instrumentation } from '../instrumentation';
|
||||||
|
@ -607,7 +607,7 @@ export class AssetRepository implements IAssetRepository {
|
||||||
.select(`COUNT(asset.id)::int`, 'count')
|
.select(`COUNT(asset.id)::int`, 'count')
|
||||||
.addSelect(truncated, 'timeBucket')
|
.addSelect(truncated, 'timeBucket')
|
||||||
.groupBy(truncated)
|
.groupBy(truncated)
|
||||||
.orderBy(truncated, 'DESC')
|
.orderBy(truncated, options.order === AssetOrder.ASC ? 'ASC' : 'DESC')
|
||||||
.getRawMany();
|
.getRawMany();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -620,7 +620,7 @@ export class AssetRepository implements IAssetRepository {
|
||||||
// First sort by the day in localtime (put it in the right bucket)
|
// First sort by the day in localtime (put it in the right bucket)
|
||||||
.orderBy(truncated, 'DESC')
|
.orderBy(truncated, 'DESC')
|
||||||
// and then sort by the actual time
|
// and then sort by the actual time
|
||||||
.addOrderBy('asset.fileCreatedAt', 'DESC')
|
.addOrderBy('asset.fileCreatedAt', options.order === AssetOrder.ASC ? 'ASC' : 'DESC')
|
||||||
.getMany()
|
.getMany()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ FROM
|
||||||
"AlbumEntity"."deletedAt" AS "AlbumEntity_deletedAt",
|
"AlbumEntity"."deletedAt" AS "AlbumEntity_deletedAt",
|
||||||
"AlbumEntity"."albumThumbnailAssetId" AS "AlbumEntity_albumThumbnailAssetId",
|
"AlbumEntity"."albumThumbnailAssetId" AS "AlbumEntity_albumThumbnailAssetId",
|
||||||
"AlbumEntity"."isActivityEnabled" AS "AlbumEntity_isActivityEnabled",
|
"AlbumEntity"."isActivityEnabled" AS "AlbumEntity_isActivityEnabled",
|
||||||
|
"AlbumEntity"."order" AS "AlbumEntity_order",
|
||||||
"AlbumEntity__AlbumEntity_owner"."id" AS "AlbumEntity__AlbumEntity_owner_id",
|
"AlbumEntity__AlbumEntity_owner"."id" AS "AlbumEntity__AlbumEntity_owner_id",
|
||||||
"AlbumEntity__AlbumEntity_owner"."name" AS "AlbumEntity__AlbumEntity_owner_name",
|
"AlbumEntity__AlbumEntity_owner"."name" AS "AlbumEntity__AlbumEntity_owner_name",
|
||||||
"AlbumEntity__AlbumEntity_owner"."avatarColor" AS "AlbumEntity__AlbumEntity_owner_avatarColor",
|
"AlbumEntity__AlbumEntity_owner"."avatarColor" AS "AlbumEntity__AlbumEntity_owner_avatarColor",
|
||||||
|
@ -91,6 +92,7 @@ SELECT
|
||||||
"AlbumEntity"."deletedAt" AS "AlbumEntity_deletedAt",
|
"AlbumEntity"."deletedAt" AS "AlbumEntity_deletedAt",
|
||||||
"AlbumEntity"."albumThumbnailAssetId" AS "AlbumEntity_albumThumbnailAssetId",
|
"AlbumEntity"."albumThumbnailAssetId" AS "AlbumEntity_albumThumbnailAssetId",
|
||||||
"AlbumEntity"."isActivityEnabled" AS "AlbumEntity_isActivityEnabled",
|
"AlbumEntity"."isActivityEnabled" AS "AlbumEntity_isActivityEnabled",
|
||||||
|
"AlbumEntity"."order" AS "AlbumEntity_order",
|
||||||
"AlbumEntity__AlbumEntity_owner"."id" AS "AlbumEntity__AlbumEntity_owner_id",
|
"AlbumEntity__AlbumEntity_owner"."id" AS "AlbumEntity__AlbumEntity_owner_id",
|
||||||
"AlbumEntity__AlbumEntity_owner"."name" AS "AlbumEntity__AlbumEntity_owner_name",
|
"AlbumEntity__AlbumEntity_owner"."name" AS "AlbumEntity__AlbumEntity_owner_name",
|
||||||
"AlbumEntity__AlbumEntity_owner"."avatarColor" AS "AlbumEntity__AlbumEntity_owner_avatarColor",
|
"AlbumEntity__AlbumEntity_owner"."avatarColor" AS "AlbumEntity__AlbumEntity_owner_avatarColor",
|
||||||
|
@ -149,6 +151,7 @@ SELECT
|
||||||
"AlbumEntity"."deletedAt" AS "AlbumEntity_deletedAt",
|
"AlbumEntity"."deletedAt" AS "AlbumEntity_deletedAt",
|
||||||
"AlbumEntity"."albumThumbnailAssetId" AS "AlbumEntity_albumThumbnailAssetId",
|
"AlbumEntity"."albumThumbnailAssetId" AS "AlbumEntity_albumThumbnailAssetId",
|
||||||
"AlbumEntity"."isActivityEnabled" AS "AlbumEntity_isActivityEnabled",
|
"AlbumEntity"."isActivityEnabled" AS "AlbumEntity_isActivityEnabled",
|
||||||
|
"AlbumEntity"."order" AS "AlbumEntity_order",
|
||||||
"AlbumEntity__AlbumEntity_owner"."id" AS "AlbumEntity__AlbumEntity_owner_id",
|
"AlbumEntity__AlbumEntity_owner"."id" AS "AlbumEntity__AlbumEntity_owner_id",
|
||||||
"AlbumEntity__AlbumEntity_owner"."name" AS "AlbumEntity__AlbumEntity_owner_name",
|
"AlbumEntity__AlbumEntity_owner"."name" AS "AlbumEntity__AlbumEntity_owner_name",
|
||||||
"AlbumEntity__AlbumEntity_owner"."avatarColor" AS "AlbumEntity__AlbumEntity_owner_avatarColor",
|
"AlbumEntity__AlbumEntity_owner"."avatarColor" AS "AlbumEntity__AlbumEntity_owner_avatarColor",
|
||||||
|
@ -279,6 +282,7 @@ SELECT
|
||||||
"AlbumEntity"."deletedAt" AS "AlbumEntity_deletedAt",
|
"AlbumEntity"."deletedAt" AS "AlbumEntity_deletedAt",
|
||||||
"AlbumEntity"."albumThumbnailAssetId" AS "AlbumEntity_albumThumbnailAssetId",
|
"AlbumEntity"."albumThumbnailAssetId" AS "AlbumEntity_albumThumbnailAssetId",
|
||||||
"AlbumEntity"."isActivityEnabled" AS "AlbumEntity_isActivityEnabled",
|
"AlbumEntity"."isActivityEnabled" AS "AlbumEntity_isActivityEnabled",
|
||||||
|
"AlbumEntity"."order" AS "AlbumEntity_order",
|
||||||
"AlbumEntity__AlbumEntity_sharedUsers"."id" AS "AlbumEntity__AlbumEntity_sharedUsers_id",
|
"AlbumEntity__AlbumEntity_sharedUsers"."id" AS "AlbumEntity__AlbumEntity_sharedUsers_id",
|
||||||
"AlbumEntity__AlbumEntity_sharedUsers"."name" AS "AlbumEntity__AlbumEntity_sharedUsers_name",
|
"AlbumEntity__AlbumEntity_sharedUsers"."name" AS "AlbumEntity__AlbumEntity_sharedUsers_name",
|
||||||
"AlbumEntity__AlbumEntity_sharedUsers"."avatarColor" AS "AlbumEntity__AlbumEntity_sharedUsers_avatarColor",
|
"AlbumEntity__AlbumEntity_sharedUsers"."avatarColor" AS "AlbumEntity__AlbumEntity_sharedUsers_avatarColor",
|
||||||
|
@ -352,6 +356,7 @@ SELECT
|
||||||
"AlbumEntity"."deletedAt" AS "AlbumEntity_deletedAt",
|
"AlbumEntity"."deletedAt" AS "AlbumEntity_deletedAt",
|
||||||
"AlbumEntity"."albumThumbnailAssetId" AS "AlbumEntity_albumThumbnailAssetId",
|
"AlbumEntity"."albumThumbnailAssetId" AS "AlbumEntity_albumThumbnailAssetId",
|
||||||
"AlbumEntity"."isActivityEnabled" AS "AlbumEntity_isActivityEnabled",
|
"AlbumEntity"."isActivityEnabled" AS "AlbumEntity_isActivityEnabled",
|
||||||
|
"AlbumEntity"."order" AS "AlbumEntity_order",
|
||||||
"AlbumEntity__AlbumEntity_sharedUsers"."id" AS "AlbumEntity__AlbumEntity_sharedUsers_id",
|
"AlbumEntity__AlbumEntity_sharedUsers"."id" AS "AlbumEntity__AlbumEntity_sharedUsers_id",
|
||||||
"AlbumEntity__AlbumEntity_sharedUsers"."name" AS "AlbumEntity__AlbumEntity_sharedUsers_name",
|
"AlbumEntity__AlbumEntity_sharedUsers"."name" AS "AlbumEntity__AlbumEntity_sharedUsers_name",
|
||||||
"AlbumEntity__AlbumEntity_sharedUsers"."avatarColor" AS "AlbumEntity__AlbumEntity_sharedUsers_avatarColor",
|
"AlbumEntity__AlbumEntity_sharedUsers"."avatarColor" AS "AlbumEntity__AlbumEntity_sharedUsers_avatarColor",
|
||||||
|
@ -462,6 +467,7 @@ SELECT
|
||||||
"AlbumEntity"."deletedAt" AS "AlbumEntity_deletedAt",
|
"AlbumEntity"."deletedAt" AS "AlbumEntity_deletedAt",
|
||||||
"AlbumEntity"."albumThumbnailAssetId" AS "AlbumEntity_albumThumbnailAssetId",
|
"AlbumEntity"."albumThumbnailAssetId" AS "AlbumEntity_albumThumbnailAssetId",
|
||||||
"AlbumEntity"."isActivityEnabled" AS "AlbumEntity_isActivityEnabled",
|
"AlbumEntity"."isActivityEnabled" AS "AlbumEntity_isActivityEnabled",
|
||||||
|
"AlbumEntity"."order" AS "AlbumEntity_order",
|
||||||
"AlbumEntity__AlbumEntity_sharedUsers"."id" AS "AlbumEntity__AlbumEntity_sharedUsers_id",
|
"AlbumEntity__AlbumEntity_sharedUsers"."id" AS "AlbumEntity__AlbumEntity_sharedUsers_id",
|
||||||
"AlbumEntity__AlbumEntity_sharedUsers"."name" AS "AlbumEntity__AlbumEntity_sharedUsers_name",
|
"AlbumEntity__AlbumEntity_sharedUsers"."name" AS "AlbumEntity__AlbumEntity_sharedUsers_name",
|
||||||
"AlbumEntity__AlbumEntity_sharedUsers"."avatarColor" AS "AlbumEntity__AlbumEntity_sharedUsers_avatarColor",
|
"AlbumEntity__AlbumEntity_sharedUsers"."avatarColor" AS "AlbumEntity__AlbumEntity_sharedUsers_avatarColor",
|
||||||
|
@ -553,6 +559,7 @@ SELECT
|
||||||
"AlbumEntity"."deletedAt" AS "AlbumEntity_deletedAt",
|
"AlbumEntity"."deletedAt" AS "AlbumEntity_deletedAt",
|
||||||
"AlbumEntity"."albumThumbnailAssetId" AS "AlbumEntity_albumThumbnailAssetId",
|
"AlbumEntity"."albumThumbnailAssetId" AS "AlbumEntity_albumThumbnailAssetId",
|
||||||
"AlbumEntity"."isActivityEnabled" AS "AlbumEntity_isActivityEnabled",
|
"AlbumEntity"."isActivityEnabled" AS "AlbumEntity_isActivityEnabled",
|
||||||
|
"AlbumEntity"."order" AS "AlbumEntity_order",
|
||||||
"AlbumEntity__AlbumEntity_owner"."id" AS "AlbumEntity__AlbumEntity_owner_id",
|
"AlbumEntity__AlbumEntity_owner"."id" AS "AlbumEntity__AlbumEntity_owner_id",
|
||||||
"AlbumEntity__AlbumEntity_owner"."name" AS "AlbumEntity__AlbumEntity_owner_name",
|
"AlbumEntity__AlbumEntity_owner"."name" AS "AlbumEntity__AlbumEntity_owner_name",
|
||||||
"AlbumEntity__AlbumEntity_owner"."avatarColor" AS "AlbumEntity__AlbumEntity_owner_avatarColor",
|
"AlbumEntity__AlbumEntity_owner"."avatarColor" AS "AlbumEntity__AlbumEntity_owner_avatarColor",
|
||||||
|
|
|
@ -87,6 +87,7 @@ FROM
|
||||||
"SharedLinkEntity__SharedLinkEntity_album"."deletedAt" AS "SharedLinkEntity__SharedLinkEntity_album_deletedAt",
|
"SharedLinkEntity__SharedLinkEntity_album"."deletedAt" AS "SharedLinkEntity__SharedLinkEntity_album_deletedAt",
|
||||||
"SharedLinkEntity__SharedLinkEntity_album"."albumThumbnailAssetId" AS "SharedLinkEntity__SharedLinkEntity_album_albumThumbnailAssetId",
|
"SharedLinkEntity__SharedLinkEntity_album"."albumThumbnailAssetId" AS "SharedLinkEntity__SharedLinkEntity_album_albumThumbnailAssetId",
|
||||||
"SharedLinkEntity__SharedLinkEntity_album"."isActivityEnabled" AS "SharedLinkEntity__SharedLinkEntity_album_isActivityEnabled",
|
"SharedLinkEntity__SharedLinkEntity_album"."isActivityEnabled" AS "SharedLinkEntity__SharedLinkEntity_album_isActivityEnabled",
|
||||||
|
"SharedLinkEntity__SharedLinkEntity_album"."order" AS "SharedLinkEntity__SharedLinkEntity_album_order",
|
||||||
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."id" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_id",
|
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."id" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_id",
|
||||||
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."deviceAssetId" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_deviceAssetId",
|
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."deviceAssetId" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_deviceAssetId",
|
||||||
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."ownerId" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_ownerId",
|
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."ownerId" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_ownerId",
|
||||||
|
@ -248,6 +249,7 @@ SELECT
|
||||||
"SharedLinkEntity__SharedLinkEntity_album"."deletedAt" AS "SharedLinkEntity__SharedLinkEntity_album_deletedAt",
|
"SharedLinkEntity__SharedLinkEntity_album"."deletedAt" AS "SharedLinkEntity__SharedLinkEntity_album_deletedAt",
|
||||||
"SharedLinkEntity__SharedLinkEntity_album"."albumThumbnailAssetId" AS "SharedLinkEntity__SharedLinkEntity_album_albumThumbnailAssetId",
|
"SharedLinkEntity__SharedLinkEntity_album"."albumThumbnailAssetId" AS "SharedLinkEntity__SharedLinkEntity_album_albumThumbnailAssetId",
|
||||||
"SharedLinkEntity__SharedLinkEntity_album"."isActivityEnabled" AS "SharedLinkEntity__SharedLinkEntity_album_isActivityEnabled",
|
"SharedLinkEntity__SharedLinkEntity_album"."isActivityEnabled" AS "SharedLinkEntity__SharedLinkEntity_album_isActivityEnabled",
|
||||||
|
"SharedLinkEntity__SharedLinkEntity_album"."order" AS "SharedLinkEntity__SharedLinkEntity_album_order",
|
||||||
"6d7fd45329a05fd86b3dbcacde87fe76e33a422d"."id" AS "6d7fd45329a05fd86b3dbcacde87fe76e33a422d_id",
|
"6d7fd45329a05fd86b3dbcacde87fe76e33a422d"."id" AS "6d7fd45329a05fd86b3dbcacde87fe76e33a422d_id",
|
||||||
"6d7fd45329a05fd86b3dbcacde87fe76e33a422d"."name" AS "6d7fd45329a05fd86b3dbcacde87fe76e33a422d_name",
|
"6d7fd45329a05fd86b3dbcacde87fe76e33a422d"."name" AS "6d7fd45329a05fd86b3dbcacde87fe76e33a422d_name",
|
||||||
"6d7fd45329a05fd86b3dbcacde87fe76e33a422d"."avatarColor" AS "6d7fd45329a05fd86b3dbcacde87fe76e33a422d_avatarColor",
|
"6d7fd45329a05fd86b3dbcacde87fe76e33a422d"."avatarColor" AS "6d7fd45329a05fd86b3dbcacde87fe76e33a422d_avatarColor",
|
||||||
|
|
12
server/test/fixtures/album.stub.ts
vendored
12
server/test/fixtures/album.stub.ts
vendored
|
@ -1,4 +1,4 @@
|
||||||
import { AlbumEntity } from '@app/infra/entities';
|
import { AlbumEntity, AssetOrder } from '@app/infra/entities';
|
||||||
import { assetStub } from './asset.stub';
|
import { assetStub } from './asset.stub';
|
||||||
import { authStub } from './auth.stub';
|
import { authStub } from './auth.stub';
|
||||||
import { userStub } from './user.stub';
|
import { userStub } from './user.stub';
|
||||||
|
@ -19,6 +19,7 @@ export const albumStub = {
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
sharedUsers: [],
|
sharedUsers: [],
|
||||||
isActivityEnabled: true,
|
isActivityEnabled: true,
|
||||||
|
order: AssetOrder.DESC,
|
||||||
}),
|
}),
|
||||||
sharedWithUser: Object.freeze<AlbumEntity>({
|
sharedWithUser: Object.freeze<AlbumEntity>({
|
||||||
id: 'album-2',
|
id: 'album-2',
|
||||||
|
@ -35,6 +36,7 @@ export const albumStub = {
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
sharedUsers: [userStub.user1],
|
sharedUsers: [userStub.user1],
|
||||||
isActivityEnabled: true,
|
isActivityEnabled: true,
|
||||||
|
order: AssetOrder.DESC,
|
||||||
}),
|
}),
|
||||||
sharedWithMultiple: Object.freeze<AlbumEntity>({
|
sharedWithMultiple: Object.freeze<AlbumEntity>({
|
||||||
id: 'album-3',
|
id: 'album-3',
|
||||||
|
@ -51,6 +53,7 @@ export const albumStub = {
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
sharedUsers: [userStub.user1, userStub.user2],
|
sharedUsers: [userStub.user1, userStub.user2],
|
||||||
isActivityEnabled: true,
|
isActivityEnabled: true,
|
||||||
|
order: AssetOrder.DESC,
|
||||||
}),
|
}),
|
||||||
sharedWithAdmin: Object.freeze<AlbumEntity>({
|
sharedWithAdmin: Object.freeze<AlbumEntity>({
|
||||||
id: 'album-3',
|
id: 'album-3',
|
||||||
|
@ -67,6 +70,7 @@ export const albumStub = {
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
sharedUsers: [userStub.admin],
|
sharedUsers: [userStub.admin],
|
||||||
isActivityEnabled: true,
|
isActivityEnabled: true,
|
||||||
|
order: AssetOrder.DESC,
|
||||||
}),
|
}),
|
||||||
oneAsset: Object.freeze<AlbumEntity>({
|
oneAsset: Object.freeze<AlbumEntity>({
|
||||||
id: 'album-4',
|
id: 'album-4',
|
||||||
|
@ -83,6 +87,7 @@ export const albumStub = {
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
sharedUsers: [],
|
sharedUsers: [],
|
||||||
isActivityEnabled: true,
|
isActivityEnabled: true,
|
||||||
|
order: AssetOrder.DESC,
|
||||||
}),
|
}),
|
||||||
twoAssets: Object.freeze<AlbumEntity>({
|
twoAssets: Object.freeze<AlbumEntity>({
|
||||||
id: 'album-4a',
|
id: 'album-4a',
|
||||||
|
@ -99,6 +104,7 @@ export const albumStub = {
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
sharedUsers: [],
|
sharedUsers: [],
|
||||||
isActivityEnabled: true,
|
isActivityEnabled: true,
|
||||||
|
order: AssetOrder.DESC,
|
||||||
}),
|
}),
|
||||||
emptyWithInvalidThumbnail: Object.freeze<AlbumEntity>({
|
emptyWithInvalidThumbnail: Object.freeze<AlbumEntity>({
|
||||||
id: 'album-5',
|
id: 'album-5',
|
||||||
|
@ -115,6 +121,7 @@ export const albumStub = {
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
sharedUsers: [],
|
sharedUsers: [],
|
||||||
isActivityEnabled: true,
|
isActivityEnabled: true,
|
||||||
|
order: AssetOrder.DESC,
|
||||||
}),
|
}),
|
||||||
emptyWithValidThumbnail: Object.freeze<AlbumEntity>({
|
emptyWithValidThumbnail: Object.freeze<AlbumEntity>({
|
||||||
id: 'album-5',
|
id: 'album-5',
|
||||||
|
@ -131,6 +138,7 @@ export const albumStub = {
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
sharedUsers: [],
|
sharedUsers: [],
|
||||||
isActivityEnabled: true,
|
isActivityEnabled: true,
|
||||||
|
order: AssetOrder.DESC,
|
||||||
}),
|
}),
|
||||||
oneAssetInvalidThumbnail: Object.freeze<AlbumEntity>({
|
oneAssetInvalidThumbnail: Object.freeze<AlbumEntity>({
|
||||||
id: 'album-6',
|
id: 'album-6',
|
||||||
|
@ -147,6 +155,7 @@ export const albumStub = {
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
sharedUsers: [],
|
sharedUsers: [],
|
||||||
isActivityEnabled: true,
|
isActivityEnabled: true,
|
||||||
|
order: AssetOrder.DESC,
|
||||||
}),
|
}),
|
||||||
oneAssetValidThumbnail: Object.freeze<AlbumEntity>({
|
oneAssetValidThumbnail: Object.freeze<AlbumEntity>({
|
||||||
id: 'album-6',
|
id: 'album-6',
|
||||||
|
@ -163,5 +172,6 @@ export const albumStub = {
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
sharedUsers: [],
|
sharedUsers: [],
|
||||||
isActivityEnabled: true,
|
isActivityEnabled: true,
|
||||||
|
order: AssetOrder.DESC,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
4
server/test/fixtures/shared-link.stub.ts
vendored
4
server/test/fixtures/shared-link.stub.ts
vendored
|
@ -1,5 +1,5 @@
|
||||||
import { AlbumResponseDto, AssetResponseDto, ExifResponseDto, mapUser, SharedLinkResponseDto } from '@app/domain';
|
import { AlbumResponseDto, AssetResponseDto, ExifResponseDto, mapUser, SharedLinkResponseDto } from '@app/domain';
|
||||||
import { AssetType, SharedLinkEntity, SharedLinkType, UserEntity } from '@app/infra/entities';
|
import { AssetOrder, AssetType, SharedLinkEntity, SharedLinkType, UserEntity } from '@app/infra/entities';
|
||||||
import { assetStub } from './asset.stub';
|
import { assetStub } from './asset.stub';
|
||||||
import { authStub } from './auth.stub';
|
import { authStub } from './auth.stub';
|
||||||
import { libraryStub } from './library.stub';
|
import { libraryStub } from './library.stub';
|
||||||
|
@ -101,6 +101,7 @@ const albumResponse: AlbumResponseDto = {
|
||||||
assets: [],
|
assets: [],
|
||||||
assetCount: 1,
|
assetCount: 1,
|
||||||
isActivityEnabled: true,
|
isActivityEnabled: true,
|
||||||
|
order: AssetOrder.DESC,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const sharedLinkStub = {
|
export const sharedLinkStub = {
|
||||||
|
@ -181,6 +182,7 @@ export const sharedLinkStub = {
|
||||||
sharedUsers: [],
|
sharedUsers: [],
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
isActivityEnabled: true,
|
isActivityEnabled: true,
|
||||||
|
order: AssetOrder.DESC,
|
||||||
assets: [
|
assets: [
|
||||||
{
|
{
|
||||||
id: 'id_1',
|
id: 'id_1',
|
||||||
|
|
|
@ -1,22 +1,55 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Icon from '$lib/components/elements/icon.svelte';
|
import Icon from '$lib/components/elements/icon.svelte';
|
||||||
import type { AlbumResponseDto, UserResponseDto } from '@immich/sdk';
|
import { updateAlbumInfo, type AlbumResponseDto, type UserResponseDto, AssetOrder } from '@immich/sdk';
|
||||||
import { mdiClose, mdiPlus } from '@mdi/js';
|
import { mdiArrowDownThin, mdiArrowUpThin, mdiClose, mdiPlus } from '@mdi/js';
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
|
||||||
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
|
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
|
||||||
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
||||||
import UserAvatar from '$lib/components/shared-components/user-avatar.svelte';
|
import UserAvatar from '$lib/components/shared-components/user-avatar.svelte';
|
||||||
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
|
||||||
|
import SettingDropdown from '../shared-components/settings/setting-dropdown.svelte';
|
||||||
|
import type { RenderedOption } from '../elements/dropdown.svelte';
|
||||||
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
|
import { findKey } from 'lodash-es';
|
||||||
|
|
||||||
export let album: AlbumResponseDto;
|
export let album: AlbumResponseDto;
|
||||||
|
export let order: AssetOrder | undefined;
|
||||||
export let user: UserResponseDto;
|
export let user: UserResponseDto;
|
||||||
|
export let onChangeOrder: (order: AssetOrder) => void;
|
||||||
|
|
||||||
|
const options: Record<AssetOrder, RenderedOption> = {
|
||||||
|
[AssetOrder.Asc]: { icon: mdiArrowUpThin, title: 'Oldest first' },
|
||||||
|
[AssetOrder.Desc]: { icon: mdiArrowDownThin, title: 'Newest first' },
|
||||||
|
};
|
||||||
|
|
||||||
|
$: selectedOption = order ? options[order] : options[AssetOrder.Desc];
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<{
|
const dispatch = createEventDispatcher<{
|
||||||
close: void;
|
close: void;
|
||||||
toggleEnableActivity: void;
|
toggleEnableActivity: void;
|
||||||
showSelectSharedUser: void;
|
showSelectSharedUser: void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const handleToggle = async (returnedOption: RenderedOption) => {
|
||||||
|
if (selectedOption === returnedOption) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let order = AssetOrder.Desc;
|
||||||
|
order = findKey(options, (option) => option === returnedOption) as AssetOrder;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await updateAlbumInfo({
|
||||||
|
id: album.id,
|
||||||
|
updateAlbumDto: {
|
||||||
|
order,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
onChangeOrder(order);
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, 'Error updating album order');
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<FullScreenModal onClose={() => dispatch('close')}>
|
<FullScreenModal onClose={() => dispatch('close')}>
|
||||||
|
@ -34,8 +67,16 @@
|
||||||
|
|
||||||
<div class=" items-center justify-center p-4">
|
<div class=" items-center justify-center p-4">
|
||||||
<div class="py-2">
|
<div class="py-2">
|
||||||
<h2 class="text-gray text-sm mb-3">SHARING</h2>
|
<h2 class="text-gray text-sm mb-2">SETTINGS</h2>
|
||||||
<div class="p-2">
|
<div class="grid p-2 gap-y-2">
|
||||||
|
{#if order}
|
||||||
|
<SettingDropdown
|
||||||
|
title="Display order"
|
||||||
|
options={Object.values(options)}
|
||||||
|
selectedOption={options[order]}
|
||||||
|
onToggle={handleToggle}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
<SettingSwitch
|
<SettingSwitch
|
||||||
title="Comments & likes"
|
title="Comments & likes"
|
||||||
subtitle="Let others respond"
|
subtitle="Let others respond"
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
[SlideshowNavigation.DescendingOrder]: { icon: mdiArrowDownThin, title: 'Forward' },
|
[SlideshowNavigation.DescendingOrder]: { icon: mdiArrowDownThin, title: 'Forward' },
|
||||||
};
|
};
|
||||||
|
|
||||||
export const handleToggle = (selectedOption: RenderedOption) => {
|
const handleToggle = (selectedOption: RenderedOption) => {
|
||||||
for (const [key, option] of Object.entries(options)) {
|
for (const [key, option] of Object.entries(options)) {
|
||||||
if (option === selectedOption) {
|
if (option === selectedOption) {
|
||||||
$slideshowNavigation = key as SlideshowNavigation;
|
$slideshowNavigation = key as SlideshowNavigation;
|
||||||
|
|
|
@ -161,7 +161,10 @@ export class AssetStore {
|
||||||
this.assetToBucket = {};
|
this.assetToBucket = {};
|
||||||
this.albumAssets = new Set();
|
this.albumAssets = new Set();
|
||||||
|
|
||||||
const buckets = await getTimeBuckets({ ...this.options, key: getKey() });
|
const buckets = await getTimeBuckets({
|
||||||
|
...this.options,
|
||||||
|
key: getKey(),
|
||||||
|
});
|
||||||
|
|
||||||
this.initialized = true;
|
this.initialized = true;
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
updateAlbumInfo,
|
updateAlbumInfo,
|
||||||
type ActivityResponseDto,
|
type ActivityResponseDto,
|
||||||
type UserResponseDto,
|
type UserResponseDto,
|
||||||
|
AssetOrder,
|
||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
import {
|
import {
|
||||||
mdiArrowLeft,
|
mdiArrowLeft,
|
||||||
|
@ -83,6 +84,7 @@
|
||||||
|
|
||||||
$: album = data.album;
|
$: album = data.album;
|
||||||
$: albumId = album.id;
|
$: albumId = album.id;
|
||||||
|
$: albumKey = `${albumId}_${albumOrder}`;
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
if (!album.isActivityEnabled && $numberOfComments === 0) {
|
if (!album.isActivityEnabled && $numberOfComments === 0) {
|
||||||
|
@ -112,8 +114,9 @@
|
||||||
let globalWidth: number;
|
let globalWidth: number;
|
||||||
let assetGridWidth: number;
|
let assetGridWidth: number;
|
||||||
let textArea: HTMLTextAreaElement;
|
let textArea: HTMLTextAreaElement;
|
||||||
|
let albumOrder: AssetOrder | undefined = data.album.order;
|
||||||
|
|
||||||
$: assetStore = new AssetStore({ albumId });
|
$: assetStore = new AssetStore({ albumId, order: albumOrder });
|
||||||
const assetInteractionStore = createAssetInteractionStore();
|
const assetInteractionStore = createAssetInteractionStore();
|
||||||
const { isMultiSelectState, selectedAssets } = assetInteractionStore;
|
const { isMultiSelectState, selectedAssets } = assetInteractionStore;
|
||||||
|
|
||||||
|
@ -512,7 +515,7 @@
|
||||||
style={`width:${assetGridWidth}px`}
|
style={`width:${assetGridWidth}px`}
|
||||||
>
|
>
|
||||||
<!-- Use key because AssetGrid can't deal with changing stores -->
|
<!-- Use key because AssetGrid can't deal with changing stores -->
|
||||||
{#key albumId}
|
{#key albumKey}
|
||||||
{#if viewMode === ViewMode.SELECT_ASSETS}
|
{#if viewMode === ViewMode.SELECT_ASSETS}
|
||||||
<AssetGrid
|
<AssetGrid
|
||||||
assetStore={timelineStore}
|
assetStore={timelineStore}
|
||||||
|
@ -679,7 +682,9 @@
|
||||||
{#if viewMode === ViewMode.OPTIONS && $user}
|
{#if viewMode === ViewMode.OPTIONS && $user}
|
||||||
<AlbumOptions
|
<AlbumOptions
|
||||||
{album}
|
{album}
|
||||||
|
order={albumOrder}
|
||||||
user={$user}
|
user={$user}
|
||||||
|
onChangeOrder={(order) => (albumOrder = order)}
|
||||||
on:close={() => (viewMode = ViewMode.VIEW)}
|
on:close={() => (viewMode = ViewMode.VIEW)}
|
||||||
on:toggleEnableActivity={handleToggleEnableActivity}
|
on:toggleEnableActivity={handleToggleEnableActivity}
|
||||||
on:showSelectSharedUser={() => (viewMode = ViewMode.SELECT_USERS)}
|
on:showSelectSharedUser={() => (viewMode = ViewMode.SELECT_USERS)}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { faker } from '@faker-js/faker';
|
import { faker } from '@faker-js/faker';
|
||||||
import type { AlbumResponseDto } from '@immich/sdk';
|
import { AssetOrder, type AlbumResponseDto } from '@immich/sdk';
|
||||||
import { Sync } from 'factory.ts';
|
import { Sync } from 'factory.ts';
|
||||||
import { userFactory } from './user-factory';
|
import { userFactory } from './user-factory';
|
||||||
|
|
||||||
|
@ -18,4 +18,5 @@ export const albumFactory = Sync.makeFactory<AlbumResponseDto>({
|
||||||
sharedUsers: [],
|
sharedUsers: [],
|
||||||
hasSharedLink: false,
|
hasSharedLink: false,
|
||||||
isActivityEnabled: true,
|
isActivityEnabled: true,
|
||||||
|
order: AssetOrder.Desc,
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue