diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 9625e10..7c56964 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 11.0 + 12.0 diff --git a/example/ios/Podfile b/example/ios/Podfile index 88359b2..279576f 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '11.0' +# platform :ios, '12.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index c4f8411..c6464f7 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,28 +1,156 @@ PODS: + - DKImagePickerController/Core (4.3.9): + - DKImagePickerController/ImageDataManager + - DKImagePickerController/Resource + - DKImagePickerController/ImageDataManager (4.3.9) + - DKImagePickerController/PhotoGallery (4.3.9): + - DKImagePickerController/Core + - DKPhotoGallery + - DKImagePickerController/Resource (4.3.9) + - DKPhotoGallery (0.0.19): + - DKPhotoGallery/Core (= 0.0.19) + - DKPhotoGallery/Model (= 0.0.19) + - DKPhotoGallery/Preview (= 0.0.19) + - DKPhotoGallery/Resource (= 0.0.19) + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Core (0.0.19): + - DKPhotoGallery/Model + - DKPhotoGallery/Preview + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Model (0.0.19): + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Preview (0.0.19): + - DKPhotoGallery/Model + - DKPhotoGallery/Resource + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Resource (0.0.19): + - SDWebImage + - SwiftyGif + - file_picker (0.0.1): + - DKImagePickerController/PhotoGallery + - Flutter - Flutter (1.0.0) - - path_provider_ios (0.0.1): + - flutter_secure_storage (6.0.0): + - Flutter + - image_picker_ios (0.0.1): + - Flutter + - media_kit_libs_ios_video (1.0.4): + - Flutter + - media_kit_native_event_loop (1.0.0): + - Flutter + - media_kit_video (0.0.1): + - Flutter + - open_filex (0.0.2): + - Flutter + - package_info_plus (0.4.5): - Flutter + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - screen_brightness_ios (0.1.0): + - Flutter + - SDWebImage (5.20.0): + - SDWebImage/Core (= 5.20.0) + - SDWebImage/Core (5.20.0) + - sqflite_darwin (0.0.4): + - Flutter + - FlutterMacOS + - SwiftyGif (5.4.5) - url_launcher_ios (0.0.1): - Flutter + - volume_controller (0.0.1): + - Flutter + - wakelock_plus (0.0.1): + - Flutter + - webview_flutter_wkwebview (0.0.1): + - Flutter + - FlutterMacOS DEPENDENCIES: + - file_picker (from `.symlinks/plugins/file_picker/ios`) - Flutter (from `Flutter`) - - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) + - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) + - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) + - media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`) + - media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`) + - media_kit_video (from `.symlinks/plugins/media_kit_video/ios`) + - open_filex (from `.symlinks/plugins/open_filex/ios`) + - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) + - screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`) + - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) + - volume_controller (from `.symlinks/plugins/volume_controller/ios`) + - wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`) + - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`) + +SPEC REPOS: + trunk: + - DKImagePickerController + - DKPhotoGallery + - SDWebImage + - SwiftyGif EXTERNAL SOURCES: + file_picker: + :path: ".symlinks/plugins/file_picker/ios" Flutter: :path: Flutter - path_provider_ios: - :path: ".symlinks/plugins/path_provider_ios/ios" + flutter_secure_storage: + :path: ".symlinks/plugins/flutter_secure_storage/ios" + image_picker_ios: + :path: ".symlinks/plugins/image_picker_ios/ios" + media_kit_libs_ios_video: + :path: ".symlinks/plugins/media_kit_libs_ios_video/ios" + media_kit_native_event_loop: + :path: ".symlinks/plugins/media_kit_native_event_loop/ios" + media_kit_video: + :path: ".symlinks/plugins/media_kit_video/ios" + open_filex: + :path: ".symlinks/plugins/open_filex/ios" + package_info_plus: + :path: ".symlinks/plugins/package_info_plus/ios" + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/darwin" + screen_brightness_ios: + :path: ".symlinks/plugins/screen_brightness_ios/ios" + sqflite_darwin: + :path: ".symlinks/plugins/sqflite_darwin/darwin" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" + volume_controller: + :path: ".symlinks/plugins/volume_controller/ios" + wakelock_plus: + :path: ".symlinks/plugins/wakelock_plus/ios" + webview_flutter_wkwebview: + :path: ".symlinks/plugins/webview_flutter_wkwebview/darwin" SPEC CHECKSUMS: - Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 - path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 - url_launcher_ios: ae1517e5e344f5544fb090b079e11f399dfbe4d2 + DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c + DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 + file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655 + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be + image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 + media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1 + media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a + media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e + open_filex: 6e26e659846ec990262224a12ef1c528bb4edbe4 + package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625 + SDWebImage: 73c6079366fea25fa4bb9640d5fb58f0893facd8 + sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d + SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 + url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe + volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9 + wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1 + webview_flutter_wkwebview: 0982481e3d9c78fd5c6f62a002fcd24fc791f1e4 -PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 +PODFILE CHECKSUM: c4c93c5f6502fe2754f48404d3594bf779584011 -COCOAPODS: 1.11.3 +COCOAPODS: 1.15.2 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 7979e62..c0f2cdd 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -156,7 +156,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -200,10 +200,12 @@ /* Begin PBXShellScriptBuildPhase section */ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( @@ -231,6 +233,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -340,7 +343,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -414,7 +417,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -463,7 +466,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 3db53b6..e67b280 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ CADisableMinimumFrameDurationOnPhone + UIApplicationSupportsIndirectInputEvents + diff --git a/example/lib/main.dart b/example/lib/main.dart index cb0ff98..ca6a576 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -46,33 +46,15 @@ class _MyHomePageState extends State { appBar: AppBar( title: Text("Chatwoot Example"), ), - body: ChatwootWidget( - websiteToken: "websiteToken", - baseUrl: "https://app.chatwoot.com", + body: ChatwootChat( + baseUrl: "https://chat.goldenprizma.com", user: ChatwootUser( identifier: "test@test.com", name: "Tester test", email: "test@test.com", ), - locale: "en", - closeWidget: () { - if (Platform.isAndroid) { - SystemNavigator.pop(); - } else if (Platform.isIOS) { - exit(0); - } - }, - //attachment only works on android for now - onAttachFile: _androidFilePicker, - onLoadStarted: () { - print("loading widget"); - }, - onLoadProgress: (int progress) { - print("loading... ${progress}"); - }, - onLoadCompleted: () { - print("widget loaded"); - }, + //attachment only works on android for now, + inboxIdentifier: 'gHgywYWNz6nbrNp2GzWBvaYo', ), ); } diff --git a/example/pubspec.lock b/example/pubspec.lock index 3b17edf..97363e8 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -37,26 +37,26 @@ packages: dependency: transitive description: name: cached_network_image - sha256: "4a5d8d2c728b0f3d0245f69f921d7be90cae4c2fd5288f773088672c0893f819" + sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f" url: "https://pub.dev" source: hosted - version: "3.4.0" + version: "3.3.1" cached_network_image_platform_interface: dependency: transitive description: name: cached_network_image_platform_interface - sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829" + sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f" url: "https://pub.dev" source: hosted - version: "4.1.1" + version: "4.0.0" cached_network_image_web: dependency: transitive description: name: cached_network_image_web - sha256: "6322dde7a5ad92202e64df659241104a43db20ed594c41ca18de1014598d7996" + sha256: "205d6a9f1862de34b93184f22b9d2d94586b2f05c581d546695e3d8f6a805cd7" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.2.0" characters: dependency: transitive description: @@ -140,18 +140,10 @@ packages: dependency: transitive description: name: dio - sha256: "5598aa796bbf4699afd5c67c0f5f6e2ed542afc956884b9cd58c306966efc260" + sha256: "7d328c4d898a61efc3cd93655a0955858e29a0aa647f0f9e02d59b3bb275e2e8" url: "https://pub.dev" source: hosted - version: "5.7.0" - dio_web_adapter: - dependency: transitive - description: - name: dio_web_adapter - sha256: "33259a9276d6cea88774a0000cfae0d861003497755969c92faa223108620dc8" - url: "https://pub.dev" - source: hosted - version: "2.0.0" + version: "4.0.6" easy_image_viewer: dependency: transitive description: @@ -232,14 +224,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.9.3+3" - fixnum: - dependency: transitive - description: - name: fixnum - sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be - url: "https://pub.dev" - source: hosted - version: "1.1.1" flutter: dependency: "direct main" description: flutter @@ -249,10 +233,10 @@ packages: dependency: transitive description: name: flutter_cache_manager - sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" + sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba" url: "https://pub.dev" source: hosted - version: "3.4.1" + version: "3.3.1" flutter_chat_types: dependency: transitive description: @@ -289,10 +273,10 @@ packages: dependency: transitive description: name: flutter_markdown - sha256: "999a4e3cb3e1532a971c86d6c73a480264f6a687959d4887cb4e2990821827e4" + sha256: "255b00afa1a7bad19727da6a7780cf3db6c3c12e68d302d85e0ff1fdf173db9e" url: "https://pub.dev" source: hosted - version: "0.7.4+2" + version: "0.7.4+3" flutter_parsed_text: dependency: transitive description: @@ -313,10 +297,10 @@ packages: dependency: transitive description: name: flutter_secure_storage - sha256: "165164745e6afb5c0e3e3fcc72a012fb9e58496fb26ffb92cf22e16a821e85d0" + sha256: "22dbf16f23a4bcf9d35e51be1c84ad5bb6f627750565edd70dab70f3ff5fff8f" url: "https://pub.dev" source: hosted - version: "9.2.2" + version: "8.1.0" flutter_secure_storage_linux: dependency: transitive description: @@ -353,10 +337,10 @@ packages: dependency: transitive description: name: flutter_secure_storage_windows - sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + sha256: "38f9501c7cb6f38961ef0e1eacacee2b2d4715c63cc83fe56449c4d3d0b47255" url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "2.1.1" flutter_test: dependency: "direct dev" description: flutter @@ -367,6 +351,14 @@ packages: description: flutter source: sdk version: "0.0.0" + freezed_annotation: + dependency: transitive + description: + name: freezed_annotation + sha256: "70776c4541e5cacfe45bcaf00fe79137b8c61aa34fb5765a05ce6c57fd72c6e9" + url: "https://pub.dev" + source: hosted + version: "0.14.3" hive: dependency: transitive description: @@ -395,10 +387,10 @@ packages: dependency: transitive description: name: http - sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 + sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "0.13.6" http_parser: dependency: transitive description: @@ -483,10 +475,10 @@ packages: dependency: transitive description: name: intl - sha256: "99f282cb0e02edcbbf8c6b3bbc7c90b65635156c412e58f3975a7e55284ce685" + sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" url: "https://pub.dev" source: hosted - version: "0.20.0" + version: "0.17.0" js: dependency: transitive description: @@ -771,18 +763,18 @@ packages: dependency: transitive description: name: riverpod - sha256: "59062512288d3056b2321804332a13ffdd1bf16df70dcc8e506e411280a72959" + sha256: "13cbe0e17b659f38027986df967b3eaf7f42c519786352167fc3db1be44eae07" url: "https://pub.dev" source: hosted - version: "2.6.1" + version: "0.14.0+3" rxdart: dependency: transitive description: name: rxdart - sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" url: "https://pub.dev" source: hosted - version: "0.28.0" + version: "0.27.7" safe_local_storage: dependency: transitive description: @@ -860,14 +852,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" - sprintf: - dependency: transitive - description: - name: sprintf - sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" - url: "https://pub.dev" - source: hosted - version: "7.0.0" sqflite: dependency: transitive description: @@ -920,10 +904,10 @@ packages: dependency: transitive description: name: state_notifier - sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb + sha256: "8fe42610f179b843b12371e40db58c9444f8757f8b69d181c97e50787caed289" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "0.7.2+1" stream_channel: dependency: transitive description: @@ -1056,10 +1040,10 @@ packages: dependency: transitive description: name: uuid - sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" url: "https://pub.dev" source: hosted - version: "4.5.1" + version: "3.0.7" vector_math: dependency: transitive description: @@ -1116,22 +1100,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.1" - web_socket: - dependency: transitive - description: - name: web_socket - sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" - url: "https://pub.dev" - source: hosted - version: "0.1.6" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" + sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "2.4.5" webview_flutter: dependency: transitive description: @@ -1189,5 +1165,5 @@ packages: source: hosted version: "6.5.0" sdks: - dart: ">=3.5.3 <4.0.0" + dart: ">=3.5.2 <4.0.0" flutter: ">=3.24.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index f6b0b41..890761c 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -18,7 +18,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: "^3.5.3" + sdk: "^3.5.2" dependencies: flutter: diff --git a/lib/data/remote/service/chatwoot_client_auth_service.dart b/lib/data/remote/service/chatwoot_client_auth_service.dart index 438c3bb..e809b5c 100644 --- a/lib/data/remote/service/chatwoot_client_auth_service.dart +++ b/lib/data/remote/service/chatwoot_client_auth_service.dart @@ -44,9 +44,9 @@ class ChatwootClientAuthServiceImpl extends ChatwootClientAuthService { createResponse.statusMessage ?? "unknown error", ChatwootClientExceptionType.CREATE_CONTACT_FAILED); } - } on DioException catch (e) { + } on DioError catch (e) { throw ChatwootClientException( - e.message ?? '', ChatwootClientExceptionType.CREATE_CONTACT_FAILED); + e.message, ChatwootClientExceptionType.CREATE_CONTACT_FAILED); } } @@ -67,9 +67,9 @@ class ChatwootClientAuthServiceImpl extends ChatwootClientAuthService { createResponse.statusMessage ?? "unknown error", ChatwootClientExceptionType.CREATE_CONVERSATION_FAILED); } - } on DioException catch (e) { + } on DioError catch (e) { throw ChatwootClientException( - e.message ?? '', ChatwootClientExceptionType.CREATE_CONVERSATION_FAILED); + e.message, ChatwootClientExceptionType.CREATE_CONVERSATION_FAILED); } } } diff --git a/lib/data/remote/service/chatwoot_client_service.dart b/lib/data/remote/service/chatwoot_client_service.dart index 0032b90..aca9eaf 100644 --- a/lib/data/remote/service/chatwoot_client_service.dart +++ b/lib/data/remote/service/chatwoot_client_service.dart @@ -14,7 +14,7 @@ import 'package:chatwoot_sdk/data/remote/service/chatwoot_client_api_interceptor import 'package:dio/dio.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; import 'package:mime/mime.dart'; - +import 'package:http_parser/http_parser.dart'; /// Service for handling chatwoot api calls /// See [ChatwootClientServiceImpl] @@ -36,7 +36,8 @@ abstract class ChatwootClientService { Future updateMessage(String messageIdentifier, update); - Future sendCsatFeedBack(String conversationUuid, SendCsatSurveyRequest request); + Future sendCsatFeedBack( + String conversationUuid, SendCsatSurveyRequest request); Future getCsatFeedback(String conversationUuid); @@ -49,7 +50,8 @@ abstract class ChatwootClientService { } class ChatwootClientServiceImpl extends ChatwootClientService { - ChatwootClientServiceImpl(String baseUrl, {required Dio dio, required Dio uDio}) + ChatwootClientServiceImpl(String baseUrl, + {required Dio dio, required Dio uDio}) : super(baseUrl, dio, uDio); ///Sends message to chatwoot inbox @@ -57,17 +59,19 @@ class ChatwootClientServiceImpl extends ChatwootClientService { Future createMessage( ChatwootNewMessageRequest request) async { try { - FormData formData = FormData.fromMap({ 'echo_id': request.echoId, 'content': request.content, }); - for(final attachment in request.attachments){ - formData.files.add(MapEntry('attachments[]',await MultipartFile.fromBytes( - attachment.bytes, - filename: attachment.name, - contentType: DioMediaType.parse(lookupMimeType(attachment.name) ?? 'application/octet-stream') - ))); + for (final attachment in request.attachments) { + formData.files.add(MapEntry( + 'attachments[]', + await MultipartFile.fromBytes( + attachment.bytes, + filename: attachment.name, + contentType: MediaType.parse(lookupMimeType(attachment.name) ?? + 'application/octet-stream'), + ))); } final createResponse = await _dio.post( "/public/api/v1/inboxes/${ChatwootClientApiInterceptor.INTERCEPTOR_INBOX_IDENTIFIER_PLACEHOLDER}/contacts/${ChatwootClientApiInterceptor.INTERCEPTOR_CONTACT_IDENTIFIER_PLACEHOLDER}/conversations/${ChatwootClientApiInterceptor.INTERCEPTOR_CONVERSATION_IDENTIFIER_PLACEHOLDER}/messages", @@ -81,9 +85,9 @@ class ChatwootClientServiceImpl extends ChatwootClientService { createResponse.statusMessage ?? "unknown error", ChatwootClientExceptionType.SEND_MESSAGE_FAILED); } - } on DioException catch (e) { + } on DioError catch (e) { throw ChatwootClientException( - e.message ?? '', ChatwootClientExceptionType.SEND_MESSAGE_FAILED); + e.message, ChatwootClientExceptionType.SEND_MESSAGE_FAILED); } } @@ -103,9 +107,9 @@ class ChatwootClientServiceImpl extends ChatwootClientService { createResponse.statusMessage ?? "unknown error", ChatwootClientExceptionType.GET_MESSAGES_FAILED); } - } on DioException catch (e) { + } on DioError catch (e) { throw ChatwootClientException( - e.message ?? '', ChatwootClientExceptionType.GET_MESSAGES_FAILED); + e.message, ChatwootClientExceptionType.GET_MESSAGES_FAILED); } } @@ -122,9 +126,9 @@ class ChatwootClientServiceImpl extends ChatwootClientService { getResponse.statusMessage ?? "unknown error", ChatwootClientExceptionType.GET_CONTACT_FAILED); } - } on DioException catch (e) { + } on DioError catch (e) { throw ChatwootClientException( - e.message ?? '', ChatwootClientExceptionType.GET_CONTACT_FAILED); + e.message, ChatwootClientExceptionType.GET_CONTACT_FAILED); } } @@ -143,9 +147,9 @@ class ChatwootClientServiceImpl extends ChatwootClientService { createResponse.statusMessage ?? "unknown error", ChatwootClientExceptionType.GET_CONVERSATION_FAILED); } - } on DioException catch (e) { + } on DioError catch (e) { throw ChatwootClientException( - e.message ?? '', ChatwootClientExceptionType.GET_CONVERSATION_FAILED); + e.message, ChatwootClientExceptionType.GET_CONVERSATION_FAILED); } } @@ -163,9 +167,9 @@ class ChatwootClientServiceImpl extends ChatwootClientService { updateResponse.statusMessage ?? "unknown error", ChatwootClientExceptionType.UPDATE_CONTACT_FAILED); } - } on DioException catch (e) { + } on DioError catch (e) { throw ChatwootClientException( - e.message ?? '', ChatwootClientExceptionType.UPDATE_CONTACT_FAILED); + e.message, ChatwootClientExceptionType.UPDATE_CONTACT_FAILED); } } @@ -185,9 +189,9 @@ class ChatwootClientServiceImpl extends ChatwootClientService { updateResponse.statusMessage ?? "unknown error", ChatwootClientExceptionType.UPDATE_MESSAGE_FAILED); } - } on DioException catch (e) { + } on DioError catch (e) { throw ChatwootClientException( - e.message ?? '', ChatwootClientExceptionType.UPDATE_MESSAGE_FAILED); + e.message, ChatwootClientExceptionType.UPDATE_MESSAGE_FAILED); } } @@ -225,45 +229,44 @@ class ChatwootClientServiceImpl extends ChatwootClientService { } @override - Future getCsatFeedback(String conversationUuid) async{ + Future getCsatFeedback( + String conversationUuid) async { try { - final response = await _dio.get( - "/public/api/v1/csat_survey/$conversationUuid"); + final response = + await _dio.get("/public/api/v1/csat_survey/$conversationUuid"); if ((response.statusCode ?? 0).isBetween(199, 300)) { - return response.data != null ? CsatSurveyFeedbackResponse.fromJson(response.data) : null; + return response.data != null + ? CsatSurveyFeedbackResponse.fromJson(response.data) + : null; } else { - throw ChatwootClientException( - response.statusMessage ?? "unknown error", + throw ChatwootClientException(response.statusMessage ?? "unknown error", ChatwootClientExceptionType.GET_CSAT_FEEDBACK); } - } on DioException catch (e) { + } on DioError catch (e) { throw ChatwootClientException( - e.message ?? '', ChatwootClientExceptionType.GET_CSAT_FEEDBACK); + e.message, ChatwootClientExceptionType.GET_CSAT_FEEDBACK); } } @override - Future sendCsatFeedBack(String conversationUuid, SendCsatSurveyRequest request) async{ + Future sendCsatFeedBack( + String conversationUuid, SendCsatSurveyRequest request) async { try { - final response = await _udio.put( - "/public/api/v1/csat_survey/$conversationUuid", - data: { - "message":{ - "submitted_values":{ - "csat_survey_response": request.toJson() - } - } - }); + final response = await _udio + .put("/public/api/v1/csat_survey/$conversationUuid", data: { + "message": { + "submitted_values": {"csat_survey_response": request.toJson()} + } + }); if ((response.statusCode ?? 0).isBetween(199, 300)) { return CsatSurveyFeedbackResponse.fromJson(response.data); } else { - throw ChatwootClientException( - response.statusMessage ?? "unknown error", + throw ChatwootClientException(response.statusMessage ?? "unknown error", ChatwootClientExceptionType.SEND_CSAT_FEEDBACK); } - } on DioException catch (e) { + } on DioError catch (e) { throw ChatwootClientException( - e.message ?? '', ChatwootClientExceptionType.SEND_CSAT_FEEDBACK); + e.message, ChatwootClientExceptionType.SEND_CSAT_FEEDBACK); } } } diff --git a/lib/ui/chatwoot_chat_page.dart b/lib/ui/chatwoot_chat_page.dart index 10a1cad..ab694a6 100644 --- a/lib/ui/chatwoot_chat_page.dart +++ b/lib/ui/chatwoot_chat_page.dart @@ -26,8 +26,7 @@ import 'package:path_provider/path_provider.dart'; import 'package:uuid/uuid.dart'; import 'package:http/http.dart' as http; - -class FileAttachment{ +class FileAttachment { final Uint8List bytes; final String name; final String path; @@ -35,7 +34,6 @@ class FileAttachment{ FileAttachment({required this.bytes, required this.name, required this.path}); } - ///Chatwoot chat widget /// {@category FlutterClientSdk} class ChatwootChat extends StatefulWidget { @@ -50,7 +48,6 @@ class ChatwootChat extends StatefulWidget { /// For more details see https://www.chatwoot.com/docs/product/channels/api/client-apis final String inboxIdentifier; - ///Key used to generate user identifier hash /// /// For more details see https://www.chatwoot.com/docs/product/channels/api/client-apis @@ -181,7 +178,8 @@ class ChatwootChat extends StatefulWidget { _ChatwootChatState createState() => _ChatwootChatState(); } -class _ChatwootChatState extends State with WidgetsBindingObserver{ +class _ChatwootChatState extends State + with WidgetsBindingObserver { /// List _messages = []; @@ -222,10 +220,10 @@ class _ChatwootChatState extends State with WidgetsBindingObserver onConfirmedSubscription: () { widget.onConfirmedSubscription?.call(); }, - onConversationIsOnline: (){ + onConversationIsOnline: () { widget.onConversationIsOnline?.call(); }, - onConversationIsOffline: (){ + onConversationIsOffline: () { widget.onConversationIsOffline?.call(); }, onConversationStartedTyping: () { @@ -238,7 +236,7 @@ class _ChatwootChatState extends State with WidgetsBindingObserver if (widget.enablePersistence) { setState(() { _messages = persistedMessages - .where((m)=>m.contentType != "input_csat") + .where((m) => m.contentType != "input_csat") .map((message) => _chatwootMessageToTextMessage(message)) .toList(); }); @@ -251,16 +249,15 @@ class _ChatwootChatState extends State with WidgetsBindingObserver } setState(() { final chatMessages = messages - .where((m)=>m.contentType != "input_csat") - .map((message){ - return _chatwootMessageToTextMessage(message); - }) - .toList(); + .where((m) => m.contentType != "input_csat") + .map((message) { + return _chatwootMessageToTextMessage(message); + }).toList(); final mergedMessages = mergeLists( - list1: chatMessages, list2: _messages, - getItemKey: (item)=>item.id, - merger: (item1, item2)=>item1 - ); + list1: chatMessages, + list2: _messages, + getItemKey: (item) => item.id, + merger: (item1, item2) => item1); final now = DateTime.now().millisecondsSinceEpoch; mergedMessages.sort((a, b) { return (b.createdAt ?? now).compareTo(a.createdAt ?? now); @@ -270,7 +267,7 @@ class _ChatwootChatState extends State with WidgetsBindingObserver widget.onMessagesRetrieved?.call(messages); }, onMessageReceived: (chatwootMessage) { - if(chatwootMessage.contentType == "input_csat"){ + if (chatwootMessage.contentType == "input_csat") { //csat message is handled manually return; } @@ -286,38 +283,36 @@ class _ChatwootChatState extends State with WidgetsBindingObserver widget.onMessageUpdated?.call(chatwootMessage); }, onMessageSent: (chatwootMessage, echoId) { - types.Message textMessage = _chatwootMessageToTextMessage(chatwootMessage, echoId: echoId, messageStatus: types.Status.sent); + types.Message textMessage = _chatwootMessageToTextMessage( + chatwootMessage, + echoId: echoId, + messageStatus: types.Status.sent); _handleMessageSent(textMessage); widget.onMessageSent?.call(chatwootMessage); }, onConversationResolved: (conversationUuid) { - final resolvedMessage = types.TextMessage( id: "resolved", text: widget.l10n.conversationResolvedMessage, author: types.User( - id: idGen.v4(),), + id: idGen.v4(), + ), status: types.Status.delivered); _addMessage(resolvedMessage); final csatMessage = types.CustomMessage( id: "csat", author: types.User( - id: idGen.v4(),), - metadata: { - "conversationUuid": conversationUuid - }, + id: idGen.v4(), + ), + metadata: {"conversationUuid": conversationUuid}, status: types.Status.delivered); _addMessage(csatMessage); }, - onCsatSurveyResponseRecorded: (feedback){ - + onCsatSurveyResponseRecorded: (feedback) { final resolvedMessage = types.CustomMessage( id: "csat", - author: types.User( - id: idGen.v4()), - metadata: { - "feedback": feedback - }, + author: types.User(id: idGen.v4()), + metadata: {"feedback": feedback}, status: types.Status.delivered); _handleMessageUpdated(resolvedMessage); }, @@ -381,8 +376,8 @@ class _ChatwootChatState extends State with WidgetsBindingObserver return result; } - - types.Message _chatwootMessageToTextMessage(ChatwootMessage message, {String? echoId, types.Status? messageStatus}) { + types.Message _chatwootMessageToTextMessage(ChatwootMessage message, + {String? echoId, types.Status? messageStatus}) { String? avatarUrl = message.sender?.avatarUrl ?? message.sender?.thumbnail; //Sets avatar url to null if its a gravatar not found url @@ -390,28 +385,28 @@ class _ChatwootChatState extends State with WidgetsBindingObserver if (avatarUrl?.contains("?d=404") ?? false) { avatarUrl = null; } - final nameSplit = (message.sender?.name??"C ").split(" "); + final nameSplit = (message.sender?.name ?? "C ").split(" "); final firstName = nameSplit.first; final lastName = nameSplit.last; types.User author = message.isMine ? _user : types.User( - id: message.sender?.id.toString() ?? idGen.v4(), - firstName: firstName, - lastName: lastName, - imageUrl: avatarUrl, - ); + id: message.sender?.id.toString() ?? idGen.v4(), + firstName: firstName, + lastName: lastName, + imageUrl: avatarUrl, + ); final metadata = { - "sentAt": DateFormat("MMM d, hh:mm a").format(DateTime.parse(message.createdAt)) + "sentAt": + DateFormat("MMM d, hh:mm a").format(DateTime.parse(message.createdAt)) }; - if(message.attachments?.first.dataUrl?.isNotEmpty ?? false){ + if (message.attachments?.first.dataUrl?.isNotEmpty ?? false) { Uri uri = Uri.parse(message.attachments!.first.dataUrl!); // Get the last path segment from the URL (after the last '/') - String fileName = uri.pathSegments.isNotEmpty - ? uri.pathSegments.last - : ''; - if(message.attachments!.first.fileType == "image"){ + String fileName = + uri.pathSegments.isNotEmpty ? uri.pathSegments.last : ''; + if (message.attachments!.first.fileType == "image") { return types.ImageMessage( id: echoId ?? message.id.toString(), author: author, @@ -420,8 +415,9 @@ class _ChatwootChatState extends State with WidgetsBindingObserver size: message.attachments!.first.fileSize ?? 0, uri: message.attachments!.first.dataUrl!, status: messageStatus ?? types.Status.seen, - createdAt: DateTime.parse(message.createdAt).millisecondsSinceEpoch); - }else if(message.attachments!.first.fileType == "video"){ + createdAt: + DateTime.parse(message.createdAt).millisecondsSinceEpoch); + } else if (message.attachments!.first.fileType == "video") { final videoMessage = types.VideoMessage( id: echoId ?? message.id.toString(), author: author, @@ -432,22 +428,25 @@ class _ChatwootChatState extends State with WidgetsBindingObserver size: message.attachments!.first.fileSize ?? 0, uri: message.attachments!.first.dataUrl!, status: messageStatus ?? types.Status.seen, - createdAt: DateTime.parse(message.createdAt).millisecondsSinceEpoch); + createdAt: + DateTime.parse(message.createdAt).millisecondsSinceEpoch); - videoPreviewLoader.getPreview(jobId: videoMessage.id, uri: videoMessage.uri); + videoPreviewLoader.getPreview( + jobId: videoMessage.id, uri: videoMessage.uri); return videoMessage; - }else if(message.attachments!.first.fileType == "audio"){ + } else if (message.attachments!.first.fileType == "audio") { return types.AudioMessage( id: echoId ?? message.id.toString(), author: author, - duration:Duration.zero, + duration: Duration.zero, name: fileName, metadata: metadata, size: message.attachments!.first.fileSize ?? 0, uri: message.attachments!.first.dataUrl!, status: messageStatus ?? types.Status.seen, - createdAt: DateTime.parse(message.createdAt).millisecondsSinceEpoch); - }else{ + createdAt: + DateTime.parse(message.createdAt).millisecondsSinceEpoch); + } else { return types.FileMessage( id: echoId ?? message.id.toString(), author: author, @@ -456,7 +455,8 @@ class _ChatwootChatState extends State with WidgetsBindingObserver size: message.attachments!.first.fileSize ?? 0, uri: message.attachments!.first.dataUrl!, status: messageStatus ?? types.Status.seen, - createdAt: DateTime.parse(message.createdAt).millisecondsSinceEpoch); + createdAt: + DateTime.parse(message.createdAt).millisecondsSinceEpoch); } } @@ -506,9 +506,7 @@ class _ChatwootChatState extends State with WidgetsBindingObserver }); } - void _handleMessageTap(BuildContext _, types.Message message) async { - if (message.status == types.Status.error && message is types.TextMessage) { _handleResendMessage(message); return; @@ -517,33 +515,29 @@ class _ChatwootChatState extends State with WidgetsBindingObserver var localPath = message.uri; if (localPath.startsWith('http')) { - - final documentsDir = (await getApplicationDocumentsDirectory()).path; - final cacheLocalPath = '$documentsDir/${message.name}'; - - if (!File(cacheLocalPath).existsSync()) { - - final client = http.Client(); - final request = await client.get(Uri.parse(localPath)); - final bytes = request.bodyBytes; - final file = File(cacheLocalPath); - await file.writeAsBytes(bytes); - } - localPath = cacheLocalPath; - + final documentsDir = (await getApplicationDocumentsDirectory()).path; + final cacheLocalPath = '$documentsDir/${message.name}'; + + if (!File(cacheLocalPath).existsSync()) { + final client = http.Client(); + final request = await client.get(Uri.parse(localPath)); + final bytes = request.bodyBytes; + final file = File(cacheLocalPath); + await file.writeAsBytes(bytes); + } + localPath = cacheLocalPath; } widget.onMessageTap?.call(context, message); await widget.openFile?.call(localPath); } - if(message is types.ImageMessage){ + if (message is types.ImageMessage) { final imageProvider = CachedNetworkImageProvider(message.uri); showImageViewer(context, imageProvider); } } - void _handleMessageSent( types.Message message, ) { @@ -557,10 +551,10 @@ class _ChatwootChatState extends State with WidgetsBindingObserver } void _handleMessageUpdated( - types.Message message, - ) { + types.Message message, + ) { final index = _messages.indexWhere((element) => element.id == message.id); - if(index == -1){ + if (index == -1) { return; } WidgetsBinding.instance.addPostFrameCallback((_) { @@ -570,17 +564,13 @@ class _ChatwootChatState extends State with WidgetsBindingObserver }); } - void _handleVideoPreviewLoaded( - VideoMessagePreviewResult result - ) { - + void _handleVideoPreviewLoaded(VideoMessagePreviewResult result) { final index = _messages.indexWhere((element) => element.id == result.jobId); - if(index > -1){ + if (index > -1) { WidgetsBinding.instance.addPostFrameCallback((_) { setState(() { - _messages[index] = _messages[index].copyWith(metadata: { - "preview": result.preview - }); + _messages[index] = + _messages[index].copyWith(metadata: {"preview": result.preview}); }); }); } @@ -601,11 +591,11 @@ class _ChatwootChatState extends State with WidgetsBindingObserver widget.onSendPressed?.call(message); } - void _handleAttachmentPressed() async{ + void _handleAttachmentPressed() async { final attachment = await widget.onAttachmentPressed?.call(); - if(attachment != null){ + if (attachment != null) { types.Message message; - if(lookupMimeType(attachment.name)?.startsWith("image") ?? false){ + if (lookupMimeType(attachment.name)?.startsWith("image") ?? false) { message = types.ImageMessage( author: _user, createdAt: DateTime.now().millisecondsSinceEpoch, @@ -614,8 +604,8 @@ class _ChatwootChatState extends State with WidgetsBindingObserver uri: attachment.path, size: attachment.bytes.length, status: types.Status.sending); - }else if(lookupMimeType(attachment.name)?.startsWith("video") ?? false){ - + } else if (lookupMimeType(attachment.name)?.startsWith("video") ?? + false) { message = types.VideoMessage( author: _user, createdAt: DateTime.now().millisecondsSinceEpoch, @@ -626,7 +616,8 @@ class _ChatwootChatState extends State with WidgetsBindingObserver status: types.Status.sending); videoPreviewLoader.getPreview(jobId: message.id, uri: attachment.path); - }else if(lookupMimeType(attachment.name)?.startsWith("audio") ?? false){ + } else if (lookupMimeType(attachment.name)?.startsWith("audio") ?? + false) { message = types.AudioMessage( author: _user, createdAt: DateTime.now().millisecondsSinceEpoch, @@ -636,7 +627,7 @@ class _ChatwootChatState extends State with WidgetsBindingObserver size: attachment.bytes.length, duration: Duration.zero, status: types.Status.sending); - }else{ + } else { message = types.FileMessage( author: _user, createdAt: DateTime.now().millisecondsSinceEpoch, @@ -649,12 +640,13 @@ class _ChatwootChatState extends State with WidgetsBindingObserver _addMessage(message); - chatwootClient! - .sendMessage(content: attachment.name, echoId: message.id, attachment: [attachment]); + chatwootClient!.sendMessage( + content: attachment.name, + echoId: message.id, + attachment: [attachment]); } } - @override Widget build(BuildContext context) { final horizontalPadding = widget.isPresentedInDialog ? 8.0 : 16.0; @@ -666,7 +658,13 @@ class _ChatwootChatState extends State with WidgetsBindingObserver children: [ //offscreen video player used to fetch first frame of video messages. media_kit screenshot doesn't work without //controller tied to the video widget - IgnorePointer(child:Opacity(opacity: 0,child: Video(controller: controller, fit: BoxFit.contain,))), + IgnorePointer( + child: Opacity( + opacity: 0, + child: Video( + controller: controller, + fit: BoxFit.contain, + ))), //actual chat Column( children: [ @@ -677,18 +675,18 @@ class _ChatwootChatState extends State with WidgetsBindingObserver child: Chat( messages: _messages, onMessageTap: _handleMessageTap, - onPreviewDataFetched: (_,__){}, - onSendPressed: (_){}, + onPreviewDataFetched: (_, __) {}, + onSendPressed: (_) {}, user: _user, onEndReached: widget.onEndReached, onEndReachedThreshold: widget.onEndReachedThreshold, onMessageLongPress: widget.onMessageLongPress, - onAttachmentPressed: (){}, + onAttachmentPressed: () {}, showUserAvatars: widget.showUserAvatars, showUserNames: widget.showUserNames, theme: theme, disableImageGallery: true, - dateHeaderBuilder: (_){ + dateHeaderBuilder: (_) { return SizedBox(); }, customBottomWidget: ChatInput( @@ -696,98 +694,90 @@ class _ChatwootChatState extends State with WidgetsBindingObserver l10n: widget.l10n, onMessageSent: _handleSendPressed, onAttachmentPressed: _handleAttachmentPressed), - textMessageBuilder: (message, {messageWidth=0, showName=true}){ + textMessageBuilder: (message, + {messageWidth = 0, showName = true}) { return TextChatMessage( theme: theme, message: message, isMine: message.author.id == _user.id, maxWidth: messageWidth, - onPreviewFetched: _handlePreviewDataFetched - ); + onPreviewFetched: _handlePreviewDataFetched); }, - videoMessageBuilder: (message, {messageWidth=0}){ + videoMessageBuilder: (message, {messageWidth = 0}) { return VideoChatMessage( theme: theme, message: message, isMine: message.author.id == _user.id, - maxWidth: messageWidth - ); + maxWidth: messageWidth); }, - audioMessageBuilder: (message, {messageWidth=0}){ + audioMessageBuilder: (message, {messageWidth = 0}) { return AudioChatMessage( - theme: theme, - message: message, - isMine: message.author.id == _user.id, + theme: theme, + message: message, + isMine: message.author.id == _user.id, ); }, - avatarBuilder: (user){ + avatarBuilder: (user) { return Row( children: [ ClipRRect( borderRadius: BorderRadius.all(Radius.circular(15)), child: CachedNetworkImage( - imageUrl: user.imageUrl ?? '', - width: 30, - height: 30, - fit:BoxFit.cover, - errorWidget: (_,__, ___){ - String name = "${user.firstName} ${user.lastName}"; - List words = name.trim().split(RegExp(r'\s+')); - String initials = words.map((word) => word[0].toUpperCase()).join(); - return PlaceholderCircle( - text: initials, - textColor: theme.primaryColor, - ); - }, + imageUrl: user.imageUrl ?? '', + width: 30, + height: 30, + fit: BoxFit.cover, + errorWidget: (_, __, ___) { + String name = + "${user.firstName} ${user.lastName}"; + List words = + name.trim().split(RegExp(r'\s+')); + String initials = words + .map((word) => word[0].toUpperCase()) + .join(); + return PlaceholderCircle( + text: initials, + textColor: theme.primaryColor, + ); + }, ), ), - SizedBox(width: 5,) + SizedBox( + width: 5, + ) ], ); }, - customMessageBuilder: (message, {messageWidth=0}){ - if(message.metadata?["feedback"] != null){ + customMessageBuilder: (message, {messageWidth = 0}) { + if (message.metadata?["feedback"] != null) { return RecordedCsatChatMessage( theme: theme, l10n: widget.l10n, - message: message, + message: types.CustomMessage( + author: message.author, id: message.id), maxWidth: messageWidth, ); } return CSATChatMessage( - theme: theme, - l10n: widget.l10n, - message: message, - maxWidth: messageWidth, - sendCsatResults: (rating, feedback){ - chatwootClient?.sendCsatSurveyResults(message.metadata!['conversationUuid'], rating, feedback); - }, + theme: theme, + l10n: widget.l10n, + message: types.CustomMessage( + author: message.author, id: message.id), + maxWidth: messageWidth, + sendCsatResults: (rating, feedback) { + chatwootClient?.sendCsatSurveyResults( + message.metadata!['conversationUuid'], + rating, + feedback); + }, ); }, l10n: widget.l10n, ), ), ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Image.asset( - "assets/logo_grey.png", - package: 'chatwoot_sdk', - width: 15, - height: 15, - ), - Padding( - padding: const EdgeInsets.only(left: 8.0), - child: Text( - "Powered by Chatwoot", - style: TextStyle(color: Colors.black45, fontSize: 12), - ), - ) - ], - ), + const SizedBox( + height: 24, ) ], ), @@ -810,13 +800,11 @@ class _ChatwootChatState extends State with WidgetsBindingObserver videoPreviewLoader.dispose(); LinkMetadata.dispose(); controller.player.dispose(); - _messages.forEach((m){ - if(m is types.VideoMessage){ - final controller= m.metadata?["controller"] as VideoController?; + _messages.forEach((m) { + if (m is types.VideoMessage) { + final controller = m.metadata?["controller"] as VideoController?; controller?.player.dispose(); } }); } } - - diff --git a/lib/ui/chatwoot_chat_theme.dart b/lib/ui/chatwoot_chat_theme.dart index 5086982..6023ea9 100644 --- a/lib/ui/chatwoot_chat_theme.dart +++ b/lib/ui/chatwoot_chat_theme.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_chat_ui/flutter_chat_ui.dart'; @@ -57,13 +56,13 @@ class ChatwootChatTheme extends ChatTheme { ), Color receivedMessageDocumentIconColor = PRIMARY, TextStyle receivedMessageLinkDescriptionTextStyle = const TextStyle( - color: primary, + color: Colors.blue, // primary, fontSize: 14, fontWeight: FontWeight.w400, height: 1.428, ), TextStyle receivedMessageLinkTitleTextStyle = const TextStyle( - color: primary, + color: Colors.blue, // primary, fontSize: 16, fontWeight: FontWeight.w800, height: 1.375, @@ -99,7 +98,7 @@ class ChatwootChatTheme extends ChatTheme { ), List userAvatarNameColors = CHATWOOT_AVATAR_COLORS, TextStyle userAvatarTextStyle = const TextStyle( - color: primary, + color: Colors.blue, // primary, fontSize: 12, fontWeight: FontWeight.w800, height: 1.333, @@ -113,34 +112,29 @@ class ChatwootChatTheme extends ChatTheme { EdgeInsets? attachmentButtonMargin, EdgeInsets dateDividerMargin = const EdgeInsets.all(8), Color inputSurfaceTintColor = Colors.blueAccent, - double inputElevation= 0, + double inputElevation = 0, EdgeInsets inputMargin = const EdgeInsets.all(8), - EdgeInsets inputPadding= const EdgeInsets.all(8), - double messageInsetsHorizontal= 8, - double messageInsetsVertical= 8, - double messageMaxWidth= 500, - TextStyle receivedEmojiMessageTextStyle= const TextStyle(), - EdgeInsets sendButtonMargin= const EdgeInsets.all(8), - TextStyle sentEmojiMessageTextStyle= const TextStyle(), - EdgeInsets statusIconPadding= const EdgeInsets.all(8), - SystemMessageTheme systemMessageTheme= const SystemMessageTheme( - margin: const EdgeInsets.all(8), - textStyle: const TextStyle() - ), - TypingIndicatorTheme typingIndicatorTheme= const TypingIndicatorTheme( + EdgeInsets inputPadding = const EdgeInsets.all(8), + double messageInsetsHorizontal = 8, + double messageInsetsVertical = 8, + double messageMaxWidth = 500, + TextStyle receivedEmojiMessageTextStyle = const TextStyle(), + EdgeInsets sendButtonMargin = const EdgeInsets.all(8), + TextStyle sentEmojiMessageTextStyle = const TextStyle(), + EdgeInsets statusIconPadding = const EdgeInsets.all(8), + SystemMessageTheme systemMessageTheme = const SystemMessageTheme( + margin: const EdgeInsets.all(8), textStyle: const TextStyle()), + TypingIndicatorTheme typingIndicatorTheme = const TypingIndicatorTheme( animatedCirclesColor: primary, animatedCircleSize: 8, bubbleBorder: const BorderRadius.all(const Radius.circular(8)), bubbleColor: Colors.white, countAvatarColor: primary, countTextColor: Colors.black87, - multipleUserTextStyle: const TextStyle() - ), - UnreadHeaderTheme unreadHeaderTheme= const UnreadHeaderTheme( - color: CHATWOOT_COLOR_PRIMARY, - textStyle: const TextStyle() - ), - Color userAvatarImageBackgroundColor= Colors.white, + multipleUserTextStyle: const TextStyle()), + UnreadHeaderTheme unreadHeaderTheme = const UnreadHeaderTheme( + color: CHATWOOT_COLOR_PRIMARY, textStyle: const TextStyle()), + Color userAvatarImageBackgroundColor = Colors.white, }) : super( attachmentButtonIcon: attachmentButtonIcon, backgroundColor: backgroundColor, diff --git a/lib/ui/video_preview.dart b/lib/ui/video_preview.dart index 821e5eb..5a9e492 100644 --- a/lib/ui/video_preview.dart +++ b/lib/ui/video_preview.dart @@ -1,5 +1,3 @@ - - import 'dart:async'; import 'dart:collection'; import 'dart:typed_data'; @@ -8,28 +6,24 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:media_kit/media_kit.dart'; import 'package:media_kit_video/media_kit_video.dart'; -class VideoMessagePreview{ +class VideoMessagePreview { final ui.FrameInfo? firstFrame; final double width; final double height; - - VideoMessagePreview({this.firstFrame, this.width=0, this.height=0}); - - + VideoMessagePreview({this.firstFrame, this.width = 0, this.height = 0}); } -class VideoMessagePreviewJob{ - final String jobId ; +class VideoMessagePreviewJob { + final String jobId; final String uri; VideoMessagePreviewJob({required this.jobId, required this.uri}); - } -class VideoMessagePreviewResult{ +class VideoMessagePreviewResult { ///Fetch preview job identification - final String jobId ; + final String jobId; ///Video uri final String uri; @@ -37,12 +31,11 @@ class VideoMessagePreviewResult{ ///Preview results final VideoMessagePreview preview; - VideoMessagePreviewResult({required this.jobId, required this.uri, required this.preview}); - + VideoMessagePreviewResult( + {required this.jobId, required this.uri, required this.preview}); } - -class VideoPreviewLoader{ +class VideoPreviewLoader { ///Video controller for loading first video frame. See [ChatwootChat.build] VideoController controller; @@ -50,53 +43,54 @@ class VideoPreviewLoader{ final _previewJobQueue = Queue(); ///Stream to send preview results - final _previewResponseStreamController = StreamController(); + final _previewResponseStreamController = + StreamController(); StreamSubscription? _previewResponseStreamSubscription; VideoPreviewLoader({required this.controller}); - void getPreview({required String jobId, required String uri})async{ - if(_previewJobQueue.isEmpty){ + void getPreview({required String jobId, required String uri}) async { + if (_previewJobQueue.isEmpty) { //no video previews are being fetched _previewJobQueue.add(VideoMessagePreviewJob(jobId: jobId, uri: uri)); _fromUri(jobId, uri); - }else{ + } else { //ongoing video preview fetch. add to queue _previewJobQueue.add(VideoMessagePreviewJob(jobId: jobId, uri: uri)); } } - - Future _getCacheVideoPreview(String jobId, String uri) async{ - final fileInfo = await CachedNetworkImageProvider.defaultCacheManager.getFileFromCache(uri); - if(fileInfo != null){ + Future _getCacheVideoPreview( + String jobId, String uri) async { + final fileInfo = await CachedNetworkImageProvider(uri) + .cacheManager! + .getFileFromCache(uri); + if (fileInfo != null) { // return cached preview - final codec = await ui.instantiateImageCodec(await fileInfo.file.readAsBytes()); + final codec = + await ui.instantiateImageCodec(await fileInfo.file.readAsBytes()); final frame = await codec.getNextFrame(); final firstframe = frame; final width = frame.image.width.toDouble(); final height = frame.image.height.toDouble(); final cachedPreview = VideoMessagePreview( - firstFrame: firstframe, - width: width, - height: height - ); + firstFrame: firstframe, width: width, height: height); return cachedPreview; } return null; } ///Loads first frame from video url - Future _fromUri(String jobId, String uri) async{ - + Future _fromUri(String jobId, String uri) async { //check for cached preview final cachedPreview = await _getCacheVideoPreview(jobId, uri); - if(cachedPreview != null){ + if (cachedPreview != null) { // return cached preview - _previewResponseStreamController.add(VideoMessagePreviewResult(jobId: jobId, uri: uri, preview: cachedPreview)); + _previewResponseStreamController.add(VideoMessagePreviewResult( + jobId: jobId, uri: uri, preview: cachedPreview)); //check for pending jobs and execute - if(_previewJobQueue.isNotEmpty){ + if (_previewJobQueue.isNotEmpty) { final job = _previewJobQueue.removeFirst(); _fromUri(job.jobId, job.uri); } @@ -112,10 +106,10 @@ class VideoPreviewLoader{ await controller.player.setVolume(0); await controller.player.open(Media(uri)); await Future.delayed(Duration(seconds: 10)); - await controller.player.stream.position.firstWhere((d)=>d>Duration.zero); + await controller.player.stream.position + .firstWhere((d) => d > Duration.zero); Uint8List? frameBytes = await controller.player.screenshot(); - if (frameBytes != null) { // Convert the Uint8List to a ui.Image final codec = await ui.instantiateImageCodec(frameBytes); @@ -123,9 +117,10 @@ class VideoPreviewLoader{ firstframe = frame; width = frame.image.width.toDouble(); height = frame.image.height.toDouble(); - await CachedNetworkImageProvider.defaultCacheManager.putFile(uri, frameBytes); + await CachedNetworkImageProvider(uri) + .cacheManager! + .putFile(uri, frameBytes); } - } catch (e) { print('Error capturing first frame: $e'); } finally { @@ -134,29 +129,25 @@ class VideoPreviewLoader{ } final p = VideoMessagePreview( - firstFrame: firstframe, - width: width, - height: height - ); - + firstFrame: firstframe, width: width, height: height); //send preview result - _previewResponseStreamController.add( - VideoMessagePreviewResult(jobId: jobId, uri: uri, preview: p) - ); + _previewResponseStreamController + .add(VideoMessagePreviewResult(jobId: jobId, uri: uri, preview: p)); //check for pending jobs and execute - if(_previewJobQueue.isNotEmpty){ + if (_previewJobQueue.isNotEmpty) { final job = _previewJobQueue.removeFirst(); _fromUri(job.jobId, job.uri); } } - listen(void Function(VideoMessagePreviewResult) callback){ - _previewResponseStreamSubscription = _previewResponseStreamController.stream.listen(callback); + listen(void Function(VideoMessagePreviewResult) callback) { + _previewResponseStreamSubscription = + _previewResponseStreamController.stream.listen(callback); } - dispose(){ + dispose() { _previewResponseStreamSubscription?.cancel(); } } diff --git a/pubspec.lock b/pubspec.lock index c213b36..bb4ca32 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -122,26 +122,26 @@ packages: dependency: "direct main" description: name: cached_network_image - sha256: "4a5d8d2c728b0f3d0245f69f921d7be90cae4c2fd5288f773088672c0893f819" + sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f" url: "https://pub.dev" source: hosted - version: "3.4.0" + version: "3.3.1" cached_network_image_platform_interface: dependency: transitive description: name: cached_network_image_platform_interface - sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829" + sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f" url: "https://pub.dev" source: hosted - version: "4.1.1" + version: "4.0.0" cached_network_image_web: dependency: transitive description: name: cached_network_image_web - sha256: "6322dde7a5ad92202e64df659241104a43db20ed594c41ca18de1014598d7996" + sha256: "205d6a9f1862de34b93184f22b9d2d94586b2f05c581d546695e3d8f6a805cd7" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.2.0" characters: dependency: transitive description: @@ -191,7 +191,7 @@ packages: source: hosted version: "3.1.2" crypto: - dependency: "direct main" + dependency: transitive description: name: crypto sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" @@ -234,18 +234,10 @@ packages: dependency: "direct main" description: name: dio - sha256: "5598aa796bbf4699afd5c67c0f5f6e2ed542afc956884b9cd58c306966efc260" - url: "https://pub.dev" - source: hosted - version: "5.7.0" - dio_web_adapter: - dependency: transitive - description: - name: dio_web_adapter - sha256: "33259a9276d6cea88774a0000cfae0d861003497755969c92faa223108620dc8" + sha256: "7d328c4d898a61efc3cd93655a0955858e29a0aa647f0f9e02d59b3bb275e2e8" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "4.0.6" easy_image_viewer: dependency: "direct main" description: @@ -303,10 +295,10 @@ packages: dependency: transitive description: name: flutter_cache_manager - sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" + sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba" url: "https://pub.dev" source: hosted - version: "3.4.1" + version: "3.3.1" flutter_chat_types: dependency: "direct main" description: @@ -343,18 +335,18 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "2.0.3" flutter_markdown: dependency: "direct main" description: name: flutter_markdown - sha256: "999a4e3cb3e1532a971c86d6c73a480264f6a687959d4887cb4e2990821827e4" + sha256: "255b00afa1a7bad19727da6a7780cf3db6c3c12e68d302d85e0ff1fdf173db9e" url: "https://pub.dev" source: hosted - version: "0.7.4+2" + version: "0.7.4+3" flutter_parsed_text: dependency: transitive description: @@ -367,10 +359,10 @@ packages: dependency: "direct main" description: name: flutter_secure_storage - sha256: "165164745e6afb5c0e3e3fcc72a012fb9e58496fb26ffb92cf22e16a821e85d0" + sha256: "22dbf16f23a4bcf9d35e51be1c84ad5bb6f627750565edd70dab70f3ff5fff8f" url: "https://pub.dev" source: hosted - version: "9.2.2" + version: "8.1.0" flutter_secure_storage_linux: dependency: transitive description: @@ -407,10 +399,10 @@ packages: dependency: transitive description: name: flutter_secure_storage_windows - sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + sha256: "38f9501c7cb6f38961ef0e1eacacee2b2d4715c63cc83fe56449c4d3d0b47255" url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "2.1.1" flutter_test: dependency: "direct dev" description: flutter @@ -421,6 +413,14 @@ packages: description: flutter source: sdk version: "0.0.0" + freezed_annotation: + dependency: transitive + description: + name: freezed_annotation + sha256: "70776c4541e5cacfe45bcaf00fe79137b8c61aa34fb5765a05ce6c57fd72c6e9" + url: "https://pub.dev" + source: hosted + version: "0.14.3" frontend_server_client: dependency: transitive description: @@ -481,10 +481,10 @@ packages: dependency: transitive description: name: http - sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 + sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "0.13.6" http_multi_server: dependency: transitive description: @@ -513,10 +513,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "99f282cb0e02edcbbf8c6b3bbc7c90b65635156c412e58f3975a7e55284ce685" + sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" url: "https://pub.dev" source: hosted - version: "0.20.0" + version: "0.17.0" io: dependency: transitive description: @@ -585,10 +585,10 @@ packages: dependency: transitive description: name: lints - sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "2.1.1" logging: dependency: transitive description: @@ -710,7 +710,7 @@ packages: source: hosted version: "1.15.0" mime: - dependency: "direct main" + dependency: transitive description: name: mime sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" @@ -873,18 +873,18 @@ packages: dependency: "direct main" description: name: riverpod - sha256: "59062512288d3056b2321804332a13ffdd1bf16df70dcc8e506e411280a72959" + sha256: "13cbe0e17b659f38027986df967b3eaf7f42c519786352167fc3db1be44eae07" url: "https://pub.dev" source: hosted - version: "2.6.1" + version: "0.14.0+3" rxdart: dependency: transitive description: name: rxdart - sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" url: "https://pub.dev" source: hosted - version: "0.28.0" + version: "0.27.7" safe_local_storage: dependency: transitive description: @@ -994,14 +994,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" - sprintf: - dependency: transitive - description: - name: sprintf - sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" - url: "https://pub.dev" - source: hosted - version: "7.0.0" sqflite: dependency: transitive description: @@ -1054,10 +1046,10 @@ packages: dependency: transitive description: name: state_notifier - sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb + sha256: "8fe42610f179b843b12371e40db58c9444f8757f8b69d181c97e50787caed289" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "0.7.2+1" stream_channel: dependency: "direct main" description: @@ -1206,10 +1198,10 @@ packages: dependency: "direct main" description: name: uuid - sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" url: "https://pub.dev" source: hosted - version: "4.5.1" + version: "3.0.7" vector_math: dependency: transitive description: @@ -1274,22 +1266,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.1" - web_socket: - dependency: transitive - description: - name: web_socket - sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" - url: "https://pub.dev" - source: hosted - version: "0.1.6" web_socket_channel: dependency: "direct main" description: name: web_socket_channel - sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" + sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "2.4.5" webview_flutter: dependency: "direct main" description: @@ -1326,10 +1310,10 @@ packages: dependency: transitive description: name: win32 - sha256: "84ba388638ed7a8cb3445a320c8273136ab2631cd5f2c57888335504ddab1bc2" + sha256: "8b338d4486ab3fbc0ba0db9f9b4f5239b6697fcee427939a40e720cbb9ee0a69" url: "https://pub.dev" source: hosted - version: "5.8.0" + version: "5.9.0" xdg_directories: dependency: transitive description: @@ -1355,5 +1339,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.5.3 <4.0.0" + dart: ">=3.5.0 <4.0.0" flutter: ">=3.24.0" diff --git a/pubspec.yaml b/pubspec.yaml index ec204ea..8b6696f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,10 +1,11 @@ name: chatwoot_sdk description: A flutter client sdk for chatwoot. Integrate Chatwoot flutter client into your flutter app and talk to your visitors/users in real time. -version: 0.1.0 +version: 0.0.9 homepage: https://github.com/chatwoot/chatwoot-flutter-sdk environment: - sdk: "^3.5.3" + sdk: ">=3.0.0 <4.0.0" + flutter: ">=1.17.0" dependencies: flutter: @@ -12,29 +13,27 @@ dependencies: flutter_chat_ui: ^1.1.9 hive: ^2.2.3 hive_flutter: ^1.1.0 - uuid: ^4.5.1 - web_socket_channel: ^3.0.1 + uuid: ^3.0.4 + web_socket_channel: ^2.1.0 json_annotation: ^4.0.1 - dio: ^5.7.0 + dio: ^4.0.0 equatable: ^2.0.3 - riverpod: ^2.6.1 + riverpod: ^0.14.0+3 synchronized: ^3.0.0 - intl: ^0.20.0 + intl: ^0.17.0 flutter_chat_types: ^3.6.2 stream_channel: ^2.1.0 async: ^2.5.0 - flutter_secure_storage: ^9.2.2 - url_launcher: ^6.3.1 + webview_flutter: ^4.0.6 + flutter_secure_storage: ^8.0.0 + url_launcher: ^6.1.10 path_provider: ^2.0.13 - mime: ^2.0.0 - media_kit: ^1.1.11 - media_kit_video: ^1.2.5 - media_kit_libs_video: ^1.0.5 + cached_network_image: ^3.3.1 + media_kit: ^1.1.11 # Primary package. + media_kit_video: ^1.2.5 # For video rendering. + media_kit_libs_video: ^1.0.5 # Native video dependencies. easy_image_viewer: ^1.5.1 - cached_network_image: ^3.4.0 - flutter_markdown: ^0.7.4+2 - webview_flutter: ^4.10.0 - crypto: ^3.0.6 + flutter_markdown: ^0.7.4+3 dev_dependencies: flutter_test: @@ -43,7 +42,7 @@ dev_dependencies: json_serializable: ^6.6.1 hive_generator: ^2.0.0 build_runner: ^2.3.3 - flutter_lints: ^5.0.0 + flutter_lints: ^2.0.1 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/test/data/remote/chatwoot_client_auth_service_test.dart b/test/data/remote/chatwoot_client_auth_service_test.dart index d16b935..b747316 100644 --- a/test/data/remote/chatwoot_client_auth_service_test.dart +++ b/test/data/remote/chatwoot_client_auth_service_test.dart @@ -86,7 +86,7 @@ void main() { 'Given contact creation fails when createNewContact is called, then throw error', () async { //GIVEN - final testError = DioException(requestOptions: RequestOptions(path: "")); + final testError = DioError(requestOptions: RequestOptions(path: "")); when(mockDio.post(any, data: testUser.toJson())).thenThrow(testError); //WHEN @@ -146,7 +146,7 @@ void main() { 'Given conversation creation fails when createNewConversation is called, then throw error', () async { //GIVEN - final testError = DioException(requestOptions: RequestOptions(path: "")); + final testError = DioError(requestOptions: RequestOptions(path: "")); when(mockDio.post(any)).thenThrow(testError); //WHEN diff --git a/test/data/remote/chatwoot_client_service_test.dart b/test/data/remote/chatwoot_client_service_test.dart index e8d90bc..2e080c9 100644 --- a/test/data/remote/chatwoot_client_service_test.dart +++ b/test/data/remote/chatwoot_client_service_test.dart @@ -27,7 +27,8 @@ void main() { final unauthenticatedmockDio = MockDio(); setUpAll(() { - clientService = ChatwootClientServiceImpl(testBaseUrl, dio: mockDio, uDio: unauthenticatedmockDio); + clientService = ChatwootClientServiceImpl(testBaseUrl, + dio: mockDio, uDio: unauthenticatedmockDio); }); _createSuccessResponse(body) { @@ -52,16 +53,16 @@ void main() { await TestResourceUtil.readJsonResource(fileName: "message"); final request = ChatwootNewMessageRequest(content: "test message", echoId: "id"); - when(mockDio.post(any, data: anyNamed("data"))).thenAnswer( - (invocation){ - assert(invocation.namedArguments[Symbol("data")] is FormData); - final form = invocation.namedArguments[Symbol("data")] as FormData; - final messageContent = form.fields.firstWhere((f)=>f.key == "content").value; - final echoId = form.fields.firstWhere((f)=>f.key == "echo_id").value; - expect(messageContent, request.content); - expect(echoId, request.echoId); - return Future.value(_createSuccessResponse(responseBody)); - }); + when(mockDio.post(any, data: anyNamed("data"))).thenAnswer((invocation) { + assert(invocation.namedArguments[Symbol("data")] is FormData); + final form = invocation.namedArguments[Symbol("data")] as FormData; + final messageContent = + form.fields.firstWhere((f) => f.key == "content").value; + final echoId = form.fields.firstWhere((f) => f.key == "echo_id").value; + expect(messageContent, request.content); + expect(echoId, request.echoId); + return Future.value(_createSuccessResponse(responseBody)); + }); //WHEN final result = await clientService.createMessage(request); @@ -97,7 +98,7 @@ void main() { 'Given sending message fails when createMessage is called, then throw error', () async { //GIVEN - final testError = DioException(requestOptions: RequestOptions(path: "")); + final testError = DioError(requestOptions: RequestOptions(path: "")); final request = ChatwootNewMessageRequest(content: "test message", echoId: "id"); when(mockDio.post(any, data: anyNamed("data"))).thenThrow(testError); @@ -159,7 +160,7 @@ void main() { 'Given fetch messages fails when getAllMessages is called, then throw error', () async { //GIVEN - final testError = DioException(requestOptions: RequestOptions(path: "")); + final testError = DioError(requestOptions: RequestOptions(path: "")); when(mockDio.get(any)).thenThrow(testError); //WHEN @@ -217,7 +218,7 @@ void main() { 'Given fetch contact fails when getContact is called, then throw error', () async { //GIVEN - final testError = DioException(requestOptions: RequestOptions(path: "")); + final testError = DioError(requestOptions: RequestOptions(path: "")); when(mockDio.get(any)).thenThrow(testError); //WHEN @@ -277,7 +278,7 @@ void main() { 'Given fetch conversations fails when getConversations is called, then throw error', () async { //GIVEN - final testError = DioException(requestOptions: RequestOptions(path: "")); + final testError = DioError(requestOptions: RequestOptions(path: "")); when(mockDio.get(any)).thenThrow(testError); //WHEN @@ -338,7 +339,7 @@ void main() { () async { //GIVEN final update = {"name": "Updated name"}; - final testError = DioException(requestOptions: RequestOptions(path: "")); + final testError = DioError(requestOptions: RequestOptions(path: "")); when(mockDio.patch(any, data: update)).thenThrow(testError); //WHEN @@ -357,156 +358,159 @@ void main() { test( 'Given message is successfully updated when updateMessage is called, then return updated message', - () async { - //GIVEN - final responseBody = + () async { + //GIVEN + final responseBody = await TestResourceUtil.readJsonResource(fileName: "message"); - final testMessageId = "id"; - final update = {"content": "Updated content"}; - when(mockDio.patch(any, data: update)).thenAnswer( - (_) => Future.value(_createSuccessResponse(responseBody))); + final testMessageId = "id"; + final update = {"content": "Updated content"}; + when(mockDio.patch(any, data: update)).thenAnswer( + (_) => Future.value(_createSuccessResponse(responseBody))); - //WHEN - final result = await clientService.updateMessage(testMessageId, update); + //WHEN + final result = await clientService.updateMessage(testMessageId, update); - //THEN - expect(result, ChatwootMessage.fromJson(responseBody)); - }); + //THEN + expect(result, ChatwootMessage.fromJson(responseBody)); + }); test( 'Given message update returns with error response when updateMessage is called, then throw error', - () async { - //GIVEN - final testMessageId = "id"; - final update = {"content": "Updated content"}; - when(mockDio.patch(any, data: update)).thenAnswer( - (_) => Future.value(_createErrorResponse(statusCode: 401, body: {}))); - - //WHEN - ChatwootClientException? chatwootClientException; - try { - await clientService.updateMessage(testMessageId, update); - } on ChatwootClientException catch (e) { - chatwootClientException = e; - } - - //THEN - verify(mockDio.patch(argThat(contains(testMessageId)), data: update)); - expect(chatwootClientException, isNotNull); - expect(chatwootClientException!.type, - equals(ChatwootClientExceptionType.UPDATE_MESSAGE_FAILED)); - }); + () async { + //GIVEN + final testMessageId = "id"; + final update = {"content": "Updated content"}; + when(mockDio.patch(any, data: update)).thenAnswer( + (_) => Future.value(_createErrorResponse(statusCode: 401, body: {}))); + + //WHEN + ChatwootClientException? chatwootClientException; + try { + await clientService.updateMessage(testMessageId, update); + } on ChatwootClientException catch (e) { + chatwootClientException = e; + } + + //THEN + verify(mockDio.patch(argThat(contains(testMessageId)), data: update)); + expect(chatwootClientException, isNotNull); + expect(chatwootClientException!.type, + equals(ChatwootClientExceptionType.UPDATE_MESSAGE_FAILED)); + }); test( 'Given message update fails when updateMessage is called, then throw error', - () async { - //GIVEN - final testMessageId = "id"; - final update = {"content": "Updated content"}; - final testError = DioException(requestOptions: RequestOptions(path: "")); - when(mockDio.patch(any, data: update)).thenThrow(testError); - - //WHEN - ChatwootClientException? chatwootClientException; - try { - await clientService.updateMessage(testMessageId, update); - } on ChatwootClientException catch (e) { - chatwootClientException = e; - } - - //THEN - verify(mockDio.patch(argThat(contains(testMessageId)), data: update)); - expect(chatwootClientException, isNotNull); - expect(chatwootClientException!.type, - equals(ChatwootClientExceptionType.UPDATE_MESSAGE_FAILED)); - }); + () async { + //GIVEN + final testMessageId = "id"; + final update = {"content": "Updated content"}; + final testError = DioError(requestOptions: RequestOptions(path: "")); + when(mockDio.patch(any, data: update)).thenThrow(testError); + + //WHEN + ChatwootClientException? chatwootClientException; + try { + await clientService.updateMessage(testMessageId, update); + } on ChatwootClientException catch (e) { + chatwootClientException = e; + } + + //THEN + verify(mockDio.patch(argThat(contains(testMessageId)), data: update)); + expect(chatwootClientException, isNotNull); + expect(chatwootClientException!.type, + equals(ChatwootClientExceptionType.UPDATE_MESSAGE_FAILED)); + }); test( 'Given csat survey is successfully sent when sendCsatFeedBack is called, then return feedback response', - () async { - //GIVEN - final responseBody = + () async { + //GIVEN + final responseBody = await TestResourceUtil.readJsonResource(fileName: "csat_feedback"); - final testConversationUuid = "conversation-uuid"; - final feedbackRequest = SendCsatSurveyRequest(rating: 1, feedbackMessage: "test message"); - final requestBody = { - "message":{ - "submitted_values":{ - "csat_survey_response": feedbackRequest.toJson() - } - } - }; - when(unauthenticatedmockDio.put(any, data: requestBody)).thenAnswer( - (_) => Future.value(_createSuccessResponse(responseBody))); - - //WHEN - final result = await clientService.sendCsatFeedBack(testConversationUuid, feedbackRequest); - - //THEN - expect(result, CsatSurveyFeedbackResponse.fromJson(responseBody)); - }); + final testConversationUuid = "conversation-uuid"; + final feedbackRequest = + SendCsatSurveyRequest(rating: 1, feedbackMessage: "test message"); + final requestBody = { + "message": { + "submitted_values": {"csat_survey_response": feedbackRequest.toJson()} + } + }; + when(unauthenticatedmockDio.put(any, data: requestBody)).thenAnswer( + (_) => Future.value(_createSuccessResponse(responseBody))); + + //WHEN + final result = await clientService.sendCsatFeedBack( + testConversationUuid, feedbackRequest); + + //THEN + expect(result, CsatSurveyFeedbackResponse.fromJson(responseBody)); + }); test( 'Given send csat survey returns with error response when sendCsatFeedBack is called, then throw error', - () async { - //GIVEN - final testConversationUuid = "conversation-uuid"; - final feedbackRequest = SendCsatSurveyRequest(rating: 1, feedbackMessage: "test message"); - final requestBody = { - "message":{ - "submitted_values":{ - "csat_survey_response": feedbackRequest.toJson() - } - } - }; - when(unauthenticatedmockDio.put(any, data: requestBody)).thenAnswer( - (_) => Future.value(_createErrorResponse(statusCode: 500, body: {}))); - - //WHEN - ChatwootClientException? chatwootClientException; - try { - await clientService.sendCsatFeedBack(testConversationUuid, feedbackRequest); - } on ChatwootClientException catch (e) { - chatwootClientException = e; - } - - //THEN - verify(unauthenticatedmockDio.put(argThat(contains(testConversationUuid)), data: requestBody)); - expect(chatwootClientException, isNotNull); - expect(chatwootClientException!.type, - equals(ChatwootClientExceptionType.SEND_CSAT_FEEDBACK)); - }); + () async { + //GIVEN + final testConversationUuid = "conversation-uuid"; + final feedbackRequest = + SendCsatSurveyRequest(rating: 1, feedbackMessage: "test message"); + final requestBody = { + "message": { + "submitted_values": {"csat_survey_response": feedbackRequest.toJson()} + } + }; + when(unauthenticatedmockDio.put(any, data: requestBody)).thenAnswer( + (_) => Future.value(_createErrorResponse(statusCode: 500, body: {}))); + + //WHEN + ChatwootClientException? chatwootClientException; + try { + await clientService.sendCsatFeedBack( + testConversationUuid, feedbackRequest); + } on ChatwootClientException catch (e) { + chatwootClientException = e; + } + + //THEN + verify(unauthenticatedmockDio.put(argThat(contains(testConversationUuid)), + data: requestBody)); + expect(chatwootClientException, isNotNull); + expect(chatwootClientException!.type, + equals(ChatwootClientExceptionType.SEND_CSAT_FEEDBACK)); + }); test( 'Given send csat feedback fails when sendCsatFeedBack is called, then throw error', - () async { - //GIVEN - final testConversationUuid = "conversation-uuid"; - final feedbackRequest = SendCsatSurveyRequest(rating: 1, feedbackMessage: "test message"); - final testError = DioException(requestOptions: RequestOptions(path: "")); - final requestBody = { - "message":{ - "submitted_values":{ - "csat_survey_response": feedbackRequest.toJson() - } - } - }; - when(unauthenticatedmockDio.put(any, data: requestBody)).thenThrow(testError); - - //WHEN - ChatwootClientException? chatwootClientException; - try { - await clientService.sendCsatFeedBack(testConversationUuid, feedbackRequest); - } on ChatwootClientException catch (e) { - chatwootClientException = e; - } - - //THEN - verify(unauthenticatedmockDio.put(argThat(contains(testConversationUuid)), data: requestBody)); - expect(chatwootClientException, isNotNull); - expect(chatwootClientException!.type, - equals(ChatwootClientExceptionType.SEND_CSAT_FEEDBACK)); - }); + () async { + //GIVEN + final testConversationUuid = "conversation-uuid"; + final feedbackRequest = + SendCsatSurveyRequest(rating: 1, feedbackMessage: "test message"); + final testError = DioError(requestOptions: RequestOptions(path: "")); + final requestBody = { + "message": { + "submitted_values": {"csat_survey_response": feedbackRequest.toJson()} + } + }; + when(unauthenticatedmockDio.put(any, data: requestBody)) + .thenThrow(testError); + + //WHEN + ChatwootClientException? chatwootClientException; + try { + await clientService.sendCsatFeedBack( + testConversationUuid, feedbackRequest); + } on ChatwootClientException catch (e) { + chatwootClientException = e; + } + + //THEN + verify(unauthenticatedmockDio.put(argThat(contains(testConversationUuid)), + data: requestBody)); + expect(chatwootClientException, isNotNull); + expect(chatwootClientException!.type, + equals(ChatwootClientExceptionType.SEND_CSAT_FEEDBACK)); + }); test( 'Given websocket connection is successful when startWebSocketConnection is called, then subscribe for events',