feat(mobile): use cached asset info if unchanged instead of downloading all assets (#1017)

* feat(mobile): use cached asset info if unchanged instead of downloading all assets

This adds an HTTP ETag to the getAllAssets endpoint and client-side support in the app.
If locally cache content is identical to the content on the server, the potentially large list of all assets does not need to be downloaded.

* use ts import instead of require
This commit is contained in:
Fynn Petersen-Frey 2022-11-26 17:16:02 +01:00 committed by GitHub
parent efa7b3ba54
commit 47f5e4134e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 322 additions and 201 deletions

View file

@ -21,7 +21,7 @@ class UserResponseDto {
required this.profileImagePath,
required this.shouldChangePassword,
required this.isAdmin,
required this.deletedAt,
this.deletedAt,
});
String id;
@ -40,49 +40,52 @@ class UserResponseDto {
bool isAdmin;
///
/// 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.
///
DateTime? deletedAt;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is UserResponseDto &&
other.id == id &&
other.email == email &&
other.firstName == firstName &&
other.lastName == lastName &&
other.createdAt == createdAt &&
other.profileImagePath == profileImagePath &&
other.shouldChangePassword == shouldChangePassword &&
other.isAdmin == isAdmin &&
other.deletedAt == deletedAt;
bool operator ==(Object other) => identical(this, other) || other is UserResponseDto &&
other.id == id &&
other.email == email &&
other.firstName == firstName &&
other.lastName == lastName &&
other.createdAt == createdAt &&
other.profileImagePath == profileImagePath &&
other.shouldChangePassword == shouldChangePassword &&
other.isAdmin == isAdmin &&
other.deletedAt == deletedAt;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(id.hashCode) +
(email.hashCode) +
(firstName.hashCode) +
(lastName.hashCode) +
(createdAt.hashCode) +
(profileImagePath.hashCode) +
(shouldChangePassword.hashCode) +
(isAdmin.hashCode) +
(deletedAt == null ? 0 : deletedAt!.hashCode);
// ignore: unnecessary_parenthesis
(id.hashCode) +
(email.hashCode) +
(firstName.hashCode) +
(lastName.hashCode) +
(createdAt.hashCode) +
(profileImagePath.hashCode) +
(shouldChangePassword.hashCode) +
(isAdmin.hashCode) +
(deletedAt == null ? 0 : deletedAt!.hashCode);
@override
String toString() =>
'UserResponseDto[id=$id, email=$email, firstName=$firstName, lastName=$lastName, createdAt=$createdAt, profileImagePath=$profileImagePath, shouldChangePassword=$shouldChangePassword, isAdmin=$isAdmin, deletedAt=$deletedAt]';
String toString() => 'UserResponseDto[id=$id, email=$email, firstName=$firstName, lastName=$lastName, createdAt=$createdAt, profileImagePath=$profileImagePath, shouldChangePassword=$shouldChangePassword, isAdmin=$isAdmin, deletedAt=$deletedAt]';
Map<String, dynamic> toJson() {
final _json = <String, dynamic>{};
_json[r'id'] = id;
_json[r'email'] = email;
_json[r'firstName'] = firstName;
_json[r'lastName'] = lastName;
_json[r'createdAt'] = createdAt;
_json[r'profileImagePath'] = profileImagePath;
_json[r'shouldChangePassword'] = shouldChangePassword;
_json[r'isAdmin'] = isAdmin;
_json[r'id'] = id;
_json[r'email'] = email;
_json[r'firstName'] = firstName;
_json[r'lastName'] = lastName;
_json[r'createdAt'] = createdAt;
_json[r'profileImagePath'] = profileImagePath;
_json[r'shouldChangePassword'] = shouldChangePassword;
_json[r'isAdmin'] = isAdmin;
if (deletedAt != null) {
_json[r'deletedAt'] = deletedAt!.toUtc().toIso8601String();
} else {
@ -101,13 +104,13 @@ class UserResponseDto {
// Ensure that the map contains the required keys.
// Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode!
// assert(() {
// requiredKeys.forEach((key) {
// assert(json.containsKey(key), 'Required key "UserResponseDto[$key]" is missing from JSON.');
// assert(json[key] != null, 'Required key "UserResponseDto[$key]" has a null value in JSON.');
// });
// return true;
// }());
assert(() {
requiredKeys.forEach((key) {
assert(json.containsKey(key), 'Required key "UserResponseDto[$key]" is missing from JSON.');
assert(json[key] != null, 'Required key "UserResponseDto[$key]" has a null value in JSON.');
});
return true;
}());
return UserResponseDto(
id: mapValueOfType<String>(json, r'id')!,
@ -116,8 +119,7 @@ class UserResponseDto {
lastName: mapValueOfType<String>(json, r'lastName')!,
createdAt: mapValueOfType<String>(json, r'createdAt')!,
profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
shouldChangePassword:
mapValueOfType<bool>(json, r'shouldChangePassword')!,
shouldChangePassword: mapValueOfType<bool>(json, r'shouldChangePassword')!,
isAdmin: mapValueOfType<bool>(json, r'isAdmin')!,
deletedAt: mapDateTime(json, r'deletedAt', ''),
);
@ -125,10 +127,7 @@ class UserResponseDto {
return null;
}
static List<UserResponseDto>? listFromJson(
dynamic json, {
bool growable = false,
}) {
static List<UserResponseDto>? listFromJson(dynamic json, {bool growable = false,}) {
final result = <UserResponseDto>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
@ -156,18 +155,12 @@ class UserResponseDto {
}
// maps a json object with a list of UserResponseDto-objects as value to a dart map
static Map<String, List<UserResponseDto>> mapListFromJson(
dynamic json, {
bool growable = false,
}) {
static Map<String, List<UserResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<UserResponseDto>>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = UserResponseDto.listFromJson(
entry.value,
growable: growable,
);
final value = UserResponseDto.listFromJson(entry.value, growable: growable,);
if (value != null) {
map[entry.key] = value;
}
@ -186,6 +179,6 @@ class UserResponseDto {
'profileImagePath',
'shouldChangePassword',
'isAdmin',
'deletedAt',
};
}