diff --git a/Makefile b/Makefile index 7c03604dd2..806815314d 100644 --- a/Makefile +++ b/Makefile @@ -27,8 +27,8 @@ open-api: open-api-dart: cd ./open-api && bash ./bin/generate-open-api.sh dart -open-api-dart-dio: - cd ./open-api && bash ./bin/generate-open-api.sh dart-dio +open-api-dart-2: + cd ./open-api && bash ./bin/generate-open-api.sh dart-2 open-api-typescript: cd ./open-api && bash ./bin/generate-open-api.sh typescript diff --git a/mobile-v2/lib/domain/services/login.service.dart b/mobile-v2/lib/domain/services/login.service.dart index 543ba16246..38e5af63b2 100644 --- a/mobile-v2/lib/domain/services/login.service.dart +++ b/mobile-v2/lib/domain/services/login.service.dart @@ -127,7 +127,14 @@ class LoginService with LogMixin { /// Set token to interceptor await di().init(accessToken: accessToken); - final user = await di().getMyUser(); + final user = await di().getMyUser().timeout( + const Duration(seconds: 10), + // ignore: function-always-returns-null + onTimeout: () { + log.w("Timedout while fetching user details using saved credentials"); + return null; + }, + ); if (user == null) { return false; } diff --git a/mobile-v2/lib/utils/immich_api_client.dart b/mobile-v2/lib/utils/immich_api_client.dart index 196115c034..15925bf6c7 100644 --- a/mobile-v2/lib/utils/immich_api_client.dart +++ b/mobile-v2/lib/utils/immich_api_client.dart @@ -1,8 +1,6 @@ -import 'dart:convert'; import 'dart:io'; import 'package:device_info_plus/device_info_plus.dart'; -import 'package:flutter/foundation.dart'; import 'package:http/http.dart'; import 'package:immich_mobile/domain/interfaces/store.interface.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; @@ -66,53 +64,6 @@ class ImmichApiClient extends ApiClient with LogMixin { return res; } - // ignore: avoid-dynamic - static dynamic _patchDto(dynamic value, String targetType) { - switch (targetType) { - case 'UserPreferencesResponseDto': - if (value is Map && value['rating'] == null) { - value['rating'] = RatingResponse().toJson(); - } - } - } - - // ignore: avoid-dynamic - static dynamic fromJson( - // ignore: avoid-dynamic - dynamic value, - String targetType, { - bool growable = false, - }) { - _patchDto(value, targetType); - return ApiClient.fromJson(value, targetType, growable: growable); - } - - @override - // ignore: avoid-dynamic - Future deserializeAsync( - String value, - String targetType, { - bool growable = false, - }) => - deserialize(value, targetType, growable: growable); - - @override - // ignore: avoid-dynamic - Future deserialize( - String value, - String targetType, { - bool growable = false, - }) async { - targetType = targetType.replaceAll(' ', ''); - return targetType == 'String' - ? value - : fromJson( - await compute((String j) => json.decode(j), value), - targetType, - growable: growable, - ); - } - UsersApi getUsersApi() => UsersApi(this); ServerApi getServerApi() => ServerApi(this); AuthenticationApi getAuthenticationApi() => AuthenticationApi(this); diff --git a/mobile-v2/lib/utils/openapi_patching.dart b/mobile-v2/lib/utils/openapi_patching.dart new file mode 100644 index 0000000000..0f4ca58438 --- /dev/null +++ b/mobile-v2/lib/utils/openapi_patching.dart @@ -0,0 +1,53 @@ +// ignore_for_file: avoid-dynamic, avoid-unsafe-collection-methods + +import 'package:openapi/api.dart'; + +dynamic upgradeDto(dynamic value, String targetType) { + switch (targetType) { + case 'UserPreferencesResponseDto': + if (value is Map) { + 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; + case 'ServerConfigDto': + if (value is Map) { + addDefault( + value, + 'mapLightStyleUrl', + 'https://tiles.immich.cloud/v1/style/light.json', + ); + addDefault( + value, + 'mapDarkStyleUrl', + 'https://tiles.immich.cloud/v1/style/dark.json', + ); + } + case 'UserResponseDto' || 'UserAdminResponseDto': + if (value is Map) { + addDefault(value, 'profileChangedAt', DateTime.now().toIso8601String()); + } + break; + } +} + +addDefault(dynamic value, String keys, dynamic defaultValue) { + // Loop through the keys and assign the default value if the key is not present + List 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; + } +} diff --git a/mobile-v2/pubspec.yaml b/mobile-v2/pubspec.yaml index b04f990b0e..c2f0026b34 100644 --- a/mobile-v2/pubspec.yaml +++ b/mobile-v2/pubspec.yaml @@ -56,11 +56,6 @@ dependencies: openapi: path: openapi -dependency_overrides: - # openapi uses an older version of http for backward compatibility. New versions do not have - # a breaking change so it is safer to override it and use the latest version for the app - http: ^1.2.1 - dev_dependencies: flutter_test: sdk: flutter diff --git a/open-api/bin/generate-open-api.sh b/open-api/bin/generate-open-api.sh index c186d44e13..8a49c6293c 100755 --- a/open-api/bin/generate-open-api.sh +++ b/open-api/bin/generate-open-api.sh @@ -22,18 +22,19 @@ function dart { rm ../mobile/openapi/analysis_options.yaml } -function dartDio { +function dart2 { rm -rf ../mobile-v2/openapi - cd ./templates/mobile-v2/serialization/native + cd ./templates/mobile/serialization/native wget -O native_class.mustache https://raw.githubusercontent.com/OpenAPITools/openapi-generator/$OPENAPI_GENERATOR_VERSION/modules/openapi-generator/src/main/resources/dart2/serialization/native/native_class.mustache patch --no-backup-if-mismatch -u native_class.mustache < native_class.mustache.patch cd ../../../../ - npx --yes @openapitools/openapi-generator-cli generate -g dart -i ./immich-openapi-specs.json -o ../mobile-v2/openapi -t ./templates/mobile-v2 + npx --yes @openapitools/openapi-generator-cli generate -g dart -i ./immich-openapi-specs.json -o ../mobile-v2/openapi -t ./templates/mobile # Post generate patches - patch --no-backup-if-mismatch -u ../mobile-v2/openapi/lib/api_client.dart <./patch/api_client.dart.patch - patch --no-backup-if-mismatch -u ../mobile-v2/openapi/lib/api.dart <./patch/api-v2.dart.patch + patch --no-backup-if-mismatch -u ../mobile-v2/openapi/lib/api_client.dart < ./patch/api_client.dart.patch + patch --no-backup-if-mismatch -u ../mobile-v2/openapi/lib/api.dart < ./patch/api.dart.patch + patch --no-backup-if-mismatch -u ../mobile-v2/openapi/pubspec.yaml < ./patch/pubspec_immich_mobile.yaml.patch # Don't include analysis_options.yaml for the generated openapi files # so that language servers can properly exclude the mobile/openapi directory rm ../mobile-v2/openapi/analysis_options.yaml @@ -49,8 +50,8 @@ npm run sync:open-api --prefix=../server if [[ $1 == 'dart' ]]; then dart -elif [[ $1 == 'dart-dio' ]]; then - dartDio +elif [[ $1 == 'dart-2' ]]; then + dart2 elif [[ $1 == 'typescript' ]]; then typescript else diff --git a/open-api/templates/mobile-v2/serialization/native/native_class.mustache.patch b/open-api/templates/mobile-v2/serialization/native/native_class.mustache.patch deleted file mode 100644 index 02e07f933a..0000000000 --- a/open-api/templates/mobile-v2/serialization/native/native_class.mustache.patch +++ /dev/null @@ -1,56 +0,0 @@ ---- native_class.mustache 2023-08-31 23:09:59.584269162 +0200 -+++ native_class1.mustache 2023-08-31 22:59:53.633083270 +0200 -@@ -91,14 +91,14 @@ - {{/isDateTime}} - {{#isNullable}} - } else { -- json[r'{{{baseName}}}'] = null; -+ // json[r'{{{baseName}}}'] = null; - } - {{/isNullable}} - {{^isNullable}} - {{^required}} - {{^defaultValue}} - } else { -- json[r'{{{baseName}}}'] = null; -+ // json[r'{{{baseName}}}'] = null; - } - {{/defaultValue}} - {{/required}} -@@ -114,17 +114,6 @@ - if (value is Map) { - final json = value.cast(); - -- // 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 "{{{classname}}}[$key]" is missing from JSON.'); -- assert(json[key] != null, 'Required key "{{{classname}}}[$key]" has a null value in JSON.'); -- }); -- return true; -- }()); -- - return {{{classname}}}( - {{#vars}} - {{#isDateTime}} -@@ -215,6 +204,10 @@ - ? {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}} - : {{{datatypeWithEnum}}}.parse(json[r'{{{baseName}}}'].toString()), - {{/isNumber}} -+ {{#isDouble}} -+ {{{name}}}: (mapValueOfType(json, r'{{{baseName}}}'){{#required}}{{^isNullable}}!{{/isNullable}}{{/required}}{{^required}}{{#defaultValue}} ?? {{{.}}}{{/defaultValue}}{{/required}}).toDouble(), -+ {{/isDouble}} -+ {{^isDouble}} - {{^isNumber}} - {{^isEnum}} - {{{name}}}: mapValueOfType<{{{datatypeWithEnum}}}>(json, r'{{{baseName}}}'){{#required}}{{^isNullable}}!{{/isNullable}}{{/required}}{{^required}}{{#defaultValue}} ?? {{{.}}}{{/defaultValue}}{{/required}}, -@@ -223,6 +216,7 @@ - {{{name}}}: {{{enumName}}}.fromJson(json[r'{{{baseName}}}']){{#required}}{{^isNullable}}!{{/isNullable}}{{/required}}{{^required}}{{#defaultValue}} ?? {{{.}}}{{/defaultValue}}{{/required}}, - {{/isEnum}} - {{/isNumber}} -+ {{/isDouble}} - {{/isMap}} - {{/isArray}} - {{/complexType}}