From 407981ca5686ed670c4954972c5b8ad35b144301 Mon Sep 17 00:00:00 2001 From: AhsanRns Date: Wed, 15 May 2024 15:19:30 +0500 Subject: [PATCH] feat: quick node for thumbnail uploading --- evently/.gitignore | 2 + .../third_party_services/quick_node.dart | 131 ++++++++++++++++++ evently/lib/utils/di/register_modules.dart | 4 + evently/pubspec.lock | 8 ++ evently/pubspec.yaml | 1 + 5 files changed, 146 insertions(+) create mode 100644 evently/lib/services/third_party_services/quick_node.dart diff --git a/evently/.gitignore b/evently/.gitignore index 29a3a5017f..392bfbbe7a 100644 --- a/evently/.gitignore +++ b/evently/.gitignore @@ -41,3 +41,5 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release + +lib/env.dart diff --git a/evently/lib/services/third_party_services/quick_node.dart b/evently/lib/services/third_party_services/quick_node.dart new file mode 100644 index 0000000000..d4739795dd --- /dev/null +++ b/evently/lib/services/third_party_services/quick_node.dart @@ -0,0 +1,131 @@ +import 'dart:async'; + +import 'package:dio/dio.dart'; +import 'package:evently/env.dart'; +import 'package:injectable/injectable.dart'; + +typedef OnUploadProgressCallback = void Function(UploadProgress uploadProgress); + +class UploadProgress { + int totalSize; + int sendSize; + double uploadedProgressData; + + UploadProgress({ + required this.totalSize, + required this.sendSize, + required this.uploadedProgressData, + }); +} + +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}); + + /// these are the list of extension required + static List listOfQuickNodeAllowedExtension() => ['jpg', 'png', 'heif', 'jpeg', 'gif']; + + /// 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", + 'jpeg': "image/jpeg", + 'gif': "image/gif", + }; + return dict[fileExtension]!; + } +} + +@LazySingleton(as: QuickNode) +class QuickNodeImpl extends QuickNode { + QuickNodeImpl({required this.httpClient}); + + final Dio httpClient; + + @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 uploadIPFSOutput = UploadIPFSOutput.fromJson(response.data as Map); + + // return //StorageResponseModel.fromQuickNode(uploadIPFSOutput: uploadIPFSOutput); + } catch (e) { + throw Exception('Failed to upload file: $e'); + } + } +} + +class UploadIPFSInput { + final String fileName; + final String filePath; + final String contentType; + + UploadIPFSInput({ + required this.fileName, + required this.filePath, + required this.contentType, + }); +} + +class UploadIPFSOutput { + final String? requestId; + final String? status; + final String? created; + final Pin? pin; + final Info? info; + final List? 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}); + + factory Pin.fromJson(Map json) => Pin( + cid: json['cid'] as String?, + name: json['name'] as String?, + ); +} + +class Info { + final String? size; + + Info({this.size}); + + factory Info.fromJson(Map json) => Info(size: json['size'] as String?); +} diff --git a/evently/lib/utils/di/register_modules.dart b/evently/lib/utils/di/register_modules.dart index 7eafd2668e..43af19d53a 100644 --- a/evently/lib/utils/di/register_modules.dart +++ b/evently/lib/utils/di/register_modules.dart @@ -3,6 +3,7 @@ // Injectable is a convenient code generator for get_it. // All you have to do now is annotate your injectable classes with @injectable and let the generator do the work. // This class is use to generate code to register objects on app start +import 'package:dio/dio.dart'; import 'package:file_picker/file_picker.dart'; import 'package:image_cropper/image_cropper.dart'; import 'package:injectable/injectable.dart'; @@ -14,4 +15,7 @@ abstract class RegisterModule { @LazySingleton() FilePicker get filePicker => FilePicker.platform; + + @LazySingleton() + Dio get dio => Dio(); } diff --git a/evently/pubspec.lock b/evently/pubspec.lock index 9d93b09ee9..a27085d01f 100644 --- a/evently/pubspec.lock +++ b/evently/pubspec.lock @@ -193,6 +193,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.3" + dio: + dependency: "direct main" + description: + name: dio + sha256: "11e40df547d418cc0c4900a9318b26304e665da6fa4755399a9ff9efd09034b5" + url: "https://pub.dev" + source: hosted + version: "5.4.3+1" dotted_border: dependency: "direct main" description: diff --git a/evently/pubspec.yaml b/evently/pubspec.yaml index b91c6f749d..68e089ac9f 100644 --- a/evently/pubspec.yaml +++ b/evently/pubspec.yaml @@ -54,6 +54,7 @@ dependencies: pylons_sdk: path: ../dart_sdk shared_preferences: ^2.2.3 + dio: ^5.4.3+1 dev_dependencies: flutter_test: