feat: user's features preferences (#12099)

* feat: metadata in UserPreference

* feat: web metadata settings

* feat: web metadata settings

* fix: typo

* patch openapi

* fix: missing translation key

* new organization of preference strucutre

* feature settings on web

* localization

* added and used feature settings

* add default value to response dto

* patch openapi

* format en.json file

* implement helper method

* use tags preference logic

* Fix logic bug and add tests

* fix preference can be null in detail panel
This commit is contained in:
Alex 2024-08-29 14:29:04 -05:00 committed by GitHub
parent 9bfaa525db
commit ebecb60f39
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 1418 additions and 296 deletions

View File

@ -4,14 +4,30 @@ dynamic upgradeDto(dynamic value, String targetType) {
switch (targetType) {
case 'UserPreferencesResponseDto':
if (value is Map) {
if (value['rating'] == null) {
value['rating'] = RatingResponse().toJson();
}
if (value['download']['includeEmbeddedVideos'] == null) {
value['download']['includeEmbeddedVideos'] = false;
}
addDefault(value, 'download.includeEmbeddedVideos', false);
addDefault(value, 'folders', FoldersResponse().toJson());
addDefault(value, 'memories', MemoriesResponse().toJson());
addDefault(value, 'ratings', RatingsResponse().toJson());
addDefault(value, 'people', PeopleResponse().toJson());
addDefault(value, 'tags', TagsResponse().toJson());
}
break;
}
}
addDefault(dynamic value, String keys, dynamic defaultValue) {
// Loop through the keys and assign the default value if the key is not present
List<String> keyList = keys.split('.');
dynamic current = value;
for (int i = 0; i < keyList.length - 1; i++) {
if (current[keyList[i]] == null) {
current[keyList[i]] = {};
}
current = current[keyList[i]];
}
if (current[keyList.last] == null) {
current[keyList.last] = defaultValue;
}
}

View File

@ -324,6 +324,8 @@ Class | Method | HTTP request | Description
- [FileReportDto](doc//FileReportDto.md)
- [FileReportFixDto](doc//FileReportFixDto.md)
- [FileReportItemDto](doc//FileReportItemDto.md)
- [FoldersResponse](doc//FoldersResponse.md)
- [FoldersUpdate](doc//FoldersUpdate.md)
- [ImageFormat](doc//ImageFormat.md)
- [JobCommand](doc//JobCommand.md)
- [JobCommandDto](doc//JobCommandDto.md)
@ -342,12 +344,12 @@ Class | Method | HTTP request | Description
- [MapMarkerResponseDto](doc//MapMarkerResponseDto.md)
- [MapReverseGeocodeResponseDto](doc//MapReverseGeocodeResponseDto.md)
- [MapTheme](doc//MapTheme.md)
- [MemoriesResponse](doc//MemoriesResponse.md)
- [MemoriesUpdate](doc//MemoriesUpdate.md)
- [MemoryCreateDto](doc//MemoryCreateDto.md)
- [MemoryLaneResponseDto](doc//MemoryLaneResponseDto.md)
- [MemoryResponse](doc//MemoryResponse.md)
- [MemoryResponseDto](doc//MemoryResponseDto.md)
- [MemoryType](doc//MemoryType.md)
- [MemoryUpdate](doc//MemoryUpdate.md)
- [MemoryUpdateDto](doc//MemoryUpdateDto.md)
- [MergePersonDto](doc//MergePersonDto.md)
- [MetadataSearchDto](doc//MetadataSearchDto.md)
@ -359,7 +361,9 @@ Class | Method | HTTP request | Description
- [PartnerResponseDto](doc//PartnerResponseDto.md)
- [PathEntityType](doc//PathEntityType.md)
- [PathType](doc//PathType.md)
- [PeopleResponse](doc//PeopleResponse.md)
- [PeopleResponseDto](doc//PeopleResponseDto.md)
- [PeopleUpdate](doc//PeopleUpdate.md)
- [PeopleUpdateDto](doc//PeopleUpdateDto.md)
- [PeopleUpdateItem](doc//PeopleUpdateItem.md)
- [Permission](doc//Permission.md)
@ -372,8 +376,8 @@ Class | Method | HTTP request | Description
- [PurchaseResponse](doc//PurchaseResponse.md)
- [PurchaseUpdate](doc//PurchaseUpdate.md)
- [QueueStatusDto](doc//QueueStatusDto.md)
- [RatingResponse](doc//RatingResponse.md)
- [RatingUpdate](doc//RatingUpdate.md)
- [RatingsResponse](doc//RatingsResponse.md)
- [RatingsUpdate](doc//RatingsUpdate.md)
- [ReactionLevel](doc//ReactionLevel.md)
- [ReactionType](doc//ReactionType.md)
- [ReverseGeocodingStateResponseDto](doc//ReverseGeocodingStateResponseDto.md)
@ -435,6 +439,8 @@ Class | Method | HTTP request | Description
- [TagResponseDto](doc//TagResponseDto.md)
- [TagUpdateDto](doc//TagUpdateDto.md)
- [TagUpsertDto](doc//TagUpsertDto.md)
- [TagsResponse](doc//TagsResponse.md)
- [TagsUpdate](doc//TagsUpdate.md)
- [TimeBucketResponseDto](doc//TimeBucketResponseDto.md)
- [TimeBucketSize](doc//TimeBucketSize.md)
- [ToneMapping](doc//ToneMapping.md)

View File

@ -138,6 +138,8 @@ part 'model/file_checksum_response_dto.dart';
part 'model/file_report_dto.dart';
part 'model/file_report_fix_dto.dart';
part 'model/file_report_item_dto.dart';
part 'model/folders_response.dart';
part 'model/folders_update.dart';
part 'model/image_format.dart';
part 'model/job_command.dart';
part 'model/job_command_dto.dart';
@ -156,12 +158,12 @@ part 'model/logout_response_dto.dart';
part 'model/map_marker_response_dto.dart';
part 'model/map_reverse_geocode_response_dto.dart';
part 'model/map_theme.dart';
part 'model/memories_response.dart';
part 'model/memories_update.dart';
part 'model/memory_create_dto.dart';
part 'model/memory_lane_response_dto.dart';
part 'model/memory_response.dart';
part 'model/memory_response_dto.dart';
part 'model/memory_type.dart';
part 'model/memory_update.dart';
part 'model/memory_update_dto.dart';
part 'model/merge_person_dto.dart';
part 'model/metadata_search_dto.dart';
@ -173,7 +175,9 @@ part 'model/partner_direction.dart';
part 'model/partner_response_dto.dart';
part 'model/path_entity_type.dart';
part 'model/path_type.dart';
part 'model/people_response.dart';
part 'model/people_response_dto.dart';
part 'model/people_update.dart';
part 'model/people_update_dto.dart';
part 'model/people_update_item.dart';
part 'model/permission.dart';
@ -186,8 +190,8 @@ part 'model/places_response_dto.dart';
part 'model/purchase_response.dart';
part 'model/purchase_update.dart';
part 'model/queue_status_dto.dart';
part 'model/rating_response.dart';
part 'model/rating_update.dart';
part 'model/ratings_response.dart';
part 'model/ratings_update.dart';
part 'model/reaction_level.dart';
part 'model/reaction_type.dart';
part 'model/reverse_geocoding_state_response_dto.dart';
@ -249,6 +253,8 @@ part 'model/tag_create_dto.dart';
part 'model/tag_response_dto.dart';
part 'model/tag_update_dto.dart';
part 'model/tag_upsert_dto.dart';
part 'model/tags_response.dart';
part 'model/tags_update.dart';
part 'model/time_bucket_response_dto.dart';
part 'model/time_bucket_size.dart';
part 'model/tone_mapping.dart';

View File

@ -331,6 +331,10 @@ class ApiClient {
return FileReportFixDto.fromJson(value);
case 'FileReportItemDto':
return FileReportItemDto.fromJson(value);
case 'FoldersResponse':
return FoldersResponse.fromJson(value);
case 'FoldersUpdate':
return FoldersUpdate.fromJson(value);
case 'ImageFormat':
return ImageFormatTypeTransformer().decode(value);
case 'JobCommand':
@ -367,18 +371,18 @@ class ApiClient {
return MapReverseGeocodeResponseDto.fromJson(value);
case 'MapTheme':
return MapThemeTypeTransformer().decode(value);
case 'MemoriesResponse':
return MemoriesResponse.fromJson(value);
case 'MemoriesUpdate':
return MemoriesUpdate.fromJson(value);
case 'MemoryCreateDto':
return MemoryCreateDto.fromJson(value);
case 'MemoryLaneResponseDto':
return MemoryLaneResponseDto.fromJson(value);
case 'MemoryResponse':
return MemoryResponse.fromJson(value);
case 'MemoryResponseDto':
return MemoryResponseDto.fromJson(value);
case 'MemoryType':
return MemoryTypeTypeTransformer().decode(value);
case 'MemoryUpdate':
return MemoryUpdate.fromJson(value);
case 'MemoryUpdateDto':
return MemoryUpdateDto.fromJson(value);
case 'MergePersonDto':
@ -401,8 +405,12 @@ class ApiClient {
return PathEntityTypeTypeTransformer().decode(value);
case 'PathType':
return PathTypeTypeTransformer().decode(value);
case 'PeopleResponse':
return PeopleResponse.fromJson(value);
case 'PeopleResponseDto':
return PeopleResponseDto.fromJson(value);
case 'PeopleUpdate':
return PeopleUpdate.fromJson(value);
case 'PeopleUpdateDto':
return PeopleUpdateDto.fromJson(value);
case 'PeopleUpdateItem':
@ -427,10 +435,10 @@ class ApiClient {
return PurchaseUpdate.fromJson(value);
case 'QueueStatusDto':
return QueueStatusDto.fromJson(value);
case 'RatingResponse':
return RatingResponse.fromJson(value);
case 'RatingUpdate':
return RatingUpdate.fromJson(value);
case 'RatingsResponse':
return RatingsResponse.fromJson(value);
case 'RatingsUpdate':
return RatingsUpdate.fromJson(value);
case 'ReactionLevel':
return ReactionLevelTypeTransformer().decode(value);
case 'ReactionType':
@ -553,6 +561,10 @@ class ApiClient {
return TagUpdateDto.fromJson(value);
case 'TagUpsertDto':
return TagUpsertDto.fromJson(value);
case 'TagsResponse':
return TagsResponse.fromJson(value);
case 'TagsUpdate':
return TagsUpdate.fromJson(value);
case 'TimeBucketResponseDto':
return TimeBucketResponseDto.fromJson(value);
case 'TimeBucketSize':

View File

@ -0,0 +1,106 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.18
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
part of openapi.api;
class FoldersResponse {
/// Returns a new [FoldersResponse] instance.
FoldersResponse({
this.enabled = false,
this.sidebarWeb = false,
});
bool enabled;
bool sidebarWeb;
@override
bool operator ==(Object other) => identical(this, other) || other is FoldersResponse &&
other.enabled == enabled &&
other.sidebarWeb == sidebarWeb;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(enabled.hashCode) +
(sidebarWeb.hashCode);
@override
String toString() => 'FoldersResponse[enabled=$enabled, sidebarWeb=$sidebarWeb]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'enabled'] = this.enabled;
json[r'sidebarWeb'] = this.sidebarWeb;
return json;
}
/// Returns a new [FoldersResponse] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static FoldersResponse? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast<String, dynamic>();
return FoldersResponse(
enabled: mapValueOfType<bool>(json, r'enabled')!,
sidebarWeb: mapValueOfType<bool>(json, r'sidebarWeb')!,
);
}
return null;
}
static List<FoldersResponse> listFromJson(dynamic json, {bool growable = false,}) {
final result = <FoldersResponse>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = FoldersResponse.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, FoldersResponse> mapFromJson(dynamic json) {
final map = <String, FoldersResponse>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = FoldersResponse.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of FoldersResponse-objects as value to a dart map
static Map<String, List<FoldersResponse>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<FoldersResponse>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = FoldersResponse.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'enabled',
'sidebarWeb',
};
}

View File

@ -0,0 +1,124 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.18
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
part of openapi.api;
class FoldersUpdate {
/// Returns a new [FoldersUpdate] instance.
FoldersUpdate({
this.enabled,
this.sidebarWeb,
});
///
/// 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? enabled;
///
/// 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? sidebarWeb;
@override
bool operator ==(Object other) => identical(this, other) || other is FoldersUpdate &&
other.enabled == enabled &&
other.sidebarWeb == sidebarWeb;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(enabled == null ? 0 : enabled!.hashCode) +
(sidebarWeb == null ? 0 : sidebarWeb!.hashCode);
@override
String toString() => 'FoldersUpdate[enabled=$enabled, sidebarWeb=$sidebarWeb]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
if (this.enabled != null) {
json[r'enabled'] = this.enabled;
} else {
// json[r'enabled'] = null;
}
if (this.sidebarWeb != null) {
json[r'sidebarWeb'] = this.sidebarWeb;
} else {
// json[r'sidebarWeb'] = null;
}
return json;
}
/// Returns a new [FoldersUpdate] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static FoldersUpdate? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast<String, dynamic>();
return FoldersUpdate(
enabled: mapValueOfType<bool>(json, r'enabled'),
sidebarWeb: mapValueOfType<bool>(json, r'sidebarWeb'),
);
}
return null;
}
static List<FoldersUpdate> listFromJson(dynamic json, {bool growable = false,}) {
final result = <FoldersUpdate>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = FoldersUpdate.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, FoldersUpdate> mapFromJson(dynamic json) {
final map = <String, FoldersUpdate>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = FoldersUpdate.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of FoldersUpdate-objects as value to a dart map
static Map<String, List<FoldersUpdate>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<FoldersUpdate>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = FoldersUpdate.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
};
}

View File

@ -10,16 +10,16 @@
part of openapi.api;
class MemoryResponse {
/// Returns a new [MemoryResponse] instance.
MemoryResponse({
required this.enabled,
class MemoriesResponse {
/// Returns a new [MemoriesResponse] instance.
MemoriesResponse({
this.enabled = true,
});
bool enabled;
@override
bool operator ==(Object other) => identical(this, other) || other is MemoryResponse &&
bool operator ==(Object other) => identical(this, other) || other is MemoriesResponse &&
other.enabled == enabled;
@override
@ -28,7 +28,7 @@ class MemoryResponse {
(enabled.hashCode);
@override
String toString() => 'MemoryResponse[enabled=$enabled]';
String toString() => 'MemoriesResponse[enabled=$enabled]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
@ -36,25 +36,25 @@ class MemoryResponse {
return json;
}
/// Returns a new [MemoryResponse] instance and imports its values from
/// Returns a new [MemoriesResponse] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static MemoryResponse? fromJson(dynamic value) {
static MemoriesResponse? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast<String, dynamic>();
return MemoryResponse(
return MemoriesResponse(
enabled: mapValueOfType<bool>(json, r'enabled')!,
);
}
return null;
}
static List<MemoryResponse> listFromJson(dynamic json, {bool growable = false,}) {
final result = <MemoryResponse>[];
static List<MemoriesResponse> listFromJson(dynamic json, {bool growable = false,}) {
final result = <MemoriesResponse>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = MemoryResponse.fromJson(row);
final value = MemoriesResponse.fromJson(row);
if (value != null) {
result.add(value);
}
@ -63,12 +63,12 @@ class MemoryResponse {
return result.toList(growable: growable);
}
static Map<String, MemoryResponse> mapFromJson(dynamic json) {
final map = <String, MemoryResponse>{};
static Map<String, MemoriesResponse> mapFromJson(dynamic json) {
final map = <String, MemoriesResponse>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = MemoryResponse.fromJson(entry.value);
final value = MemoriesResponse.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
@ -77,14 +77,14 @@ class MemoryResponse {
return map;
}
// maps a json object with a list of MemoryResponse-objects as value to a dart map
static Map<String, List<MemoryResponse>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<MemoryResponse>>{};
// maps a json object with a list of MemoriesResponse-objects as value to a dart map
static Map<String, List<MemoriesResponse>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<MemoriesResponse>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = MemoryResponse.listFromJson(entry.value, growable: growable,);
map[entry.key] = MemoriesResponse.listFromJson(entry.value, growable: growable,);
}
}
return map;

View File

@ -10,9 +10,9 @@
part of openapi.api;
class RatingUpdate {
/// Returns a new [RatingUpdate] instance.
RatingUpdate({
class MemoriesUpdate {
/// Returns a new [MemoriesUpdate] instance.
MemoriesUpdate({
this.enabled,
});
@ -25,7 +25,7 @@ class RatingUpdate {
bool? enabled;
@override
bool operator ==(Object other) => identical(this, other) || other is RatingUpdate &&
bool operator ==(Object other) => identical(this, other) || other is MemoriesUpdate &&
other.enabled == enabled;
@override
@ -34,7 +34,7 @@ class RatingUpdate {
(enabled == null ? 0 : enabled!.hashCode);
@override
String toString() => 'RatingUpdate[enabled=$enabled]';
String toString() => 'MemoriesUpdate[enabled=$enabled]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
@ -46,25 +46,25 @@ class RatingUpdate {
return json;
}
/// Returns a new [RatingUpdate] instance and imports its values from
/// Returns a new [MemoriesUpdate] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static RatingUpdate? fromJson(dynamic value) {
static MemoriesUpdate? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast<String, dynamic>();
return RatingUpdate(
return MemoriesUpdate(
enabled: mapValueOfType<bool>(json, r'enabled'),
);
}
return null;
}
static List<RatingUpdate> listFromJson(dynamic json, {bool growable = false,}) {
final result = <RatingUpdate>[];
static List<MemoriesUpdate> listFromJson(dynamic json, {bool growable = false,}) {
final result = <MemoriesUpdate>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = RatingUpdate.fromJson(row);
final value = MemoriesUpdate.fromJson(row);
if (value != null) {
result.add(value);
}
@ -73,12 +73,12 @@ class RatingUpdate {
return result.toList(growable: growable);
}
static Map<String, RatingUpdate> mapFromJson(dynamic json) {
final map = <String, RatingUpdate>{};
static Map<String, MemoriesUpdate> mapFromJson(dynamic json) {
final map = <String, MemoriesUpdate>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = RatingUpdate.fromJson(entry.value);
final value = MemoriesUpdate.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
@ -87,14 +87,14 @@ class RatingUpdate {
return map;
}
// maps a json object with a list of RatingUpdate-objects as value to a dart map
static Map<String, List<RatingUpdate>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<RatingUpdate>>{};
// maps a json object with a list of MemoriesUpdate-objects as value to a dart map
static Map<String, List<MemoriesUpdate>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<MemoriesUpdate>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = RatingUpdate.listFromJson(entry.value, growable: growable,);
map[entry.key] = MemoriesUpdate.listFromJson(entry.value, growable: growable,);
}
}
return map;

View File

@ -0,0 +1,106 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.18
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
part of openapi.api;
class PeopleResponse {
/// Returns a new [PeopleResponse] instance.
PeopleResponse({
this.enabled = true,
this.sidebarWeb = false,
});
bool enabled;
bool sidebarWeb;
@override
bool operator ==(Object other) => identical(this, other) || other is PeopleResponse &&
other.enabled == enabled &&
other.sidebarWeb == sidebarWeb;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(enabled.hashCode) +
(sidebarWeb.hashCode);
@override
String toString() => 'PeopleResponse[enabled=$enabled, sidebarWeb=$sidebarWeb]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'enabled'] = this.enabled;
json[r'sidebarWeb'] = this.sidebarWeb;
return json;
}
/// Returns a new [PeopleResponse] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static PeopleResponse? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast<String, dynamic>();
return PeopleResponse(
enabled: mapValueOfType<bool>(json, r'enabled')!,
sidebarWeb: mapValueOfType<bool>(json, r'sidebarWeb')!,
);
}
return null;
}
static List<PeopleResponse> listFromJson(dynamic json, {bool growable = false,}) {
final result = <PeopleResponse>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = PeopleResponse.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, PeopleResponse> mapFromJson(dynamic json) {
final map = <String, PeopleResponse>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = PeopleResponse.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of PeopleResponse-objects as value to a dart map
static Map<String, List<PeopleResponse>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<PeopleResponse>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = PeopleResponse.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'enabled',
'sidebarWeb',
};
}

View File

@ -0,0 +1,124 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.18
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
part of openapi.api;
class PeopleUpdate {
/// Returns a new [PeopleUpdate] instance.
PeopleUpdate({
this.enabled,
this.sidebarWeb,
});
///
/// 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? enabled;
///
/// 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? sidebarWeb;
@override
bool operator ==(Object other) => identical(this, other) || other is PeopleUpdate &&
other.enabled == enabled &&
other.sidebarWeb == sidebarWeb;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(enabled == null ? 0 : enabled!.hashCode) +
(sidebarWeb == null ? 0 : sidebarWeb!.hashCode);
@override
String toString() => 'PeopleUpdate[enabled=$enabled, sidebarWeb=$sidebarWeb]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
if (this.enabled != null) {
json[r'enabled'] = this.enabled;
} else {
// json[r'enabled'] = null;
}
if (this.sidebarWeb != null) {
json[r'sidebarWeb'] = this.sidebarWeb;
} else {
// json[r'sidebarWeb'] = null;
}
return json;
}
/// Returns a new [PeopleUpdate] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static PeopleUpdate? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast<String, dynamic>();
return PeopleUpdate(
enabled: mapValueOfType<bool>(json, r'enabled'),
sidebarWeb: mapValueOfType<bool>(json, r'sidebarWeb'),
);
}
return null;
}
static List<PeopleUpdate> listFromJson(dynamic json, {bool growable = false,}) {
final result = <PeopleUpdate>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = PeopleUpdate.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, PeopleUpdate> mapFromJson(dynamic json) {
final map = <String, PeopleUpdate>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = PeopleUpdate.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of PeopleUpdate-objects as value to a dart map
static Map<String, List<PeopleUpdate>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<PeopleUpdate>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = PeopleUpdate.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
};
}

View File

@ -10,16 +10,16 @@
part of openapi.api;
class RatingResponse {
/// Returns a new [RatingResponse] instance.
RatingResponse({
class RatingsResponse {
/// Returns a new [RatingsResponse] instance.
RatingsResponse({
this.enabled = false,
});
bool enabled;
@override
bool operator ==(Object other) => identical(this, other) || other is RatingResponse &&
bool operator ==(Object other) => identical(this, other) || other is RatingsResponse &&
other.enabled == enabled;
@override
@ -28,7 +28,7 @@ class RatingResponse {
(enabled.hashCode);
@override
String toString() => 'RatingResponse[enabled=$enabled]';
String toString() => 'RatingsResponse[enabled=$enabled]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
@ -36,25 +36,25 @@ class RatingResponse {
return json;
}
/// Returns a new [RatingResponse] instance and imports its values from
/// Returns a new [RatingsResponse] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static RatingResponse? fromJson(dynamic value) {
static RatingsResponse? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast<String, dynamic>();
return RatingResponse(
return RatingsResponse(
enabled: mapValueOfType<bool>(json, r'enabled')!,
);
}
return null;
}
static List<RatingResponse> listFromJson(dynamic json, {bool growable = false,}) {
final result = <RatingResponse>[];
static List<RatingsResponse> listFromJson(dynamic json, {bool growable = false,}) {
final result = <RatingsResponse>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = RatingResponse.fromJson(row);
final value = RatingsResponse.fromJson(row);
if (value != null) {
result.add(value);
}
@ -63,12 +63,12 @@ class RatingResponse {
return result.toList(growable: growable);
}
static Map<String, RatingResponse> mapFromJson(dynamic json) {
final map = <String, RatingResponse>{};
static Map<String, RatingsResponse> mapFromJson(dynamic json) {
final map = <String, RatingsResponse>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = RatingResponse.fromJson(entry.value);
final value = RatingsResponse.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
@ -77,14 +77,14 @@ class RatingResponse {
return map;
}
// maps a json object with a list of RatingResponse-objects as value to a dart map
static Map<String, List<RatingResponse>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<RatingResponse>>{};
// maps a json object with a list of RatingsResponse-objects as value to a dart map
static Map<String, List<RatingsResponse>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<RatingsResponse>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = RatingResponse.listFromJson(entry.value, growable: growable,);
map[entry.key] = RatingsResponse.listFromJson(entry.value, growable: growable,);
}
}
return map;

View File

@ -10,9 +10,9 @@
part of openapi.api;
class MemoryUpdate {
/// Returns a new [MemoryUpdate] instance.
MemoryUpdate({
class RatingsUpdate {
/// Returns a new [RatingsUpdate] instance.
RatingsUpdate({
this.enabled,
});
@ -25,7 +25,7 @@ class MemoryUpdate {
bool? enabled;
@override
bool operator ==(Object other) => identical(this, other) || other is MemoryUpdate &&
bool operator ==(Object other) => identical(this, other) || other is RatingsUpdate &&
other.enabled == enabled;
@override
@ -34,7 +34,7 @@ class MemoryUpdate {
(enabled == null ? 0 : enabled!.hashCode);
@override
String toString() => 'MemoryUpdate[enabled=$enabled]';
String toString() => 'RatingsUpdate[enabled=$enabled]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
@ -46,25 +46,25 @@ class MemoryUpdate {
return json;
}
/// Returns a new [MemoryUpdate] instance and imports its values from
/// Returns a new [RatingsUpdate] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static MemoryUpdate? fromJson(dynamic value) {
static RatingsUpdate? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast<String, dynamic>();
return MemoryUpdate(
return RatingsUpdate(
enabled: mapValueOfType<bool>(json, r'enabled'),
);
}
return null;
}
static List<MemoryUpdate> listFromJson(dynamic json, {bool growable = false,}) {
final result = <MemoryUpdate>[];
static List<RatingsUpdate> listFromJson(dynamic json, {bool growable = false,}) {
final result = <RatingsUpdate>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = MemoryUpdate.fromJson(row);
final value = RatingsUpdate.fromJson(row);
if (value != null) {
result.add(value);
}
@ -73,12 +73,12 @@ class MemoryUpdate {
return result.toList(growable: growable);
}
static Map<String, MemoryUpdate> mapFromJson(dynamic json) {
final map = <String, MemoryUpdate>{};
static Map<String, RatingsUpdate> mapFromJson(dynamic json) {
final map = <String, RatingsUpdate>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = MemoryUpdate.fromJson(entry.value);
final value = RatingsUpdate.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
@ -87,14 +87,14 @@ class MemoryUpdate {
return map;
}
// maps a json object with a list of MemoryUpdate-objects as value to a dart map
static Map<String, List<MemoryUpdate>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<MemoryUpdate>>{};
// maps a json object with a list of RatingsUpdate-objects as value to a dart map
static Map<String, List<RatingsUpdate>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<RatingsUpdate>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = MemoryUpdate.listFromJson(entry.value, growable: growable,);
map[entry.key] = RatingsUpdate.listFromJson(entry.value, growable: growable,);
}
}
return map;

View File

@ -0,0 +1,106 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.18
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
part of openapi.api;
class TagsResponse {
/// Returns a new [TagsResponse] instance.
TagsResponse({
this.enabled = true,
this.sidebarWeb = true,
});
bool enabled;
bool sidebarWeb;
@override
bool operator ==(Object other) => identical(this, other) || other is TagsResponse &&
other.enabled == enabled &&
other.sidebarWeb == sidebarWeb;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(enabled.hashCode) +
(sidebarWeb.hashCode);
@override
String toString() => 'TagsResponse[enabled=$enabled, sidebarWeb=$sidebarWeb]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'enabled'] = this.enabled;
json[r'sidebarWeb'] = this.sidebarWeb;
return json;
}
/// Returns a new [TagsResponse] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static TagsResponse? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast<String, dynamic>();
return TagsResponse(
enabled: mapValueOfType<bool>(json, r'enabled')!,
sidebarWeb: mapValueOfType<bool>(json, r'sidebarWeb')!,
);
}
return null;
}
static List<TagsResponse> listFromJson(dynamic json, {bool growable = false,}) {
final result = <TagsResponse>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = TagsResponse.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, TagsResponse> mapFromJson(dynamic json) {
final map = <String, TagsResponse>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = TagsResponse.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of TagsResponse-objects as value to a dart map
static Map<String, List<TagsResponse>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<TagsResponse>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = TagsResponse.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'enabled',
'sidebarWeb',
};
}

124
mobile/openapi/lib/model/tags_update.dart generated Normal file
View File

@ -0,0 +1,124 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.18
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
part of openapi.api;
class TagsUpdate {
/// Returns a new [TagsUpdate] instance.
TagsUpdate({
this.enabled,
this.sidebarWeb,
});
///
/// 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? enabled;
///
/// 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? sidebarWeb;
@override
bool operator ==(Object other) => identical(this, other) || other is TagsUpdate &&
other.enabled == enabled &&
other.sidebarWeb == sidebarWeb;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(enabled == null ? 0 : enabled!.hashCode) +
(sidebarWeb == null ? 0 : sidebarWeb!.hashCode);
@override
String toString() => 'TagsUpdate[enabled=$enabled, sidebarWeb=$sidebarWeb]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
if (this.enabled != null) {
json[r'enabled'] = this.enabled;
} else {
// json[r'enabled'] = null;
}
if (this.sidebarWeb != null) {
json[r'sidebarWeb'] = this.sidebarWeb;
} else {
// json[r'sidebarWeb'] = null;
}
return json;
}
/// Returns a new [TagsUpdate] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static TagsUpdate? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast<String, dynamic>();
return TagsUpdate(
enabled: mapValueOfType<bool>(json, r'enabled'),
sidebarWeb: mapValueOfType<bool>(json, r'sidebarWeb'),
);
}
return null;
}
static List<TagsUpdate> listFromJson(dynamic json, {bool growable = false,}) {
final result = <TagsUpdate>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = TagsUpdate.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, TagsUpdate> mapFromJson(dynamic json) {
final map = <String, TagsUpdate>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = TagsUpdate.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of TagsUpdate-objects as value to a dart map
static Map<String, List<TagsUpdate>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<TagsUpdate>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = TagsUpdate.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
};
}

View File

@ -16,9 +16,12 @@ class UserPreferencesResponseDto {
required this.avatar,
required this.download,
required this.emailNotifications,
required this.folders,
required this.memories,
required this.people,
required this.purchase,
required this.rating,
required this.ratings,
required this.tags,
});
AvatarResponse avatar;
@ -27,20 +30,29 @@ class UserPreferencesResponseDto {
EmailNotificationsResponse emailNotifications;
MemoryResponse memories;
FoldersResponse folders;
MemoriesResponse memories;
PeopleResponse people;
PurchaseResponse purchase;
RatingResponse rating;
RatingsResponse ratings;
TagsResponse tags;
@override
bool operator ==(Object other) => identical(this, other) || other is UserPreferencesResponseDto &&
other.avatar == avatar &&
other.download == download &&
other.emailNotifications == emailNotifications &&
other.folders == folders &&
other.memories == memories &&
other.people == people &&
other.purchase == purchase &&
other.rating == rating;
other.ratings == ratings &&
other.tags == tags;
@override
int get hashCode =>
@ -48,21 +60,27 @@ class UserPreferencesResponseDto {
(avatar.hashCode) +
(download.hashCode) +
(emailNotifications.hashCode) +
(folders.hashCode) +
(memories.hashCode) +
(people.hashCode) +
(purchase.hashCode) +
(rating.hashCode);
(ratings.hashCode) +
(tags.hashCode);
@override
String toString() => 'UserPreferencesResponseDto[avatar=$avatar, download=$download, emailNotifications=$emailNotifications, memories=$memories, purchase=$purchase, rating=$rating]';
String toString() => 'UserPreferencesResponseDto[avatar=$avatar, download=$download, emailNotifications=$emailNotifications, folders=$folders, memories=$memories, people=$people, purchase=$purchase, ratings=$ratings, tags=$tags]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'avatar'] = this.avatar;
json[r'download'] = this.download;
json[r'emailNotifications'] = this.emailNotifications;
json[r'folders'] = this.folders;
json[r'memories'] = this.memories;
json[r'people'] = this.people;
json[r'purchase'] = this.purchase;
json[r'rating'] = this.rating;
json[r'ratings'] = this.ratings;
json[r'tags'] = this.tags;
return json;
}
@ -77,9 +95,12 @@ class UserPreferencesResponseDto {
avatar: AvatarResponse.fromJson(json[r'avatar'])!,
download: DownloadResponse.fromJson(json[r'download'])!,
emailNotifications: EmailNotificationsResponse.fromJson(json[r'emailNotifications'])!,
memories: MemoryResponse.fromJson(json[r'memories'])!,
folders: FoldersResponse.fromJson(json[r'folders'])!,
memories: MemoriesResponse.fromJson(json[r'memories'])!,
people: PeopleResponse.fromJson(json[r'people'])!,
purchase: PurchaseResponse.fromJson(json[r'purchase'])!,
rating: RatingResponse.fromJson(json[r'rating'])!,
ratings: RatingsResponse.fromJson(json[r'ratings'])!,
tags: TagsResponse.fromJson(json[r'tags'])!,
);
}
return null;
@ -130,9 +151,12 @@ class UserPreferencesResponseDto {
'avatar',
'download',
'emailNotifications',
'folders',
'memories',
'people',
'purchase',
'rating',
'ratings',
'tags',
};
}

View File

@ -16,9 +16,12 @@ class UserPreferencesUpdateDto {
this.avatar,
this.download,
this.emailNotifications,
this.folders,
this.memories,
this.people,
this.purchase,
this.rating,
this.ratings,
this.tags,
});
///
@ -51,7 +54,23 @@ class UserPreferencesUpdateDto {
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
MemoryUpdate? memories;
FoldersUpdate? folders;
///
/// 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.
///
MemoriesUpdate? memories;
///
/// 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.
///
PeopleUpdate? people;
///
/// Please note: This property should have been non-nullable! Since the specification file
@ -67,16 +86,27 @@ class UserPreferencesUpdateDto {
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
RatingUpdate? rating;
RatingsUpdate? ratings;
///
/// 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.
///
TagsUpdate? tags;
@override
bool operator ==(Object other) => identical(this, other) || other is UserPreferencesUpdateDto &&
other.avatar == avatar &&
other.download == download &&
other.emailNotifications == emailNotifications &&
other.folders == folders &&
other.memories == memories &&
other.people == people &&
other.purchase == purchase &&
other.rating == rating;
other.ratings == ratings &&
other.tags == tags;
@override
int get hashCode =>
@ -84,12 +114,15 @@ class UserPreferencesUpdateDto {
(avatar == null ? 0 : avatar!.hashCode) +
(download == null ? 0 : download!.hashCode) +
(emailNotifications == null ? 0 : emailNotifications!.hashCode) +
(folders == null ? 0 : folders!.hashCode) +
(memories == null ? 0 : memories!.hashCode) +
(people == null ? 0 : people!.hashCode) +
(purchase == null ? 0 : purchase!.hashCode) +
(rating == null ? 0 : rating!.hashCode);
(ratings == null ? 0 : ratings!.hashCode) +
(tags == null ? 0 : tags!.hashCode);
@override
String toString() => 'UserPreferencesUpdateDto[avatar=$avatar, download=$download, emailNotifications=$emailNotifications, memories=$memories, purchase=$purchase, rating=$rating]';
String toString() => 'UserPreferencesUpdateDto[avatar=$avatar, download=$download, emailNotifications=$emailNotifications, folders=$folders, memories=$memories, people=$people, purchase=$purchase, ratings=$ratings, tags=$tags]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
@ -108,20 +141,35 @@ class UserPreferencesUpdateDto {
} else {
// json[r'emailNotifications'] = null;
}
if (this.folders != null) {
json[r'folders'] = this.folders;
} else {
// json[r'folders'] = null;
}
if (this.memories != null) {
json[r'memories'] = this.memories;
} else {
// json[r'memories'] = null;
}
if (this.people != null) {
json[r'people'] = this.people;
} else {
// json[r'people'] = null;
}
if (this.purchase != null) {
json[r'purchase'] = this.purchase;
} else {
// json[r'purchase'] = null;
}
if (this.rating != null) {
json[r'rating'] = this.rating;
if (this.ratings != null) {
json[r'ratings'] = this.ratings;
} else {
// json[r'rating'] = null;
// json[r'ratings'] = null;
}
if (this.tags != null) {
json[r'tags'] = this.tags;
} else {
// json[r'tags'] = null;
}
return json;
}
@ -137,9 +185,12 @@ class UserPreferencesUpdateDto {
avatar: AvatarUpdate.fromJson(json[r'avatar']),
download: DownloadUpdate.fromJson(json[r'download']),
emailNotifications: EmailNotificationsUpdate.fromJson(json[r'emailNotifications']),
memories: MemoryUpdate.fromJson(json[r'memories']),
folders: FoldersUpdate.fromJson(json[r'folders']),
memories: MemoriesUpdate.fromJson(json[r'memories']),
people: PeopleUpdate.fromJson(json[r'people']),
purchase: PurchaseUpdate.fromJson(json[r'purchase']),
rating: RatingUpdate.fromJson(json[r'rating']),
ratings: RatingsUpdate.fromJson(json[r'ratings']),
tags: TagsUpdate.fromJson(json[r'tags']),
);
}
return null;

View File

@ -0,0 +1,49 @@
import 'dart:convert';
import 'package:flutter_test/flutter_test.dart';
import 'package:openapi/api.dart';
import 'package:immich_mobile/utils/openapi_patching.dart';
void main() {
group('Test OpenApi Patching', () {
test('upgradeDto', () {
dynamic value;
String targetType;
targetType = 'UserPreferencesResponseDto';
value = jsonDecode("""
{
"download": {
"archiveSize": 4294967296,
"includeEmbeddedVideos": false
}
}
""");
upgradeDto(value, targetType);
expect(value['tags'], TagsResponse().toJson());
expect(value['download']['includeEmbeddedVideos'], false);
});
test('addDefault', () {
dynamic value = jsonDecode("""
{
"download": {
"archiveSize": 4294967296,
"includeEmbeddedVideos": false
}
}
""");
String keys = 'download.unknownKey';
dynamic defaultValue = 69420;
addDefault(value, keys, defaultValue);
expect(value['download']['unknownKey'], 69420);
keys = 'alpha.beta';
defaultValue = 'gamma';
addDefault(value, keys, defaultValue);
expect(value['alpha']['beta'], 'gamma');
});
});
}

View File

@ -9164,6 +9164,34 @@
],
"type": "object"
},
"FoldersResponse": {
"properties": {
"enabled": {
"default": false,
"type": "boolean"
},
"sidebarWeb": {
"default": false,
"type": "boolean"
}
},
"required": [
"enabled",
"sidebarWeb"
],
"type": "object"
},
"FoldersUpdate": {
"properties": {
"enabled": {
"type": "boolean"
},
"sidebarWeb": {
"type": "boolean"
}
},
"type": "object"
},
"ImageFormat": {
"enum": [
"jpeg",
@ -9534,6 +9562,26 @@
],
"type": "string"
},
"MemoriesResponse": {
"properties": {
"enabled": {
"default": true,
"type": "boolean"
}
},
"required": [
"enabled"
],
"type": "object"
},
"MemoriesUpdate": {
"properties": {
"enabled": {
"type": "boolean"
}
},
"type": "object"
},
"MemoryCreateDto": {
"properties": {
"assetIds": {
@ -9586,17 +9634,6 @@
],
"type": "object"
},
"MemoryResponse": {
"properties": {
"enabled": {
"type": "boolean"
}
},
"required": [
"enabled"
],
"type": "object"
},
"MemoryResponseDto": {
"properties": {
"assets": {
@ -9660,14 +9697,6 @@
],
"type": "string"
},
"MemoryUpdate": {
"properties": {
"enabled": {
"type": "boolean"
}
},
"type": "object"
},
"MemoryUpdateDto": {
"properties": {
"isSaved": {
@ -9953,6 +9982,23 @@
],
"type": "string"
},
"PeopleResponse": {
"properties": {
"enabled": {
"default": true,
"type": "boolean"
},
"sidebarWeb": {
"default": false,
"type": "boolean"
}
},
"required": [
"enabled",
"sidebarWeb"
],
"type": "object"
},
"PeopleResponseDto": {
"properties": {
"hasNextPage": {
@ -9979,6 +10025,17 @@
],
"type": "object"
},
"PeopleUpdate": {
"properties": {
"enabled": {
"type": "boolean"
},
"sidebarWeb": {
"type": "boolean"
}
},
"type": "object"
},
"PeopleUpdateDto": {
"properties": {
"people": {
@ -10300,7 +10357,7 @@
],
"type": "object"
},
"RatingResponse": {
"RatingsResponse": {
"properties": {
"enabled": {
"default": false,
@ -10312,7 +10369,7 @@
],
"type": "object"
},
"RatingUpdate": {
"RatingsUpdate": {
"properties": {
"enabled": {
"type": "boolean"
@ -12002,6 +12059,34 @@
],
"type": "object"
},
"TagsResponse": {
"properties": {
"enabled": {
"default": true,
"type": "boolean"
},
"sidebarWeb": {
"default": true,
"type": "boolean"
}
},
"required": [
"enabled",
"sidebarWeb"
],
"type": "object"
},
"TagsUpdate": {
"properties": {
"enabled": {
"type": "boolean"
},
"sidebarWeb": {
"type": "boolean"
}
},
"type": "object"
},
"TimeBucketResponseDto": {
"properties": {
"count": {
@ -12379,23 +12464,35 @@
"emailNotifications": {
"$ref": "#/components/schemas/EmailNotificationsResponse"
},
"folders": {
"$ref": "#/components/schemas/FoldersResponse"
},
"memories": {
"$ref": "#/components/schemas/MemoryResponse"
"$ref": "#/components/schemas/MemoriesResponse"
},
"people": {
"$ref": "#/components/schemas/PeopleResponse"
},
"purchase": {
"$ref": "#/components/schemas/PurchaseResponse"
},
"rating": {
"$ref": "#/components/schemas/RatingResponse"
"ratings": {
"$ref": "#/components/schemas/RatingsResponse"
},
"tags": {
"$ref": "#/components/schemas/TagsResponse"
}
},
"required": [
"avatar",
"download",
"emailNotifications",
"folders",
"memories",
"people",
"purchase",
"rating"
"ratings",
"tags"
],
"type": "object"
},
@ -12410,14 +12507,23 @@
"emailNotifications": {
"$ref": "#/components/schemas/EmailNotificationsUpdate"
},
"folders": {
"$ref": "#/components/schemas/FoldersUpdate"
},
"memories": {
"$ref": "#/components/schemas/MemoryUpdate"
"$ref": "#/components/schemas/MemoriesUpdate"
},
"people": {
"$ref": "#/components/schemas/PeopleUpdate"
},
"purchase": {
"$ref": "#/components/schemas/PurchaseUpdate"
},
"rating": {
"$ref": "#/components/schemas/RatingUpdate"
"ratings": {
"$ref": "#/components/schemas/RatingsUpdate"
},
"tags": {
"$ref": "#/components/schemas/TagsUpdate"
}
},
"type": "object"

View File

@ -93,23 +93,38 @@ export type EmailNotificationsResponse = {
albumUpdate: boolean;
enabled: boolean;
};
export type MemoryResponse = {
export type FoldersResponse = {
enabled: boolean;
sidebarWeb: boolean;
};
export type MemoriesResponse = {
enabled: boolean;
};
export type PeopleResponse = {
enabled: boolean;
sidebarWeb: boolean;
};
export type PurchaseResponse = {
hideBuyButtonUntil: string;
showSupportBadge: boolean;
};
export type RatingResponse = {
export type RatingsResponse = {
enabled: boolean;
};
export type TagsResponse = {
enabled: boolean;
sidebarWeb: boolean;
};
export type UserPreferencesResponseDto = {
avatar: AvatarResponse;
download: DownloadResponse;
emailNotifications: EmailNotificationsResponse;
memories: MemoryResponse;
folders: FoldersResponse;
memories: MemoriesResponse;
people: PeopleResponse;
purchase: PurchaseResponse;
rating: RatingResponse;
ratings: RatingsResponse;
tags: TagsResponse;
};
export type AvatarUpdate = {
color?: UserAvatarColor;
@ -123,23 +138,38 @@ export type EmailNotificationsUpdate = {
albumUpdate?: boolean;
enabled?: boolean;
};
export type MemoryUpdate = {
export type FoldersUpdate = {
enabled?: boolean;
sidebarWeb?: boolean;
};
export type MemoriesUpdate = {
enabled?: boolean;
};
export type PeopleUpdate = {
enabled?: boolean;
sidebarWeb?: boolean;
};
export type PurchaseUpdate = {
hideBuyButtonUntil?: string;
showSupportBadge?: boolean;
};
export type RatingUpdate = {
export type RatingsUpdate = {
enabled?: boolean;
};
export type TagsUpdate = {
enabled?: boolean;
sidebarWeb?: boolean;
};
export type UserPreferencesUpdateDto = {
avatar?: AvatarUpdate;
download?: DownloadUpdate;
emailNotifications?: EmailNotificationsUpdate;
memories?: MemoryUpdate;
folders?: FoldersUpdate;
memories?: MemoriesUpdate;
people?: PeopleUpdate;
purchase?: PurchaseUpdate;
rating?: RatingUpdate;
ratings?: RatingsUpdate;
tags?: TagsUpdate;
};
export type AlbumUserResponseDto = {
role: AlbumUserRole;

View File

@ -12,16 +12,40 @@ class AvatarUpdate {
color?: UserAvatarColor;
}
class MemoryUpdate {
class MemoriesUpdate {
@ValidateBoolean({ optional: true })
enabled?: boolean;
}
class RatingUpdate {
class RatingsUpdate {
@ValidateBoolean({ optional: true })
enabled?: boolean;
}
class FoldersUpdate {
@ValidateBoolean({ optional: true })
enabled?: boolean;
@ValidateBoolean({ optional: true })
sidebarWeb?: boolean;
}
class PeopleUpdate {
@ValidateBoolean({ optional: true })
enabled?: boolean;
@ValidateBoolean({ optional: true })
sidebarWeb?: boolean;
}
class TagsUpdate {
@ValidateBoolean({ optional: true })
enabled?: boolean;
@ValidateBoolean({ optional: true })
sidebarWeb?: boolean;
}
class EmailNotificationsUpdate {
@ValidateBoolean({ optional: true })
enabled?: boolean;
@ -56,19 +80,34 @@ class PurchaseUpdate {
export class UserPreferencesUpdateDto {
@Optional()
@ValidateNested()
@Type(() => RatingUpdate)
rating?: RatingUpdate;
@Type(() => FoldersUpdate)
folders?: FoldersUpdate;
@Optional()
@ValidateNested()
@Type(() => MemoriesUpdate)
memories?: MemoriesUpdate;
@Optional()
@ValidateNested()
@Type(() => PeopleUpdate)
people?: PeopleUpdate;
@Optional()
@ValidateNested()
@Type(() => RatingsUpdate)
ratings?: RatingsUpdate;
@Optional()
@ValidateNested()
@Type(() => TagsUpdate)
tags?: TagsUpdate;
@Optional()
@ValidateNested()
@Type(() => AvatarUpdate)
avatar?: AvatarUpdate;
@Optional()
@ValidateNested()
@Type(() => MemoryUpdate)
memories?: MemoryUpdate;
@Optional()
@ValidateNested()
@Type(() => EmailNotificationsUpdate)
@ -90,12 +129,27 @@ class AvatarResponse {
color!: UserAvatarColor;
}
class RatingResponse {
class RatingsResponse {
enabled: boolean = false;
}
class MemoryResponse {
enabled!: boolean;
class MemoriesResponse {
enabled: boolean = true;
}
class FoldersResponse {
enabled: boolean = false;
sidebarWeb: boolean = false;
}
class PeopleResponse {
enabled: boolean = true;
sidebarWeb: boolean = false;
}
class TagsResponse {
enabled: boolean = true;
sidebarWeb: boolean = true;
}
class EmailNotificationsResponse {
@ -117,8 +171,11 @@ class PurchaseResponse {
}
export class UserPreferencesResponseDto implements UserPreferences {
rating!: RatingResponse;
memories!: MemoryResponse;
folders!: FoldersResponse;
memories!: MemoriesResponse;
people!: PeopleResponse;
ratings!: RatingsResponse;
tags!: TagsResponse;
avatar!: AvatarResponse;
emailNotifications!: EmailNotificationsResponse;
download!: DownloadResponse;

View File

@ -19,12 +19,24 @@ export class UserMetadataEntity<T extends keyof UserMetadata = UserMetadataKey>
}
export interface UserPreferences {
rating: {
folders: {
enabled: boolean;
sidebarWeb: boolean;
};
memories: {
enabled: boolean;
};
people: {
enabled: boolean;
sidebarWeb: boolean;
};
ratings: {
enabled: boolean;
};
tags: {
enabled: boolean;
sidebarWeb: boolean;
};
avatar: {
color: UserAvatarColor;
};
@ -50,12 +62,24 @@ export const getDefaultPreferences = (user: { email: string }): UserPreferences
);
return {
rating: {
folders: {
enabled: false,
sidebarWeb: false,
},
memories: {
enabled: true,
},
people: {
enabled: true,
sidebarWeb: false,
},
ratings: {
enabled: false,
},
tags: {
enabled: false,
sidebarWeb: false,
},
avatar: {
color: values[randomIndex],
},

View File

@ -20,7 +20,7 @@
};
</script>
{#if !isSharedLink() && $preferences?.rating?.enabled}
{#if !isSharedLink() && $preferences?.ratings.enabled}
<section class="px-4 pt-2">
<StarRating {rating} readOnly={!isOwner} onRating={(rating) => handlePromiseError(handleChangeRating(rating))} />
</section>

View File

@ -6,7 +6,7 @@
import { boundingBoxesArray } from '$lib/stores/people.store';
import { locale } from '$lib/stores/preferences.store';
import { featureFlags } from '$lib/stores/server-config.store';
import { user } from '$lib/stores/user.store';
import { preferences, user } from '$lib/stores/user.store';
import { getAssetThumbnailUrl, getPeopleThumbnailUrl, handlePromiseError, isSharedLink } from '$lib/utils';
import { delay, isFlipped } from '$lib/utils/asset-utils';
import {
@ -502,9 +502,11 @@
</section>
{/if}
<section class="relative px-2 pb-12 dark:bg-immich-dark-bg dark:text-immich-dark-fg">
<DetailPanelTags {asset} {isOwner} />
</section>
{#if $preferences?.tags?.enabled}
<section class="relative px-2 pb-12 dark:bg-immich-dark-bg dark:text-immich-dark-fg">
<DetailPanelTags {asset} {isOwner} />
</section>
{/if}
{#if showEditFaces}
<PersonSidePanel

View File

@ -9,6 +9,7 @@
export let subtitle = '';
export let key: string;
export let isOpen = $accordionState.has(key);
export let autoScrollTo = false;
let accordionElement: HTMLDivElement;
@ -18,12 +19,14 @@
if (isOpen) {
$accordionState = $accordionState.add(key);
setTimeout(() => {
accordionElement.scrollIntoView({
behavior: 'smooth',
block: 'start',
});
}, 200);
if (autoScrollTo) {
setTimeout(() => {
accordionElement.scrollIntoView({
behavior: 'smooth',
block: 'start',
});
}, 200);
}
} else {
$accordionState.delete(key);
$accordionState = $accordionState;
@ -72,7 +75,7 @@
</button>
{#if isOpen}
<ul transition:slide={{ duration: 250 }} class="mb-2 ml-4">
<ul transition:slide={{ duration: 150 }} class="mb-2 ml-4">
<slot />
</ul>
{/if}

View File

@ -1,5 +1,4 @@
<script lang="ts">
import { sidebarSettings } from '$lib/stores/preferences.store';
import { featureFlags } from '$lib/stores/server-config.store';
import {
mdiAccount,
@ -29,6 +28,7 @@
import MoreInformationAlbums from '$lib/components/shared-components/side-bar/more-information-albums.svelte';
import { t } from 'svelte-i18n';
import BottomInfo from '$lib/components/shared-components/side-bar/bottom-info.svelte';
import { preferences } from '$lib/stores/user.store';
let isArchiveSelected: boolean;
let isFavoritesSelected: boolean;
@ -52,6 +52,7 @@
<MoreInformationAssets assetStats={{ isArchived: false }} />
</svelte:fragment>
</SideBarLink>
{#if $featureFlags.search}
<SideBarLink title={$t('explore')} routeId="/(user)/explore" icon={mdiMagnify} />
{/if}
@ -65,7 +66,7 @@
/>
{/if}
{#if $sidebarSettings.people}
{#if $preferences.people.enabled && $preferences.people.sidebarWeb}
<SideBarLink
title={$t('people')}
routeId="/(user)/people"
@ -73,23 +74,23 @@
icon={isPeopleSelected ? mdiAccount : mdiAccountOutline}
/>
{/if}
{#if $sidebarSettings.sharing}
<SideBarLink
title={$t('sharing')}
routeId="/(user)/sharing"
icon={isSharingSelected ? mdiAccountMultiple : mdiAccountMultipleOutline}
bind:isSelected={isSharingSelected}
>
<svelte:fragment slot="moreInformation">
<MoreInformationAlbums albumType="shared" />
</svelte:fragment>
</SideBarLink>
{/if}
<SideBarLink
title={$t('sharing')}
routeId="/(user)/sharing"
icon={isSharingSelected ? mdiAccountMultiple : mdiAccountMultipleOutline}
bind:isSelected={isSharingSelected}
>
<svelte:fragment slot="moreInformation">
<MoreInformationAlbums albumType="shared" />
</svelte:fragment>
</SideBarLink>
<div class="text-xs transition-all duration-200 dark:text-immich-dark-fg">
<p class="hidden p-6 group-hover:sm:block md:block">{$t('library').toUpperCase()}</p>
<hr class="mx-4 mb-[31px] mt-8 block group-hover:sm:hidden md:hidden" />
</div>
<SideBarLink
title={$t('favorites')}
routeId="/(user)/favorites"
@ -100,15 +101,20 @@
<MoreInformationAssets assetStats={{ isFavorite: true }} />
</svelte:fragment>
</SideBarLink>
<SideBarLink title={$t('albums')} routeId="/(user)/albums" icon={mdiImageAlbum} flippedLogo>
<svelte:fragment slot="moreInformation">
<MoreInformationAlbums albumType="owned" />
</svelte:fragment>
</SideBarLink>
<SideBarLink title={$t('tags')} routeId="/(user)/tags" icon={mdiTagMultipleOutline} flippedLogo />
{#if $preferences.tags.enabled && $preferences.tags.sidebarWeb}
<SideBarLink title={$t('tags')} routeId="/(user)/tags" icon={mdiTagMultipleOutline} flippedLogo />
{/if}
<SideBarLink title={$t('folders')} routeId="/(user)/folders" icon={mdiFolderOutline} flippedLogo />
{#if $preferences.folders.enabled && $preferences.folders.sidebarWeb}
<SideBarLink title={$t('folders')} routeId="/(user)/folders" icon={mdiFolderOutline} flippedLogo />
{/if}
<SideBarLink
title={$t('utilities')}

View File

@ -11,7 +11,6 @@
loopVideo,
playVideoThumbnailOnHover,
showDeleteModal,
sidebarSettings,
} from '$lib/stores/preferences.store';
import { findLocale } from '$lib/utils';
import { getClosestAvailableLocale, langCodes } from '$lib/utils/i18n';
@ -19,13 +18,6 @@
import { locale as i18nLocale, t } from 'svelte-i18n';
import { fade } from 'svelte/transition';
import { invalidateAll } from '$app/navigation';
import { preferences } from '$lib/stores/user.store';
import { updateMyPreferences } from '@immich/sdk';
import { handleError } from '../../utils/handle-error';
import {
notificationController,
NotificationType,
} from '$lib/components/shared-components/notification/notification';
let time = new Date();
@ -46,7 +38,6 @@
label: findLocale(editedLocale).name || fallbackLocale.name,
};
$: closestLanguage = getClosestAvailableLocale([$lang], langCodes);
$: ratingEnabled = $preferences?.rating?.enabled;
onMount(() => {
const interval = setInterval(() => {
@ -98,17 +89,6 @@
$locale = newLocale;
}
};
const handleRatingChange = async (enabled: boolean) => {
try {
const data = await updateMyPreferences({ userPreferencesUpdateDto: { rating: { enabled } } });
$preferences.rating.enabled = data.rating.enabled;
notificationController.show({ message: $t('saved_settings'), type: NotificationType.Info });
} catch (error) {
handleError(error, $t('errors.unable_to_update_settings'));
}
};
</script>
<section class="my-4">
@ -189,29 +169,6 @@
bind:checked={$showDeleteModal}
/>
</div>
<div class="ml-4">
<SettingSwitch
title={$t('people')}
subtitle={$t('people_sidebar_description')}
bind:checked={$sidebarSettings.people}
/>
</div>
<div class="ml-4">
<SettingSwitch
title={$t('sharing')}
subtitle={$t('sharing_sidebar_description')}
bind:checked={$sidebarSettings.sharing}
/>
</div>
<div class="ml-4">
<SettingSwitch
title={$t('rating')}
subtitle={$t('rating_description')}
bind:checked={ratingEnabled}
on:toggle={({ detail: enabled }) => handleRatingChange(enabled)}
/>
</div>
</div>
</div>
</section>

View File

@ -0,0 +1,124 @@
<script lang="ts">
import {
notificationController,
NotificationType,
} from '$lib/components/shared-components/notification/notification';
import { updateMyPreferences } from '@immich/sdk';
import { fade } from 'svelte/transition';
import { handleError } from '../../utils/handle-error';
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
import { preferences } from '$lib/stores/user.store';
import Button from '../elements/buttons/button.svelte';
import { t } from 'svelte-i18n';
import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
// Folders
let foldersEnabled = $preferences?.folders?.enabled ?? false;
let foldersSidebar = $preferences?.folders?.sidebarWeb ?? false;
// Memories
let memoriesEnabled = $preferences?.memories?.enabled ?? true;
// People
let peopleEnabled = $preferences?.people?.enabled ?? false;
let peopleSidebar = $preferences?.people?.sidebarWeb ?? false;
// Ratings
let ratingsEnabled = $preferences?.ratings?.enabled ?? false;
// Tags
let tagsEnabled = $preferences?.tags?.enabled ?? false;
let tagsSidebar = $preferences?.tags?.sidebarWeb ?? false;
const handleSave = async () => {
try {
const data = await updateMyPreferences({
userPreferencesUpdateDto: {
folders: { enabled: foldersEnabled, sidebarWeb: foldersSidebar },
memories: { enabled: memoriesEnabled },
people: { enabled: peopleEnabled, sidebarWeb: peopleSidebar },
ratings: { enabled: ratingsEnabled },
tags: { enabled: tagsEnabled, sidebarWeb: tagsSidebar },
},
});
$preferences = { ...data };
notificationController.show({ message: $t('saved_settings'), type: NotificationType.Info });
} catch (error) {
handleError(error, $t('errors.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">
<SettingAccordion key="folders" title={$t('folders')} subtitle={$t('folders_feature_description')}>
<div class="ml-4 mt-6">
<SettingSwitch title={$t('enable')} bind:checked={foldersEnabled} />
</div>
{#if foldersEnabled}
<div class="ml-4 mt-6">
<SettingSwitch
title={$t('sidebar')}
subtitle={$t('sidebar_display_description')}
bind:checked={foldersSidebar}
/>
</div>
{/if}
</SettingAccordion>
<SettingAccordion key="memories" title={$t('time_based_memories')} subtitle={$t('photos_from_previous_years')}>
<div class="ml-4 mt-6">
<SettingSwitch title={$t('enable')} bind:checked={memoriesEnabled} />
</div>
</SettingAccordion>
<SettingAccordion key="people" title={$t('people')} subtitle={$t('people_feature_description')}>
<div class="ml-4 mt-6">
<SettingSwitch title={$t('enable')} bind:checked={peopleEnabled} />
</div>
{#if peopleEnabled}
<div class="ml-4 mt-6">
<SettingSwitch
title={$t('sidebar')}
subtitle={$t('sidebar_display_description')}
bind:checked={peopleSidebar}
/>
</div>
{/if}
</SettingAccordion>
<SettingAccordion key="rating" title={$t('rating')} subtitle={$t('rating_description')}>
<div class="ml-4 mt-6">
<SettingSwitch title={$t('enable')} bind:checked={ratingsEnabled} />
</div>
</SettingAccordion>
<SettingAccordion key="tags" title={$t('tags')} subtitle={$t('tag_feature_description')}>
<div class="ml-4 mt-6">
<SettingSwitch title={$t('enable')} bind:checked={tagsEnabled} />
</div>
{#if tagsEnabled}
<div class="ml-4 mt-6">
<SettingSwitch
title={$t('sidebar')}
subtitle={$t('sidebar_display_description')}
bind:checked={tagsSidebar}
/>
</div>
{/if}
</SettingAccordion>
<div class="flex justify-end">
<Button type="submit" size="sm" on:click={() => handleSave()}>{$t('save')}</Button>
</div>
</div>
</form>
</div>
</section>

View File

@ -1,46 +0,0 @@
<script lang="ts">
import {
notificationController,
NotificationType,
} from '$lib/components/shared-components/notification/notification';
import { updateMyPreferences } from '@immich/sdk';
import { fade } from 'svelte/transition';
import { handleError } from '../../utils/handle-error';
import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
import { preferences } from '$lib/stores/user.store';
import Button from '../elements/buttons/button.svelte';
import { t } from 'svelte-i18n';
let memoriesEnabled = $preferences?.memories?.enabled ?? false;
const handleSave = async () => {
try {
const data = await updateMyPreferences({ userPreferencesUpdateDto: { memories: { enabled: memoriesEnabled } } });
$preferences.memories.enabled = data.memories.enabled;
notificationController.show({ message: $t('saved_settings'), type: NotificationType.Info });
} catch (error) {
handleError(error, $t('errors.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={$t('time_based_memories')}
subtitle={$t('photos_from_previous_years')}
bind:checked={memoriesEnabled}
/>
</div>
<div class="flex justify-end">
<Button type="submit" size="sm" on:click={() => handleSave()}>{$t('save')}</Button>
</div>
</div>
</form>
</div>
</section>

View File

@ -10,7 +10,6 @@
import AppSettings from './app-settings.svelte';
import ChangePasswordSettings from './change-password-settings.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';
@ -19,6 +18,7 @@
import { t } from 'svelte-i18n';
import DownloadSettings from '$lib/components/user-settings-page/download-settings.svelte';
import UserPurchaseSettings from '$lib/components/user-settings-page/user-purchase-settings.svelte';
import FeatureSettings from '$lib/components/user-settings-page/feature-settings.svelte';
export let keys: ApiKeyResponseDto[] = [];
export let sessions: SessionResponseDto[] = [];
@ -53,8 +53,8 @@
<DownloadSettings />
</SettingAccordion>
<SettingAccordion key="memories" title={$t('memories')} subtitle={$t('memories_setting_description')}>
<MemoriesSettings />
<SettingAccordion key="feature" title={$t('features')} subtitle={$t('features_setting_description')}>
<FeatureSettings />
</SettingAccordion>
<SettingAccordion key="notifications" title={$t('notifications')} subtitle={$t('notifications_setting_description')}>
@ -84,6 +84,7 @@
key="user-purchase-settings"
title={$t('user_purchase_settings')}
subtitle={$t('user_purchase_settings_description')}
autoScrollTo={true}
>
<UserPurchaseSettings />
</SettingAccordion>

View File

@ -701,6 +701,8 @@
"favorite_or_unfavorite_photo": "Favorite or unfavorite photo",
"favorites": "Favorites",
"feature_photo_updated": "Feature photo updated",
"features": "Features",
"features_setting_description": "Manage the app features",
"file_name": "File name",
"file_name_or_extension": "File name or extension",
"filename": "Filename",
@ -709,6 +711,7 @@
"find_them_fast": "Find them fast by name with search",
"fix_incorrect_match": "Fix incorrect match",
"folders": "Folders",
"folders_feature_description": "Browsing the folder view for the photos and videos on the file system",
"force_re-scan_library_files": "Force Re-scan All Library Files",
"forward": "Forward",
"general": "General",
@ -912,6 +915,7 @@
"pending": "Pending",
"people": "People",
"people_edits_count": "Edited {count, plural, one {# person} other {# people}}",
"people_feature_description": "Browsing photos and videos grouped by people",
"people_sidebar_description": "Display a link to People in the sidebar",
"permanent_deletion_warning": "Permanent deletion warning",
"permanent_deletion_warning_setting_description": "Show a warning when permanently deleting assets",
@ -981,7 +985,7 @@
"rating": "Star rating",
"rating_clear": "Clear rating",
"rating_count": "{count, plural, one {# star} other {# stars}}",
"rating_description": "Display the exif rating in the info panel",
"rating_description": "Display the EXIF rating in the info panel",
"reaction_options": "Reaction options",
"read_changelog": "Read Changelog",
"reassign": "Reassign",
@ -1130,6 +1134,8 @@
"show_supporter_badge": "Supporter badge",
"show_supporter_badge_description": "Show a supporter badge",
"shuffle": "Shuffle",
"sidebar": "Sidebar",
"sidebar_display_description": "Display a link to the view in the sidebar",
"sign_out": "Sign Out",
"sign_up": "Sign up",
"size": "Size",
@ -1169,6 +1175,7 @@
"tag": "Tag",
"tag_assets": "Tag assets",
"tag_created": "Created tag: {tag}",
"tag_feature_description": "Browsing photos and videos grouped by logical tag topics",
"tag_updated": "Updated tag: {tag}",
"tagged_assets": "Tagged {count, plural, one {# asset} other {# assets}}",
"tags": "Tags",

View File

@ -96,11 +96,6 @@ export interface SidebarSettings {
sharing: boolean;
}
export const sidebarSettings = persisted<SidebarSettings>('sidebar-settings-1', {
people: false,
sharing: true,
});
export enum SortOrder {
Asc = 'asc',
Desc = 'desc',

View File

@ -81,7 +81,9 @@
<ChangeDate menuItem />
<ChangeLocation menuItem />
<ArchiveAction menuItem onArchive={(assetIds) => assetStore.removeAssets(assetIds)} />
<TagAction menuItem />
{#if $preferences.tags.enabled}
<TagAction menuItem />
{/if}
<DeleteAssets menuItem onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)} />
<hr />
<AssetJobActions />