From a6eb22733039f4391934962a873626bafd8b45ff Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 9 Aug 2023 22:01:16 -0400 Subject: [PATCH] feat(web,server): user memory settings (#3628) * feat(web,server): user preference for time-based memories * chore: open api * dev: mobile * fix: update * mobile work --------- Co-authored-by: Alex Co-authored-by: Alex Tran --- cli/src/api/open-api/api.ts | 18 +++++++ mobile/lib/modules/home/views/home_page.dart | 5 +- .../providers/authentication.provider.dart | 6 +-- .../lib/routing/tab_navigation_observer.dart | 18 +++++++ mobile/lib/shared/models/user.dart | 11 ++-- mobile/lib/shared/models/user.g.dart | Bin 41134 -> 48158 bytes mobile/makefile | 4 +- mobile/openapi/doc/CreateUserDto.md | Bin 599 -> 647 bytes mobile/openapi/doc/UpdateUserDto.md | Bin 762 -> 810 bytes mobile/openapi/doc/UserResponseDto.md | Bin 865 -> 902 bytes mobile/openapi/lib/model/create_user_dto.dart | Bin 4258 -> 5034 bytes mobile/openapi/lib/model/update_user_dto.dart | Bin 8374 -> 9150 bytes .../openapi/lib/model/user_response_dto.dart | Bin 6437 -> 6765 bytes mobile/openapi/test/create_user_dto_test.dart | Bin 1080 -> 1193 bytes mobile/openapi/test/update_user_dto_test.dart | Bin 1389 -> 1502 bytes .../openapi/test/user_response_dto_test.dart | Bin 1825 -> 1938 bytes server/immich-openapi-specs.json | 12 ++++- server/src/domain/album/album.service.spec.ts | 1 + .../domain/partner/partner.service.spec.ts | 7 ++- server/src/domain/user/dto/create-user.dto.ts | 6 ++- server/src/domain/user/dto/update-user.dto.ts | 4 ++ .../user/response-dto/user-response.dto.ts | 2 + server/src/domain/user/user.core.ts | 1 + server/src/domain/user/user.service.spec.ts | 10 +++- server/src/infra/entities/user.entity.ts | 3 ++ .../1691600216749-UserMemoryPreference.ts | 13 +++++ server/test/e2e/user.e2e-spec.ts | 33 ++++++++++++ server/test/fixtures/user.stub.ts | 4 ++ web/src/api/open-api/api.ts | 18 +++++++ .../memories-settings.svelte | 49 ++++++++++++++++++ .../user-settings-list.svelte | 9 +++- web/src/routes/(user)/photos/+page.svelte | 4 +- web/src/test-data/factories/user-factory.ts | 1 + 33 files changed, 220 insertions(+), 19 deletions(-) create mode 100644 server/src/infra/migrations/1691600216749-UserMemoryPreference.ts create mode 100644 web/src/lib/components/user-settings-page/memories-settings.svelte diff --git a/cli/src/api/open-api/api.ts b/cli/src/api/open-api/api.ts index a4a8ff275..c58a4a4c5 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 311bf98a7..a628df9bd 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 f2657e8f9..57723ed74 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 { Future 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 { 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 245192996..1aaef3af5 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 362adebc0..304150064 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 albums = IsarLinks(); @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 26f20b98544d35f44b864d0c509cd1b88dffc651..461168f6ebf35d1b2b9c612e1d1287677bbcd9a4 100644 GIT binary patch delta 1398 zcmZ2?kZIlzrVX3ecym*8^NT87^AeMCQd1`Tv&l}r!6t|%I+s;jpr9x}Ei)(8GdD3k zH6XDh1Ef^ZLWfHM4Du3lQ>_$=)G<`OW!9a%iA{~sa`R0#CDzFW+>#h3ZGOWo#5B2| zmsi3(R!1Q}DJwO(L=V+jyLc6(EfA8Jw(u$eZ4lsNW_y}d$7QE4i~ zx#-$~4o^)?NiEU?QNblenR)5{MSi6@IU4zCX~n4}#nBcJBh=L=^9tw!ZL$_0{ojV2ptMVbG$*w zJRan5n0(MpVN$3wMgUHpsBr{SFfqCVLvZoOXhsad$=Fm*_P6{+oXXA8@j_S({b&6Q zQ~x=yQ<#EStq-(2Mx@G(_D6|SDd31jWrKe&ruGCAEP@8<@fa#66$VUBNKu~rE=+W? zdn`dV*%kui7~fv_pq7mXS=r>k3E^nTVq+&*nLq$~ zjszOWhnlu0PwaC88OevLcJjNv2u!C!&D}h&bq5b&r+O21rZjaqq diff --git a/mobile/openapi/doc/UpdateUserDto.md b/mobile/openapi/doc/UpdateUserDto.md index d3b41f3c38a79fcb0f2849383d29306eb3b5228f..ff6bc8d4299ef4bb536d7d6f9d518c3505a360d0 100644 GIT binary patch delta 32 ocmeyxx{7VX2}b_h)ZF}{%+zAnyu_rO)Rf6JjEb92Fd8ue0M_aZBme*a delta 12 TcmZ3*_KS7H3C7Jo7>yVKB>V*k diff --git a/mobile/openapi/doc/UserResponseDto.md b/mobile/openapi/doc/UserResponseDto.md index 320827f984dbdf6e17a0456daee99fbd7190a019..6455c12d0378a00a6facb8fefdabd3f9224eebad 100644 GIT binary patch delta 31 mcmaFJ*2cc!J0o9iYHof}W@@o(USd*C>f}Hs@y*{Evls!#jtk}h delta 12 TcmZo;f5^7sJL6^xM9kqRXBNqH%wPX{ML!M9!Sh z3WbpLV$L<$XpKOXo)Z{270h2rhdGm;(DzoTv&N;{K+=u6uMu_!DD?%+$qX9G87VT% z5r6Tw+MAFe*TH(`twX`2#FXVf;B*Wgy&3*zhP$k2FKFZbIMm7tP6mwgXWknLLh^T3 zY$gPKfJ14qzv%p2>{M$xK~nsFJK!hIgju!!u$^@i9OH2+sjruIvh;wF7dOHLpD{`$ zoyFbqQ~EgGPG;3VSAJ!iqCo%6Uu3`av7ab<-!X1hP2())t*y0$y!hSPs@GTBOOpf$ iaar{(UjA!w8jkTLHg(WHw^|KgdI`I?Td%ioTK@n!v-?H> delta 43 zcmV+`0M!4gC!!&+SP5b(akwbHjE-k`T02tsFEq07c(<6DWM4} z*xF*Ko6c&)#E+(K^LF-$jOZ$DRnS$raY{35pb4tSD%dKNWMmfWp-F5G;@Qc9Zqejf U0`ll$oBs*yVMiC7yh)-M0B|^Ag8%>k delta 46 zcmV+}0MY;LG^H}IYyz{70z3h;72a^IC EYnkd03jhEB diff --git a/mobile/openapi/test/create_user_dto_test.dart b/mobile/openapi/test/create_user_dto_test.dart index 872635e2fb959a92624c61753e072f08523068e8..9ce64c1e054ad3c3e2152f5bb377f3ac760401d7 100644 GIT binary patch delta 55 zcmdnNv66E`GP7_}etwQZZfb6RQD$nfYhGefPU_@BW+i@9p_IuNnM@{UF`okfHUt#B delta 15 XcmZ3 { 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 c8e048969..ac15abc8c 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: { 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: { 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 afb5aec23..c9a9945af 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 7eb98f538..a1e053855 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 a2bd50883..9a3372ad5 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 e7c0f7f2b..6c59d9ca7 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 a573a8c20..ef1a07d17 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({ 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({ 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 7cdac1f82..e6555153a 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 000000000..723874908 --- /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 { + await queryRunner.query(`ALTER TABLE "users" ADD "memoriesEnabled" boolean NOT NULL DEFAULT true`); + } + + public async down(queryRunner: QueryRunner): Promise { + 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 0ba37e33c..90c79ed96 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 4367bf60e..f2a8dcab8 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({ ...authStub.user1, @@ -33,6 +34,7 @@ export const userStub = { updatedAt: new Date('2021-01-01'), tags: [], assets: [], + memoriesEnabled: true, }), user2: Object.freeze({ ...authStub.user2, @@ -49,6 +51,7 @@ export const userStub = { updatedAt: new Date('2021-01-01'), tags: [], assets: [], + memoriesEnabled: true, }), storageLabel: Object.freeze({ ...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 a4a8ff275..c58a4a4c5 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 000000000..9e269fc09 --- /dev/null +++ b/web/src/lib/components/user-settings-page/memories-settings.svelte @@ -0,0 +1,49 @@ + + +
+
+
+
+
+ +
+
+ +
+
+
+
+
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 76311414c..376c78e63 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 @@ + + + + {#if oauthEnabled} {#if assetCount} - + {#if data.user.memoriesEnabled} + + {/if} {:else} openFileUploadDialog()} /> diff --git a/web/src/test-data/factories/user-factory.ts b/web/src/test-data/factories/user-factory.ts index ebf280159..507242fe8 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({ createdAt: Sync.each(() => faker.date.past().toISOString()), deletedAt: null, updatedAt: Sync.each(() => faker.date.past().toISOString()), + memoriesEnabled: true, oauthId: '', });