From 6c735f63a0e5ea5ed35879041c5f7ab1e02edd7c Mon Sep 17 00:00:00 2001 From: AhsanAli Date: Thu, 28 Mar 2024 13:56:23 +0500 Subject: [PATCH] fix: feeback --- easel/lib/easel_provider.dart | 13 +-- easel/lib/repository/repository.dart | 6 +- easel/lib/screens/choose_format_screen.dart | 7 +- .../datasources/remote_datasource.dart | 7 +- .../third_party_services/quick_node.dart | 109 ++++++++---------- .../dependency_injection_container.dart | 5 +- easel/pubspec.yaml | 1 - easel/test/mock/mock_repository.dart | 2 +- 8 files changed, 63 insertions(+), 87 deletions(-) diff --git a/easel/lib/easel_provider.dart b/easel/lib/easel_provider.dart index d38fe0c0d6..73a5dcbdcd 100644 --- a/easel/lib/easel_provider.dart +++ b/easel/lib/easel_provider.dart @@ -813,11 +813,11 @@ class EaselProvider extends ChangeNotifier { uploadThumbnailResponse = uploadResponse.getOrElse(() => StorageResponseModel.initial()); } - final whereToUpload = QuickNode.listOfQuickNodeAllowedExtension().contains(fileExtension.toLowerCase()); + final shouldUploadToQuickNode = QuickNode.listOfQuickNodeAllowedExtension().contains(fileExtension.toLowerCase()); Either response; - if (!whereToUpload) { + if (!shouldUploadToQuickNode) { response = await repository.uploadFile( file: _file!, onUploadProgressCallback: (value) { @@ -826,11 +826,10 @@ class EaselProvider extends ChangeNotifier { ); } else { response = await repository.uploadFileUsingQuickNode( - uploadIPFSInput: UploadIPFSInput( - fileName: fileName, - filePath: file?.path ?? '', - contentType: QuickNode.getContentType(fileExtension), - ), + uploadIPFSInput: UploadIPFSInput(fileName: fileName, filePath: file!.path, contentType: QuickNode.getContentType(fileExtension)), + onUploadProgressCallback: (value) { + _uploadProgressController.sink.add(value); + }, ); } diff --git a/easel/lib/repository/repository.dart b/easel/lib/repository/repository.dart index a1ed7536d0..9c28fe3a22 100644 --- a/easel/lib/repository/repository.dart +++ b/easel/lib/repository/repository.dart @@ -115,7 +115,7 @@ abstract class Repository { /// 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}); + Future> uploadFileUsingQuickNode({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}); /// This method will get the drafts List from the local database /// Output: [List] returns that contains a number of [NFT] @@ -331,13 +331,13 @@ class RepositoryImp implements Repository { } @override - Future> uploadFileUsingQuickNode({required UploadIPFSInput uploadIPFSInput}) async { + Future> uploadFileUsingQuickNode({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}) async { if (!await networkInfo.isConnected) { return Left(NoInternetFailure(LocaleKeys.no_internet.tr())); } try { - final storageResponseModel = await remoteDataSource.uploadFileUsingQuickNode(uploadIPFSInput: uploadIPFSInput); + final storageResponseModel = await remoteDataSource.uploadFileUsingQuickNode(uploadIPFSInput: uploadIPFSInput, onUploadProgressCallback: onUploadProgressCallback); return Right(storageResponseModel); } on Exception catch (_) { crashlyticsHelper.recordFatalError(error: _.toString()); diff --git a/easel/lib/screens/choose_format_screen.dart b/easel/lib/screens/choose_format_screen.dart index f10ba65ed5..84f838d0ec 100644 --- a/easel/lib/screens/choose_format_screen.dart +++ b/easel/lib/screens/choose_format_screen.dart @@ -36,9 +36,6 @@ class _ChooseFormatScreenState extends State { return; } - - - if (!provider.nftFormat.extensions.contains(result.extension)) { final fileName = result.fileName.replaceAll(".${result.extension}", ""); errorText.value = LocaleKeys.could_not_uploaded.tr( @@ -354,9 +351,7 @@ class _ErrorMessageWidget extends StatelessWidget { ), SizedBox(height: 10.h), Text( - (nftTypes == NFTTypes.video || nftTypes == NFTTypes.audio) - ? "• ${(kFileSizeLimitForAudioVideoInGB * 1000).toStringAsFixed(0)}MB limit" - : "• ${kFileSizeLimitInGB}GB limit", + (nftTypes == NFTTypes.video || nftTypes == NFTTypes.audio) ? "• ${(kFileSizeLimitForAudioVideoInGB * 1000).toStringAsFixed(0)}MB limit" : "• ${kFileSizeLimitInGB}GB limit", style: Theme.of(context).textTheme.bodyMedium!.copyWith( color: Colors.white, fontSize: 12.sp, diff --git a/easel/lib/services/datasources/remote_datasource.dart b/easel/lib/services/datasources/remote_datasource.dart index 0c336e0657..b5953a8d20 100644 --- a/easel/lib/services/datasources/remote_datasource.dart +++ b/easel/lib/services/datasources/remote_datasource.dart @@ -30,7 +30,7 @@ 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}); + Future uploadFileUsingQuickNode({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}); } class RemoteDataSourceImpl implements RemoteDataSource { @@ -45,8 +45,9 @@ class RemoteDataSourceImpl implements RemoteDataSource { }); @override - Future uploadFileUsingQuickNode({required UploadIPFSInput uploadIPFSInput}) async { - final response = await quickNode.uploadNewObjectToIPFS(uploadIPFSInput); + Future uploadFileUsingQuickNode({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}) async { + + final response = await quickNode.uploadNewObjectToIPFS(uploadIPFSInput: uploadIPFSInput, onUploadProgressCallback: onUploadProgressCallback); return response; } diff --git a/easel/lib/services/third_party_services/quick_node.dart b/easel/lib/services/third_party_services/quick_node.dart index b85caa4cb4..67ea309ec9 100644 --- a/easel/lib/services/third_party_services/quick_node.dart +++ b/easel/lib/services/third_party_services/quick_node.dart @@ -1,16 +1,15 @@ -import 'dart:convert'; -import 'dart:io'; +import 'dart:async'; +import 'package:dio/dio.dart'; +import 'package:easel_flutter/easel_provider.dart'; import 'package:easel_flutter/env.dart'; import 'package:easel_flutter/models/storage_response_model.dart'; - -///* using this because dio is already init with other base url -import 'package:http/http.dart' as http; +import 'package:easel_flutter/models/upload_progress.dart'; 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(UploadIPFSInput uploadIPFSInput); + Future uploadNewObjectToIPFS({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}); /// these are the list of extension required static List listOfQuickNodeAllowedExtension() { @@ -25,40 +24,36 @@ abstract class QuickNode { } class QuickNodeImpl extends QuickNode { - @override - Future uploadNewObjectToIPFS(UploadIPFSInput uploadIPFSInput) async { - final url = Uri.parse('https://api.quicknode.com/ipfs/rest/v1/s3/put-object'); - - final headers = { - 'x-api-key': xApiKey, - }; - - final fields = { - 'Key': uploadIPFSInput.fileName, - 'ContentType': uploadIPFSInput.contentType, - }; - - final file = File(uploadIPFSInput.filePath); + QuickNodeImpl({required this.httpClient}); - final request = http.MultipartRequest('POST', url); + final Dio httpClient; - request.headers.addAll(headers); - - fields.forEach((key, value) { - request.fields[key] = value; - }); - - final fileStream = http.ByteStream(file.openRead()); - final fileLength = await file.length(); - final multipartFile = http.MultipartFile('Body', fileStream, fileLength, filename: file.path.split('/').last); - - request.files.add(multipartFile); - - final response = await http.Response.fromStream(await request.send()); + @override + Future uploadNewObjectToIPFS({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}) async { + try { + httpClient.options.headers['x-api-key'] = xApiKey; + + final response = await httpClient.post( + 'https://api.quicknode.com/ipfs/rest/v1/s3/put-object', + data: FormData.fromMap({ + 'Body': await MultipartFile.fromFile(uploadIPFSInput.filePath), + 'Key': uploadIPFSInput.fileName, + 'ContentType': uploadIPFSInput.contentType, + }), + onSendProgress: (uploaded, total) { + final double uploadedPercentage = uploaded / total; + onUploadProgressCallback( + UploadProgress(totalSize: total, sendSize: uploaded, uploadedProgressData: uploadedPercentage), + ); + }, + ); - final a = UploadIPFSOutput.fromJson(json.decode(response.body) as Map); + final uploadIPFSOutput = UploadIPFSOutput.fromJson(response.data as Map); - return StorageResponseModel.fromQuickNode(uploadIPFSOutput: a); + return StorageResponseModel.fromQuickNode(uploadIPFSOutput: uploadIPFSOutput); + } catch (e) { + throw Exception('Failed to upload file: $e'); + } } } @@ -72,10 +67,6 @@ class UploadIPFSInput { required this.filePath, required this.contentType, }); - - Map toJson() { - return {}; - } } class UploadIPFSOutput { @@ -86,33 +77,25 @@ class UploadIPFSOutput { final Info? info; final List? delegates; - UploadIPFSOutput({ - this.requestId, - this.status, - this.created, - this.pin, - this.info, - this.delegates, - }); - - factory UploadIPFSOutput.fromJson(Map json) => UploadIPFSOutput( - requestId: json['requestid'] as String?, - status: json['status'] as String?, - created: json['created'] as String?, - pin: Pin.fromJson(json['pin'] as Map), - info: Info.fromJson(json['info'] as Map), - delegates: [], - ); + UploadIPFSOutput({this.requestId, this.status, this.created, this.pin, this.info, this.delegates}); + + factory UploadIPFSOutput.fromJson(Map json) { + return UploadIPFSOutput( + requestId: json['requestid'] as String?, + status: json['status'] as String?, + created: json['created'] as String?, + pin: Pin.fromJson(json['pin'] as Map), + info: Info.fromJson(json['info'] as Map), + delegates: [], + ); + } } class Pin { String? cid; String? name; - Pin({ - this.cid, - this.name, - }); + Pin({this.cid, this.name}); factory Pin.fromJson(Map json) => Pin( cid: json['cid'] as String?, @@ -123,9 +106,7 @@ class Pin { class Info { final String? size; - Info({ - this.size, - }); + Info({this.size}); factory Info.fromJson(Map json) => Info(size: json['size'] as String?); } diff --git a/easel/lib/utils/dependency_injection/dependency_injection_container.dart b/easel/lib/utils/dependency_injection/dependency_injection_container.dart index 26ff1d7807..c73a3c9312 100644 --- a/easel/lib/utils/dependency_injection/dependency_injection_container.dart +++ b/easel/lib/utils/dependency_injection/dependency_injection_container.dart @@ -72,7 +72,8 @@ void _registerLocalDataSources() { void _registerProviders() { sl.registerLazySingleton( - () => EaselProvider(videoPlayerHelper: sl(), audioPlayerHelperForFile: sl(), fileUtilsHelper: sl(), repository: sl(), audioPlayerHelperForUrl: sl(), mediaInfo: sl())); + () => EaselProvider(videoPlayerHelper: sl(), audioPlayerHelperForFile: sl(), fileUtilsHelper: sl(), repository: sl(), audioPlayerHelperForUrl: sl(), mediaInfo: sl()), + ); sl.registerLazySingleton(() => CreatorHubViewModel(sl())); sl.registerLazySingleton(() => HomeViewModel(sl())); sl.registerLazySingleton(() => TutorialScreenViewModel(repository: sl())); @@ -88,5 +89,5 @@ void _registerServices() { sl.registerLazySingleton(() => CrashlyticsHelperImp(crashlytics: sl())); sl.registerLazySingleton(() => RepositoryImp(networkInfo: sl(), localDataSource: sl(), remoteDataSource: sl(), fileUtilsHelper: sl(), crashlyticsHelper: sl())); - sl.registerLazySingleton(() => QuickNodeImpl()); + sl.registerLazySingleton(() => QuickNodeImpl(httpClient: Dio())); } diff --git a/easel/pubspec.yaml b/easel/pubspec.yaml index d1eac4dbb7..d94cc58bb8 100644 --- a/easel/pubspec.yaml +++ b/easel/pubspec.yaml @@ -59,7 +59,6 @@ dependencies: focus_detector: ^2.0.1 get_it: ^7.2.0 google_fonts: ^4.0.3 - http: ^0.13.6 image_cropper: ^4.0.1 internet_connection_checker: ^0.0.1+4 intl: diff --git a/easel/test/mock/mock_repository.dart b/easel/test/mock/mock_repository.dart index 90375e7fee..3d4a57202f 100644 --- a/easel/test/mock/mock_repository.dart +++ b/easel/test/mock/mock_repository.dart @@ -169,7 +169,7 @@ class MockRepositoryImp implements Repository { } @override - Future> uploadFileUsingQuickNode({required UploadIPFSInput uploadIPFSInput}) { + Future> uploadFileUsingQuickNode({required UploadIPFSInput uploadIPFSInput, required OnUploadProgressCallback onUploadProgressCallback}) { throw UnimplementedError(); } }