From 0ce678dae191a01225df9ca8790e1875d26a8695 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Wed, 15 May 2024 16:09:55 +0500 Subject: [PATCH] feat: repositoty and remote data sources for the uploading thumbnail --- evently/i18n/de.json | 3 +- evently/i18n/en-US.json | 3 +- evently/i18n/es.json | 3 +- evently/i18n/ru-RU.json | 3 +- evently/lib/generated/locale_keys.g.dart | 1 + .../lib/models/storage_response_model.dart | 111 ++++++++++++++++++ evently/lib/repository/repository.dart | 22 +++- .../datasources/remote_datasource.dart | 23 ++++ .../third_party_services/quick_node.dart | 10 +- evently/lib/utils/di/di.config.dart | 39 +++--- evently/lib/utils/failure/failure.dart | 7 ++ 11 files changed, 199 insertions(+), 26 deletions(-) create mode 100644 evently/lib/models/storage_response_model.dart create mode 100644 evently/lib/services/datasources/remote_datasource.dart diff --git a/evently/i18n/de.json b/evently/i18n/de.json index b140eac4eb..7429990434 100644 --- a/evently/i18n/de.json +++ b/evently/i18n/de.json @@ -58,5 +58,6 @@ "free_gift": "Free Gift", "free_drink": "1 free drink", "publish" : "Publish", - "recipe_created": "Recipe созданный" + "recipe_created": "Recipe созданный", + "update_failed": "Upload Fehlgeschlagen" } \ No newline at end of file diff --git a/evently/i18n/en-US.json b/evently/i18n/en-US.json index 53e3d3bd59..6e7cda9719 100644 --- a/evently/i18n/en-US.json +++ b/evently/i18n/en-US.json @@ -58,5 +58,6 @@ "free_gift": "Free Gift", "free_drink": "1 free drink", "publish" : "Publish", - "recipe_created": "Recipe created" + "recipe_created": "Recipe created", + "update_failed": "Upload Failed" } \ No newline at end of file diff --git a/evently/i18n/es.json b/evently/i18n/es.json index a539f4f67c..4d4f7ac878 100644 --- a/evently/i18n/es.json +++ b/evently/i18n/es.json @@ -58,5 +58,6 @@ "free_gift": "Free Gift", "free_drink": "1 free drink", "publish" : "Publish", - "recipe_created": "Recipe creada" + "recipe_created": "Recipe creada", + "update_failed": "Subida ha fallado" } \ No newline at end of file diff --git a/evently/i18n/ru-RU.json b/evently/i18n/ru-RU.json index 706db0ce31..5699723f32 100644 --- a/evently/i18n/ru-RU.json +++ b/evently/i18n/ru-RU.json @@ -58,5 +58,6 @@ "free_gift": "Free Gift", "free_drink": "1 free drink", "publish" : "Publish", - "recipe_created": "Recipe созданный" + "recipe_created": "Recipe созданный", + "update_failed": "Загрузка не удалась" } \ No newline at end of file diff --git a/evently/lib/generated/locale_keys.g.dart b/evently/lib/generated/locale_keys.g.dart index a79682b06a..561dcc6fd1 100644 --- a/evently/lib/generated/locale_keys.g.dart +++ b/evently/lib/generated/locale_keys.g.dart @@ -59,5 +59,6 @@ abstract class LocaleKeys { static const free_gift = 'free_gift'; static const free_drink = 'free_drink'; static const recipe_created = 'recipe_created'; + static const update_failed = 'update_failed'; } diff --git a/evently/lib/models/storage_response_model.dart b/evently/lib/models/storage_response_model.dart new file mode 100644 index 0000000000..8b0b760cd0 --- /dev/null +++ b/evently/lib/models/storage_response_model.dart @@ -0,0 +1,111 @@ +import 'package:evently/services/third_party_services/quick_node.dart'; + +class StorageResponseModel { + bool? ok; + Value? value; + + StorageResponseModel({this.ok, this.value}); + + StorageResponseModel.fromJson(Map json) { + ok = json['ok'] as bool?; + value = json['value'] != null ? Value.fromJson(json['value'] as Map) : null; + } + + Map toJson() { + final map = {}; + map['ok'] = ok; + if (value != null) { + map['value'] = value?.toJson(); + } + return map; + } + + factory StorageResponseModel.initial() { + return StorageResponseModel(); + } + + factory StorageResponseModel.fromQuickNode({required UploadIPFSOutput uploadIPFSOutput}) { + return StorageResponseModel( + ok: true, + value: Value( + cid: uploadIPFSOutput.pin?.cid, + created: uploadIPFSOutput.created, + size: int.parse( + uploadIPFSOutput.info?.size ?? '0', + ), + pin: Pin( + cid: uploadIPFSOutput.pin?.cid, + created: uploadIPFSOutput.created, + size: int.parse( + uploadIPFSOutput.info?.size ?? '0', + ), + status: uploadIPFSOutput.status, + )), + ); + } +} + +class Value { + String? cid; + String? created; + String? type; + String? scope; + int? size; + Pin? pin; + + Value({ + this.cid, + this.created, + this.type, + this.scope, + this.size, + this.pin, + }); + + Value.fromJson(Map json) { + cid = json['cid'] as String?; + created = json['created'] as String?; + type = json['type'] as String?; + scope = json['scope'] as String?; + size = json['size'] as int?; + pin = json['pin'] != null ? Pin.fromJson(json['pin'] as Map) : null; + } + + Map toJson() { + final map = {}; + map['cid'] = cid; + map['created'] = created; + map['type'] = type; + map['scope'] = scope; + map['size'] = size; + if (pin != null) { + map['pin'] = pin?.toJson(); + } + return map; + } +} + +class Pin { + String? cid; + String? created; + int? size; + String? status; + + Pin({this.cid, this.created, this.size, this.status}); + + Pin.fromJson(Map json) { + cid = json['cid'] as String?; + created = json['created'] as String?; + size = json['size'] as int?; + status = json['status'] as String?; + } + + Map toJson() { + final map = {}; + map['cid'] = cid; + map['created'] = created; + map['size'] = size; + map['status'] = status; + return map; + } +} diff --git a/evently/lib/repository/repository.dart b/evently/lib/repository/repository.dart index 2706d8d076..8cafb3c8aa 100644 --- a/evently/lib/repository/repository.dart +++ b/evently/lib/repository/repository.dart @@ -2,11 +2,15 @@ import 'package:dartz/dartz.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:evently/generated/locale_keys.g.dart'; import 'package:evently/models/picked_file_model.dart'; +import 'package:evently/models/storage_response_model.dart'; import 'package:evently/services/datasources/local_datasource.dart'; -import 'package:evently/utils/failure/failure.dart'; +import 'package:evently/services/datasources/remote_datasource.dart'; +import 'package:evently/services/third_party_services/quick_node.dart'; import 'package:evently/utils/file_utils_helper.dart'; import 'package:injectable/injectable.dart'; +import '../utils/failure/failure.dart'; + abstract class Repository { /// This function picks a file from device storage /// Input: [format] it is the file format which needs to be picked from local storage @@ -33,6 +37,11 @@ abstract class Repository { /// This method will generate easel Id for the NFT /// Output: [String] the id of the NFT that is going to be added in the recipe String autoGenerateEventlyId(); + + /// This method is used uploading provided file to the server using [QuickNode] + /// Input : [UploadIPFSInput] which needs to be uploaded + /// Output : [ApiResponse] the ApiResponse which can contain [success] or [error] response + Future> uploadFileUsingQuickNode({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}); } @LazySingleton(as: Repository) @@ -40,10 +49,12 @@ class RepositoryImp implements Repository { RepositoryImp({ required this.fileUtilsHelper, required this.localDataSource, + required this.remoteDataSource, }); final FileUtilsHelper fileUtilsHelper; final LocalDataSource localDataSource; + final RemoteDataSource remoteDataSource; @override Future> pickFile() async { @@ -79,4 +90,13 @@ class RepositoryImp implements Repository { return localDataSource.autoGenerateEventlyId(); } + @override + Future> uploadFileUsingQuickNode({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}) async { + try { + final storageResponseModel = await remoteDataSource.uploadFileUsingQuickNode(uploadIPFSInput: uploadIPFSInput, onUploadProgressCallback: onUploadProgressCallback); + return Right(storageResponseModel); + } on Exception catch (_) { + return Left(CacheFailure(LocaleKeys.update_failed.tr())); + } + } } diff --git a/evently/lib/services/datasources/remote_datasource.dart b/evently/lib/services/datasources/remote_datasource.dart new file mode 100644 index 0000000000..b82bdd55e7 --- /dev/null +++ b/evently/lib/services/datasources/remote_datasource.dart @@ -0,0 +1,23 @@ +import 'package:evently/models/storage_response_model.dart'; +import 'package:evently/services/third_party_services/quick_node.dart'; +import 'package:injectable/injectable.dart'; + +abstract class RemoteDataSource { + /// This method is used uploading provided file to the server using [QuickNode] + /// Input : [UploadIPFSInput] which needs to be uploaded + /// Output : [Future>] the ApiResponse which can contain [success] or [error] response + Future uploadFileUsingQuickNode({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}); +} + +@LazySingleton(as: RemoteDataSource) +class RemoteDataSourceImpl extends RemoteDataSource { + RemoteDataSourceImpl({required this.quickNode}); + + final QuickNode quickNode; + + @override + Future uploadFileUsingQuickNode({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}) async { + final response = await quickNode.uploadNewObjectToIPFS(uploadIPFSInput: uploadIPFSInput, onUploadProgressCallback: onUploadProgressCallback); + return response; + } +} diff --git a/evently/lib/services/third_party_services/quick_node.dart b/evently/lib/services/third_party_services/quick_node.dart index d4739795dd..4707240201 100644 --- a/evently/lib/services/third_party_services/quick_node.dart +++ b/evently/lib/services/third_party_services/quick_node.dart @@ -1,7 +1,7 @@ import 'dart:async'; - import 'package:dio/dio.dart'; import 'package:evently/env.dart'; +import 'package:evently/models/storage_response_model.dart'; import 'package:injectable/injectable.dart'; typedef OnUploadProgressCallback = void Function(UploadProgress uploadProgress); @@ -22,7 +22,7 @@ abstract class QuickNode { /// Upload a new object to IPFS and pins it for permanent storage on the network. /// [UploadIPFSInput] as an input /// [UploadIPFSOutput] as an output - Future uploadNewObjectToIPFS({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}); + Future uploadNewObjectToIPFS({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}); /// these are the list of extension required static List listOfQuickNodeAllowedExtension() => ['jpg', 'png', 'heif', 'jpeg', 'gif']; @@ -30,7 +30,6 @@ abstract class QuickNode { /// this method is used to get the content type while making request input to quick node static String getContentType(String fileExtension) { final dict = { - ///* images "jpg": "image/jpg", "png": "image/png", 'heif': "image/heif", @@ -48,7 +47,7 @@ class QuickNodeImpl extends QuickNode { final Dio httpClient; @override - Future uploadNewObjectToIPFS({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}) async { + Future uploadNewObjectToIPFS({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}) async { try { httpClient.options.headers['x-api-key'] = xApiKey; @@ -68,8 +67,7 @@ class QuickNodeImpl extends QuickNode { ); final uploadIPFSOutput = UploadIPFSOutput.fromJson(response.data as Map); - - // return //StorageResponseModel.fromQuickNode(uploadIPFSOutput: uploadIPFSOutput); + return StorageResponseModel.fromQuickNode(uploadIPFSOutput: uploadIPFSOutput); } catch (e) { throw Exception('Failed to upload file: $e'); } diff --git a/evently/lib/utils/di/di.config.dart b/evently/lib/utils/di/di.config.dart index aa017fe664..84941fbb75 100644 --- a/evently/lib/utils/di/di.config.dart +++ b/evently/lib/utils/di/di.config.dart @@ -8,17 +8,20 @@ // coverage:ignore-file // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:evently/evently_provider.dart' as _i10; -import 'package:evently/repository/repository.dart' as _i9; -import 'package:evently/services/datasources/local_datasource.dart' as _i6; -import 'package:evently/utils/di/register_modules.dart' as _i11; -import 'package:evently/utils/file_utils_helper.dart' as _i8; +import 'package:dio/dio.dart' as _i6; +import 'package:evently/evently_provider.dart' as _i13; +import 'package:evently/repository/repository.dart' as _i12; +import 'package:evently/services/datasources/local_datasource.dart' as _i7; +import 'package:evently/services/datasources/remote_datasource.dart' as _i11; +import 'package:evently/services/third_party_services/quick_node.dart' as _i10; +import 'package:evently/utils/di/register_modules.dart' as _i14; +import 'package:evently/utils/file_utils_helper.dart' as _i9; import 'package:evently/viewmodels/create_event_viewmodel.dart' as _i3; import 'package:file_picker/file_picker.dart' as _i5; import 'package:get_it/get_it.dart' as _i1; import 'package:image_cropper/image_cropper.dart' as _i4; import 'package:injectable/injectable.dart' as _i2; -import 'package:shared_preferences/shared_preferences.dart' as _i7; +import 'package:shared_preferences/shared_preferences.dart' as _i8; extension GetItInjectableX on _i1.GetIt { // initializes the registration of main-scope dependencies inside of GetIt @@ -36,20 +39,26 @@ extension GetItInjectableX on _i1.GetIt { () => _i3.CreateEventViewModel()); gh.lazySingleton<_i4.ImageCropper>(() => registerModule.imageCropper); gh.lazySingleton<_i5.FilePicker>(() => registerModule.filePicker); - gh.lazySingleton<_i6.LocalDataSource>(() => _i6.LocalDataSourceImpl( - sharedPreferences: gh<_i7.SharedPreferences>())); - gh.lazySingleton<_i8.FileUtilsHelper>(() => _i8.FileUtilsHelperImpl( + gh.lazySingleton<_i6.Dio>(() => registerModule.dio); + gh.lazySingleton<_i7.LocalDataSource>(() => _i7.LocalDataSourceImpl( + sharedPreferences: gh<_i8.SharedPreferences>())); + gh.lazySingleton<_i9.FileUtilsHelper>(() => _i9.FileUtilsHelperImpl( imageCropper: gh<_i4.ImageCropper>(), filePicker: gh<_i5.FilePicker>(), )); - gh.lazySingleton<_i9.Repository>(() => _i9.RepositoryImp( - fileUtilsHelper: gh<_i8.FileUtilsHelper>(), - localDataSource: gh<_i6.LocalDataSource>(), + gh.lazySingleton<_i10.QuickNode>( + () => _i10.QuickNodeImpl(httpClient: gh<_i6.Dio>())); + gh.lazySingleton<_i11.RemoteDataSource>( + () => _i11.RemoteDataSourceImpl(quickNode: gh<_i10.QuickNode>())); + gh.lazySingleton<_i12.Repository>(() => _i12.RepositoryImp( + fileUtilsHelper: gh<_i9.FileUtilsHelper>(), + localDataSource: gh<_i7.LocalDataSource>(), + remoteDataSource: gh<_i11.RemoteDataSource>(), )); - gh.lazySingleton<_i10.EventlyProvider>( - () => _i10.EventlyProvider(repository: gh<_i9.Repository>())); + gh.lazySingleton<_i13.EventlyProvider>( + () => _i13.EventlyProvider(repository: gh<_i12.Repository>())); return this; } } -class _$RegisterModule extends _i11.RegisterModule {} +class _$RegisterModule extends _i14.RegisterModule {} diff --git a/evently/lib/utils/failure/failure.dart b/evently/lib/utils/failure/failure.dart index 31034608d1..c6cdf1afe5 100644 --- a/evently/lib/utils/failure/failure.dart +++ b/evently/lib/utils/failure/failure.dart @@ -12,3 +12,10 @@ class PickingFileFailure extends Failure { @override List get props => [message]; } + +class CacheFailure extends Failure { + const CacheFailure(super.message); + + @override + List get props => [message]; +}