mirror of
https://github.com/immich-app/immich.git
synced 2024-12-25 10:43:13 +02:00
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 <alex.tran1502@gmail.com> Co-authored-by: Alex Tran <Alex.Tran@conductix.com>
This commit is contained in:
parent
343087e2b4
commit
a6eb227330
18
cli/src/api/open-api/api.ts
generated
18
cli/src/api/open-api/api.ts
generated
@ -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}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
Binary file not shown.
@ -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
|
||||
|
BIN
mobile/openapi/doc/CreateUserDto.md
generated
BIN
mobile/openapi/doc/CreateUserDto.md
generated
Binary file not shown.
BIN
mobile/openapi/doc/UpdateUserDto.md
generated
BIN
mobile/openapi/doc/UpdateUserDto.md
generated
Binary file not shown.
BIN
mobile/openapi/doc/UserResponseDto.md
generated
BIN
mobile/openapi/doc/UserResponseDto.md
generated
Binary file not shown.
BIN
mobile/openapi/lib/model/create_user_dto.dart
generated
BIN
mobile/openapi/lib/model/create_user_dto.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/model/update_user_dto.dart
generated
BIN
mobile/openapi/lib/model/update_user_dto.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/model/user_response_dto.dart
generated
BIN
mobile/openapi/lib/model/user_response_dto.dart
generated
Binary file not shown.
BIN
mobile/openapi/test/create_user_dto_test.dart
generated
BIN
mobile/openapi/test/create_user_dto_test.dart
generated
Binary file not shown.
BIN
mobile/openapi/test/update_user_dto_test.dart
generated
BIN
mobile/openapi/test/update_user_dto_test.dart
generated
Binary file not shown.
BIN
mobile/openapi/test/user_response_dto_test.dart
generated
BIN
mobile/openapi/test/user_response_dto_test.dart
generated
Binary file not shown.
@ -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"
|
||||
},
|
||||
|
@ -176,6 +176,7 @@ describe(AlbumService.name, () => {
|
||||
deletedAt: null,
|
||||
updatedAt: new Date('2021-01-01'),
|
||||
externalPath: null,
|
||||
memoriesEnabled: true,
|
||||
},
|
||||
ownerId: 'admin_id',
|
||||
shared: false,
|
||||
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -45,4 +45,8 @@ export class UpdateUserDto {
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
shouldChangePassword?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
memoriesEnabled?: boolean;
|
||||
}
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
@ -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');
|
||||
|
@ -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,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
@ -54,6 +54,9 @@ export class UserEntity {
|
||||
@UpdateDateColumn({ type: 'timestamptz' })
|
||||
updatedAt!: Date;
|
||||
|
||||
@Column({ default: true })
|
||||
memoriesEnabled!: boolean;
|
||||
|
||||
@OneToMany(() => TagEntity, (tag) => tag.user)
|
||||
tags!: TagEntity[];
|
||||
|
||||
|
@ -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"`);
|
||||
}
|
||||
}
|
@ -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', () => {
|
||||
|
4
server/test/fixtures/user.stub.ts
vendored
4
server/test/fixtures/user.stub.ts
vendored
@ -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,
|
||||
}),
|
||||
};
|
||||
|
18
web/src/api/open-api/api.ts
generated
18
web/src/api/open-api/api.ts
generated
@ -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}
|
||||
|
@ -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>
|
@ -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"
|
||||
|
@ -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()} />
|
||||
|
@ -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: '',
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user