diff --git a/cli/src/api/open-api/api.ts b/cli/src/api/open-api/api.ts
index 549cc59d0..d1defea5b 100644
--- a/cli/src/api/open-api/api.ts
+++ b/cli/src/api/open-api/api.ts
@@ -399,6 +399,12 @@ export interface AssetBulkUpdateDto {
* @memberof AssetBulkUpdateDto
*/
'isFavorite'?: boolean;
+ /**
+ *
+ * @type {boolean}
+ * @memberof AssetBulkUpdateDto
+ */
+ 'isShownInMemory'?: boolean;
}
/**
*
@@ -682,6 +688,12 @@ export interface AssetResponseDto {
* @memberof AssetResponseDto
*/
'isReadOnly': boolean;
+ /**
+ *
+ * @type {boolean}
+ * @memberof AssetResponseDto
+ */
+ 'isShownInMemory': boolean;
/**
*
* @type {boolean}
diff --git a/mobile/openapi/doc/AssetBulkUpdateDto.md b/mobile/openapi/doc/AssetBulkUpdateDto.md
index b48268464..3ade86c10 100644
--- a/mobile/openapi/doc/AssetBulkUpdateDto.md
+++ b/mobile/openapi/doc/AssetBulkUpdateDto.md
@@ -11,6 +11,7 @@ Name | Type | Description | Notes
**ids** | **List** | | [default to const []]
**isArchived** | **bool** | | [optional]
**isFavorite** | **bool** | | [optional]
+**isShownInMemory** | **bool** | | [optional]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
diff --git a/mobile/openapi/doc/AssetResponseDto.md b/mobile/openapi/doc/AssetResponseDto.md
index a08be71ac..48e85fbe8 100644
--- a/mobile/openapi/doc/AssetResponseDto.md
+++ b/mobile/openapi/doc/AssetResponseDto.md
@@ -22,6 +22,7 @@ Name | Type | Description | Notes
**isFavorite** | **bool** | |
**isOffline** | **bool** | |
**isReadOnly** | **bool** | |
+**isShownInMemory** | **bool** | |
**isTrashed** | **bool** | |
**libraryId** | **String** | |
**livePhotoVideoId** | **String** | | [optional]
diff --git a/mobile/openapi/lib/model/asset_bulk_update_dto.dart b/mobile/openapi/lib/model/asset_bulk_update_dto.dart
index 7eb0e31af..baae3e3cb 100644
--- a/mobile/openapi/lib/model/asset_bulk_update_dto.dart
+++ b/mobile/openapi/lib/model/asset_bulk_update_dto.dart
@@ -16,6 +16,7 @@ class AssetBulkUpdateDto {
this.ids = const [],
this.isArchived,
this.isFavorite,
+ this.isShownInMemory,
});
List ids;
@@ -36,21 +37,31 @@ class AssetBulkUpdateDto {
///
bool? isFavorite;
+ ///
+ /// 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.
+ ///
+ bool? isShownInMemory;
+
@override
bool operator ==(Object other) => identical(this, other) || other is AssetBulkUpdateDto &&
other.ids == ids &&
other.isArchived == isArchived &&
- other.isFavorite == isFavorite;
+ other.isFavorite == isFavorite &&
+ other.isShownInMemory == isShownInMemory;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(ids.hashCode) +
(isArchived == null ? 0 : isArchived!.hashCode) +
- (isFavorite == null ? 0 : isFavorite!.hashCode);
+ (isFavorite == null ? 0 : isFavorite!.hashCode) +
+ (isShownInMemory == null ? 0 : isShownInMemory!.hashCode);
@override
- String toString() => 'AssetBulkUpdateDto[ids=$ids, isArchived=$isArchived, isFavorite=$isFavorite]';
+ String toString() => 'AssetBulkUpdateDto[ids=$ids, isArchived=$isArchived, isFavorite=$isFavorite, isShownInMemory=$isShownInMemory]';
Map toJson() {
final json = {};
@@ -65,6 +76,11 @@ class AssetBulkUpdateDto {
} else {
// json[r'isFavorite'] = null;
}
+ if (this.isShownInMemory != null) {
+ json[r'isShownInMemory'] = this.isShownInMemory;
+ } else {
+ // json[r'isShownInMemory'] = null;
+ }
return json;
}
@@ -81,6 +97,7 @@ class AssetBulkUpdateDto {
: const [],
isArchived: mapValueOfType(json, r'isArchived'),
isFavorite: mapValueOfType(json, r'isFavorite'),
+ isShownInMemory: mapValueOfType(json, r'isShownInMemory'),
);
}
return null;
diff --git a/mobile/openapi/lib/model/asset_response_dto.dart b/mobile/openapi/lib/model/asset_response_dto.dart
index b2feb0ee8..cfdea1fbe 100644
--- a/mobile/openapi/lib/model/asset_response_dto.dart
+++ b/mobile/openapi/lib/model/asset_response_dto.dart
@@ -27,6 +27,7 @@ class AssetResponseDto {
required this.isFavorite,
required this.isOffline,
required this.isReadOnly,
+ required this.isShownInMemory,
required this.isTrashed,
required this.libraryId,
this.livePhotoVideoId,
@@ -79,6 +80,8 @@ class AssetResponseDto {
bool isReadOnly;
+ bool isShownInMemory;
+
bool isTrashed;
String libraryId;
@@ -137,6 +140,7 @@ class AssetResponseDto {
other.isFavorite == isFavorite &&
other.isOffline == isOffline &&
other.isReadOnly == isReadOnly &&
+ other.isShownInMemory == isShownInMemory &&
other.isTrashed == isTrashed &&
other.libraryId == libraryId &&
other.livePhotoVideoId == livePhotoVideoId &&
@@ -170,6 +174,7 @@ class AssetResponseDto {
(isFavorite.hashCode) +
(isOffline.hashCode) +
(isReadOnly.hashCode) +
+ (isShownInMemory.hashCode) +
(isTrashed.hashCode) +
(libraryId.hashCode) +
(livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode) +
@@ -187,7 +192,7 @@ class AssetResponseDto {
(updatedAt.hashCode);
@override
- String toString() => 'AssetResponseDto[checksum=$checksum, deviceAssetId=$deviceAssetId, deviceId=$deviceId, duration=$duration, exifInfo=$exifInfo, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, hasMetadata=$hasMetadata, id=$id, isArchived=$isArchived, isExternal=$isExternal, isFavorite=$isFavorite, isOffline=$isOffline, isReadOnly=$isReadOnly, isTrashed=$isTrashed, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, originalPath=$originalPath, owner=$owner, ownerId=$ownerId, people=$people, resized=$resized, smartInfo=$smartInfo, tags=$tags, thumbhash=$thumbhash, type=$type, updatedAt=$updatedAt]';
+ String toString() => 'AssetResponseDto[checksum=$checksum, deviceAssetId=$deviceAssetId, deviceId=$deviceId, duration=$duration, exifInfo=$exifInfo, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, hasMetadata=$hasMetadata, id=$id, isArchived=$isArchived, isExternal=$isExternal, isFavorite=$isFavorite, isOffline=$isOffline, isReadOnly=$isReadOnly, isShownInMemory=$isShownInMemory, isTrashed=$isTrashed, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, originalPath=$originalPath, owner=$owner, ownerId=$ownerId, people=$people, resized=$resized, smartInfo=$smartInfo, tags=$tags, thumbhash=$thumbhash, type=$type, updatedAt=$updatedAt]';
Map toJson() {
final json = {};
@@ -209,6 +214,7 @@ class AssetResponseDto {
json[r'isFavorite'] = this.isFavorite;
json[r'isOffline'] = this.isOffline;
json[r'isReadOnly'] = this.isReadOnly;
+ json[r'isShownInMemory'] = this.isShownInMemory;
json[r'isTrashed'] = this.isTrashed;
json[r'libraryId'] = this.libraryId;
if (this.livePhotoVideoId != null) {
@@ -265,6 +271,7 @@ class AssetResponseDto {
isFavorite: mapValueOfType(json, r'isFavorite')!,
isOffline: mapValueOfType(json, r'isOffline')!,
isReadOnly: mapValueOfType(json, r'isReadOnly')!,
+ isShownInMemory: mapValueOfType(json, r'isShownInMemory')!,
isTrashed: mapValueOfType(json, r'isTrashed')!,
libraryId: mapValueOfType(json, r'libraryId')!,
livePhotoVideoId: mapValueOfType(json, r'livePhotoVideoId'),
@@ -340,6 +347,7 @@ class AssetResponseDto {
'isFavorite',
'isOffline',
'isReadOnly',
+ 'isShownInMemory',
'isTrashed',
'libraryId',
'localDateTime',
diff --git a/mobile/openapi/test/asset_bulk_update_dto_test.dart b/mobile/openapi/test/asset_bulk_update_dto_test.dart
index cb23751e0..429b0c42d 100644
--- a/mobile/openapi/test/asset_bulk_update_dto_test.dart
+++ b/mobile/openapi/test/asset_bulk_update_dto_test.dart
@@ -31,6 +31,11 @@ void main() {
// TODO
});
+ // bool isShownInMemory
+ test('to test the property `isShownInMemory`', () async {
+ // TODO
+ });
+
});
diff --git a/mobile/openapi/test/asset_response_dto_test.dart b/mobile/openapi/test/asset_response_dto_test.dart
index f450aae27..dbafb4f0a 100644
--- a/mobile/openapi/test/asset_response_dto_test.dart
+++ b/mobile/openapi/test/asset_response_dto_test.dart
@@ -87,6 +87,11 @@ void main() {
// TODO
});
+ // bool isShownInMemory
+ test('to test the property `isShownInMemory`', () async {
+ // TODO
+ });
+
// bool isTrashed
test('to test the property `isTrashed`', () async {
// TODO
diff --git a/server/immich-openapi-specs.json b/server/immich-openapi-specs.json
index cc649d784..49f167dcb 100644
--- a/server/immich-openapi-specs.json
+++ b/server/immich-openapi-specs.json
@@ -5696,6 +5696,9 @@
},
"isFavorite": {
"type": "boolean"
+ },
+ "isShownInMemory": {
+ "type": "boolean"
}
},
"required": [
@@ -5903,6 +5906,9 @@
"isReadOnly": {
"type": "boolean"
},
+ "isShownInMemory": {
+ "type": "boolean"
+ },
"isTrashed": {
"type": "boolean"
},
@@ -5971,6 +5977,7 @@
"fileCreatedAt",
"fileModifiedAt",
"updatedAt",
+ "isShownInMemory",
"isFavorite",
"isArchived",
"isTrashed",
diff --git a/server/src/domain/asset/asset.service.ts b/server/src/domain/asset/asset.service.ts
index abd0dbe0d..1839e40bd 100644
--- a/server/src/domain/asset/asset.service.ts
+++ b/server/src/domain/asset/asset.service.ts
@@ -165,7 +165,7 @@ export class AssetService {
const assets = await this.assetRepository.getByDayOfYear(authUser.id, dto);
return _.chain(assets)
- .filter((asset) => asset.localDateTime.getFullYear() < currentYear)
+ .filter((asset) => asset.localDateTime.getFullYear() < currentYear && asset.isShownInMemory)
.map((asset) => {
const years = currentYear - asset.localDateTime.getFullYear();
diff --git a/server/src/domain/asset/dto/asset.dto.ts b/server/src/domain/asset/dto/asset.dto.ts
index f5ada315c..18c36c52d 100644
--- a/server/src/domain/asset/dto/asset.dto.ts
+++ b/server/src/domain/asset/dto/asset.dto.ts
@@ -11,6 +11,10 @@ export class AssetBulkUpdateDto extends BulkIdsDto {
@Optional()
@IsBoolean()
isArchived?: boolean;
+
+ @Optional()
+ @IsBoolean()
+ isShownInMemory?: boolean;
}
export class UpdateAssetDto {
diff --git a/server/src/domain/asset/response-dto/asset-response.dto.ts b/server/src/domain/asset/response-dto/asset-response.dto.ts
index e7d5061be..e76f994e1 100644
--- a/server/src/domain/asset/response-dto/asset-response.dto.ts
+++ b/server/src/domain/asset/response-dto/asset-response.dto.ts
@@ -30,6 +30,7 @@ export class AssetResponseDto extends SanitizedAssetResponseDto {
fileCreatedAt!: Date;
fileModifiedAt!: Date;
updatedAt!: Date;
+ isShownInMemory!: boolean;
isFavorite!: boolean;
isArchived!: boolean;
isTrashed!: boolean;
@@ -77,6 +78,7 @@ export function mapAsset(entity: AssetEntity, stripMetadata = false): AssetRespo
fileModifiedAt: entity.fileModifiedAt,
localDateTime: entity.localDateTime,
updatedAt: entity.updatedAt,
+ isShownInMemory: entity.isShownInMemory,
isFavorite: entity.isFavorite,
isArchived: entity.isArchived,
isTrashed: !!entity.deletedAt,
diff --git a/server/src/infra/entities/asset.entity.ts b/server/src/infra/entities/asset.entity.ts
index 31935ae5f..7314e6990 100644
--- a/server/src/infra/entities/asset.entity.ts
+++ b/server/src/infra/entities/asset.entity.ts
@@ -106,6 +106,9 @@ export class AssetEntity {
@Column({ type: 'boolean', default: false })
isOffline!: boolean;
+ @Column({ type: 'boolean', default: true })
+ isShownInMemory!: boolean;
+
@Column({ type: 'bytea' })
@Index()
checksum!: Buffer; // sha1 checksum
diff --git a/server/src/infra/migrations/1697484859613-RemoveFromMemory.ts b/server/src/infra/migrations/1697484859613-RemoveFromMemory.ts
new file mode 100644
index 000000000..bd721235a
--- /dev/null
+++ b/server/src/infra/migrations/1697484859613-RemoveFromMemory.ts
@@ -0,0 +1,28 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+
+export class RemoveFromMemory1697484859613 implements MigrationInterface {
+ name = 'RemoveFromMemory1697484859613'
+
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "isSkipMotion" TO "isShownInMemory"`);
+ await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "PK_6df76ab2eb6f5b57b7c2f1fc684"`);
+ await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "id"`);
+ await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "PK_bf339a24070dac7e71304ec530a" PRIMARY KEY ("assetId", "personId")`);
+ await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "FK_95ad7106dd7b484275443f580f9"`);
+ await queryRunner.query(`ALTER TABLE "asset_faces" ALTER COLUMN "personId" SET NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "isShownInMemory" SET DEFAULT true`);
+ await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" FOREIGN KEY ("personId") REFERENCES "person"("id") ON DELETE CASCADE ON UPDATE CASCADE`);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "FK_95ad7106dd7b484275443f580f9"`);
+ await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "isShownInMemory" SET DEFAULT false`);
+ await queryRunner.query(`ALTER TABLE "asset_faces" ALTER COLUMN "personId" DROP NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" FOREIGN KEY ("personId") REFERENCES "person"("id") ON DELETE CASCADE ON UPDATE CASCADE`);
+ await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "PK_bf339a24070dac7e71304ec530a"`);
+ await queryRunner.query(`ALTER TABLE "asset_faces" ADD "id" uuid NOT NULL DEFAULT uuid_generate_v4()`);
+ await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "PK_6df76ab2eb6f5b57b7c2f1fc684" PRIMARY KEY ("id")`);
+ await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "isShownInMemory" TO "isSkipMotion"`);
+ }
+
+}
diff --git a/server/test/fixtures/asset.stub.ts b/server/test/fixtures/asset.stub.ts
index 5fef9f6d1..0caab1a77 100644
--- a/server/test/fixtures/asset.stub.ts
+++ b/server/test/fixtures/asset.stub.ts
@@ -24,6 +24,7 @@ export const assetStub = {
createdAt: new Date('2023-02-23T05:06:29.716Z'),
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
localDateTime: new Date('2023-02-23T05:06:29.716Z'),
+ isShownInMemory: true,
isFavorite: true,
isArchived: false,
duration: null,
@@ -59,6 +60,7 @@ export const assetStub = {
createdAt: new Date('2023-02-23T05:06:29.716Z'),
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
localDateTime: new Date('2023-02-23T05:06:29.716Z'),
+ isShownInMemory: true,
isFavorite: true,
isArchived: false,
duration: null,
@@ -98,6 +100,7 @@ export const assetStub = {
createdAt: new Date('2023-02-23T05:06:29.716Z'),
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
localDateTime: new Date('2023-02-23T05:06:29.716Z'),
+ isShownInMemory: true,
isFavorite: true,
isArchived: false,
isReadOnly: false,
@@ -134,6 +137,7 @@ export const assetStub = {
createdAt: new Date('2023-02-23T05:06:29.716Z'),
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
localDateTime: new Date('2023-02-23T05:06:29.716Z'),
+ isShownInMemory: true,
isFavorite: true,
isArchived: false,
isReadOnly: false,
@@ -173,6 +177,7 @@ export const assetStub = {
createdAt: new Date('2023-02-23T05:06:29.716Z'),
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
localDateTime: new Date('2023-02-23T05:06:29.716Z'),
+ isShownInMemory: true,
isFavorite: true,
isArchived: false,
isReadOnly: false,
@@ -212,6 +217,7 @@ export const assetStub = {
createdAt: new Date('2023-02-23T05:06:29.716Z'),
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
localDateTime: new Date('2023-02-23T05:06:29.716Z'),
+ isShownInMemory: true,
isFavorite: true,
isArchived: false,
isReadOnly: false,
@@ -251,6 +257,7 @@ export const assetStub = {
createdAt: new Date('2023-02-23T05:06:29.716Z'),
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
localDateTime: new Date('2023-02-23T05:06:29.716Z'),
+ isShownInMemory: true,
isFavorite: true,
isArchived: false,
isReadOnly: false,
@@ -291,6 +298,7 @@ export const assetStub = {
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
deletedAt: null,
localDateTime: new Date('2023-02-23T05:06:29.716Z'),
+ isShownInMemory: true,
isFavorite: true,
isArchived: false,
isReadOnly: false,
@@ -329,6 +337,7 @@ export const assetStub = {
createdAt: new Date('2015-02-23T05:06:29.716Z'),
updatedAt: new Date('2015-02-23T05:06:29.716Z'),
localDateTime: new Date('2015-02-23T05:06:29.716Z'),
+ isShownInMemory: true,
isFavorite: true,
isArchived: false,
isExternal: false,
@@ -369,6 +378,7 @@ export const assetStub = {
createdAt: new Date('2023-02-23T05:06:29.716Z'),
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
localDateTime: new Date('2023-02-23T05:06:29.716Z'),
+ isShownInMemory: true,
isFavorite: true,
isArchived: false,
isReadOnly: false,
@@ -439,6 +449,7 @@ export const assetStub = {
createdAt: new Date('2023-02-23T05:06:29.716Z'),
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
localDateTime: new Date('2023-02-23T05:06:29.716Z'),
+ isShownInMemory: true,
isFavorite: false,
isArchived: false,
isReadOnly: false,
@@ -479,6 +490,7 @@ export const assetStub = {
createdAt: new Date('2023-02-23T05:06:29.716Z'),
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
localDateTime: new Date('2023-02-23T05:06:29.716Z'),
+ isShownInMemory: true,
isFavorite: true,
isArchived: false,
isReadOnly: false,
diff --git a/server/test/fixtures/shared-link.stub.ts b/server/test/fixtures/shared-link.stub.ts
index acb14c6b2..915dc1788 100644
--- a/server/test/fixtures/shared-link.stub.ts
+++ b/server/test/fixtures/shared-link.stub.ts
@@ -51,6 +51,7 @@ const assetResponse: AssetResponseDto = {
resized: false,
thumbhash: null,
fileModifiedAt: today,
+ isShownInMemory: true,
isExternal: false,
isReadOnly: false,
isOffline: false,
@@ -191,6 +192,7 @@ export const sharedLinkStub = {
localDateTime: today,
createdAt: today,
updatedAt: today,
+ isShownInMemory: true,
isFavorite: false,
isArchived: false,
isExternal: false,
diff --git a/web/src/api/open-api/api.ts b/web/src/api/open-api/api.ts
index 549cc59d0..d1defea5b 100644
--- a/web/src/api/open-api/api.ts
+++ b/web/src/api/open-api/api.ts
@@ -399,6 +399,12 @@ export interface AssetBulkUpdateDto {
* @memberof AssetBulkUpdateDto
*/
'isFavorite'?: boolean;
+ /**
+ *
+ * @type {boolean}
+ * @memberof AssetBulkUpdateDto
+ */
+ 'isShownInMemory'?: boolean;
}
/**
*
@@ -682,6 +688,12 @@ export interface AssetResponseDto {
* @memberof AssetResponseDto
*/
'isReadOnly': boolean;
+ /**
+ *
+ * @type {boolean}
+ * @memberof AssetResponseDto
+ */
+ 'isShownInMemory': boolean;
/**
*
* @type {boolean}
diff --git a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte
index fe83d087b..a9767c264 100644
--- a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte
+++ b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte
@@ -49,6 +49,7 @@
asProfileImage: void;
runJob: AssetJobName;
playSlideShow: void;
+ enableMemories: void;
}>();
let contextMenuPosition = { x: 0, y: 0 };
@@ -185,6 +186,9 @@
text={api.getAssetJobName(AssetJobName.TranscodeVideo)}
/>
{/if}
+ {#if !asset.isShownInMemory}
+ dispatch('enableMemories')} text="Show in memories" />
+ {/if}
{/if}
{/if}
diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte
index b1837b860..e06c9dbc5 100644
--- a/web/src/lib/components/asset-viewer/asset-viewer.svelte
+++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte
@@ -346,6 +346,20 @@
}
};
+ const handleEnableMemories = async () => {
+ try {
+ await api.assetApi.updateAssets({ assetBulkUpdateDto: { ids: [asset.id], isShownInMemory: true } });
+
+ notificationController.show({
+ message: `Asset can be shown in memories`,
+ type: NotificationType.Info,
+ });
+ asset.isShownInMemory = true;
+ } catch (error) {
+ handleError(error, "Can't remove asset from memory");
+ }
+ };
+
const handleStopSlideshow = async () => {
try {
await document.exitFullscreen();
@@ -408,6 +422,7 @@
on:asProfileImage={() => (isShowProfileImageCrop = true)}
on:runJob={({ detail: job }) => handleRunJob(job)}
on:playSlideShow={handlePlaySlideshow}
+ on:enableMemories={handleEnableMemories}
/>
{/if}
diff --git a/web/src/lib/components/memory-page/memory-viewer.svelte b/web/src/lib/components/memory-page/memory-viewer.svelte
index 4cd0b2cec..9322b4427 100644
--- a/web/src/lib/components/memory-page/memory-viewer.svelte
+++ b/web/src/lib/components/memory-page/memory-viewer.svelte
@@ -20,6 +20,12 @@
import IntersectionObserver from '$lib/components/asset-viewer/intersection-observer.svelte';
import { fade } from 'svelte/transition';
import { tweened } from 'svelte/motion';
+ import ContextMenu from '../shared-components/context-menu/context-menu.svelte';
+ import MenuOption from '../shared-components/context-menu/menu-option.svelte';
+ import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
+ import { clickOutside } from '$lib/utils/click-outside';
+ import { handleError } from '$lib/utils/handle-error';
+ import { NotificationType, notificationController } from '../shared-components/notification/notification';
const parseIndex = (s: string | null, max: number | null) => Math.max(Math.min(parseInt(s ?? '') || 0, max ?? 0), 0);
@@ -58,6 +64,69 @@
let paused = false;
+ let isShowAssetOptions = false;
+ let contextMenuPosition = { x: 0, y: 0 };
+
+ const updateMemoryStoreAsset = () => {
+ if ($memoryStore && $memoryStore[memoryIndex].assets[assetIndex]) {
+ memoryStore.update((memoryStore) => {
+ const updatedMemoryStore = [...memoryStore];
+
+ if (memoryIndex >= 0 && memoryIndex < updatedMemoryStore.length) {
+ const memoryItem = updatedMemoryStore[memoryIndex];
+
+ if (assetIndex >= 0 && memoryItem.assets.length > 1) {
+ memoryItem.assets.splice(assetIndex, 1);
+ }
+ }
+ return updatedMemoryStore; // Return the updated memoryStore
+ });
+ }
+ };
+
+ const updateMemoryStoreMemory = () => {
+ if ($memoryStore && $memoryStore[memoryIndex].assets[assetIndex]) {
+ memoryStore.update((memoryStore) => {
+ const updatedMemoryStore = [...memoryStore];
+ updatedMemoryStore.splice(memoryIndex, 1);
+
+ return updatedMemoryStore; // Return the updated memoryStore
+ });
+ }
+ };
+
+ const removeFromMemory = async () => {
+ try {
+ await api.assetApi.updateAssets({ assetBulkUpdateDto: { ids: [currentAsset.id], isShownInMemory: false } });
+
+ notificationController.show({
+ message: `Removed asset from memory`,
+ type: NotificationType.Info,
+ });
+ if (currentMemory?.assets.length === 1) {
+ if ($memoryStore?.length === 1) {
+ goto(AppRoute.PHOTOS);
+ } else {
+ if ($memoryStore?.length === memoryIndex + 1) {
+ toPreviousMemory();
+ } else {
+ toNextMemory();
+ }
+ updateMemoryStoreAsset();
+ updateMemoryStoreMemory();
+ }
+ } else {
+ updateMemoryStoreAsset();
+ }
+ isShowAssetOptions = false;
+ } catch (error) {
+ handleError(error, "Can't remove asset from memory");
+ }
+ };
+ const handleRemoveFromMemory = () => {
+ isShowAssetOptions = !isShowAssetOptions;
+ };
+
// Play or pause progress when the paused state changes.
$: paused ? pause() : play();
@@ -222,6 +291,16 @@
{currentAsset.exifInfo?.country || ''}
+
+
(isShowAssetOptions = false)}>
+
+ {#if isShowAssetOptions}
+
+
+
+ {/if}
+
+