├── .gitignore ├── CHANGELOG.md ├── README.md ├── RNKommunicateChat.podspec ├── SECURITY.md ├── android ├── build.gradle ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── io │ └── kommunicate │ └── app │ ├── KmEventListener.java │ ├── RNKommunicateChatModule.java │ └── RNKommunicateChatPackage.java ├── index.js ├── ios ├── Bridge.h ├── KMEventEmitter.swift ├── RNKommunicateChat.m ├── RNKommunicateChat.swift ├── RNKommunicateChat.xcodeproj │ └── project.pbxproj └── RNKommunicateChat.xcworkspace │ └── contents.xcworkspacedata └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Android/IntelliJ 2 | package-lock.json 3 | build/ 4 | .idea 5 | local.properties 6 | android/.gradle/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # React Native Kommunicate Chat v2.5.1 2 | - [Android] Renamed the applozic references 3 | # React Native Kommunicate Chat v2.5.0 4 | - [iOS] Added Business Hours feature. 5 | - [iOS] Disabled conversation restart when the restart button is hidden. 6 | - [iOS] Enabled form data persistence in local cache. 7 | - [iOS] Fixed character limit bug in Dialogflow bot. 8 | - [iOS] Resolved bug in Text Area component. 9 | - [Android] Persisted Data in Form Rich Message 10 | - [Android] Added download file listner 11 | - [Android] ReDirected URL from Help center to Browser. 12 | - [Android] Fixed dialogue flow crash 13 | - [Android] Removed restarted coversation from form button 14 | # React Native Kommunicate Chat v2.4.3 15 | - Fixed Recording button not working properly in iOS. 16 | # React Native Kommunicate Chat v2.4.2 17 | - Internal Code Refactor for IOS. 18 | - Typing indicator For bot message delay for Android and IOS. 19 | - Updated TableView Update function for IOS. 20 | - Updated depricated Code and resolved Warnings for IOS. 21 | - Migration to Coroutines and removed Depricated Async Tasks for Android. 22 | - Remove Raw query querries usage for Android. 23 | - Added edge to edge support for Android 15 and Above. 24 | # React Native Kommunicate Chat v2.4.1 25 | - Added waiting queue in IOS 26 | - Fixed bugs in Android 27 | # React Native Kommunicate Chat v2.4.0 28 | - Disable SSL Pinning in Android and IOS by default. 29 | - Improved the CSAT 3 Star and 5 Star Ratings in Android 30 | # React Native Kommunicate Chat v2.3.11 31 | - Added the SQL Cipher in android side. (with gradle 7.4.0, wrapper 7.5.1) 32 | # React Native Kommunicate Chat v2.3.10 33 | - Added the SQL Cipher in android side. (with gradle 7.4.0) 34 | # React Native Kommunicate Chat v2.3.9 35 | - Added the SQL Cipher in android side. (with gradle 8.7.3) 36 | # React Native Kommunicate Chat v2.3.8 37 | - Added the functionality to send message using code. 38 | # React Native Kommunicate Chat v2.3.7 39 | - Fixed message metadata bug (Android) 40 | # React Native Kommunicate Chat v2.3.6 41 | - Fixed camera and video attachment visiblity from applogics json (Android) 42 | - Remove font-awesome font (Android) 43 | # React Native Kommunicate Chat v2.3.5 44 | - Added 5 Star CSAT Rating UI. (Android - iOS) 45 | - Fixed Minor Bugs. (Android - iOS) 46 | - Updated the Code of iOS to Support Xcode version 15.4+. - iOS 47 | - Added Customisation to Hide Chat Bar when bot is handeling the Conversation. (Android - iOS) 48 | # React Native Kommunicate Chat v2.3.4 49 | - Fixed Build issue due to latest release of iOS Module 50 | # React Native Kommunicate Chat v2.3.3 51 | - Fixed assignee profile image on conversation screen - iOS 52 | - Fixed Assigne name is cropping issue on covnersation screen - iOS 53 | - Improved read receipt label for messages. - iOS 54 | - Improved the Flow of Showing Rating Bar. - iOS 55 | - Fixed Link preview issue in iOS 56 | - Added the last Message icon in Conversation List Screen in iOS 57 | - Added support for iframe in HTML type rich message - Android 58 | - Fixed location sharing issues - Android 59 | - Image full screen view improvement - Android 60 | - Added back button in in-app browser - Android 61 | 62 | # React Native Kommunicate Chat v2.3.2 63 | - Upgraded iOS SDK to 7.1.3 64 | 65 | # React Native Kommunicate Chat v2.3.1 66 | - Exposed a function to fetch unreadcount 67 | - Fixed `openparticularconversation` bug when single threaded is enabled 68 | - Exposed a function to fetch channel info, conversation assignee info 69 | 70 | # React Native Kommunicate Chat v2.3.0 71 | - Fixed a crash that was happening by message template customisation - Android 72 | - Added Support For Auto Suggestions Rich Message - iOS 73 | - Added pseudonym support - iOS 74 | - Added support for custom input field rich message - iOS 75 | - Added support for XCode 15 - iOS 76 | 77 | # React Native Kommunicate Chat v2.2.9 78 | - Fixed android build issue 79 | 80 | # React Native Kommunicate Chat v2.2.8 81 | - Upgraded minimum SDK version to iOS 13.0 - iOS 82 | - Passed kmUserLocale in groupMetadata and messageMetaData - iOS 83 | - Added support for Sending GIF from device - iOS 84 | - Exposed a customisation function for a rating menu icon on conversation screen. - Android & iOS 85 | 86 | # React Native Kommunicate Chat v2.2.7 87 | - UI/UX improvements and fixed tool bar issue in android 88 | - Fixed Rating bar & keyboard overlap issue 89 | # React Native Kommunicate Chat v2.2.6 90 | - Updated Refresh icon on conversation screen - iOS 91 | - Improved the UI of Multiple language selection Page - iOS 92 | 93 | # React Native Kommunicate Chat v2.2.5 94 | - Fixed down arrow coming in bottom of the screen when welcome message get rendered issue. 95 | - Form Submit button width is corrected. 96 | - Added border to the form and removed paddding form the top of each cell. 97 | - Fixed UI/UX mismatches across platforms 98 | 99 | ## React Native Kommunicate Chat v2.2.4 100 | - Added support to send message on language change 101 | - Show/Hide Template message buttons when resolved conversation 102 | - Fixed refresh icon squeeze and crashes - Android 103 | - Fixed the form submission with empty fields issue 104 | 105 | ## React Native Kommunicate Chat v2.2.3 106 | - Fixed visibility of startNewConversation button 107 | - Added support for API Suggestions 108 | - Added "showTypingIndicatorWhileFetchingResponse" settings to show typing indicator when bot fetching response 109 | - Crashes fixed 110 | 111 | ## React Native Kommunicate Chat v2.2.1 112 | - UI/UX changes to match Android and iOS 113 | - Added support for Custom Input Field - Android 114 | - Bug fixes and optimizations 115 | 116 | ## React Native Kommunicate Chat v2.2.0 117 | - Added support for Default Settings - 118 | ```javascript 119 | var setting = { 120 | "defaultAgentIds": [""], //list of agentID 121 | "defaultBotIds": [""], // list of BotID 122 | "defaultAssignee": "amantoppo3199@gmail.com", //string of conversation Assignee 123 | "skipRouting": true, // If you pass this value true then it will skip routing rules set from conversation rules section. 124 | "teamId": "" 125 | }; 126 | RNKommunicateChat.updateDefaultSetting(setting, (status, message) => { 127 | console.log(message); 128 | }); 129 | ``` 130 | - Fixed closeConversatioNScreen not working in iOS 131 | 132 | ## React Native Kommunicate Chat v2.1.8 133 | - Upgraded iOS SDK to 6.8.9 134 | - Fixed event data mismatch 135 | 136 | ## React Native Kommunicate Chat v2.1.7 137 | - Upgraded iOS SDK to 6.8.8 138 | - Upgraded Android SDK to 2.7.0 139 | - Fixed form template submission issues in iOS 140 | 141 | 142 | ## React Native Kommunicate Chat v2.1.6 143 | - Upgraded iOS SDK version to 6.8.7 144 | - Fixed build issue for iOS 145 | 146 | 147 | ## React Native Kommunicate Chat v2.1.5 148 | - Exposed enableSpeechToText() method which will create UI for multiple speech to text language - iOS 149 | - Added customizable option to disable chat widget in Helpcenter "hideChatInHelpcenter" 150 | - Attachment name and icon improvement - Android 151 | - Added support to delete conversation for end-user - Android 152 | 153 | ## React Native Kommunicate Chat v2.1.0 154 | - Exposed createSettings() methods which will create settings for both Android and iOS 155 | - Exposed enableSpeechToText() method which will create UI for multiple speech to text language - Android 156 | - Added option to rate conversation anytime from the conversation menu 157 | - Added multiple customizations 158 | 159 | ## React Native Kommunicate Chat v2.0.2 160 | - Upgraded iOS SDK to 6.7.8 161 | 162 | ## React Native Kommunicate Chat v2.0.1 163 | - Added support for listening to Events - iOS 164 | ```javascript 165 | const eventEmitter = new NativeEventEmitter(RNKommunicateChat); 166 | const eventListener = eventEmitter.addListener("onMessageReceived", event => { 167 | console.log("onMessageReceived" ); 168 | }); 169 | ``` 170 | - Exposed CloseConversationScreen() to close the conversation screen 171 | ## React Native Kommunicate Chat v2.0.0 172 | - Added support for listening to Events - Android only 173 | ```javascript 174 | const eventEmitter = new NativeEventEmitter(RNKommunicateChat); 175 | const eventListener = eventEmitter.addListener("onPluginLaunch", event => { 176 | console.log("onPluginLaunch" ); 177 | }); 178 | ``` 179 | For more events, refer to our docs 180 | - Expose method to close Kommunicate screen - ```RNKommunicateChat.closeConversationScreen()``` 181 | - Typing Indicator Design Changed - Android 182 | - Added Support for Android API 33 183 | - Added support for AES256 encryption and decryption 184 | - Added multiple customization option 185 | - Multiple bug fixes and optimization 186 | 187 | 188 | ## React Native Kommunicate Chat v1.6.8 189 | 190 | - Exposed methods to iOS: updateConversationAssignee and updateConversationInfo 191 | ```javascript 192 | RNKommunicateChat.updateConversationAssignee({ 193 | clientConversationId: "", 194 | conversationAssignee: ""}, (success, error) => { 195 | console.log("Updated conversation assignee"); 196 | }); 197 | 198 | RNKommunicateChat.updateConversationInfo({ 199 | clientConversationId: "", 200 | conversationInfo: { 201 | "test1": "value1", 202 | "test2": "value2" 203 | } 204 | }, (success, error) => { 205 | console.log("Updated conversation info"); 206 | }); 207 | ``` 208 | - HTML Rich Message Video template - Android 209 | - Fixed showing empty body for rich message having no text - Android 210 | - Fix for attachment sending empty message - Android 211 | - Sync deleted message from dashboard - Android 212 | - Optimized group list API - Android 213 | - Fixed empty notification body - Android 214 | - Added Text to Speech Support for messages. To enable it, add this in AppDelegate didFinishLaunchingWithOptions method: 'Kommunicate.defaultConfiguration.enableTextToSpeechInConversation = true' - iOS -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Installation 2 | 3 | This is the installation doc for the Kommunicate SDK for React Native. 4 | 5 | To add the Kommunicate module to you react native application, add it using npm: 6 | 7 | ``` 8 | npm install react-native-kommunicate-chat --save 9 | ``` 10 | 11 | Then link the module: 12 | 13 | ``` 14 | react-native link react-native-kommunicate-chat 15 | ``` 16 | 17 | For iOS, navigate to YourApp/ios directory from terminal and run the below command: 18 | ``` 19 | pod install 20 | ``` 21 | 22 | Note: Kommunicate requires a minimum iOS platform version of 13, uses dynamic frameworks, and requires New Architecture to be disabled. Ensure the following settings are added at the top of your Podfile: 23 | ``` 24 | platform :ios, '13.0' 25 | use_frameworks! 26 | ``` 27 | 28 | Add permissions for Camera, Photo Library, Microphone, Contacts and Location usage.
29 | 30 | Note: We won't be asking the users for these permissions unless they use the respective feature. Due to Apple's requirement, we have to add these permissions if we are using any of their APIs related to Camera, Microphone etc. 31 | 32 | Open `Info.plist` from `/ios/YOUR_PROJECT_NAME/Info.plist` file and paste these permissions anywhere inside the `` tag. 33 | ``` 34 | NSCameraUsageDescription 35 | Allow Camera 36 | NSContactsUsageDescription 37 | Allow Contacts 38 | NSLocationWhenInUseUsageDescription 39 | Allow location sharing!! 40 | NSMicrophoneUsageDescription 41 | Allow MicroPhone 42 | NSPhotoLibraryUsageDescription 43 | Allow Photos 44 | NSPhotoLibraryAddUsageDescription 45 | Allow write access 46 | ``` 47 | ## Import the module 48 | 49 | You can use the module by importing it in your react native files as below: 50 | 51 | ```js 52 | import RNKommunicateChat from 'react-native-kommunicate-chat'; 53 | ``` 54 | 55 | ## Get your Application Id 56 | Sign up for [Kommunicate](https://www.kommunicate.io/?utm_source=github&utm_medium=readme&utm_campaign=react+native) to get your [APP_ID](https://dashboard.kommunicate.io/settings/install). This APP_ID is used to create/launch conversations. 57 | 58 | ## Launch chat 59 | Kommunicate provides buildConversation function to create and launch chat directly saving you the extra steps of authentication, creation, initialization and launch. You can customize the process by building the conversationObject according to your requirements. 60 | To launch the chat you need to create a conversation object. This object is passed to the `buildConversation` function and based on the parameters of the object the chat is created/launched. 61 | 62 | Below are some examples to launch chat in different scenarios: 63 | 64 | ### Launching chat for visitor: 65 | If you would like to launch the chat directly without the visiting user entering any details, then use the method as below: 66 | 67 | ```js 68 | let conversationObject = { 69 | 'appId' : '' // The [APP_ID](https://dashboard.kommunicate.io/settings/install) obtained from kommunicate dashboard. 70 | } 71 | 72 | RNKommunicateChat.buildConversation(conversationObject, (response, responseMessage) => { 73 | if(response == "Success") { 74 | console.log("Conversation Successfully with id:" + responseMessage); 75 | } 76 | }); 77 | ``` 78 | ### Launching chat for visitor with lead collection: 79 | If you need the user to fill in details like phone number, emailId and name before starting the support chat then launch the chat with `withPreChat` flag as true. In this case you wouldn't need to pass the kmUser. A screen would open up for the user asking for details like emailId, phone number and name. Once the user fills the valid details (atleast emailId or phone number is required), the chat would be launched. Use the function as below: 80 | 81 | ```js 82 | let conversationObject = { 83 | 'appId' : '', // The [APP_ID](https://dashboard.kommunicate.io/settings/install) obtained from kommunicate dashboard. 84 | 'withPreChat' : true 85 | } 86 | 87 | RNKommunicateChat.buildConversation(conversationObject, (response, responseMessage) => { 88 | if(response == "Success") { 89 | console.log("Conversation Successfully with id:" + responseMessage); 90 | } 91 | }); 92 | ``` 93 | 94 | ### Launching chat with existing user: 95 | If you already have the user details then create a KMUser object using the details and launch the chat. Use the method as below to create KMUser with already existing details: 96 | 97 | ```js 98 | let user = { 99 | 'userId' : '', //Replace it with the userId of the logged in user 100 | 'password' : '' //Put password here if user has password, ignore otherwise 101 | } 102 | 103 | let conversationObject = { 104 | 'appId' : '', // The [APP_ID](https://dashboard.kommunicate.io/settings/install) obtained from kommunicate dashboard. 105 | 'kmUser' : JSON.stringify(user) 106 | } 107 | 108 | RNKommunicateChat.buildConversation(conversationObject, (response, responseMessage) => { 109 | if(response == "Success") { 110 | console.log("Conversation Successfully with id:" + responseMessage); 111 | } 112 | }); 113 | ``` 114 | 115 | If you have a different use-case and would like to customize the chat creation, user creation and chat launch, you can use more parameters in the conversationObject. 116 | 117 | Below are all the parameters you can use to customize the conversation according to your requirements: 118 | 119 | | Parameter | Type | Description | 120 | | ------------- |:-------------:| :-----| 121 | | appId | String | The [APP_ID](https://dashboard.kommunicate.io/settings/install) obtained from kommunicate dashboard | 122 | | groupName | String | Optional, you can pass a group name or ignore | 123 | | kmUser | KMUser | Optional, Pass the details if you have the user details, ignore otherwise. The details you pass here are used **only the first time**, to login the user. These login details persists until the app is uninstalled or you call [logout](https://docs.kommunicate.io/docs/reactnative-logout). | 124 | | withPreChat | boolean | Optional, Pass true if you would like the user to fill the details before starting the chat. If you have user details then you can pass false or ignore. | 125 | | isUnique | boolean | Optional, Pass true if you would like to create only one conversation for every user. The next time user starts the chat the same conversation would open, false if you would like to create a new conversation everytime the user starts the chat. True is recommended for single chat| 126 | | metadata | Any | Optional. This metadata if set will be sent with all the messages sent from that device. Also this metadata will be set to the conversations created from that device. | 127 | | agentIds | List | Optional, Pass the list of agents you want to add in this conversation. The agent ID is the email ID with which your agent is registered on Kommunicate. You may use this to add agents to the conversation while creating the conversation. Note that, conversation assignment will be done on the basis of the routing rules set in the [Conversation Rules section](https://dashboard.kommunicate.io/settings/conversation-rules). Adding agent ID here will only add the agents to the conversation and will not alter the routing rules.| 128 | | botIds | List | Optional, Pass the list of bots you want to add in this conversation. Go to [bots](https://dashboard.kommunicate.io/bot) -> Manage Bots -> Copy botID . Ignore if you haven't integrated any bots. You may use this to add any number of bots to the conversation while creating the conversation. Note that this has no effect on the conversation assignee, as the [Conversation Rules](https://dashboard.kommunicate.io/settings/conversation-rules) set forth in the Dashboard will prevail.| 129 | | createOnly | boolean | Optional. Pass true if you need to create the conversation and not launch it. In this case you will receive the clientChannelKey of the created conversation in the success callback function.| 130 | 131 | 132 | For more detailed documentation, follow this: https://docs.kommunicate.io/docs/reactnative-installation 133 | Here is the sample app which implements this SDK: https://github.com/Kommunicate-io/Kommunicate-React-Native-Sample 134 | -------------------------------------------------------------------------------- /RNKommunicateChat.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "RNKommunicateChat" 3 | s.version = "1.0.0" 4 | s.summary = "React Native SDK for Kommunicate customer support live chat" 5 | s.description = <<-DESC 6 | RNKommunicateChat 7 | DESC 8 | s.homepage = "https://kommunicate.io" 9 | s.license = "BSD-3" 10 | # s.license = { :type => "MIT", :file => "FILE_LICENSE" } 11 | s.author = { "author" => "codemonk@kommunicate.io" } 12 | s.platform = :ios, "13.0" 13 | s.source = { :git => "https://github.com/Kommunicate-io/Kommunicate-React-Native-SDK", :tag => "master" } 14 | s.source_files = "ios/*.{h,m,swift}" 15 | s.requires_arc = true 16 | s.dependency 'React' 17 | s.dependency 'Kommunicate', '7.3.0' 18 | end 19 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Kommunicate Security Policy and Procedures 2 | 3 | This document outlines security procedures and general policies for the Kommunicate. 4 | 5 | * [Reporting a Vulnerability](#reporting-a-vulnerability) 6 | * [Disclosure Policy](#disclosure-policy) 7 | 8 | ## Reporting a Vulnerability 9 | 10 | The Kommunicate team takes all security vulnerabilities seriously. Thank you for improving the security of our product. We appreciate your efforts and responsible disclosure and will make every effort to acknowledge your contributions. 11 | 12 | Report security vulnerabilities by emailing to the Kommunicate security team at: `security@kommunicate.io` 13 | 14 | Please include as much of the information listed below as you can to help us better understand and resolve the issue: 15 | 16 | * The type of Issue. 17 | * Any special configuration required to reproduce the issue. 18 | * Step-by-step instructions to reproduce the issue. 19 | * Proof-of-concept or exploit code or screen-shot (if possible). 20 | * Impact of the issue, including how an attacker might exploit the issue. 21 | 22 | The lead maintainer will acknowledge your email within 48 hours, and will send a more detailed response within next 48 hours indicating the next steps in handling your report. After the initial reply to your report, the security team will endeavor to keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance (if required). 23 | 24 | ## Disclosure Policy 25 | 26 | When the security team receives a security bug report, they will assign it to a primary handler. This person will coordinate the fix and release process, involving the following steps: 27 | 28 | * Confirm the problem and determine the affected versions. 29 | * Audit code to find any potential similar problems. 30 | * Prepare fixes for all releases still under maintenance. 31 | * Release a patch as soon as possible depending on complexity of the reported issue. 32 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:7.4.2' // Update this 9 | } 10 | } 11 | 12 | apply plugin: 'com.android.library' 13 | rootProject.allprojects { 14 | repositories { 15 | google() 16 | mavenCentral() 17 | gradlePluginPortal() 18 | maven { 19 | url 'https://kommunicate.jfrog.io/artifactory/kommunicate-android-sdk' 20 | } 21 | } 22 | } 23 | android { 24 | compileSdkVersion 33 25 | 26 | defaultConfig { 27 | minSdkVersion 16 28 | targetSdkVersion 33 29 | versionCode 1 30 | versionName "1.0" 31 | } 32 | lintOptions { 33 | abortOnError false 34 | } 35 | } 36 | 37 | repositories { 38 | mavenCentral() 39 | } 40 | 41 | dependencies { 42 | implementation 'com.facebook.react:react-native:+' 43 | api 'io.kommunicate.sdk:kommunicateui:2.14.0' 44 | } 45 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/src/main/java/io/kommunicate/app/KmEventListener.java: -------------------------------------------------------------------------------- 1 | package io.kommunicate.app; 2 | 3 | import io.kommunicate.callbacks.KmPluginEventListener; 4 | import io.kommunicate.commons.json.GsonUtils; 5 | import io.kommunicate.devkit.api.conversation.Message; 6 | import io.kommunicate.devkit.broadcast.EventManager; 7 | 8 | import com.facebook.react.bridge.ReactApplicationContext; 9 | import org.json.JSONObject; 10 | import org.json.JSONException; 11 | import androidx.annotation.Nullable; 12 | import com.facebook.react.modules.core.DeviceEventManagerModule; 13 | import com.facebook.react.bridge.WritableMap; 14 | import com.facebook.react.bridge.Arguments; 15 | import com.google.gson.Gson; 16 | 17 | public class KmEventListener implements KmPluginEventListener { 18 | private ReactApplicationContext reactContext; 19 | 20 | public void register(ReactApplicationContext reactContext) { 21 | this.reactContext = reactContext; 22 | EventManager.getInstance().registerPluginEventListener(this); 23 | } 24 | 25 | public void unregister() { 26 | EventManager.getInstance().unregisterPluginEventListener(); 27 | } 28 | 29 | private void sendEvent(String eventName, String value) { 30 | WritableMap params = Arguments.createMap(); 31 | params.putString("data", value); 32 | reactContext. 33 | getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) 34 | .emit(eventName, params); 35 | } 36 | 37 | @Override 38 | public void onPluginLaunch() { 39 | sendEvent("onPluginLaunch", "launch"); 40 | } 41 | 42 | @Override 43 | public void onPluginDismiss() { 44 | sendEvent("onPluginDismiss", "dismiss"); 45 | } 46 | 47 | @Override 48 | public void onConversationResolved(Integer conversationId) { 49 | sendEvent("onConversationResolved", String.valueOf(conversationId)); 50 | } 51 | 52 | @Override 53 | public void onConversationRestarted(Integer conversationId) { 54 | sendEvent("onConversationRestarted", String.valueOf(conversationId)); 55 | } 56 | 57 | @Override 58 | public void onRichMessageButtonClick(Integer conversationId, String actionType, Object action) { 59 | try { 60 | JSONObject messageActionObject = new JSONObject(); 61 | messageActionObject.put("conversationId", conversationId); 62 | messageActionObject.put("actionType", actionType); 63 | messageActionObject.put("action", new Gson().toJson(action)); 64 | sendEvent("onRichMessageButtonClick", String.valueOf(messageActionObject)); 65 | } catch(JSONException e) { 66 | sendEvent("onRichMessageButtonClick", "error fetching data"); 67 | e.printStackTrace(); 68 | } 69 | } 70 | 71 | @Override 72 | public void onStartNewConversation(Integer conversationId) { 73 | sendEvent("onStartNewConversation", String.valueOf(conversationId)); 74 | } 75 | 76 | @Override 77 | public void onSubmitRatingClick(Integer conversationId, Integer rating, String feedback) { 78 | try { 79 | JSONObject ratingObject = new JSONObject(); 80 | ratingObject.put("conversationId", conversationId); 81 | ratingObject.put("rating", rating); 82 | ratingObject.put("feedback", feedback); 83 | sendEvent("onSubmitRatingClick", String.valueOf(ratingObject)); 84 | } catch(JSONException e) { 85 | sendEvent("onSubmitRatingClick", "error fetching data"); 86 | e.printStackTrace(); 87 | } 88 | } 89 | 90 | @Override 91 | public void onMessageSent(Message message) { 92 | sendEvent("onMessageSent", GsonUtils.getJsonFromObject(message, Message.class)); 93 | } 94 | 95 | @Override 96 | public void onMessageReceived(Message message) { 97 | sendEvent("onMessageReceived", GsonUtils.getJsonFromObject(message, Message.class)); 98 | } 99 | 100 | @Override 101 | public void onBackButtonClicked(boolean isConversationOpened) { 102 | sendEvent("onBackButtonClicked", String.valueOf(isConversationOpened)); 103 | } 104 | @Override 105 | public void onAttachmentClick(String attachmentType) { 106 | sendEvent("onAttachmentClick", attachmentType); 107 | } 108 | 109 | @Override 110 | public void onFaqClick(String FaqUrl) { 111 | sendEvent("onFaqClick", FaqUrl); 112 | } 113 | 114 | @Override 115 | public void onLocationClick() { 116 | sendEvent("onLocationClick", "clicked"); 117 | } 118 | 119 | @Override 120 | public void onNotificationClick(Message message){ 121 | sendEvent("onNotificationClick", GsonUtils.getJsonFromObject(message, Message.class)); 122 | } 123 | 124 | @Override 125 | public void onVoiceButtonClick(String action){ 126 | sendEvent("onVoiceButtonClick", action); 127 | } 128 | 129 | @Override 130 | public void onRatingEmoticonsClick(Integer ratingValue){ 131 | sendEvent("onRatingEmoticonsClick", String.valueOf(ratingValue)); 132 | } 133 | 134 | @Override 135 | public void onRateConversationClick(){ 136 | sendEvent("onRateConversationClick", "clicked"); 137 | } 138 | 139 | } -------------------------------------------------------------------------------- /android/src/main/java/io/kommunicate/app/RNKommunicateChatModule.java: -------------------------------------------------------------------------------- 1 | package io.kommunicate.app; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.text.TextUtils; 6 | import android.os.AsyncTask; 7 | 8 | import com.facebook.react.bridge.ReactApplicationContext; 9 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 10 | import com.facebook.react.bridge.ReactMethod; 11 | import com.facebook.react.bridge.Callback; 12 | import com.facebook.react.bridge.ReadableMap; 13 | import com.facebook.react.modules.core.DeviceEventManagerModule; 14 | import com.facebook.react.bridge.WritableMap; 15 | import com.facebook.react.bridge.Arguments; 16 | 17 | import io.kommunicate.KmConversationHelper; 18 | import io.kommunicate.KmException; 19 | import io.kommunicate.KmSettings; 20 | import io.kommunicate.Kommunicate; 21 | import io.kommunicate.callbacks.KMLoginHandler; 22 | import io.kommunicate.callbacks.KMLogoutHandler; 23 | import io.kommunicate.callbacks.KmCallback; 24 | import io.kommunicate.callbacks.KmPushNotificationHandler; 25 | import io.kommunicate.commons.file.FileUtils; 26 | import io.kommunicate.commons.json.GsonUtils; 27 | import io.kommunicate.commons.people.channel.Channel; 28 | import io.kommunicate.commons.people.contact.Contact; 29 | import io.kommunicate.devkit.api.account.register.RegistrationResponse; 30 | import io.kommunicate.devkit.api.account.user.MobiComUserPreference; 31 | import io.kommunicate.devkit.api.account.user.UserUpdateTask; 32 | import io.kommunicate.devkit.api.conversation.MessageBuilder; 33 | import io.kommunicate.devkit.api.conversation.database.MessageDatabaseService; 34 | import io.kommunicate.devkit.channel.service.ChannelService; 35 | import io.kommunicate.devkit.contact.AppContactService; 36 | import io.kommunicate.devkit.listners.ResultCallback; 37 | import io.kommunicate.usecase.UserUpdateUseCase; 38 | import io.kommunicate.users.KMUser; 39 | import io.kommunicate.KmConversationBuilder; 40 | 41 | import java.lang.ref.WeakReference; 42 | import java.util.HashMap; 43 | import java.util.Map; 44 | import java.util.List; 45 | import java.util.ArrayList; 46 | import io.kommunicate.callbacks.KmGetConversationInfoCallback; 47 | import io.kommunicate.async.KmConversationInfoTask; 48 | 49 | import com.google.gson.JsonSyntaxException; 50 | 51 | 52 | public class RNKommunicateChatModule extends ReactContextBaseJavaModule { 53 | 54 | private static final String SUCCESS = "Success"; 55 | private static final String ERROR = "Error"; 56 | private static final String CONVERSATION_ID = "conversationId"; 57 | private static final String CLIENT_CONVERSATION_ID = "clientConversationId"; 58 | private static final String CONVERSATION_ASSIGNEE = "conversationAssignee"; 59 | private static final String MESSAGE_METADATA = "messageMetadata"; 60 | private static final String TEAM_ID = "teamId"; 61 | private static final String CONVERSATION_INFO = "conversationInfo"; 62 | private static final String LANGUAGES = "languages"; 63 | private static final String KM_USER = "kmUser"; 64 | private KmEventListener kmEventListener; 65 | private ReactApplicationContext reactContext; 66 | 67 | public RNKommunicateChatModule(ReactApplicationContext reactContext) { 68 | super(reactContext); 69 | this.reactContext = reactContext; 70 | } 71 | 72 | @Override 73 | public String getName() { 74 | return "RNKommunicateChat"; 75 | } 76 | @ReactMethod 77 | public void addListener(String eventName) { 78 | kmEventListener = new KmEventListener(); 79 | kmEventListener.register(reactContext); 80 | } 81 | 82 | @ReactMethod 83 | public void removeListeners(Integer count) { 84 | kmEventListener.unregister(); 85 | } 86 | 87 | @ReactMethod 88 | public void loginUser(final ReadableMap config, final Callback callback) { 89 | final Activity currentActivity = getCurrentActivity(); 90 | if (currentActivity == null) { 91 | callback.invoke(ERROR, "Activity doesn't exist"); 92 | return; 93 | } 94 | 95 | KMUser user = (KMUser) GsonUtils.getObjectFromJson(GsonUtils.getJsonFromObject(config.toHashMap(), HashMap.class), KMUser.class); 96 | 97 | if (user != null && !TextUtils.isEmpty(user.getApplicationId())) { 98 | Kommunicate.init(currentActivity, user.getApplicationId()); 99 | } 100 | 101 | Kommunicate.login(currentActivity, user, new KMLoginHandler() { 102 | @Override 103 | public void onSuccess(RegistrationResponse registrationResponse, Context context) { 104 | callback.invoke(SUCCESS, GsonUtils.getJsonFromObject(registrationResponse, RegistrationResponse.class)); 105 | } 106 | 107 | @Override 108 | public void onFailure(RegistrationResponse registrationResponse, Exception exception) { 109 | callback.invoke(ERROR, registrationResponse != null ? GsonUtils.getJsonFromObject(registrationResponse, RegistrationResponse.class) : exception != null ? exception.getMessage() : null); 110 | } 111 | }); 112 | } 113 | 114 | @ReactMethod 115 | public void sendMessage(final ReadableMap jsonObject, final Callback callback) { 116 | Activity currentActivity = getCurrentActivity(); 117 | if (currentActivity == null) { 118 | callback.invoke(ERROR, "Activity doesn't exist"); 119 | return; 120 | } 121 | final WeakReference activityRef = new WeakReference<>(currentActivity); 122 | 123 | try { 124 | if (!jsonObject.hasKey("channelID") || !jsonObject.hasKey("message")) { 125 | callback.invoke(ERROR, "channelID and message are required parameters"); 126 | return; 127 | } 128 | String message = jsonObject.getString("message"); 129 | Map messageMetadata = null; 130 | if (TextUtils.isEmpty(message)) { 131 | callback.invoke(ERROR, "message cannot be empty"); 132 | return; 133 | } 134 | String channelID = jsonObject.getString("channelID"); 135 | if (TextUtils.isEmpty(channelID)) { 136 | callback.invoke(ERROR, "channelID cannot be empty"); 137 | return; 138 | } 139 | Activity activity = activityRef.get(); 140 | if (activity == null) { 141 | callback.invoke(ERROR, "Activity no longer exists"); 142 | return; 143 | } 144 | 145 | MessageBuilder messageBuilder = new MessageBuilder(activity); 146 | messageBuilder.setClientGroupId(channelID); 147 | messageBuilder.setMessage(message); 148 | 149 | if (jsonObject.hasKey(MESSAGE_METADATA)) { 150 | try { 151 | messageMetadata = (Map) GsonUtils.getObjectFromJson(jsonObject.getString(MESSAGE_METADATA), Map.class); 152 | } catch (JsonSyntaxException e) { 153 | callback.invoke(ERROR, "Invalid messageMetadata format: " + e.getMessage()); 154 | return; 155 | } 156 | } 157 | 158 | if (messageMetadata != null) { 159 | messageBuilder.setMetadata(messageMetadata); 160 | } 161 | 162 | messageBuilder.send(); 163 | callback.invoke(SUCCESS,"Message sent successfully"); 164 | } catch (IllegalArgumentException e) { 165 | callback.invoke(ERROR, "Invalid parameters: " + e.getMessage()); 166 | } catch (JsonSyntaxException e) { 167 | callback.invoke(ERROR, "Invalid messageMetadata format: " + e.getMessage()); 168 | } catch (Exception e) { 169 | callback.invoke(ERROR, "Error sending message: " + e.getMessage()); 170 | } 171 | } 172 | 173 | @ReactMethod 174 | public void loginAsVisitor(final String applicationId, final Callback callback) { 175 | final Activity activity = getCurrentActivity(); 176 | if (activity == null) { 177 | callback.invoke(ERROR, "Activity doesn't exist"); 178 | return; 179 | } 180 | 181 | if (applicationId != null && !TextUtils.isEmpty(applicationId)) { 182 | Kommunicate.init(activity, applicationId); 183 | } 184 | 185 | Kommunicate.loginAsVisitor(activity, new KMLoginHandler() { 186 | @Override 187 | public void onSuccess(RegistrationResponse registrationResponse, Context context) { 188 | callback.invoke(SUCCESS, GsonUtils.getJsonFromObject(registrationResponse, RegistrationResponse.class)); 189 | } 190 | 191 | @Override 192 | public void onFailure(RegistrationResponse registrationResponse, Exception exception) { 193 | callback.invoke(ERROR, registrationResponse != null ? GsonUtils.getJsonFromObject(registrationResponse, RegistrationResponse.class) : exception != null ? exception.getMessage() : null); 194 | } 195 | }); 196 | } 197 | 198 | @ReactMethod 199 | public void registerPushNotification(final Callback callback) { 200 | final Activity currentActivity = getCurrentActivity(); 201 | if (currentActivity == null) { 202 | callback.invoke(ERROR, "Activity doesn't exist"); 203 | return; 204 | } 205 | 206 | Kommunicate.registerForPushNotification(currentActivity, new KmPushNotificationHandler() { 207 | @Override 208 | public void onSuccess(RegistrationResponse registrationResponse) { 209 | callback.invoke(SUCCESS, GsonUtils.getJsonFromObject(registrationResponse, RegistrationResponse.class)); 210 | } 211 | 212 | @Override 213 | public void onFailure(RegistrationResponse registrationResponse, Exception exception) { 214 | callback.invoke(ERROR, registrationResponse != null ? GsonUtils.getJsonFromObject(registrationResponse, RegistrationResponse.class) : exception != null ? exception.getMessage() : null); 215 | } 216 | }); 217 | } 218 | 219 | @ReactMethod 220 | public void updateUserDetails(final ReadableMap config, final Callback callback) { 221 | final Activity currentActivity = getCurrentActivity(); 222 | if (currentActivity == null) { 223 | callback.invoke(ERROR, "Activity doesn't exist"); 224 | return; 225 | } 226 | 227 | if (KMUser.isLoggedIn(currentActivity)) { 228 | KMUser user = (KMUser) GsonUtils.getObjectFromJson(GsonUtils.getJsonFromObject(config.toHashMap(), HashMap.class), KMUser.class); 229 | new UserUpdateTask(currentActivity, user, new ResultCallback() { 230 | @Override 231 | public void onSuccess(Object message) { 232 | callback.invoke(SUCCESS, "User details updated"); 233 | } 234 | 235 | @Override 236 | public void onError(Object error) { 237 | callback.invoke(ERROR, "Unable to update user details"); 238 | } 239 | }).execute(); 240 | } else { 241 | callback.invoke(ERROR, "User not authorised. This usually happens when calling the function before conversationBuilder or loginUser. Make sure you call either of the two functions before updating the user details"); 242 | } 243 | } 244 | 245 | @ReactMethod 246 | public void updatePushToken(String token, final Callback callback) { 247 | final Activity currentActivity = getCurrentActivity(); 248 | if (currentActivity == null) { 249 | callback.invoke(ERROR, "Activity doesn't exist"); 250 | return; 251 | } 252 | if (MobiComUserPreference.getInstance(currentActivity).isRegistered()) { 253 | try { 254 | Kommunicate.registerForPushNotification(currentActivity, token, new KmPushNotificationHandler() { 255 | @Override 256 | public void onSuccess(RegistrationResponse registrationResponse) { 257 | callback.invoke(SUCCESS, GsonUtils.getJsonFromObject(registrationResponse, RegistrationResponse.class)); 258 | } 259 | 260 | @Override 261 | public void onFailure(RegistrationResponse registrationResponse, Exception exception) { 262 | callback.invoke(ERROR, registrationResponse != null ? GsonUtils.getJsonFromObject(registrationResponse, RegistrationResponse.class) : exception != null ? exception.getMessage() : null); 263 | } 264 | }); 265 | } catch (Exception e) { 266 | e.printStackTrace(); 267 | } 268 | } 269 | } 270 | 271 | @ReactMethod 272 | public void openConversation(final Callback callback) { 273 | final Activity currentActivity = getCurrentActivity(); 274 | if (currentActivity == null) { 275 | callback.invoke(ERROR, "Activity doesn't exist"); 276 | return; 277 | } 278 | 279 | Kommunicate.openConversation(currentActivity, new KmCallback() { 280 | @Override 281 | public void onSuccess(Object message) { 282 | callback.invoke(SUCCESS, message.toString()); 283 | } 284 | 285 | @Override 286 | public void onFailure(Object error) { 287 | callback.invoke(ERROR, error.toString()); 288 | } 289 | }); 290 | } 291 | 292 | @ReactMethod 293 | public void buildConversation(final ReadableMap jsonObject, final Callback callback) { 294 | final Activity currentActivity = getCurrentActivity(); 295 | if (currentActivity == null) { 296 | callback.invoke(ERROR, "Activity doesn't exist"); 297 | return; 298 | } 299 | 300 | try { 301 | KMUser user = null; 302 | Map conversationInfo = null; 303 | Map dataMap = jsonObject.toHashMap(); 304 | Map messageMetadata = null; 305 | 306 | if (jsonObject.hasKey(KM_USER)) { 307 | user = (KMUser) GsonUtils.getObjectFromJson(jsonObject.getString(KM_USER), KMUser.class); 308 | dataMap.remove(KM_USER); 309 | } 310 | 311 | if (jsonObject.hasKey(CONVERSATION_INFO)) { 312 | conversationInfo = (Map) GsonUtils.getObjectFromJson(jsonObject.getString(CONVERSATION_INFO), Map.class); 313 | dataMap.remove(CONVERSATION_INFO); 314 | } 315 | 316 | if (jsonObject.hasKey(MESSAGE_METADATA)) { 317 | messageMetadata = (Map) GsonUtils.getObjectFromJson(jsonObject.getString(MESSAGE_METADATA), Map.class); 318 | dataMap.remove(MESSAGE_METADATA); 319 | } 320 | 321 | KmConversationBuilder conversationBuilder = (KmConversationBuilder) GsonUtils.getObjectFromJson(GsonUtils.getJsonFromObject(dataMap, HashMap.class), KmConversationBuilder.class); 322 | conversationBuilder.setContext(currentActivity); 323 | 324 | if (!jsonObject.hasKey("isSingleConversation")) { 325 | conversationBuilder.setSingleConversation(true); 326 | } 327 | if (!jsonObject.hasKey("skipConversationList")) { 328 | conversationBuilder.setSkipConversationList(true); 329 | } 330 | if (user != null) { 331 | conversationBuilder.setKmUser(user); 332 | } 333 | if (conversationInfo != null) { 334 | conversationBuilder.setConversationInfo(conversationInfo); 335 | } 336 | if (messageMetadata != null) { 337 | conversationBuilder.setMessageMetadata(messageMetadata); 338 | } 339 | 340 | if (jsonObject.hasKey("createOnly") && jsonObject.getBoolean("createOnly")) { 341 | conversationBuilder.createConversation(new KmCallback() { 342 | @Override 343 | public void onSuccess(Object message) { 344 | Channel channel = ChannelService.getInstance(currentActivity).getChannelByChannelKey((Integer) message); 345 | callback.invoke(SUCCESS, channel != null && !TextUtils.isEmpty(channel.getClientGroupId()) ? channel.getClientGroupId() : (String) message); 346 | } 347 | 348 | @Override 349 | public void onFailure(Object error) { 350 | callback.invoke(ERROR, error != null ? error.toString() : "Unknown error occurred"); 351 | } 352 | }); 353 | } else { 354 | conversationBuilder.launchConversation(new KmCallback() { 355 | @Override 356 | public void onSuccess(Object message) { 357 | callback.invoke(SUCCESS, message != null ? ChannelService.getInstance(currentActivity).getChannelByChannelKey((Integer) message).getClientGroupId() : "Success"); 358 | } 359 | 360 | @Override 361 | public void onFailure(Object error) { 362 | callback.invoke(ERROR, error != null ? error.toString() : "Unknown error occurred"); 363 | } 364 | }); 365 | } 366 | } catch (Exception e) { 367 | e.printStackTrace(); 368 | callback.invoke(ERROR, e.getMessage()); 369 | } 370 | } 371 | 372 | 373 | @ReactMethod 374 | public void openParticularConversation(final String conversationId, final boolean skipConversationList, final Callback callback) { 375 | final Activity currentActivity = getCurrentActivity(); 376 | if (currentActivity == null) { 377 | callback.invoke(ERROR, "Activity does not exist."); 378 | return; 379 | } 380 | if (TextUtils.isEmpty(conversationId)) { 381 | callback.invoke(ERROR, "Invalid or empty clientConversationId."); 382 | return; 383 | } 384 | 385 | new KmConversationInfoTask(currentActivity, conversationId, new KmGetConversationInfoCallback() { 386 | @Override 387 | public void onSuccess(Channel channel, Context context) { 388 | if (channel != null) { 389 | try { 390 | KmConversationHelper.openConversation(context, true, channel.getKey(), new KmCallback() { 391 | @Override 392 | public void onSuccess(Object message) { 393 | callback.invoke(SUCCESS,message.toString()); 394 | } 395 | 396 | @Override 397 | public void onFailure(Object error) { 398 | callback.invoke(ERROR, error.toString()); 399 | } 400 | }); 401 | } catch (KmException k) { 402 | callback.invoke(ERROR, k.getMessage()); 403 | } 404 | } 405 | } 406 | 407 | @Override 408 | public void onFailure(Exception e, Context context) { 409 | new KmConversationInfoTask(context, Integer.valueOf(conversationId), new KmGetConversationInfoCallback() { 410 | @Override 411 | public void onSuccess(Channel channel, Context context) { 412 | if (channel != null) { 413 | 414 | Kommunicate.openConversation(context, channel.getKey(), new KmCallback() { 415 | @Override 416 | public void onSuccess(Object message) { 417 | callback.invoke(SUCCESS,message.toString()); 418 | } 419 | 420 | @Override 421 | public void onFailure(Object error) { 422 | callback.invoke(ERROR, error.toString()); 423 | } 424 | }); 425 | 426 | } 427 | } 428 | 429 | @Override 430 | public void onFailure(Exception e, Context context) { 431 | callback.invoke(ERROR, e != null ? e.getMessage() : "Invalid conversationId"); 432 | } 433 | }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 434 | } 435 | }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 436 | } 437 | 438 | 439 | 440 | 441 | @ReactMethod 442 | public void isLoggedIn(final Callback callback) { 443 | final Activity currentActivity = getCurrentActivity(); 444 | if (currentActivity == null) { 445 | callback.invoke("False"); 446 | } else { 447 | if (Kommunicate.isLoggedIn(currentActivity)) 448 | callback.invoke("True"); 449 | else 450 | callback.invoke("False"); 451 | 452 | } 453 | } 454 | 455 | @ReactMethod 456 | public void updateChatContext(ReadableMap chatContext, Callback callback) { 457 | final Activity currentActivity = getCurrentActivity(); 458 | try { 459 | if (Kommunicate.isLoggedIn(currentActivity)) { 460 | Kommunicate.updateChatContext(currentActivity, getStringMap(chatContext.toHashMap())); 461 | callback.invoke(SUCCESS, "Updated chat context"); 462 | } else { 463 | callback.invoke(ERROR, "User not authorised. This usually happens when calling the function before conversationBuilder or loginUser. Make sure you call either of the two functions before updating the chatContext"); 464 | } 465 | } catch (Exception e) { 466 | callback.invoke(ERROR, e.toString()); 467 | } 468 | } 469 | 470 | private Map getStringMap(HashMap objectMap) { 471 | if (objectMap == null) { 472 | return null; 473 | } 474 | Map newMap = new HashMap<>(); 475 | for (Map.Entry entry : objectMap.entrySet()) { 476 | newMap.put(entry.getKey(), entry.getValue() instanceof String ? (String) entry.getValue() : entry.getValue().toString()); 477 | } 478 | return newMap; 479 | } 480 | 481 | @ReactMethod 482 | public void logout(final Callback callback) { 483 | final Activity currentActivity = getCurrentActivity(); 484 | if (currentActivity == null) { 485 | callback.invoke(ERROR); 486 | return; 487 | } 488 | Kommunicate.logout(currentActivity, new KMLogoutHandler() { 489 | @Override 490 | public void onSuccess(Context context) { 491 | callback.invoke(SUCCESS); 492 | } 493 | 494 | @Override 495 | public void onFailure(Exception exception) { 496 | callback.invoke(ERROR); 497 | } 498 | }); 499 | } 500 | 501 | @ReactMethod 502 | public void updateConversationAssignee(ReadableMap assigneeObject, final Callback callback) { 503 | final Activity currentActivity = getCurrentActivity(); 504 | try { 505 | if (Kommunicate.isLoggedIn(currentActivity)) { 506 | KmInfoProcessor infoProcessor = new KmInfoProcessor(assigneeObject, callback); 507 | KmSettings.updateConversationAssignee(currentActivity, infoProcessor.getConversationId(), infoProcessor.getClientConversationId(), infoProcessor.getConversationAssignee(), new KmCallback() { 508 | @Override 509 | public void onSuccess(Object o) { 510 | callback.invoke(SUCCESS, o); 511 | } 512 | 513 | @Override 514 | public void onFailure(Object o) { 515 | callback.invoke(ERROR, o); 516 | } 517 | }); 518 | } else { 519 | callback.invoke(ERROR, "User not authorised. This usually happens when calling the function before conversationBuilder or loginUser."); 520 | } 521 | } catch (Exception e) { 522 | callback.invoke(ERROR, e.toString()); 523 | } 524 | } 525 | 526 | @ReactMethod 527 | public void updateTeamId(final ReadableMap teamObject, final Callback callback) { 528 | final Activity currentActivity = getCurrentActivity(); 529 | try { 530 | if (Kommunicate.isLoggedIn(currentActivity)) { 531 | final KmInfoProcessor infoProcessor = new KmInfoProcessor(teamObject, callback); 532 | KmSettings.updateTeamId(currentActivity, infoProcessor.getConversationId(), infoProcessor.getClientConversationId(), infoProcessor.getTeamId(), new KmCallback() { 533 | @Override 534 | public void onSuccess(Object o) { 535 | if (!(o instanceof Channel)) { 536 | callback.invoke(SUCCESS, o); 537 | } 538 | } 539 | 540 | @Override 541 | public void onFailure(Object o) { 542 | callback.invoke(ERROR, o); 543 | } 544 | }); 545 | } else { 546 | callback.invoke(ERROR, "User not authorised. This usually happens when calling the function before conversationBuilder or loginUser."); 547 | } 548 | } catch (Exception e) { 549 | callback.invoke(ERROR, e.toString()); 550 | } 551 | } 552 | 553 | @ReactMethod 554 | public void updateConversationInfo(ReadableMap conversationInfoObject, final Callback callback) { 555 | final Activity currentActivity = getCurrentActivity(); 556 | try { 557 | if (Kommunicate.isLoggedIn(currentActivity)) { 558 | final KmInfoProcessor infoProcessor = new KmInfoProcessor(conversationInfoObject, callback); 559 | if (infoProcessor.getConversationInfo() == null) { 560 | callback.invoke(ERROR, "conversationInfo cannot be null"); 561 | return; 562 | } 563 | KmSettings.updateConversationInfo(currentActivity, infoProcessor.getConversationId(), infoProcessor.getClientConversationId(), getStringMap(infoProcessor.getConversationInfo().toHashMap()), new KmCallback() { 564 | @Override 565 | public void onSuccess(Object o) { 566 | if (!(o instanceof Channel)) { 567 | callback.invoke(SUCCESS, o); 568 | } 569 | } 570 | 571 | @Override 572 | public void onFailure(Object o) { 573 | callback.invoke(ERROR, o); 574 | } 575 | }); 576 | } else { 577 | callback.invoke(ERROR, "User not authorised. This usually happens when calling the function before conversationBuilder or loginUser."); 578 | } 579 | } catch (Exception e) { 580 | callback.invoke(ERROR, e.toString()); 581 | } 582 | } 583 | 584 | @ReactMethod 585 | public void closeConversationScreen() { 586 | final Activity currentActivity = getCurrentActivity(); 587 | if(currentActivity != null) { 588 | Kommunicate.closeConversationScreen(currentActivity); 589 | } 590 | } 591 | 592 | @ReactMethod 593 | public void createSettings(final String setting) { 594 | final Activity currentActivity = getCurrentActivity(); 595 | FileUtils.writeSettingsToFile(currentActivity, setting); 596 | } 597 | 598 | @ReactMethod 599 | public void updateDefaultSetting(final ReadableMap settingMap, final Callback callback) { 600 | final Activity currentActivity = getCurrentActivity(); 601 | try { 602 | KmSettings.clearDefaultSettings(); 603 | if (settingMap.hasKey("defaultAgentIds")) { 604 | List agentList = new ArrayList(); 605 | for(int i = 0; i < settingMap.getArray("defaultAgentIds").size(); i++){ 606 | agentList.add(settingMap.getArray("defaultAgentIds").getString(i)); 607 | } 608 | KmSettings.setDefaultAgentIds(agentList); 609 | } 610 | if (settingMap.hasKey("defaultBotIds")) { 611 | List botList = new ArrayList(); 612 | for(int i = 0; i < settingMap.getArray("defaultBotIds").size(); i++){ 613 | botList.add(settingMap.getArray("defaultBotIds").getString(i)); 614 | } 615 | KmSettings.setDefaultBotIds(botList); 616 | } 617 | if (settingMap.hasKey("defaultAssignee")) { 618 | KmSettings.setDefaultAssignee(settingMap.getString("defaultAssignee")); 619 | } 620 | if (settingMap.hasKey("teamId")) { 621 | KmSettings.setDefaultTeamId(settingMap.getString("teamId")); 622 | } 623 | if (settingMap.hasKey("skipRouting")) { 624 | KmSettings.setSkipRouting(settingMap.getBoolean("skipRouting")); 625 | } 626 | callback.invoke(SUCCESS, "Successfully set default settings"); 627 | } catch(Exception e) { 628 | callback.invoke(ERROR, e.toString()); 629 | } 630 | } 631 | 632 | @ReactMethod 633 | public void fetchUnreadCount(final Callback callback) { 634 | final Activity currentActivity = getCurrentActivity(); 635 | try { 636 | callback.invoke(SUCCESS, String.valueOf(new MessageDatabaseService(currentActivity).getTotalUnreadCount())); 637 | } catch (Exception e) { 638 | callback.invoke(ERROR, e.toString()); 639 | } 640 | } 641 | 642 | @ReactMethod 643 | public void fetchConversationInformation(final ReadableMap data,final Callback callback) { 644 | final Activity currentActivity = getCurrentActivity(); 645 | if (data.hasKey("conversationID") && !TextUtils.isEmpty(data.getString("conversationID"))) { 646 | String conversationID = data.getString("conversationID") ; 647 | new KmConversationInfoTask(currentActivity, Integer.valueOf(conversationID), new KmGetConversationInfoCallback() { 648 | @Override 649 | public void onSuccess(Channel channel, Context context) { 650 | if (channel != null) { 651 | callback.invoke(SUCCESS, GsonUtils.getJsonFromObject(channel, Channel.class)); 652 | } else { 653 | callback.invoke(ERROR,"Conversation Not Found"); 654 | } 655 | } 656 | @Override 657 | public void onFailure(Exception e, Context context) { 658 | callback.invoke(ERROR,e != null ? e.getMessage() : "Invalid ConversationID"); 659 | } 660 | }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 661 | 662 | } else if (data.hasKey("clientConversationID") && !TextUtils.isEmpty(data.getString("clientConversationID"))) { 663 | String clientConversationID = data.getString("clientConversationID"); 664 | new KmConversationInfoTask(currentActivity, clientConversationID, new KmGetConversationInfoCallback() { 665 | @Override 666 | public void onSuccess(Channel channel, Context context) { 667 | if (channel != null) { 668 | callback.invoke(SUCCESS, GsonUtils.getJsonFromObject(channel, Channel.class)); 669 | } else { 670 | callback.invoke(ERROR, "Conversation Not Found", null); 671 | } 672 | } 673 | @Override 674 | public void onFailure(Exception e, Context context) { 675 | callback.invoke(ERROR, e != null ? e.getMessage() : "Invalid clientChannelID"); 676 | } 677 | }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 678 | } else { 679 | callback.invoke(ERROR, "Object doesn't contain 'clientChannelID' or 'conversationID'"); 680 | } 681 | } 682 | 683 | @ReactMethod 684 | public void fetchConversationAssigneeInfo(final ReadableMap data,final Callback callback) { 685 | final Activity currentActivity = getCurrentActivity(); 686 | if (data.hasKey("conversationID") && !TextUtils.isEmpty(data.getString("conversationID"))) { 687 | String conversationID = data.getString("conversationID") ; 688 | new KmConversationInfoTask(currentActivity, Integer.valueOf(conversationID), new KmGetConversationInfoCallback() { 689 | @Override 690 | public void onSuccess(Channel channel, Context context) { 691 | if (channel != null) { 692 | Contact assignee = new AppContactService(currentActivity).getContactById(channel.getConversationAssignee()); 693 | callback.invoke(SUCCESS, GsonUtils.getJsonFromObject(assignee, Contact.class)); 694 | } else { 695 | callback.invoke(ERROR,"Conversation Not Found"); 696 | } 697 | } 698 | @Override 699 | public void onFailure(Exception e, Context context) { 700 | callback.invoke(ERROR,e != null ? e.getMessage() : "Invalid ConversationID"); 701 | } 702 | }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 703 | 704 | } else if (data.hasKey("clientConversationID") && !TextUtils.isEmpty(data.getString("clientConversationID"))) { 705 | String clientConversationID = data.getString("clientConversationID"); 706 | new KmConversationInfoTask(currentActivity, clientConversationID, new KmGetConversationInfoCallback() { 707 | @Override 708 | public void onSuccess(Channel channel, Context context) { 709 | if (channel != null) { 710 | Contact assignee = new AppContactService(currentActivity).getContactById(channel.getConversationAssignee()); 711 | callback.invoke(SUCCESS, GsonUtils.getJsonFromObject(assignee, Contact.class)); 712 | } else { 713 | callback.invoke(ERROR, "Conversation Not Found", null); 714 | } 715 | } 716 | @Override 717 | public void onFailure(Exception e, Context context) { 718 | callback.invoke(ERROR, e != null ? e.getMessage() : "Invalid clientChannelID"); 719 | } 720 | }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 721 | } else { 722 | callback.invoke(ERROR, "Object doesn't contain 'clientChannelID' or 'conversationID'"); 723 | } 724 | } 725 | 726 | static class KmInfoProcessor { 727 | private String clientConversationId; 728 | private Integer conversationId; 729 | private ReadableMap conversationInfo; 730 | private String teamId; 731 | private String conversationAssignee; 732 | 733 | public KmInfoProcessor(ReadableMap map, Callback callback) { 734 | if (map.hasKey(CLIENT_CONVERSATION_ID)) { 735 | clientConversationId = map.getString(CLIENT_CONVERSATION_ID); 736 | } 737 | if (map.hasKey(CONVERSATION_ID)) { 738 | conversationId = map.getInt(CONVERSATION_ID); 739 | } 740 | if (clientConversationId == null && conversationId == null) { 741 | callback.invoke(ERROR, "Either conversationId or clientConversationId is required"); 742 | return; 743 | } 744 | if (map.hasKey(CONVERSATION_ASSIGNEE)) { 745 | conversationAssignee = map.getString(CONVERSATION_ASSIGNEE); 746 | } 747 | if (map.hasKey(TEAM_ID)) { 748 | teamId = map.getString(TEAM_ID); 749 | } 750 | if (map.hasKey(CONVERSATION_INFO)) { 751 | conversationInfo = map.getMap(CONVERSATION_INFO); 752 | } 753 | } 754 | 755 | public String getClientConversationId() { 756 | return clientConversationId; 757 | } 758 | 759 | public Integer getConversationId() { 760 | return conversationId; 761 | } 762 | 763 | public ReadableMap getConversationInfo() { 764 | return conversationInfo; 765 | } 766 | 767 | public String getTeamId() { 768 | return teamId; 769 | } 770 | 771 | public String getConversationAssignee() { 772 | return conversationAssignee; 773 | } 774 | } 775 | } -------------------------------------------------------------------------------- /android/src/main/java/io/kommunicate/app/RNKommunicateChatPackage.java: -------------------------------------------------------------------------------- 1 | 2 | package io.kommunicate.app; 3 | 4 | import java.util.Arrays; 5 | import java.util.Collections; 6 | import java.util.List; 7 | 8 | import com.facebook.react.ReactPackage; 9 | import com.facebook.react.bridge.NativeModule; 10 | import com.facebook.react.bridge.ReactApplicationContext; 11 | import com.facebook.react.uimanager.ViewManager; 12 | import com.facebook.react.bridge.JavaScriptModule; 13 | public class RNKommunicateChatPackage implements ReactPackage { 14 | @Override 15 | public List createNativeModules(ReactApplicationContext reactContext) { 16 | return Arrays.asList(new RNKommunicateChatModule(reactContext)); 17 | } 18 | 19 | // Deprecated from RN 0.47 20 | public List> createJSModules() { 21 | return Collections.emptyList(); 22 | } 23 | 24 | @Override 25 | public List createViewManagers(ReactApplicationContext reactContext) { 26 | return Collections.emptyList(); 27 | } 28 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | import { NativeModules } from 'react-native'; 3 | 4 | const { RNKommunicateChat } = NativeModules; 5 | 6 | export default RNKommunicateChat; -------------------------------------------------------------------------------- /ios/Bridge.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | 5 | 6 | @interface RCT_EXTERN_MODULE(KMEventEmitter, RCTEventEmitter) 7 | 8 | RCT_EXTERN_METHOD(supportedEvents) 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /ios/KMEventEmitter.swift: -------------------------------------------------------------------------------- 1 | 2 | import React 3 | import Kommunicate 4 | import KommunicateChatUI_iOS_SDK 5 | import KommunicateCore_iOS_SDK 6 | 7 | @objc(KMEventEmitter) 8 | open class KMEventEmitter: RCTEventEmitter { 9 | 10 | public static var emitter: RCTEventEmitter! 11 | 12 | open override class func requiresMainQueueSetup() -> Bool { 13 | return true 14 | } 15 | override init() { 16 | super.init(disabledObservation: ()) 17 | KMEventEmitter.emitter = self 18 | } 19 | 20 | open override func supportedEvents() -> [String]! { 21 | return ["onMessageReceived", "onMessageSent", "onRichMessageButtonClick", "onStartNewConversation", "onSubmitRatingClick", "onBackButtonClicked", "onFaqClick", "onConversationRestarted"] 22 | } 23 | } -------------------------------------------------------------------------------- /ios/RNKommunicateChat.m: -------------------------------------------------------------------------------- 1 | // 2 | // RNKommunicateChat.m 3 | // KommunicateReactNativeSample 4 | // 5 | // Created by Ashish Kanswal on 30/07/19. 6 | // Copyright © 2019 Facebook. All rights reserved. 7 | // 8 | #import 9 | #import 10 | #import 11 | 12 | @interface RCT_EXTERN_MODULE(RNKommunicateChat, NSObject) 13 | 14 | RCT_EXTERN_METHOD (isLoggedIn: (RCTResponseSenderBlock)); 15 | 16 | RCT_EXTERN_METHOD (loginUser:(NSDictionary * _Nonnull)user :(RCTResponseSenderBlock _Nonnull)callback); 17 | 18 | RCT_EXTERN_METHOD (loginAsVisitor:(NSString * _Nonnull) appId :(RCTResponseSenderBlock _Nonnull) callback); 19 | RCT_EXTERN_METHOD (registerPushNotification: (RCTResponseSenderBlock)); 20 | RCT_EXTERN_METHOD (updatePushToken:(NSString * _Nonnull)token :(RCTResponseSenderBlock _Nonnull)callback); 21 | RCT_EXTERN_METHOD (openConversation: (RCTResponseSenderBlock)); 22 | RCT_EXTERN_METHOD (buildConversation: (NSDictionary * _Nonnull)jsonObj :(RCTResponseSenderBlock _Nonnull)callback); 23 | RCT_EXTERN_METHOD (openParticularConversation:(NSString * _Nonnull)conversationId :(BOOL)skipConversationList :(RCTResponseSenderBlock _Nonnull)callback); 24 | RCT_EXTERN_METHOD (logout: (RCTResponseSenderBlock)); 25 | RCT_EXTERN_METHOD (updateChatContext: (NSDictionary * _Nonnull)chatContext :(RCTResponseSenderBlock _Nonnull)callback); 26 | RCT_EXTERN_METHOD (updateUserDetails: (NSDictionary * _Nonnull)kmUser :(RCTResponseSenderBlock _Nonnull)callback); 27 | RCT_EXTERN_METHOD (updateConversationAssignee: (NSDictionary * _Nonnull)assigneeData :(RCTResponseSenderBlock _Nonnull)callback); 28 | RCT_EXTERN_METHOD (updateTeamId: (NSDictionary * _Nonnull)teamData :(RCTResponseSenderBlock _Nonnull)callback); 29 | RCT_EXTERN_METHOD (updateConversationInfo: (NSDictionary * _Nonnull)infoData :(RCTResponseSenderBlock _Nonnull)callback); 30 | RCT_EXTERN_METHOD (closeConversationScreen); 31 | RCT_EXTERN_METHOD (createSettings: (NSString * _Nonnull) ); 32 | RCT_EXTERN_METHOD (enableSpeechToText: (NSDictionary * _Nonnull)infoData :(RCTResponseSenderBlock _Nonnull)callback); 33 | RCT_EXTERN_METHOD (updateDefaultSetting: (NSDictionary * _Nonnull)settingData :(RCTResponseSenderBlock _Nonnull)callback); 34 | RCT_EXTERN_METHOD (fetchUnreadCount: (RCTResponseSenderBlock)); 35 | RCT_EXTERN_METHOD (fetchConversationInformation : (NSDictionary * _Nonnull) data :(RCTResponseSenderBlock _Nonnull)callback); 36 | RCT_EXTERN_METHOD (fetchConversationAssigneeInfo : (NSDictionary * _Nonnull) data :(RCTResponseSenderBlock _Nonnull)callback); 37 | RCT_EXTERN_METHOD (sendMessage: (NSDictionary * _Nonnull) data :(RCTResponseSenderBlock _Nonnull)callback); 38 | 39 | 40 | + (BOOL)requiresMainQueueSetup { return YES; } 41 | @end 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /ios/RNKommunicateChat.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KommunicateChat.swift 3 | // KommunicateReactNativeSample 4 | // 5 | // Created by Ashish Kanswal on 30/07/19. 6 | // Copyright © 2019 Facebook. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Kommunicate 11 | import KommunicateChatUI_iOS_SDK 12 | import KommunicateCore_iOS_SDK 13 | import React 14 | 15 | @objc (RNKommunicateChat) 16 | class RNKommunicateChat : RCTEventEmitter, KMPreChatFormViewControllerDelegate, ALKCustomEventCallback { 17 | public static var emitter: RCTEventEmitter! 18 | 19 | override init() { 20 | super.init(disabledObservation: ()) 21 | KMEventEmitter.emitter = self 22 | } 23 | 24 | var appId : String? = nil; 25 | var agentIds: [String]? = []; 26 | var botIds: [String]? = []; 27 | var createOnly: Bool = false 28 | var callback: RCTResponseSenderBlock? = nil; 29 | var isSingleConversation: Bool? = true; 30 | var conversationAssignee: String? = nil; 31 | var clientConversationId: String? = nil; 32 | var teamId: String? = nil; 33 | var conversationTitle: String? = nil; 34 | var conversationInfo: [AnyHashable: Any]? = nil; 35 | static let KM_CONVERSATION_METADATA: String = "conversationMetadata"; 36 | static let KM_LANGUAGES: String = "languages"; 37 | 38 | 39 | @objc 40 | func isLoggedIn(_ callback: RCTResponseSenderBlock) -> Void { 41 | var msg = "False" 42 | 43 | if Kommunicate.isLoggedIn { 44 | msg = "True" 45 | } 46 | 47 | callback([msg]) 48 | } 49 | 50 | @objc 51 | func loginUser(_ user: Dictionary, _ callback: @escaping RCTResponseSenderBlock)-> Void{ 52 | let kmUser = KMUser() 53 | 54 | if(user["userId"] != nil){ 55 | kmUser.userId = user["userId"] as? String 56 | } 57 | if(user["applicationId"] != nil){ 58 | kmUser.applicationId = user["applicationId"] as? String 59 | } 60 | if(user["password"] != nil){ 61 | kmUser.password = user["password"] as? String 62 | } 63 | if(user["email"] != nil){ 64 | kmUser.email = user["email"] as? String 65 | } 66 | if(user["displayName"] != nil){ 67 | kmUser.displayName = user["displayName"] as? String 68 | } 69 | if(user["imageLink"] != nil){ 70 | kmUser.imageLink = user["imageLink"] as? String 71 | } 72 | if(user["contactNumber"] != nil){ 73 | kmUser.contactNumber = user["contactNumber"] as? String 74 | } 75 | if(user["authenticationTypeId"] != nil){ 76 | kmUser.authenticationTypeId = user["authenticationTypeId"] as! Int16 77 | } 78 | if(user["pushNotificationFormat"] != nil){ 79 | kmUser.pushNotificationFormat = user["pushNotificationFormat"] as! Int16 80 | } 81 | if(user["registrationId"] != nil){ 82 | kmUser.registrationId = user["registrationId"] as? String 83 | } 84 | if(user["deviceApnsType"] != nil){ 85 | kmUser.deviceApnsType = user["deviceApnsType"] as! Int16 86 | } 87 | if(user["metadata"] != nil){ 88 | kmUser.metadata = user["metadata"] as? NSMutableDictionary 89 | } 90 | kmUser.platform = 7 // 7 is for React Native 91 | Kommunicate.setup(applicationId: kmUser.applicationId) 92 | Kommunicate.registerUser(kmUser, completion: { 93 | response, error in 94 | guard error == nil else{ 95 | callback(["Error", error as Any]) 96 | return 97 | } 98 | callback(["Success", response as Any]) 99 | }) 100 | } 101 | 102 | @objc 103 | func sendMessage(_ jsonObj: Dictionary, _ callback: @escaping RCTResponseSenderBlock) -> Void { 104 | do { 105 | let sendMessage = KMMessageBuilder() 106 | guard let conversationID = jsonObj["channelID"] as? String, !conversationID.isEmpty else { 107 | callback(["Error", "channelID is required and cannot be empty"]) 108 | return 109 | } 110 | guard let message = jsonObj["message"] as? String, !message.isEmpty else { 111 | callback(["Error", "message is required and cannot be empty"]) 112 | return 113 | } 114 | sendMessage.withConversationId(conversationID) 115 | sendMessage.withText(message) 116 | 117 | if let messageMetadataStr = (jsonObj["messageMetadata"] as? String)?.data(using: .utf8) { 118 | if let messageMetadataDict = try JSONSerialization.jsonObject(with: messageMetadataStr, options : .allowFragments) as? Dictionary { 119 | Kommunicate.defaultConfiguration.messageMetadata = messageMetadataDict 120 | } 121 | } 122 | 123 | Kommunicate.sendMessage(message: sendMessage.build()) { error in 124 | guard error == nil else { 125 | callback(["Error", error?.localizedDescription ?? "There is error in sending Mesage to the shared channelID"]) 126 | return 127 | } 128 | callback(["Success", "Message is sent successfully"]) 129 | } 130 | } catch let error as NSError { 131 | callback(["Error", "Failed to process message metadata: \(error.localizedDescription)"]) 132 | } 133 | } 134 | 135 | @objc 136 | func loginAsVisitor(_ appId: String, _ callback: @escaping RCTResponseSenderBlock) -> Void { 137 | let kmUser = KMUser() 138 | kmUser.userId = Kommunicate.randomId() 139 | Kommunicate.setup(applicationId: appId) 140 | kmUser.applicationId = appId 141 | kmUser.platform = 7 // 7 is for React Native 142 | Kommunicate.registerUser(kmUser, completion: { 143 | response, error in 144 | guard error == nil else{ 145 | callback(["Error", error as Any]) 146 | return 147 | } 148 | callback(["Success", response as Any]) 149 | }) 150 | } 151 | 152 | @objc 153 | func registerPushNotification(_ callback: RCTResponseSenderBlock) -> Void{ 154 | 155 | } 156 | 157 | @objc 158 | func updatePushToken(_ token: String, _ callback: @escaping RCTResponseSenderBlock) -> Void{ 159 | 160 | } 161 | 162 | @objc 163 | func openConversation(_ callback: @escaping RCTResponseSenderBlock) -> Void{ 164 | DispatchQueue.main.async{ 165 | if let top = UIApplication.topViewController(){ 166 | Kommunicate.showConversations(from: top) 167 | callback(["Success", "Successfully launched conversation list screen"]) 168 | }else{ 169 | callback(["Error", "Failed to launch conversation list screen"]) 170 | } 171 | } 172 | } 173 | 174 | @objc 175 | func buildConversation(_ jsonObj: Dictionary, _ callback: @escaping RCTResponseSenderBlock) -> Void { 176 | self.isSingleConversation = true 177 | self.createOnly = false; 178 | self.agentIds = []; 179 | self.botIds = []; 180 | self.conversationAssignee = nil 181 | self.clientConversationId = nil 182 | self.teamId = nil 183 | self.callback = callback; 184 | self.conversationInfo = nil 185 | self.conversationTitle = nil 186 | 187 | do { 188 | var withPrechat : Bool = false 189 | var kmUser : KMUser? = nil 190 | 191 | if jsonObj["appId"] != nil { 192 | self.appId = jsonObj["appId"] as? String 193 | } 194 | 195 | if jsonObj["withPreChat"] != nil { 196 | withPrechat = jsonObj["withPreChat"] as! Bool 197 | } 198 | 199 | if jsonObj["isSingleConversation"] != nil { 200 | self.isSingleConversation = jsonObj["isSingleConversation"] as? Bool 201 | } 202 | 203 | if (jsonObj["createOnly"] != nil) { 204 | self.createOnly = jsonObj["createOnly"] as! Bool 205 | } 206 | 207 | if (jsonObj["conversationAssignee"] != nil) { 208 | self.conversationAssignee = jsonObj["conversationAssignee"] as? String 209 | } 210 | 211 | if (jsonObj["clientConversationId"] != nil) { 212 | self.clientConversationId = jsonObj["clientConversationId"] as? String 213 | } 214 | 215 | if (jsonObj["teamId"] != nil) { 216 | self.teamId = jsonObj["teamId"] as? String 217 | } 218 | 219 | if jsonObj["conversationTitle"] != nil { 220 | self.conversationTitle = jsonObj["conversationTitle"] as? String 221 | } 222 | 223 | if jsonObj["conversationInfo"] != nil { 224 | conversationInfo = [RNKommunicateChat.KM_CONVERSATION_METADATA: jsonObj["conversationInfo"] as Any] 225 | } 226 | 227 | if let messageMetadataStr = (jsonObj["messageMetadata"] as? String)?.data(using: .utf8) { 228 | if let messageMetadataDict = try JSONSerialization.jsonObject(with: messageMetadataStr, options : .allowFragments) as? Dictionary { 229 | Kommunicate.defaultConfiguration.messageMetadata = messageMetadataDict 230 | } 231 | } 232 | 233 | self.agentIds = jsonObj["agentIds"] as? [String] 234 | self.botIds = jsonObj["botIds"] as? [String] 235 | 236 | if Kommunicate.isLoggedIn{ 237 | self.handleCreateConversation() 238 | }else{ 239 | if jsonObj["appId"] != nil { 240 | Kommunicate.setup(applicationId: jsonObj["appId"] as! String) 241 | } 242 | 243 | if !withPrechat { 244 | if jsonObj["kmUser"] != nil{ 245 | var jsonSt = jsonObj["kmUser"] as! String 246 | jsonSt = jsonSt.replacingOccurrences(of: "\\\"", with: "\"") 247 | jsonSt = "\(jsonSt)" 248 | kmUser = KMUser(jsonString: jsonSt) 249 | kmUser?.applicationId = appId 250 | }else { 251 | kmUser = KMUser.init() 252 | kmUser?.userId = Kommunicate.randomId() 253 | kmUser?.applicationId = appId 254 | } 255 | 256 | Kommunicate.registerUser(kmUser!, completion:{ 257 | response, error in 258 | guard error == nil else{ 259 | callback(["Error", error as Any]) 260 | return 261 | } 262 | self.handleCreateConversation() 263 | }) 264 | }else{ 265 | DispatchQueue.main.async { 266 | let controller = KMPreChatFormViewController(configuration: Kommunicate.defaultConfiguration) 267 | controller.delegate = self 268 | UIApplication.topViewController()?.present(controller, animated: false, completion: nil) 269 | } 270 | } 271 | } 272 | }catch _ as NSError{ 273 | 274 | } 275 | } 276 | 277 | func handleCreateConversation() { 278 | let builder = KMConversationBuilder(); 279 | 280 | if let agentIds = self.agentIds, !agentIds.isEmpty { 281 | builder.withAgentIds(agentIds) 282 | } 283 | 284 | if let botIds = self.botIds, !botIds.isEmpty { 285 | builder.withBotIds(botIds) 286 | } 287 | 288 | builder.useLastConversation(self.isSingleConversation ?? true) 289 | 290 | if let assignee = self.conversationAssignee { 291 | builder.withConversationAssignee(assignee) 292 | } 293 | 294 | if let clientConversationId = self.clientConversationId { 295 | builder.withClientConversationId(clientConversationId) 296 | } 297 | 298 | if let teamId = self.teamId { 299 | builder.withTeamId(teamId) 300 | } 301 | 302 | if let conversationTitle = self.conversationTitle { 303 | builder.withConversationTitle(conversationTitle) 304 | } 305 | 306 | if let conversationInfo = self.conversationInfo { 307 | builder.withMetaData(conversationInfo) 308 | } 309 | 310 | Kommunicate.createConversation(conversation: builder.build(), completion: { response in 311 | switch response { 312 | case .success(let conversationId): 313 | if self.createOnly { 314 | self.callback!(["Success", conversationId]) 315 | } else { 316 | self.openParticularConversation(conversationId, true, self.callback!) 317 | } 318 | 319 | case .failure(let error): 320 | self.callback!(["Error", error.localizedDescription]) 321 | } 322 | }) 323 | } 324 | 325 | @objc 326 | func openParticularConversation(_ conversationId: String,_ skipConversationList: Bool, _ callback: @escaping RCTResponseSenderBlock) -> Void { 327 | DispatchQueue.main.async{ 328 | if let top = UIApplication.topViewController(){ 329 | Kommunicate.showConversationWith(groupId: conversationId, from: top, completionHandler: ({ (shown) in 330 | if(shown){ 331 | callback(["Success", conversationId]) 332 | } else { 333 | callback(["Error", "Failed to launch conversation with conversationId : " + conversationId]) 334 | } 335 | })) 336 | }else{ 337 | callback(["Error", "Failed to launch conversation with conversationId : " + conversationId]) 338 | }} 339 | } 340 | 341 | @objc 342 | func updateChatContext(_ chatContext: Dictionary, _ callback: @escaping RCTResponseSenderBlock) -> Void { 343 | do { 344 | if(Kommunicate.isLoggedIn){ 345 | try Kommunicate.defaultConfiguration.updateChatContext(with: chatContext) 346 | callback(["Success", "Updated chat context"]) 347 | }else{ 348 | callback(["Error", "User not authorised. This usually happens when calling the function before conversationBuilder or loginUser. Make sure you call either of the two functions before updating the chatContext"]) 349 | } 350 | } catch { 351 | print(error) 352 | callback(["Error", error]) 353 | } 354 | } 355 | 356 | @objc 357 | func updateUserDetails(_ kmUser: Dictionary, _ callback: @escaping RCTResponseSenderBlock) -> Void { 358 | if(Kommunicate.isLoggedIn) { 359 | self.updateUser(displayName: kmUser["displayName"] as? String, imageLink: kmUser["imageLink"] as? String, status: kmUser["status"] as? String, metadata: kmUser["metadata"] as? NSMutableDictionary, phoneNumber: kmUser["contactNumber"] as? String, email: kmUser["email"] as? String, callback: callback) 360 | } else { 361 | callback(["Error", "User not authorised. This usually happens when calling the function before conversationBuilder or loginUser. Make sure you call either of the two functions before updating the user details"]) 362 | } 363 | } 364 | 365 | @objc 366 | func logout(_ callback: @escaping RCTResponseSenderBlock) -> Void { 367 | Kommunicate.logoutUser{ (logoutResult) in 368 | switch logoutResult { 369 | case .success(_): 370 | callback(["Success", "Logout success"]) 371 | case .failure( _): 372 | callback(["Error", "Error in logout"]) 373 | } 374 | } 375 | } 376 | 377 | @objc 378 | func updateConversationAssignee(_ assigneeData: Dictionary, _ callback: @escaping RCTResponseSenderBlock) -> Void { 379 | guard let assignee = assigneeData["conversationAssignee"] as? String else { 380 | callback(["Error", "Conversation Assignee is empty or invalid"]) 381 | return 382 | } 383 | guard let clientConversationId = assigneeData["clientConversationId"] as? String else { 384 | callback(["Error", "clientConversationId is empty or invalid"]) 385 | return 386 | } 387 | 388 | let conversation = KMConversationBuilder().withClientConversationId(clientConversationId) 389 | .withConversationAssignee(assignee).build() 390 | 391 | Kommunicate.updateConversation(conversation: conversation) { response in 392 | switch response { 393 | case .success(let clientConversationId): 394 | callback(["Success", "Successfully updated conversation Assignee"]) 395 | case .failure(let error): 396 | callback(["Error", error.localizedDescription]) 397 | } 398 | } 399 | } 400 | 401 | @objc 402 | func updateTeamId(_ teamData: Dictionary, _ callback: @escaping RCTResponseSenderBlock) -> Void { 403 | let metadata = NSMutableDictionary( 404 | dictionary: ALChannelService().metadataToHideActionMessagesAndTurnOffNotifications()) 405 | 406 | guard let teamID = teamData["teamId"] as? String, let groupID = teamData["clientConversationId"] as? String else { return } 407 | 408 | if !teamID.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { 409 | metadata.setValue(teamID, forKey: "KM_TEAM_ID") 410 | } 411 | if Int(groupID) != nil { 412 | ALChannelService().updateChannelMetaData(NSNumber(value: Int(groupID)!), orClientChannelKey: nil , metadata: metadata) { error in 413 | guard error == nil else { 414 | callback(["false", error.debugDescription]) 415 | return 416 | } 417 | } 418 | } 419 | ALChannelService().updateChannelMetaData(nil, orClientChannelKey: groupID , metadata: metadata) { error in 420 | guard error == nil else { 421 | callback(["false", error.debugDescription]) 422 | return 423 | } 424 | } 425 | } 426 | 427 | @objc 428 | func updateConversationInfo(_ infoData: Dictionary, _ callback: @escaping RCTResponseSenderBlock) -> Void { 429 | 430 | guard let conversationInfo = infoData["conversationInfo"] as? Dictionary else { 431 | callback(["Error", "Conversation Info is empty or invalid"]) 432 | return 433 | } 434 | guard let clientConversationId = infoData["clientConversationId"] as? String else { 435 | callback(["Error", "clientConversationId is empty or invalid"]) 436 | return 437 | } 438 | 439 | let conversation = KMConversationBuilder().withClientConversationId(clientConversationId) 440 | .withMetaData(conversationInfo).build() 441 | 442 | Kommunicate.updateConversation(conversation: conversation) { response in 443 | switch response { 444 | case .success(let clientConversationId): 445 | callback(["Success", "Successfully updated conversation info"]) 446 | case .failure(let error): 447 | callback(["Error", error.localizedDescription]) 448 | } 449 | } 450 | } 451 | 452 | @objc 453 | func createSettings(_ settingString: String) { 454 | Kommunicate.createSettings(settings: settingString) 455 | } 456 | 457 | @objc 458 | func updateDefaultSetting(_ settingDict: Dictionary, _ callback: @escaping RCTResponseSenderBlock) -> Void { 459 | do { 460 | Kommunicate.defaultConfiguration.clearDefaultConversationSettings() 461 | if let defaultAssignee = settingDict["defaultAssignee"] as? String, !defaultAssignee.isEmpty { 462 | Kommunicate.defaultConfiguration.defaultAssignee = defaultAssignee 463 | } 464 | if let teamId = settingDict["teamId"] as? String, !teamId.isEmpty { 465 | Kommunicate.defaultConfiguration.defaultTeamId = teamId 466 | } 467 | if let skipRouting = settingDict["skipRouting"] as? Bool { 468 | Kommunicate.defaultConfiguration.defaultSkipRouting = skipRouting 469 | } 470 | if let agentIds = settingDict["defaultAgentIds"] as? [String], !agentIds.isEmpty { 471 | Kommunicate.defaultConfiguration.defaultAgentIds = agentIds 472 | } 473 | if let botIds = settingDict["defaultBotIds"] as? [String], !botIds.isEmpty { 474 | Kommunicate.defaultConfiguration.defaultBotIds = botIds 475 | } 476 | callback(["Success", "Successfully set default settings"]) 477 | } 478 | catch { 479 | callback(["Error", "Invalid language data"]) 480 | } 481 | } 482 | 483 | @objc 484 | func fetchUnreadCount(_ callback: RCTResponseSenderBlock) -> Void { 485 | let unreadcount = ALUserService().getTotalUnreadCount()?.stringValue 486 | callback(["Success",unreadcount]) 487 | } 488 | 489 | @objc 490 | func fetchConversationInformation(_ data: Dictionary, _ callback: @escaping RCTResponseSenderBlock) -> Void { 491 | let alChannelService = ALChannelService() 492 | if let conversationID = data["conversationID"] as? String { 493 | guard let channelID = Int(conversationID) as? Int else { 494 | callback(["Error", "conversationID is not Integer"]) 495 | return 496 | } 497 | alChannelService.getChannelInformation(NSNumber(integerLiteral: channelID), orClientChannelKey: nil) { channel in 498 | guard let channel = channel else { 499 | callback(["Error", "Conversation Not Found"]) 500 | return 501 | } 502 | callback(["Success", self.convertDictToString(dict: channel.toDictionary() as NSDictionary) ]) 503 | } 504 | } else if let clientConversationID = data["clientConversationID"] { 505 | guard let clientChannelKey = clientConversationID as? String else { 506 | callback(["Error", "clientConversationID is not String"]) 507 | return 508 | } 509 | 510 | alChannelService.getChannelInformation(nil, orClientChannelKey: clientChannelKey) { channel in 511 | guard let channel = channel else { 512 | callback(["Error", "Conversation Not Found"]) 513 | return 514 | } 515 | callback(["Success", self.convertDictToString(dict: channel.toDictionary() as NSDictionary)]) 516 | } 517 | } else { 518 | callback(["Error", "Object doesn't contain 'conversationID' or 'clientConversationID'"]) 519 | } 520 | } 521 | 522 | @objc 523 | func fetchConversationAssigneeInfo(_ data: Dictionary, _ callback: @escaping RCTResponseSenderBlock) { 524 | let alChannelService = ALChannelService() 525 | if let conversationID = data["conversationID"] as? String { 526 | guard let channelID = Int(conversationID) as? Int else { 527 | callback(["Error", "conversationID is not Integer"]) 528 | return 529 | } 530 | alChannelService.getChannelInformation(NSNumber(integerLiteral: channelID), orClientChannelKey: nil) { channel in 531 | guard let channel = channel else { 532 | callback(["Error", "Channel Not Found"]) 533 | return 534 | } 535 | 536 | if let assigneeId = channel.metadata?["CONVERSATION_ASSIGNEE"] as? String, 537 | let user = ALContactService().loadContact(byKey: "userId", value: assigneeId) { 538 | let userString = self.convertDictToString(dict:user.toDictionary() as NSDictionary) 539 | callback(["Success", userString]) 540 | 541 | } else { 542 | callback(["Error", "Conversation Assignee Not Found"]) 543 | } 544 | } 545 | } else if let clientConversationID = data["clientConversationID"] { 546 | guard let clientChannelKey = clientConversationID as? String else { 547 | callback(["Error", "clientConversationID is not String"]) 548 | return 549 | } 550 | 551 | alChannelService.getChannelInformation(nil, orClientChannelKey: clientChannelKey) { channel in 552 | guard let channel = channel else { 553 | callback(["Error", "Conversation Not Found"]) 554 | return 555 | } 556 | if let assigneeId = channel.metadata?["CONVERSATION_ASSIGNEE"] as? String, 557 | let user = ALContactService().loadContact(byKey: "userId", value: assigneeId) { 558 | let userString = self.convertDictToString(dict:user.toDictionary() as NSDictionary) 559 | callback(["Success", userString]) 560 | } else { 561 | callback(["Error", "Conversation Assignee Not Found"]) 562 | } 563 | } 564 | } else { 565 | callback(["Error", "Object doesn't contain 'conversationID' or 'clientConversationID'"]) 566 | } 567 | } 568 | 569 | 570 | 571 | func closeButtonTapped() { 572 | UIApplication.topViewController()?.dismiss(animated: false, completion: nil) 573 | } 574 | 575 | func userSubmittedResponse(name: String, email: String, phoneNumber: String, password: String) { 576 | UIApplication.topViewController()?.dismiss(animated: false, completion: nil) 577 | 578 | let kmUser = KMUser.init() 579 | guard let applicationKey = appId else { 580 | return 581 | } 582 | 583 | kmUser.applicationId = applicationKey 584 | 585 | if (!email.isEmpty) { 586 | kmUser.userId = email 587 | kmUser.email = email 588 | } else if(!phoneNumber.isEmpty) { 589 | kmUser.contactNumber = phoneNumber 590 | } 591 | 592 | kmUser.contactNumber = phoneNumber 593 | kmUser.displayName = name 594 | 595 | Kommunicate.registerUser(kmUser, completion:{ 596 | response, error in 597 | guard error == nil else{ 598 | self.callback!(["Error", "Unable to login"]) 599 | return 600 | } 601 | self.handleCreateConversation() 602 | }) 603 | } 604 | 605 | func updateUser (displayName: String?, imageLink : String?, status: String?, metadata: NSMutableDictionary?,phoneNumber: String?,email : String?, callback: RCTResponseSenderBlock!) { 606 | 607 | let theUrlString = "\(KMCoreUserDefaultsHandler.getBASEURL() as String)/rest/ws/user/update" 608 | 609 | let dictionary = NSMutableDictionary() 610 | if (displayName != nil) { 611 | dictionary["displayName"] = displayName 612 | } 613 | if imageLink != nil { 614 | dictionary["imageLink"] = imageLink 615 | } 616 | if status != nil { 617 | dictionary["statusMessage"] = status 618 | } 619 | if (metadata != nil) { 620 | dictionary["metadata"] = metadata 621 | } 622 | if phoneNumber != nil { 623 | dictionary["phoneNumber"] = phoneNumber 624 | } 625 | if email != nil { 626 | dictionary["email"] = email 627 | } 628 | var postdata: Data? = nil 629 | do { 630 | postdata = try JSONSerialization.data(withJSONObject: dictionary, options: []) 631 | } catch { 632 | callback(["Error", error.localizedDescription]) 633 | return 634 | } 635 | var theParamString: String? = nil 636 | if let postdata = postdata { 637 | theParamString = String(data: postdata, encoding: .utf8) 638 | } 639 | let theRequest = ALRequestHandler.createPOSTRequest(withUrlString: theUrlString, paramString: theParamString) 640 | ALResponseHandler().authenticateAndProcessRequest(theRequest,andTag: "UPDATE_DISPLAY_NAME_AND_PROFILE_IMAGE", withCompletionHandler: { 641 | theJson, theError in 642 | guard theError == nil else { 643 | callback(["Error", theError!.localizedDescription]) 644 | return 645 | } 646 | guard let apiResponse = ALAPIResponse(jsonString: theJson as? String),apiResponse.status != "error" else { 647 | let reponseError = NSError(domain: "Kommunicate", code: 1, userInfo: [NSLocalizedDescriptionKey : "ERROR IN JSON STATUS WHILE UPDATING USER STATUS"]) 648 | callback(["Error", reponseError.localizedDescription]) 649 | return 650 | } 651 | 652 | //Update the local contact 653 | let alContact = ALContactDBService().loadContact(byKey: "userId", value: KMCoreUserDefaultsHandler.getUserId()) 654 | if alContact == nil { 655 | callback(["Error", "User not found"]) 656 | return 657 | } 658 | if email != nil { 659 | alContact?.email = email 660 | } 661 | if phoneNumber != nil { 662 | alContact?.contactNumber = phoneNumber 663 | } 664 | if displayName != nil { 665 | alContact?.displayName = displayName 666 | } 667 | if imageLink != nil { 668 | alContact?.contactImageUrl = imageLink 669 | } 670 | if metadata != nil { 671 | alContact?.metadata = metadata 672 | } 673 | ALContactDBService().updateContact(inDatabase: alContact) 674 | 675 | callback(["Success", "User details updated"]) 676 | }) 677 | } 678 | 679 | // Events 680 | 681 | open override func supportedEvents() -> [String]! { 682 | return ["onMessageReceived", "onMessageSent", "onRichMessageButtonClick", "onStartNewConversation", "onSubmitRatingClick", "onBackButtonClicked", "onFaqClick", "onConversationRestarted", "onConversationInfoClicked"] 683 | } 684 | 685 | open override func addListener(_ eventName: String!) { 686 | Kommunicate.subscribeCustomEvents(events: [KMCustomEvent.messageReceive, KMCustomEvent.messageSend,KMCustomEvent.faqClick, KMCustomEvent.newConversation, KMCustomEvent.submitRatingClick, KMCustomEvent.restartConversationClick, KMCustomEvent.richMessageClick, KMCustomEvent.conversationBackPress, KMCustomEvent.conversationListBackPress, KMCustomEvent.conversationInfoClick ], callback: self) 687 | } 688 | 689 | open override func removeListeners(_ count: Double) { 690 | // TODO: call unsubscribe listeners function 691 | } 692 | 693 | func messageSent(message: ALMessage) { 694 | guard let messageDict = message.dictionary() as? NSDictionary else { return } 695 | KMEventEmitter.emitter.sendEvent(withName: "onMessageSent", body: ["data":convertDictToString(dict: messageDict)]) 696 | } 697 | 698 | func messageReceived(message: ALMessage) { 699 | guard let messageDict = message.dictionary() as? NSDictionary else { return } 700 | KMEventEmitter.emitter.sendEvent(withName: "onMessageReceived", body: ["data":convertDictToString(dict: messageDict)]) 701 | } 702 | 703 | func conversationRestarted(conversationId: String) { 704 | KMEventEmitter.emitter.sendEvent(withName: "onConversationRestarted", body: ["data":conversationId]) 705 | 706 | } 707 | 708 | func onBackButtonClick(isConversationOpened: Bool) { 709 | KMEventEmitter.emitter.sendEvent(withName: "onBackButtonClicked", body: ["data":isConversationOpened]) 710 | } 711 | 712 | func faqClicked(url: String) { 713 | KMEventEmitter.emitter.sendEvent(withName: "onFaqClick", body: ["data":url]) 714 | } 715 | 716 | func conversationCreated(conversationId: String) { 717 | KMEventEmitter.emitter.sendEvent(withName: "onStartNewConversation", body: ["data":conversationId]) 718 | } 719 | 720 | func ratingSubmitted(conversationId: String, rating: Int, comment: String) { 721 | let ratingDict: NSDictionary = ["conversationId": conversationId, "rating":rating, "feedback": comment] 722 | KMEventEmitter.emitter.sendEvent(withName: "onSubmitRatingClick", body: ["data": convertDictToString(dict: ratingDict)]) 723 | } 724 | 725 | func conversationResolved(conversationId: String) { 726 | KMEventEmitter.emitter.sendEvent(withName: "onConversationResolve", body: ["data":conversationId]) 727 | } 728 | 729 | func richMessageClicked(conversationId: String, action: Any, type: String) { 730 | let jsonEncoder = JSONEncoder() 731 | var actionString: String = "" 732 | if action is KMListTemplate.Element, let actionElement = action as? KMListTemplate.Element, 733 | let jsonData = try? jsonEncoder.encode(actionElement) 734 | { 735 | actionString = String(data: jsonData, encoding: String.Encoding.utf8) ?? "" 736 | } else if let actionDict = action as? [String: Any] { 737 | actionString = convertDictToString(dict: actionDict as NSDictionary) 738 | } else { 739 | print("Could not parse Rich Message action object") 740 | } 741 | let richMessageDict: [String:Any] = ["conversationId": conversationId,"action": actionString, "actionType": type] 742 | KMEventEmitter.emitter.sendEvent(withName: "onRichMessageButtonClick", body: ["data": convertDictToString(dict: richMessageDict as NSDictionary)]) 743 | } 744 | 745 | func conversationInfoClicked() { 746 | KMEventEmitter.emitter.sendEvent(withName: "onConversationInfoClicked", body: nil) 747 | } 748 | 749 | func convertDictToString(dict: NSDictionary) -> String { 750 | guard let data = try? JSONSerialization.data(withJSONObject: dict, options: []) else { 751 | return "" 752 | } 753 | return String(data:data, encoding:.utf8) ?? "" 754 | } 755 | 756 | @objc 757 | func closeConversationScreen() -> Void { 758 | DispatchQueue.main.async { 759 | UIApplication.topViewController()?.dismiss(animated: false, completion: nil) 760 | } 761 | } 762 | 763 | } 764 | 765 | extension UIApplication { 766 | class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? { 767 | if let navigationController = controller as? UINavigationController { 768 | return topViewController(controller: navigationController.visibleViewController) 769 | } 770 | if let tabController = controller as? UITabBarController { 771 | if let selected = tabController.selectedViewController { 772 | return topViewController(controller: selected) 773 | } 774 | } 775 | if let presented = controller?.presentedViewController { 776 | return topViewController(controller: presented) 777 | } 778 | return controller 779 | } 780 | } 781 | 782 | extension ALContact { 783 | func toDictionary() -> [String:Any] { 784 | var dict: [String: Any] = [:] 785 | dict["userID"] = self.userId 786 | dict["displayName"] = self.displayName 787 | dict["imageURL"] = self.contactImageUrl 788 | dict["email"] = self.email 789 | dict["roleType"] = self.roleType 790 | dict["metadata"] = self.metadata 791 | return dict 792 | } 793 | } 794 | 795 | extension ALChannel { 796 | func toDictionary() -> [String:Any] { 797 | var dict: [String: Any] = [:] 798 | dict["key"] = self.key 799 | dict["clientConversationID"] = self.clientChannelKey 800 | dict["name"] = self.name 801 | dict["imageUrl"] = self.channelImageURL 802 | dict["adminKey"] = self.adminKey 803 | dict["unreadCount"] = self.unreadCount 804 | dict["metadata"] = self.metadata 805 | dict["userCount"] = self.userCount 806 | return dict 807 | } 808 | } 809 | -------------------------------------------------------------------------------- /ios/RNKommunicateChat.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B3E7B58A1CC2AC0600A0062D /* RNKommunicateChat.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNKommunicateChat.m */; }; 11 | /* End PBXBuildFile section */ 12 | 13 | /* Begin PBXCopyFilesBuildPhase section */ 14 | 58B511D91A9E6C8500147676 /* CopyFiles */ = { 15 | isa = PBXCopyFilesBuildPhase; 16 | buildActionMask = 2147483647; 17 | dstPath = "include/$(PRODUCT_NAME)"; 18 | dstSubfolderSpec = 16; 19 | files = ( 20 | ); 21 | runOnlyForDeploymentPostprocessing = 0; 22 | }; 23 | /* End PBXCopyFilesBuildPhase section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | 134814201AA4EA6300B7C361 /* libRNKommunicateChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNKommunicateChat.a; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | B3E7B5881CC2AC0600A0062D /* RNKommunicateChat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNKommunicateChat.h; sourceTree = ""; }; 28 | B3E7B5891CC2AC0600A0062D /* RNKommunicateChat.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNKommunicateChat.m; sourceTree = ""; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | 58B511D81A9E6C8500147676 /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXFrameworksBuildPhase section */ 40 | 41 | /* Begin PBXGroup section */ 42 | 134814211AA4EA7D00B7C361 /* Products */ = { 43 | isa = PBXGroup; 44 | children = ( 45 | 134814201AA4EA6300B7C361 /* libRNKommunicateChat.a */, 46 | ); 47 | name = Products; 48 | sourceTree = ""; 49 | }; 50 | 58B511D21A9E6C8500147676 = { 51 | isa = PBXGroup; 52 | children = ( 53 | B3E7B5881CC2AC0600A0062D /* RNKommunicateChat.h */, 54 | B3E7B5891CC2AC0600A0062D /* RNKommunicateChat.m */, 55 | 134814211AA4EA7D00B7C361 /* Products */, 56 | ); 57 | sourceTree = ""; 58 | }; 59 | /* End PBXGroup section */ 60 | 61 | /* Begin PBXNativeTarget section */ 62 | 58B511DA1A9E6C8500147676 /* RNKommunicateChat */ = { 63 | isa = PBXNativeTarget; 64 | buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNKommunicateChat" */; 65 | buildPhases = ( 66 | 58B511D71A9E6C8500147676 /* Sources */, 67 | 58B511D81A9E6C8500147676 /* Frameworks */, 68 | 58B511D91A9E6C8500147676 /* CopyFiles */, 69 | ); 70 | buildRules = ( 71 | ); 72 | dependencies = ( 73 | ); 74 | name = RNKommunicateChat; 75 | productName = RCTDataManager; 76 | productReference = 134814201AA4EA6300B7C361 /* libRNKommunicateChat.a */; 77 | productType = "com.apple.product-type.library.static"; 78 | }; 79 | /* End PBXNativeTarget section */ 80 | 81 | /* Begin PBXProject section */ 82 | 58B511D31A9E6C8500147676 /* Project object */ = { 83 | isa = PBXProject; 84 | attributes = { 85 | LastUpgradeCheck = 0830; 86 | ORGANIZATIONNAME = Facebook; 87 | TargetAttributes = { 88 | 58B511DA1A9E6C8500147676 = { 89 | CreatedOnToolsVersion = 6.1.1; 90 | }; 91 | }; 92 | }; 93 | buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNKommunicateChat" */; 94 | compatibilityVersion = "Xcode 3.2"; 95 | developmentRegion = English; 96 | hasScannedForEncodings = 0; 97 | knownRegions = ( 98 | en, 99 | ); 100 | mainGroup = 58B511D21A9E6C8500147676; 101 | productRefGroup = 58B511D21A9E6C8500147676; 102 | projectDirPath = ""; 103 | projectRoot = ""; 104 | targets = ( 105 | 58B511DA1A9E6C8500147676 /* RNKommunicateChat */, 106 | ); 107 | }; 108 | /* End PBXProject section */ 109 | 110 | /* Begin PBXSourcesBuildPhase section */ 111 | 58B511D71A9E6C8500147676 /* Sources */ = { 112 | isa = PBXSourcesBuildPhase; 113 | buildActionMask = 2147483647; 114 | files = ( 115 | B3E7B58A1CC2AC0600A0062D /* RNKommunicateChat.m in Sources */, 116 | ); 117 | runOnlyForDeploymentPostprocessing = 0; 118 | }; 119 | /* End PBXSourcesBuildPhase section */ 120 | 121 | /* Begin XCBuildConfiguration section */ 122 | 58B511ED1A9E6C8500147676 /* Debug */ = { 123 | isa = XCBuildConfiguration; 124 | buildSettings = { 125 | ALWAYS_SEARCH_USER_PATHS = NO; 126 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 127 | CLANG_CXX_LIBRARY = "libc++"; 128 | CLANG_ENABLE_MODULES = YES; 129 | CLANG_ENABLE_OBJC_ARC = YES; 130 | CLANG_WARN_BOOL_CONVERSION = YES; 131 | CLANG_WARN_CONSTANT_CONVERSION = YES; 132 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 133 | CLANG_WARN_EMPTY_BODY = YES; 134 | CLANG_WARN_ENUM_CONVERSION = YES; 135 | CLANG_WARN_INFINITE_RECURSION = YES; 136 | CLANG_WARN_INT_CONVERSION = YES; 137 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 138 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 139 | CLANG_WARN_UNREACHABLE_CODE = YES; 140 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 141 | COPY_PHASE_STRIP = NO; 142 | ENABLE_STRICT_OBJC_MSGSEND = YES; 143 | ENABLE_TESTABILITY = YES; 144 | GCC_C_LANGUAGE_STANDARD = gnu99; 145 | GCC_DYNAMIC_NO_PIC = NO; 146 | GCC_NO_COMMON_BLOCKS = YES; 147 | GCC_OPTIMIZATION_LEVEL = 0; 148 | GCC_PREPROCESSOR_DEFINITIONS = ( 149 | "DEBUG=1", 150 | "$(inherited)", 151 | ); 152 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 153 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 154 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 155 | GCC_WARN_UNDECLARED_SELECTOR = YES; 156 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 157 | GCC_WARN_UNUSED_FUNCTION = YES; 158 | GCC_WARN_UNUSED_VARIABLE = YES; 159 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 160 | MTL_ENABLE_DEBUG_INFO = YES; 161 | ONLY_ACTIVE_ARCH = YES; 162 | SDKROOT = iphoneos; 163 | }; 164 | name = Debug; 165 | }; 166 | 58B511EE1A9E6C8500147676 /* Release */ = { 167 | isa = XCBuildConfiguration; 168 | buildSettings = { 169 | ALWAYS_SEARCH_USER_PATHS = NO; 170 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 171 | CLANG_CXX_LIBRARY = "libc++"; 172 | CLANG_ENABLE_MODULES = YES; 173 | CLANG_ENABLE_OBJC_ARC = YES; 174 | CLANG_WARN_BOOL_CONVERSION = YES; 175 | CLANG_WARN_CONSTANT_CONVERSION = YES; 176 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 177 | CLANG_WARN_EMPTY_BODY = YES; 178 | CLANG_WARN_ENUM_CONVERSION = YES; 179 | CLANG_WARN_INFINITE_RECURSION = YES; 180 | CLANG_WARN_INT_CONVERSION = YES; 181 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 182 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 183 | CLANG_WARN_UNREACHABLE_CODE = YES; 184 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 185 | COPY_PHASE_STRIP = YES; 186 | ENABLE_NS_ASSERTIONS = NO; 187 | ENABLE_STRICT_OBJC_MSGSEND = YES; 188 | GCC_C_LANGUAGE_STANDARD = gnu99; 189 | GCC_NO_COMMON_BLOCKS = YES; 190 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 191 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 192 | GCC_WARN_UNDECLARED_SELECTOR = YES; 193 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 194 | GCC_WARN_UNUSED_FUNCTION = YES; 195 | GCC_WARN_UNUSED_VARIABLE = YES; 196 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 197 | MTL_ENABLE_DEBUG_INFO = NO; 198 | SDKROOT = iphoneos; 199 | VALIDATE_PRODUCT = YES; 200 | }; 201 | name = Release; 202 | }; 203 | 58B511F01A9E6C8500147676 /* Debug */ = { 204 | isa = XCBuildConfiguration; 205 | buildSettings = { 206 | HEADER_SEARCH_PATHS = ( 207 | "$(inherited)", 208 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 209 | "$(SRCROOT)/../../../React/**", 210 | "$(SRCROOT)/../../react-native/React/**", 211 | ); 212 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 213 | OTHER_LDFLAGS = "-ObjC"; 214 | PRODUCT_NAME = RNKommunicateChat; 215 | SKIP_INSTALL = YES; 216 | }; 217 | name = Debug; 218 | }; 219 | 58B511F11A9E6C8500147676 /* Release */ = { 220 | isa = XCBuildConfiguration; 221 | buildSettings = { 222 | HEADER_SEARCH_PATHS = ( 223 | "$(inherited)", 224 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 225 | "$(SRCROOT)/../../../React/**", 226 | "$(SRCROOT)/../../react-native/React/**", 227 | ); 228 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 229 | OTHER_LDFLAGS = "-ObjC"; 230 | PRODUCT_NAME = RNKommunicateChat; 231 | SKIP_INSTALL = YES; 232 | }; 233 | name = Release; 234 | }; 235 | /* End XCBuildConfiguration section */ 236 | 237 | /* Begin XCConfigurationList section */ 238 | 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNKommunicateChat" */ = { 239 | isa = XCConfigurationList; 240 | buildConfigurations = ( 241 | 58B511ED1A9E6C8500147676 /* Debug */, 242 | 58B511EE1A9E6C8500147676 /* Release */, 243 | ); 244 | defaultConfigurationIsVisible = 0; 245 | defaultConfigurationName = Release; 246 | }; 247 | 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNKommunicateChat" */ = { 248 | isa = XCConfigurationList; 249 | buildConfigurations = ( 250 | 58B511F01A9E6C8500147676 /* Debug */, 251 | 58B511F11A9E6C8500147676 /* Release */, 252 | ); 253 | defaultConfigurationIsVisible = 0; 254 | defaultConfigurationName = Release; 255 | }; 256 | /* End XCConfigurationList section */ 257 | }; 258 | rootObject = 58B511D31A9E6C8500147676 /* Project object */; 259 | } 260 | -------------------------------------------------------------------------------- /ios/RNKommunicateChat.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | 3 | 5 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-kommunicate-chat", 3 | "version": "2.5.1", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "react-native" 11 | ], 12 | "author": "codemonk@kommunicate.io", 13 | "license": "" 14 | } --------------------------------------------------------------------------------