diff --git a/cli/src/api/open-api/api.ts b/cli/src/api/open-api/api.ts index a4a8ff2759..c58a4a4c53 100644 --- a/cli/src/api/open-api/api.ts +++ b/cli/src/api/open-api/api.ts @@ -954,6 +954,12 @@ export interface CreateUserDto { * @memberof CreateUserDto */ 'lastName': string; + /** + * + * @type {boolean} + * @memberof CreateUserDto + */ + 'memoriesEnabled'?: boolean; /** * * @type {string} @@ -2995,6 +3001,12 @@ export interface UpdateUserDto { * @memberof UpdateUserDto */ 'lastName'?: string; + /** + * + * @type {boolean} + * @memberof UpdateUserDto + */ + 'memoriesEnabled'?: boolean; /** * * @type {string} @@ -3124,6 +3136,12 @@ export interface UserResponseDto { * @memberof UserResponseDto */ 'lastName': string; + /** + * + * @type {boolean} + * @memberof UserResponseDto + */ + 'memoriesEnabled': boolean; /** * * @type {string} diff --git a/mobile/lib/modules/home/views/home_page.dart b/mobile/lib/modules/home/views/home_page.dart index 311bf98a79..a628df9bd0 100644 --- a/mobile/lib/modules/home/views/home_page.dart +++ b/mobile/lib/modules/home/views/home_page.dart @@ -342,7 +342,10 @@ class HomePage extends HookConsumerWidget { listener: selectionListener, selectionActive: selectionEnabledHook.value, onRefresh: refreshAssets, - topWidget: const MemoryLane(), + topWidget: + (currentUser != null && currentUser.memoryEnabled) + ? const MemoryLane() + : const SizedBox(), ), error: (error, _) => Center(child: Text(error.toString())), loading: buildLoadingIndicator, diff --git a/mobile/lib/modules/login/providers/authentication.provider.dart b/mobile/lib/modules/login/providers/authentication.provider.dart index f2657e8f94..57723ed745 100644 --- a/mobile/lib/modules/login/providers/authentication.provider.dart +++ b/mobile/lib/modules/login/providers/authentication.provider.dart @@ -97,12 +97,11 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> { Future<void> logout() async { var log = Logger('AuthenticationNotifier'); try { - String? userEmail = Store.tryGet(StoreKey.currentUser)?.email; _apiService.authenticationApi .logout() - .then((_) => log.info("Logout was successfull for $userEmail")) + .then((_) => log.info("Logout was successful for $userEmail")) .onError( (error, stackTrace) => log.severe("Error logging out $userEmail", error, stackTrace), @@ -186,8 +185,7 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> { user = User.fromDto(userResponseDto); retResult = true; - } - else { + } else { _log.severe("Unable to get user information from the server."); return false; } diff --git a/mobile/lib/routing/tab_navigation_observer.dart b/mobile/lib/routing/tab_navigation_observer.dart index 2451929963..1aaef3af51 100644 --- a/mobile/lib/routing/tab_navigation_observer.dart +++ b/mobile/lib/routing/tab_navigation_observer.dart @@ -1,4 +1,5 @@ import 'package:auto_route/auto_route.dart'; +import 'package:flutter/foundation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/modules/album/providers/album.provider.dart'; import 'package:immich_mobile/modules/memories/providers/memory.provider.dart'; @@ -6,6 +7,9 @@ import 'package:immich_mobile/modules/search/providers/people.provider.dart'; import 'package:immich_mobile/modules/search/providers/search_page_state.provider.dart'; import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart'; +import 'package:immich_mobile/shared/models/store.dart'; +import 'package:immich_mobile/shared/models/user.dart'; +import 'package:immich_mobile/shared/providers/api.provider.dart'; import 'package:immich_mobile/shared/providers/server_info.provider.dart'; class TabNavigationObserver extends AutoRouterObserver { @@ -46,6 +50,20 @@ class TabNavigationObserver extends AutoRouterObserver { if (route.name == 'HomeRoute') { ref.invalidate(memoryFutureProvider); + + // Update user info + try { + final userResponseDto = + await ref.read(apiServiceProvider).userApi.getMyUserInfo(); + + if (userResponseDto == null) { + return; + } + + Store.put(StoreKey.currentUser, User.fromDto(userResponseDto)); + } catch (e) { + debugPrint("Error refreshing user info $e"); + } } ref.watch(serverInfoProvider.notifier).getServerVersion(); } diff --git a/mobile/lib/shared/models/user.dart b/mobile/lib/shared/models/user.dart index 362adebc0b..3041500641 100644 --- a/mobile/lib/shared/models/user.dart +++ b/mobile/lib/shared/models/user.dart @@ -17,6 +17,7 @@ class User { this.isPartnerSharedBy = false, this.isPartnerSharedWith = false, this.profileImagePath = '', + this.memoryEnabled = true, }); Id get isarId => fastHash(id); @@ -30,7 +31,8 @@ class User { isPartnerSharedBy = false, isPartnerSharedWith = false, profileImagePath = dto.profileImagePath, - isAdmin = dto.isAdmin; + isAdmin = dto.isAdmin, + memoryEnabled = dto.memoriesEnabled; @Index(unique: true, replace: false, type: IndexType.hash) String id; @@ -42,6 +44,7 @@ class User { bool isPartnerSharedWith; bool isAdmin; String profileImagePath; + bool memoryEnabled; @Backlink(to: 'owner') final IsarLinks<Album> albums = IsarLinks<Album>(); @Backlink(to: 'sharedUsers') @@ -58,7 +61,8 @@ class User { isPartnerSharedBy == other.isPartnerSharedBy && isPartnerSharedWith == other.isPartnerSharedWith && profileImagePath == other.profileImagePath && - isAdmin == other.isAdmin; + isAdmin == other.isAdmin && + memoryEnabled == other.memoryEnabled; } @override @@ -72,5 +76,6 @@ class User { isPartnerSharedBy.hashCode ^ isPartnerSharedWith.hashCode ^ profileImagePath.hashCode ^ - isAdmin.hashCode; + isAdmin.hashCode ^ + memoryEnabled.hashCode; } diff --git a/mobile/lib/shared/models/user.g.dart b/mobile/lib/shared/models/user.g.dart index 26f20b9854..461168f6eb 100644 Binary files a/mobile/lib/shared/models/user.g.dart and b/mobile/lib/shared/models/user.g.dart differ diff --git a/mobile/makefile b/mobile/makefile index f4ce1450d5..a04fe36215 100644 --- a/mobile/makefile +++ b/mobile/makefile @@ -1,8 +1,8 @@ build: - flutter packages pub run build_runner build --delete-conflicting-outputs + dart run build_runner build --delete-conflicting-outputs watch: - flutter packages pub run build_runner watch --delete-conflicting-outputs + dart run build_runner watch --delete-conflicting-outputs create_app_icon: flutter pub run flutter_launcher_icons:main diff --git a/mobile/openapi/doc/CreateUserDto.md b/mobile/openapi/doc/CreateUserDto.md index 531e2cf199..0a25b4df4d 100644 Binary files a/mobile/openapi/doc/CreateUserDto.md and b/mobile/openapi/doc/CreateUserDto.md differ diff --git a/mobile/openapi/doc/UpdateUserDto.md b/mobile/openapi/doc/UpdateUserDto.md index d3b41f3c38..ff6bc8d429 100644 Binary files a/mobile/openapi/doc/UpdateUserDto.md and b/mobile/openapi/doc/UpdateUserDto.md differ diff --git a/mobile/openapi/doc/UserResponseDto.md b/mobile/openapi/doc/UserResponseDto.md index 320827f984..6455c12d03 100644 Binary files a/mobile/openapi/doc/UserResponseDto.md and b/mobile/openapi/doc/UserResponseDto.md differ diff --git a/mobile/openapi/lib/model/create_user_dto.dart b/mobile/openapi/lib/model/create_user_dto.dart index 0535d94060..1345ac995c 100644 Binary files a/mobile/openapi/lib/model/create_user_dto.dart and b/mobile/openapi/lib/model/create_user_dto.dart differ diff --git a/mobile/openapi/lib/model/update_user_dto.dart b/mobile/openapi/lib/model/update_user_dto.dart index 71f3f16e8a..6202c6d1ef 100644 Binary files a/mobile/openapi/lib/model/update_user_dto.dart and b/mobile/openapi/lib/model/update_user_dto.dart differ diff --git a/mobile/openapi/lib/model/user_response_dto.dart b/mobile/openapi/lib/model/user_response_dto.dart index 2720827d79..5ecc26b492 100644 Binary files a/mobile/openapi/lib/model/user_response_dto.dart and b/mobile/openapi/lib/model/user_response_dto.dart differ diff --git a/mobile/openapi/test/create_user_dto_test.dart b/mobile/openapi/test/create_user_dto_test.dart index 872635e2fb..9ce64c1e05 100644 Binary files a/mobile/openapi/test/create_user_dto_test.dart and b/mobile/openapi/test/create_user_dto_test.dart differ diff --git a/mobile/openapi/test/update_user_dto_test.dart b/mobile/openapi/test/update_user_dto_test.dart index 0654364354..511de33af0 100644 Binary files a/mobile/openapi/test/update_user_dto_test.dart and b/mobile/openapi/test/update_user_dto_test.dart differ diff --git a/mobile/openapi/test/user_response_dto_test.dart b/mobile/openapi/test/user_response_dto_test.dart index cd6fa7694f..9e2095607b 100644 Binary files a/mobile/openapi/test/user_response_dto_test.dart and b/mobile/openapi/test/user_response_dto_test.dart differ diff --git a/server/immich-openapi-specs.json b/server/immich-openapi-specs.json index 1aac93051b..bbf28e8d19 100644 --- a/server/immich-openapi-specs.json +++ b/server/immich-openapi-specs.json @@ -5396,6 +5396,9 @@ "lastName": { "type": "string" }, + "memoriesEnabled": { + "type": "boolean" + }, "password": { "type": "string" }, @@ -7004,6 +7007,9 @@ "lastName": { "type": "string" }, + "memoriesEnabled": { + "type": "boolean" + }, "password": { "type": "string" }, @@ -7092,6 +7098,9 @@ "lastName": { "type": "string" }, + "memoriesEnabled": { + "type": "boolean" + }, "oauthId": { "type": "string" }, @@ -7123,7 +7132,8 @@ "createdAt", "deletedAt", "updatedAt", - "oauthId" + "oauthId", + "memoriesEnabled" ], "type": "object" }, diff --git a/server/src/domain/album/album.service.spec.ts b/server/src/domain/album/album.service.spec.ts index 50eed510d6..febacbb385 100644 --- a/server/src/domain/album/album.service.spec.ts +++ b/server/src/domain/album/album.service.spec.ts @@ -176,6 +176,7 @@ describe(AlbumService.name, () => { deletedAt: null, updatedAt: new Date('2021-01-01'), externalPath: null, + memoriesEnabled: true, }, ownerId: 'admin_id', shared: false, diff --git a/server/src/domain/partner/partner.service.spec.ts b/server/src/domain/partner/partner.service.spec.ts index c8e0489695..ac15abc8c0 100644 --- a/server/src/domain/partner/partner.service.spec.ts +++ b/server/src/domain/partner/partner.service.spec.ts @@ -1,10 +1,11 @@ import { BadRequestException } from '@nestjs/common'; import { authStub, newPartnerRepositoryMock, partnerStub } from '@test'; +import { UserResponseDto } from '../index'; import { IPartnerRepository, PartnerDirection } from './partner.repository'; import { PartnerService } from './partner.service'; const responseDto = { - admin: { + admin: <UserResponseDto>{ email: 'admin@test.com', firstName: 'admin_first_name', id: 'admin_id', @@ -18,8 +19,9 @@ const responseDto = { deletedAt: null, updatedAt: new Date('2021-01-01'), externalPath: null, + memoriesEnabled: true, }, - user1: { + user1: <UserResponseDto>{ email: 'immich@test.com', firstName: 'immich_first_name', id: 'user-id', @@ -33,6 +35,7 @@ const responseDto = { deletedAt: null, updatedAt: new Date('2021-01-01'), externalPath: null, + memoriesEnabled: true, }, }; diff --git a/server/src/domain/user/dto/create-user.dto.ts b/server/src/domain/user/dto/create-user.dto.ts index afb5aec237..c9a9945af4 100644 --- a/server/src/domain/user/dto/create-user.dto.ts +++ b/server/src/domain/user/dto/create-user.dto.ts @@ -1,5 +1,5 @@ import { Transform } from 'class-transformer'; -import { IsEmail, IsNotEmpty, IsOptional, IsString } from 'class-validator'; +import { IsBoolean, IsEmail, IsNotEmpty, IsOptional, IsString } from 'class-validator'; import { toEmail, toSanitized } from '../../domain.util'; export class CreateUserDto { @@ -27,6 +27,10 @@ export class CreateUserDto { @IsOptional() @IsString() externalPath?: string | null; + + @IsOptional() + @IsBoolean() + memoriesEnabled?: boolean; } export class CreateAdminDto { diff --git a/server/src/domain/user/dto/update-user.dto.ts b/server/src/domain/user/dto/update-user.dto.ts index 7eb98f5384..a1e053855e 100644 --- a/server/src/domain/user/dto/update-user.dto.ts +++ b/server/src/domain/user/dto/update-user.dto.ts @@ -45,4 +45,8 @@ export class UpdateUserDto { @IsOptional() @IsBoolean() shouldChangePassword?: boolean; + + @IsOptional() + @IsBoolean() + memoriesEnabled?: boolean; } diff --git a/server/src/domain/user/response-dto/user-response.dto.ts b/server/src/domain/user/response-dto/user-response.dto.ts index a2bd508837..9a3372ad55 100644 --- a/server/src/domain/user/response-dto/user-response.dto.ts +++ b/server/src/domain/user/response-dto/user-response.dto.ts @@ -14,6 +14,7 @@ export class UserResponseDto { deletedAt!: Date | null; updatedAt!: Date; oauthId!: string; + memoriesEnabled!: boolean; } export function mapUser(entity: UserEntity): UserResponseDto { @@ -31,5 +32,6 @@ export function mapUser(entity: UserEntity): UserResponseDto { deletedAt: entity.deletedAt, updatedAt: entity.updatedAt, oauthId: entity.oauthId, + memoriesEnabled: entity.memoriesEnabled, }; } diff --git a/server/src/domain/user/user.core.ts b/server/src/domain/user/user.core.ts index e7c0f7f2b1..6c59d9ca71 100644 --- a/server/src/domain/user/user.core.ts +++ b/server/src/domain/user/user.core.ts @@ -60,6 +60,7 @@ export class UserCore { dto.externalPath = null; } + console.log(dto.memoriesEnabled); return this.userRepository.update(id, dto); } catch (e) { Logger.error(e, 'Failed to update user info'); diff --git a/server/src/domain/user/user.service.spec.ts b/server/src/domain/user/user.service.spec.ts index a573a8c20d..ef1a07d177 100644 --- a/server/src/domain/user/user.service.spec.ts +++ b/server/src/domain/user/user.service.spec.ts @@ -16,6 +16,7 @@ import { ICryptoRepository } from '../crypto'; import { IJobRepository, JobName } from '../job'; import { IStorageRepository } from '../storage'; import { UpdateUserDto } from './dto/update-user.dto'; +import { UserResponseDto } from './response-dto'; import { IUserRepository } from './user.repository'; import { UserService } from './user.service'; @@ -54,6 +55,7 @@ const adminUser: UserEntity = Object.freeze({ assets: [], storageLabel: 'admin', externalPath: null, + memoriesEnabled: true, }); const immichUser: UserEntity = Object.freeze({ @@ -73,9 +75,10 @@ const immichUser: UserEntity = Object.freeze({ assets: [], storageLabel: null, externalPath: null, + memoriesEnabled: true, }); -const updatedImmichUser: UserEntity = Object.freeze({ +const updatedImmichUser = Object.freeze<UserEntity>({ id: immichUserAuth.id, email: 'immich@test.com', password: 'immich_password', @@ -92,9 +95,10 @@ const updatedImmichUser: UserEntity = Object.freeze({ assets: [], storageLabel: null, externalPath: null, + memoriesEnabled: true, }); -const adminUserResponse = Object.freeze({ +const adminUserResponse = Object.freeze<UserResponseDto>({ id: adminUserAuth.id, email: 'admin@test.com', firstName: 'admin_first_name', @@ -108,6 +112,7 @@ const adminUserResponse = Object.freeze({ updatedAt: new Date('2021-01-01'), storageLabel: 'admin', externalPath: null, + memoriesEnabled: true, }); describe(UserService.name, () => { @@ -158,6 +163,7 @@ describe(UserService.name, () => { updatedAt: new Date('2021-01-01'), storageLabel: 'admin', externalPath: null, + memoriesEnabled: true, }, ]); }); diff --git a/server/src/infra/entities/user.entity.ts b/server/src/infra/entities/user.entity.ts index 7cdac1f824..e6555153ae 100644 --- a/server/src/infra/entities/user.entity.ts +++ b/server/src/infra/entities/user.entity.ts @@ -54,6 +54,9 @@ export class UserEntity { @UpdateDateColumn({ type: 'timestamptz' }) updatedAt!: Date; + @Column({ default: true }) + memoriesEnabled!: boolean; + @OneToMany(() => TagEntity, (tag) => tag.user) tags!: TagEntity[]; diff --git a/server/src/infra/migrations/1691600216749-UserMemoryPreference.ts b/server/src/infra/migrations/1691600216749-UserMemoryPreference.ts new file mode 100644 index 0000000000..7238749080 --- /dev/null +++ b/server/src/infra/migrations/1691600216749-UserMemoryPreference.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UserMemoryPreference1691600216749 implements MigrationInterface { + name = 'UserMemoryPreference1691600216749'; + + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`ALTER TABLE "users" ADD "memoriesEnabled" boolean NOT NULL DEFAULT true`); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "memoriesEnabled"`); + } +} diff --git a/server/test/e2e/user.e2e-spec.ts b/server/test/e2e/user.e2e-spec.ts index 0ba37e33cc..90c79ed966 100644 --- a/server/test/e2e/user.e2e-spec.ts +++ b/server/test/e2e/user.e2e-spec.ts @@ -143,6 +143,24 @@ describe(`${UserController.name}`, () => { }); expect(status).toBe(201); }); + + it('should create a user without memories enabled', async () => { + const { status, body } = await request(server) + .post(`/user`) + .send({ + email: 'no-memories@immich.app', + password: 'Password123', + firstName: 'No Memories', + lastName: 'User', + memoriesEnabled: false, + }) + .set('Authorization', `Bearer ${accessToken}`); + expect(body).toMatchObject({ + email: 'no-memories@immich.app', + memoriesEnabled: false, + }); + expect(status).toBe(201); + }); }); describe('PUT /user', () => { @@ -206,6 +224,21 @@ describe(`${UserController.name}`, () => { }); expect(before.updatedAt).not.toEqual(after.updatedAt); }); + + it('should update memories enabled', async () => { + const before = await api.userApi.get(server, accessToken, loginResponse.userId); + const after = await api.userApi.update(server, accessToken, { + id: before.id, + memoriesEnabled: false, + }); + + expect(after).toMatchObject({ + ...before, + updatedAt: expect.anything(), + memoriesEnabled: false, + }); + expect(before.updatedAt).not.toEqual(after.updatedAt); + }); }); describe('GET /user/count', () => { diff --git a/server/test/fixtures/user.stub.ts b/server/test/fixtures/user.stub.ts index 4367bf60ef..f2a8dcab81 100644 --- a/server/test/fixtures/user.stub.ts +++ b/server/test/fixtures/user.stub.ts @@ -17,6 +17,7 @@ export const userStub = { updatedAt: new Date('2021-01-01'), tags: [], assets: [], + memoriesEnabled: true, }), user1: Object.freeze<UserEntity>({ ...authStub.user1, @@ -33,6 +34,7 @@ export const userStub = { updatedAt: new Date('2021-01-01'), tags: [], assets: [], + memoriesEnabled: true, }), user2: Object.freeze<UserEntity>({ ...authStub.user2, @@ -49,6 +51,7 @@ export const userStub = { updatedAt: new Date('2021-01-01'), tags: [], assets: [], + memoriesEnabled: true, }), storageLabel: Object.freeze<UserEntity>({ ...authStub.user1, @@ -65,5 +68,6 @@ export const userStub = { updatedAt: new Date('2021-01-01'), tags: [], assets: [], + memoriesEnabled: true, }), }; diff --git a/web/src/api/open-api/api.ts b/web/src/api/open-api/api.ts index a4a8ff2759..c58a4a4c53 100644 --- a/web/src/api/open-api/api.ts +++ b/web/src/api/open-api/api.ts @@ -954,6 +954,12 @@ export interface CreateUserDto { * @memberof CreateUserDto */ 'lastName': string; + /** + * + * @type {boolean} + * @memberof CreateUserDto + */ + 'memoriesEnabled'?: boolean; /** * * @type {string} @@ -2995,6 +3001,12 @@ export interface UpdateUserDto { * @memberof UpdateUserDto */ 'lastName'?: string; + /** + * + * @type {boolean} + * @memberof UpdateUserDto + */ + 'memoriesEnabled'?: boolean; /** * * @type {string} @@ -3124,6 +3136,12 @@ export interface UserResponseDto { * @memberof UserResponseDto */ 'lastName': string; + /** + * + * @type {boolean} + * @memberof UserResponseDto + */ + 'memoriesEnabled': boolean; /** * * @type {string} diff --git a/web/src/lib/components/user-settings-page/memories-settings.svelte b/web/src/lib/components/user-settings-page/memories-settings.svelte new file mode 100644 index 0000000000..9e269fc092 --- /dev/null +++ b/web/src/lib/components/user-settings-page/memories-settings.svelte @@ -0,0 +1,49 @@ +<script lang="ts"> + import { + notificationController, + NotificationType, + } from '$lib/components/shared-components/notification/notification'; + import { api, UserResponseDto } from '@api'; + import { fade } from 'svelte/transition'; + import { handleError } from '../../utils/handle-error'; + import SettingSwitch from '../admin-page/settings/setting-switch.svelte'; + import Button from '../elements/buttons/button.svelte'; + + export let user: UserResponseDto; + + const handleSave = async () => { + try { + const { data } = await api.userApi.updateUser({ + updateUserDto: { + id: user.id, + memoriesEnabled: user.memoriesEnabled, + }, + }); + + Object.assign(user, data); + + notificationController.show({ message: 'Saved settings', type: NotificationType.Info }); + } catch (error) { + handleError(error, 'Unable to update settings'); + } + }; +</script> + +<section class="my-4"> + <div in:fade={{ duration: 500 }}> + <form autocomplete="off" on:submit|preventDefault> + <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ml-4"> + <SettingSwitch + title="Time-based memories" + subtitle="Photos from previous years" + bind:checked={user.memoriesEnabled} + /> + </div> + <div class="flex justify-end"> + <Button type="submit" size="sm" on:click={() => handleSave()}>Save</Button> + </div> + </div> + </form> + </div> +</section> diff --git a/web/src/lib/components/user-settings-page/user-settings-list.svelte b/web/src/lib/components/user-settings-page/user-settings-list.svelte index 76311414c3..376c78e63e 100644 --- a/web/src/lib/components/user-settings-page/user-settings-list.svelte +++ b/web/src/lib/components/user-settings-page/user-settings-list.svelte @@ -4,10 +4,11 @@ import { onMount } from 'svelte'; import SettingAccordion from '../admin-page/settings/setting-accordion.svelte'; import ChangePasswordSettings from './change-password-settings.svelte'; - import OAuthSettings from './oauth-settings.svelte'; - import UserAPIKeyList from './user-api-key-list.svelte'; import DeviceList from './device-list.svelte'; + import MemoriesSettings from './memories-settings.svelte'; + import OAuthSettings from './oauth-settings.svelte'; import PartnerSettings from './partner-settings.svelte'; + import UserAPIKeyList from './user-api-key-list.svelte'; import UserProfileSettings from './user-profile-settings.svelte'; export let user: UserResponseDto; @@ -39,6 +40,10 @@ <DeviceList /> </SettingAccordion> +<SettingAccordion title="Memories" subtitle="Manage what you see in your memories."> + <MemoriesSettings {user} /> +</SettingAccordion> + {#if oauthEnabled} <SettingAccordion title="OAuth" diff --git a/web/src/routes/(user)/photos/+page.svelte b/web/src/routes/(user)/photos/+page.svelte index caf8c786a9..27a3b424e4 100644 --- a/web/src/routes/(user)/photos/+page.svelte +++ b/web/src/routes/(user)/photos/+page.svelte @@ -59,7 +59,9 @@ <svelte:fragment slot="content"> {#if assetCount} <AssetGrid {assetStore} {assetInteractionStore} removeAction={AssetAction.ARCHIVE}> - <MemoryLane /> + {#if data.user.memoriesEnabled} + <MemoryLane /> + {/if} </AssetGrid> {:else} <EmptyPlaceholder text="CLICK TO UPLOAD YOUR FIRST PHOTO" actionHandler={() => openFileUploadDialog()} /> diff --git a/web/src/test-data/factories/user-factory.ts b/web/src/test-data/factories/user-factory.ts index ebf280159e..507242fe8b 100644 --- a/web/src/test-data/factories/user-factory.ts +++ b/web/src/test-data/factories/user-factory.ts @@ -15,5 +15,6 @@ export const userFactory = Sync.makeFactory<UserResponseDto>({ createdAt: Sync.each(() => faker.date.past().toISOString()), deletedAt: null, updatedAt: Sync.each(() => faker.date.past().toISOString()), + memoriesEnabled: true, oauthId: '', });