diff --git a/README.md b/README.md index c878ce5..f549cb6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![SupportedLanguages](https://img.shields.io/badge/Platforms-iOS%20%7C%20%20Android-green.svg)](https://www.zoho.com/salesiq/help/developer-section/react-native-sdk-installation.html) [![Version](https://img.shields.io/badge/version-6.1.1-blue.svg)](https://mobilisten.io/) [![Mobilisten NPM CD](https://github.com/zoho/SalesIQ-Mobilisten-ReactNative/workflows/Mobilisten%20NPM%20CD/badge.svg)](https://github.com/zoho/SalesIQ-Mobilisten-ReactNative/actions) +[![SupportedLanguages](https://img.shields.io/badge/Platforms-iOS%20%7C%20%20Android-green.svg)](https://www.zoho.com/salesiq/help/developer-section/react-native-sdk-installation.html) [![Version](https://img.shields.io/badge/version-7.0.0-blue.svg)](https://mobilisten.io/) [![Mobilisten NPM CD](https://github.com/zoho/SalesIQ-Mobilisten-ReactNative/workflows/Mobilisten%20NPM%20CD/badge.svg)](https://github.com/zoho/SalesIQ-Mobilisten-ReactNative/actions) # React Native module for SalesIQ Mobilisten SDK @@ -10,7 +10,7 @@ Connect with customers at every step of their journey. Give them the best in-app ## Requirements **iOS**: Minimum deployment target should be set to iOS 11. -**Android**: Android API level 16 and above is required. +**Android**: Android API level 21 and above is required. ## Installation Follow the below steps given below to complete installation of **Mobilisten** in your React-Native app. diff --git a/android/build.gradle b/android/build.gradle index bff7822..edd3b15 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -44,5 +44,6 @@ repositories { dependencies { implementation 'com.facebook.react:react-native:+' + implementation 'com.squareup.retrofit2:converter-gson:2.9.0' implementation 'com.zoho.salesiq:mobilisten:6.0.2' } diff --git a/android/src/main/java/com/zohosalesiq/reactlibrary/RNZohoSalesIQ.java b/android/src/main/java/com/zohosalesiq/reactlibrary/RNZohoSalesIQ.java index d8d7847..c0a6784 100644 --- a/android/src/main/java/com/zohosalesiq/reactlibrary/RNZohoSalesIQ.java +++ b/android/src/main/java/com/zohosalesiq/reactlibrary/RNZohoSalesIQ.java @@ -16,6 +16,7 @@ import androidx.annotation.Nullable; import androidx.appcompat.content.res.AppCompatResources; +import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; @@ -27,6 +28,7 @@ import com.facebook.react.bridge.WritableNativeArray; import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.modules.core.DeviceEventManagerModule; +import com.google.gson.reflect.TypeToken; import com.zoho.commons.ChatComponent; import com.zoho.commons.LauncherModes; import com.zoho.commons.LauncherProperties; @@ -52,19 +54,28 @@ import com.zoho.livechat.android.listeners.SalesIQListener; import com.zoho.livechat.android.models.SalesIQArticle; import com.zoho.livechat.android.models.SalesIQArticleCategory; +import com.zoho.livechat.android.modules.common.DataModule; import com.zoho.livechat.android.modules.knowledgebase.ui.entities.Resource; +import com.zoho.livechat.android.modules.knowledgebase.ui.entities.ResourceCategory; +import com.zoho.livechat.android.modules.knowledgebase.ui.entities.ResourceDepartment; import com.zoho.livechat.android.modules.knowledgebase.ui.listeners.OpenResourceListener; +import com.zoho.livechat.android.modules.knowledgebase.ui.listeners.ResourceCategoryListener; +import com.zoho.livechat.android.modules.knowledgebase.ui.listeners.ResourceDepartmentsListener; +import com.zoho.livechat.android.modules.knowledgebase.ui.listeners.ResourceListener; import com.zoho.livechat.android.modules.knowledgebase.ui.listeners.ResourcesListener; import com.zoho.livechat.android.modules.knowledgebase.ui.listeners.SalesIQKnowledgeBaseListener; import com.zoho.livechat.android.operation.SalesIQApplicationManager; import com.zoho.livechat.android.utils.LiveChatUtil; import com.zoho.salesiqembed.ZohoSalesIQ; +import com.zoho.salesiqembed.ktx.GsonExtensionsKt; import java.io.ByteArrayOutputStream; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Hashtable; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.UUID; @@ -137,6 +148,12 @@ public class RNZohoSalesIQ extends ReactContextBaseJavaModule { private static final Handler HANDLER = new Handler(Looper.getMainLooper()); + private static final String EVENT_RESOURCE_LIKED = "EVENT_RESOURCE_LIKED"; // No I18N + private static final String EVENT_RESOURCE_DISLIKED = "EVENT_RESOURCE_DISLIKED"; // No I18N + private static final String EVENT_RESOURCE_OPENED = "EVENT_RESOURCE_OPENED"; // No I18N + private static final String EVENT_RESOURCE_CLOSED = "EVENT_RESOURCE_CLOSED"; // No I18N + + enum Tab { CONVERSATIONS("TAB_CONVERSATIONS"), @Deprecated FAQ("TAB_FAQ"), @@ -192,16 +209,24 @@ public Map getConstants() { constants.put("CHAT_QUEUE_POSITION_CHANGED", EVENT_CHAT_QUEUE_POSITION_CHANGED); // No I18N constants.put("CHAT_UNREAD_COUNT_CHANGED", EVENT_CHAT_UNREAD_COUNT_CHANGED); // No I18N + constants.put("EVENT_RESOURCE_LIKED", EVENT_RESOURCE_LIKED); // No I18N + constants.put("EVENT_RESOURCE_DISLIKED", EVENT_RESOURCE_DISLIKED); // No I18N + constants.put("EVENT_RESOURCE_OPENED", EVENT_RESOURCE_OPENED); // No I18N + constants.put("EVENT_RESOURCE_CLOSED", EVENT_RESOURCE_CLOSED); // No I18N + constants.put("ARTICLE_LIKED", EVENT_ARTICLE_LIKED); // No I18N constants.put("ARTICLE_DISLIKED", EVENT_ARTICLE_DISLIKED); // No I18N constants.put("ARTICLE_OPENED", EVENT_ARTICLE_OPENED); // No I18N - constants.put("ARTICLE_CLOSED", EVENT_ARTICLE_CLOSED); // No I18N + constants.put("ARTICLE_CLOSED", EVENT_ARTICLE_CLOSED); + constants.put("LAUNCHER_MODE_STATIC", LAUNCHER_MODE_STATIC); // No I18N constants.put("LAUNCHER_MODE_FLOATING", LAUNCHER_MODE_FLOATING); // No I18N constants.put("LAUNCHER_HORIZONTAL_RIGHT", LAUNCHER_HORIZONTAL_RIGHT); // No I18N constants.put("LAUNCHER_HORIZONTAL_LEFT", LAUNCHER_HORIZONTAL_LEFT); // No I18N constants.put("LAUNCHER_VERTICAL_TOP", LAUNCHER_VERTICAL_TOP); // No I18N constants.put("LAUNCHER_VERTICAL_BOTTOM", LAUNCHER_VERTICAL_BOTTOM); // No I18N + + constants.put("RESOURCE_ARTICLES", RESOURCE_ARTICLES); // No I18N return constants; } @@ -1202,21 +1227,37 @@ public void handleOperatorsOffline() { @Override public void handleResourceOpened(@NonNull ZohoSalesIQ.ResourceType resourceType, @Nullable Resource resource) { + WritableMap resourceMap = new WritableNativeMap(); + resourceMap.putString("type", RESOURCE_ARTICLES); // No I18N + resourceMap.putMap("resource", getWritableMap(resource)); // No I18N + eventEmitter(EVENT_RESOURCE_OPENED, resourceMap); eventEmitter(EVENT_ARTICLE_OPENED, resource != null ? resource.getId() : null); } @Override public void handleResourceClosed(@NonNull ZohoSalesIQ.ResourceType resourceType, @Nullable Resource resource) { + WritableMap resourceMap = new WritableNativeMap(); + resourceMap.putString("type", RESOURCE_ARTICLES); // No I18N + resourceMap.putMap("resource", getWritableMap(resource)); // No I18N + eventEmitter(EVENT_RESOURCE_CLOSED, resourceMap); eventEmitter(EVENT_ARTICLE_CLOSED, resource != null ? resource.getId() : null); } @Override public void handleResourceLiked(@NonNull ZohoSalesIQ.ResourceType resourceType, @Nullable Resource resource) { + WritableMap resourceMap = new WritableNativeMap(); + resourceMap.putString("type", RESOURCE_ARTICLES); // No I18N + resourceMap.putMap("resource", getWritableMap(resource)); // No I18N + eventEmitter(EVENT_RESOURCE_LIKED, resourceMap); eventEmitter(EVENT_ARTICLE_LIKED, resource != null ? resource.getId() : null); } @Override public void handleResourceDisliked(@NonNull ZohoSalesIQ.ResourceType resourceType, @Nullable Resource resource) { + WritableMap resourceMap = new WritableNativeMap(); + resourceMap.putString("type", RESOURCE_ARTICLES); // No I18N + resourceMap.putMap("resource", getWritableMap(resource)); // No I18N + eventEmitter(EVENT_RESOURCE_DISLIKED, resourceMap); eventEmitter(EVENT_ARTICLE_DISLIKED, resource != null ? resource.getId() : null); } @@ -1448,4 +1489,202 @@ void setLoggerPathForiOS(final String value) {} @ReactMethod void writeLogForiOS(final String message, final String logLevel, final Callback callback) {} + + public static final String RESOURCE_ARTICLES = "RESOURCE_ARTICLES"; // No I18N + + private @Nullable ZohoSalesIQ.ResourceType getResourceType(String value) { + ZohoSalesIQ.ResourceType resourceType; + if (RESOURCE_ARTICLES.equals(value)) { + resourceType = ZohoSalesIQ.ResourceType.Articles; + } else { + resourceType = null; + } + return resourceType; + } + + @ReactMethod + public void setKnowledgeBaseVisibility(final String type, final boolean shouldShow) { + executeIfResourceTypeIsValid(type, null, () -> ZohoSalesIQ.KnowledgeBase.setVisibility(getResourceType(type), shouldShow)); + } + + @ReactMethod + public void categorizeKnowledgeBase(final String type, final boolean shouldCategorize) { + executeIfResourceTypeIsValid(type, null, () -> ZohoSalesIQ.KnowledgeBase.categorize(getResourceType(type), shouldCategorize)); + } + + @ReactMethod + public void combineKnowledgeBaseDepartments(final String type, final boolean merge) { + executeIfResourceTypeIsValid(type, null, () -> ZohoSalesIQ.KnowledgeBase.combineDepartments(getResourceType(type), merge)); + } + + //void setRecentShowLimit(String type) { + // ZohoSalesIQ.KnowledgeBase.setRecentShowLimit(value) + // } + + @ReactMethod + public void getKnowledgeBaseResourceDepartments(final Callback callback) { + ZohoSalesIQ.KnowledgeBase.getResourceDepartments(new ResourceDepartmentsListener() { + @Override + public void onSuccess(@NonNull List resourceDepartments) { + callback.invoke(null, getWritableArray(resourceDepartments)); + } + + @Override + public void onFailure(int code, @Nullable String message) { + callback.invoke(getErrorMap(code, message), null); + } + }); + } + + @ReactMethod + public void openKnowledgeBase(final String type, final String id, final Callback callback) { + executeIfResourceTypeIsValid(type, callback, () -> ZohoSalesIQ.KnowledgeBase.open(getResourceType(type), id, new OpenResourceListener() { + @Override + public void onSuccess() { + callback.invoke(null, Boolean.TRUE); + } + + @Override + public void onFailure(int code, @Nullable String message) { + callback.invoke(getErrorMap(code, message), null); + } + })); + } + + @ReactMethod + public void getKnowledgeBaseSingleResource(final String type, final String id, final Callback callback) { + executeIfResourceTypeIsValid(type, callback, () -> ZohoSalesIQ.KnowledgeBase.getSingleResource(getResourceType(type), id, new ResourceListener() { + @Override + public void onSuccess(@Nullable Resource resource) { + callback.invoke(null, getWritableMap(resource)); + } + + @Override + public void onFailure(int code, @Nullable String message) { + callback.invoke(getErrorMap(code, message), null); + } + })); + } + + @ReactMethod + public void getKnowledgeBaseResources(final String type, final String departmentID, final String parentCategoryID, final int page, final int limit, final String searchKey, final Callback callback) { + executeIfResourceTypeIsValid(type, callback, () -> ZohoSalesIQ.KnowledgeBase.getResources(getResourceType(type), departmentID, parentCategoryID, searchKey, page, limit, new ResourcesListener() { + @Override + public void onSuccess(@NonNull List resources, boolean moreDataAvailable) { + callback.invoke(null, getWritableArray(resources), moreDataAvailable); + } + + @Override + public void onFailure(int code, @Nullable String message) { + callback.invoke(getErrorMap(code, message), null); + } + })); + } + + @ReactMethod + public void getKnowledgeBaseCategories(final String type, final String departmentID, final String parentCategoryID, final Callback callback) { + executeIfResourceTypeIsValid(type, callback, () -> ZohoSalesIQ.KnowledgeBase.getCategories(getResourceType(type), departmentID, parentCategoryID, new ResourceCategoryListener() { + @Override + public void onSuccess(@NonNull List resourceCategories) { + callback.invoke(null, getWritableArray(resourceCategories)); + } + + @Override + public void onFailure(int code, @Nullable String message) { + callback.invoke(getErrorMap(code, message), null); + } + })); + } + + static WritableArray getWritableArray(Object object) { + WritableNativeArray finalWritableNativeArray = new WritableNativeArray(); + if (object != null) { + Type mapType = new TypeToken>>() {}.getType(); + List> mapList = GsonExtensionsKt.fromJsonSafe(DataModule.getGson(), DataModule.getGson().toJson(object), mapType); + WritableNativeArray writableNativeArray = Arguments.makeNativeArray(mapList); + int size = writableNativeArray.size(); + for (int index = 0; index < size; index++) { + finalWritableNativeArray.pushMap(getWritableMap(writableNativeArray.getMap(index))); + } + } + return finalWritableNativeArray; + } + + static WritableMap getWritableMap(Object object) { + WritableNativeMap writableNativeMap = new WritableNativeMap(); + if (object != null) { + Map map = null; + if (!(object instanceof ReadableMap)) { + Type mapType = new TypeToken>() {}.getType(); + map = GsonExtensionsKt.fromJsonSafe(DataModule.getGson(), DataModule.getGson().toJson(object), mapType); + } + ReadableMap readableMap = object instanceof ReadableMap ? (ReadableMap) object : Arguments.makeNativeMap(map); + for (Iterator> it = (readableMap).getEntryIterator(); it.hasNext(); ) { + Map.Entry entry = it.next(); + String key = convertToCamelCase(entry.getKey()); + Object value = entry.getValue(); + if (value == null) { + writableNativeMap.putNull(key); + } else if (value instanceof ReadableArray) { + writableNativeMap.putArray(key, (ReadableArray) value); + } else if (value instanceof String) { + writableNativeMap.putString(key, (String) value); + } else if (value instanceof Number) { + if (value instanceof Integer) { + writableNativeMap.putInt(key, (Integer) value); + } else { + writableNativeMap.putDouble(key, ((Number) value).doubleValue()); + } + } else if (value instanceof Boolean) { + writableNativeMap.putBoolean(key, (Boolean) value); + } else if (value instanceof ReadableMap) { + writableNativeMap.putMap(key, getWritableMap(value)); + } + } + } + return writableNativeMap; + } + + static String convertToCamelCase(String input) { + StringBuilder camelCase = new StringBuilder(30); + boolean capitalizeNext = false; + + if (input != null) { + for (char c : input.toCharArray()) { + if (c == '_') { + capitalizeNext = true; + } else { + camelCase.append(capitalizeNext ? Character.toUpperCase(c) : c); + capitalizeNext = false; + } + } + } + + return camelCase.toString(); + } + + WritableMap getErrorMap(int code, String message) { + WritableMap errorMap = new WritableNativeMap(); + errorMap.putInt("code", code); // No I18N + errorMap.putString("message", message); // No I18N + return errorMap; + } + + private void executeIfResourceTypeIsValid(String type, Callback callback, Runnable runnable) { + ZohoSalesIQ.ResourceType resourceType = getResourceType(type); + if (resourceType != null) { + runnable.run(); + } else { + if (callback != null) { + callback.invoke(getResourceTypeErrorMap()); + } + } + } + + static WritableMap getResourceTypeErrorMap() { + WritableMap errorMap = new WritableNativeMap(); + errorMap.putInt("code", 100); // No I18N + errorMap.putString("message", "Invalid resource type"); // No I18N + return errorMap; + } } diff --git a/components/zohosalesiqJSWrapper.js b/components/zohosalesiqJSWrapper.js index 480d984..439023d 100644 --- a/components/zohosalesiqJSWrapper.js +++ b/components/zohosalesiqJSWrapper.js @@ -4,8 +4,12 @@ import { NativeEventEmitter } from 'react-native'; //No I18N const emitter = new NativeEventEmitter(RNZohoSalesIQ); module.exports = { + Tab: { - CONVERSATIONS: 'TAB_CONVERSATIONS', FAQ: 'TAB_FAQ' + CONVERSATIONS: 'TAB_CONVERSATIONS', //No I18N + KNOWLEDGE_BASE: 'TAB_KNOWLEDGE_BASE', //No I18N + // DEPRECATED + FAQ: 'TAB_FAQ' //No I18N }, TYPE_OPEN: RNZohoSalesIQ.TYPE_OPEN, TYPE_WAITING: RNZohoSalesIQ.TYPE_WAITING, @@ -46,11 +50,20 @@ module.exports = { EVENT_BOT_TRIGGER: RNZohoSalesIQ.BOT_TRIGGER, EVENT_HANDLE_URL: RNZohoSalesIQ.EVENT_HANDLE_URL, + EVENT_RESOURCE_OPENED: RNZohoSalesIQ.EVENT_RESOURCE_OPENED, + EVENT_RESOURCE_CLOSED: RNZohoSalesIQ.EVENT_RESOURCE_CLOSED, + EVENT_RESOURCE_LIKED: RNZohoSalesIQ.EVENT_RESOURCE_LIKED, + EVENT_RESOURCE_DISLIKED: RNZohoSalesIQ.EVENT_RESOURCE_DISLIKED, + Event: { OPEN_URL: RNZohoSalesIQ.EVENT_OPEN_URL, COMPLETE_CHAT_ACTION: RNZohoSalesIQ.EVENT_COMPLETE_CHAT_ACTION }, + Resource: { + ARTICLES: RNZohoSalesIQ.RESOURCE_ARTICLES + }, + Launcher: { MODE_STATIC: RNZohoSalesIQ.LAUNCHER_MODE_STATIC, MODE_FLOATING: RNZohoSalesIQ.LAUNCHER_MODE_FLOATING, @@ -292,6 +305,39 @@ module.exports = { writeLogForiOS: function (log, level, callback) { RNZohoSalesIQ.writeLogForiOS(log, level, callback); } + }, + + KnowledgeBase: { +// isEnabled: function (type, callback) { +// RNZohoSalesIQ.isKnowledgeBaseEnabled(type, callback); +// }, + setVisibility: function (type, shouldShow) { + RNZohoSalesIQ.setKnowledgeBaseVisibility(type, shouldShow) + }, + categorize: function (type, shouldCategorize) { + RNZohoSalesIQ.categorizeKnowledgeBase(type, shouldCategorize) + }, + combineDepartments: function (type, merge) { + RNZohoSalesIQ.combineKnowledgeBaseDepartments(type, merge) + }, + // setRecentShowLimit: function (value) { + // RNZohoSalesIQ.setKnowledgeBaseRecentShowLimit(value) + // }, + getResourceDepartments: function (callback) { + RNZohoSalesIQ.getKnowledgeBaseResourceDepartments(callback); + }, + open: function (type, id, callback) { + RNZohoSalesIQ.openKnowledgeBase(type, id, callback); + }, + getSingleResource: function (type, id, callback) { + RNZohoSalesIQ.getKnowledgeBaseSingleResource(type, id, callback); + }, + getResources: function (type, departmentId = null, parentCategoryId = null, page = 1, limit = 99, searchKey = null, callback) { + RNZohoSalesIQ.getKnowledgeBaseResources(type, departmentId, parentCategoryId, page, limit, searchKey, callback); + }, + getCategories: function (type, departmentId = null, parentCategoryId = null, callback) { + RNZohoSalesIQ.getKnowledgeBaseCategories(type, departmentId, parentCategoryId, callback); + } } // MessageStatus: { diff --git a/ios/RNZohoSalesIQ.m b/ios/RNZohoSalesIQ.m index a947007..61e7c54 100644 --- a/ios/RNZohoSalesIQ.m +++ b/ios/RNZohoSalesIQ.m @@ -21,6 +21,7 @@ - (void)performAdditionalSetup{ ZohoSalesIQ.delegate = self; [ZohoSalesIQ Chat].delegate = self; [ZohoSalesIQ FAQ].delegate = self; + [ZohoSalesIQ KnowledgeBase].delegate = self; if(actionDictionary == nil){ actionDictionary = [[NSMutableDictionary alloc] init]; } @@ -45,6 +46,8 @@ - (void)performAdditionalSetup{ ZohoSalesIQ.delegate = self; [ZohoSalesIQ Chat].delegate = self; [ZohoSalesIQ FAQ].delegate = self; + [ZohoSalesIQ KnowledgeBase].delegate = self; + if(actionDictionary == nil){ actionDictionary = [[NSMutableDictionary alloc] init]; } @@ -110,10 +113,18 @@ -(void)stopObserving { NSString *TAB_CONVERSATIONS = @"TAB_CONVERSATIONS"; NSString *TAB_FAQ = @"TAB_FAQ"; +NSString *TAB_KNOWLEDGE_BASE = @"TAB_KNOWLEDGE_BASE"; NSString *EVENT_HANDLE_URL = @"EVENT_HANDLE_URL"; NSString *EVENT_OPEN_URL = @"EVENT_OPEN_URL"; NSString *EVENT_COMPLETE_CHAT_ACTION = @"EVENT_COMPLETE_CHAT_ACTION"; +NSString *RESOURCE_ARTICLES = @"RESOURCE_ARTICLES"; + +// --------------------------------------------------------- +NSString *EVENT_RESOURCE_OPENED = @"EVENT_RESOURCE_OPENED"; +NSString *EVENT_RESOURCE_CLOSED = @"EVENT_RESOURCE_CLOSED"; +NSString *EVENT_RESOURCE_LIKED = @"EVENT_RESOURCE_LIKED"; +NSString *EVENT_RESOURCE_DISLIKED = @"EVENT_RESOURCE_DISLIKED"; - (NSArray *)supportedEvents { return @[OPERATORS_OFFLINE, @@ -146,9 +157,15 @@ -(void)stopObserving { ERROR_LOG, TAB_CONVERSATIONS, TAB_FAQ, + TAB_KNOWLEDGE_BASE, EVENT_HANDLE_URL, EVENT_OPEN_URL, - EVENT_COMPLETE_CHAT_ACTION]; + EVENT_COMPLETE_CHAT_ACTION, + RESOURCE_ARTICLES, + EVENT_RESOURCE_OPENED, + EVENT_RESOURCE_CLOSED, + EVENT_RESOURCE_LIKED, + EVENT_RESOURCE_DISLIKED]; } - (NSDictionary *) constantsToExport { @@ -190,10 +207,16 @@ - (NSDictionary *) constantsToExport { @"WARNING_LOG": WARNING_LOG, @"ERROR_LOG": ERROR_LOG, @"TAB_CONVERSATIONS": TAB_CONVERSATIONS, + @"TAB_KNOWLEDGE_BASE": TAB_KNOWLEDGE_BASE, @"TAB_FAQ": TAB_FAQ, @"EVENT_HANDLE_URL": EVENT_HANDLE_URL, @"EVENT_OPEN_URL": EVENT_OPEN_URL, @"EVENT_COMPLETE_CHAT_ACTION": EVENT_COMPLETE_CHAT_ACTION, + @"RESOURCE_ARTICLES": RESOURCE_ARTICLES, + @"EVENT_RESOURCE_OPENED": EVENT_RESOURCE_OPENED, + @"EVENT_RESOURCE_CLOSED": EVENT_RESOURCE_CLOSED, + @"EVENT_RESOURCE_LIKED": EVENT_RESOURCE_LIKED, + @"EVENT_RESOURCE_DISLIKED": EVENT_RESOURCE_DISLIKED, }; } @@ -207,6 +230,21 @@ - (dispatch_queue_t)methodQueue return dispatch_get_main_queue(); } +- (NSMutableDictionary *)prepareResourceInformation:(enum SIQResourceType)type resource:(SIQKnowledgeBaseResource * _Nullable)resource { + NSMutableDictionary *resourceInformation = [NSMutableDictionary dictionary]; + NSMutableDictionary *resourceObject = [RNZohoSalesIQ getResourceObject:resource]; + + if (resourceObject != nil) { + [resourceInformation setObject:resourceObject forKey:@"resource"]; + } + + if (type == SIQResourceTypeArticles) { + [resourceInformation setObject:RESOURCE_ARTICLES forKey:@"type"]; + } + + return resourceInformation; +} + + (NSMutableDictionary *)getVisitorObject: (SIQVisitor*)arguments { NSMutableDictionary *visitorDict = [NSMutableDictionary dictionary]; @@ -356,6 +394,13 @@ + (NSMutableDictionary *)getErrorObject: (NSError*) error{ return errorDict; } ++ (NSMutableDictionary *)getSIQErrorObject:(id)siqError { + NSMutableDictionary *errorDictionary = [NSMutableDictionary dictionary]; + [errorDictionary setObject:@(siqError.code) forKey:@"code"]; + [errorDictionary setObject:siqError.message forKey:@"message"]; + return errorDictionary; +} + + (NSMutableArray *)getFAQCategoryList: (NSArray *) categories { NSMutableArray *categoryArray = [NSMutableArray array]; @@ -501,7 +546,7 @@ + (NSMutableDictionary *)getChatObject: (SIQVisitorChat*) chat if (fileSize != nil) { [fileMessageDict setObject: @(fileSize) forKey: @"size"]; } - + if (fileContent != nil){ if (comment != nil){ [chatDict setObject:[NSString stringWithFormat:@"%@:%@",fileContent,comment] forKey: @"lastMessage"]; @@ -545,6 +590,192 @@ + (NSMutableDictionary *)getChatObject: (SIQVisitorChat*) chat return chatDict; } ++ (NSMutableDictionary *)getResourceObject: (SIQKnowledgeBaseResource*)resource { + NSMutableDictionary *resourceDictionary = [NSMutableDictionary dictionary]; + if([resource id] != nil){ + NSString *articleID = [resource id]; + [resourceDictionary setObject: articleID forKey: @"id"]; + + if ([resource category] != nil) { + NSMutableDictionary *resourceCategory = [NSMutableDictionary dictionary]; + if ([[resource category] id] != nil) { + [resourceCategory setObject: [[resource category] id] forKey: @"id"]; + } + if ([[resource category] name] != nil) { + [resourceCategory setObject: [[resource category] name] forKey: @"name"]; + } + [resourceDictionary setObject: resourceCategory forKey: @"category"]; + } + + if ([resource title] != nil) { + [resourceDictionary setObject: [resource title] forKey: @"title"]; + } + + if ([resource departmentId] != nil) { + [resourceDictionary setObject: [resource departmentId] forKey: @"departmentId"]; + } + + if ([resource language] != nil) { + NSMutableDictionary *resourceLanguage = [NSMutableDictionary dictionary]; + if ([[resource language] id] != nil) { + [resourceLanguage setObject: [[resource language] id] forKey: @"id"]; + } + if ([[resource language] code] != nil) { + [resourceLanguage setObject: [[resource language] code] forKey: @"code"]; + } + [resourceDictionary setObject: resourceLanguage forKey: @"language"]; + } + + if ([resource creator] != nil) { + NSMutableDictionary *resourceCreator = [NSMutableDictionary dictionary]; + if ([[resource creator] id] != nil) { + [resourceCreator setObject: [[resource creator] id] forKey: @"id"]; + } + if ([[resource creator] name] != nil) { + [resourceCreator setObject: [[resource creator] name] forKey: @"name"]; + } + if ([[resource creator] email] != nil) { + [resourceCreator setObject: [[resource creator] email] forKey: @"email"]; + } + if ([[resource creator] displayName] != nil) { + [resourceCreator setObject: [[resource creator] displayName] forKey: @"displayName"]; + } + if ([[resource creator] imageUrl] != nil) { + [resourceCreator setObject: [[resource creator] imageUrl] forKey: @"imageUrl"]; + } + [resourceDictionary setObject: resourceCreator forKey: @"creator"]; + } + + if ([resource modifier] != nil) { + NSMutableDictionary *resourceModifier = [NSMutableDictionary dictionary]; + if ([[resource modifier] id] != nil) { + [resourceModifier setObject: [[resource modifier] id] forKey: @"id"]; + } + if ([[resource modifier] name] != nil) { + [resourceModifier setObject: [[resource modifier] name] forKey: @"name"]; + } + if ([[resource modifier] email] != nil) { + [resourceModifier setObject: [[resource modifier] email] forKey: @"email"]; + } + if ([[resource modifier] displayName] != nil) { + [resourceModifier setObject: [[resource modifier] displayName] forKey: @"displayName"]; + } + if ([[resource modifier] imageUrl] != nil) { + [resourceModifier setObject: [[resource modifier] imageUrl] forKey: @"imageUrl"]; + } + [resourceDictionary setObject: resourceModifier forKey: @"modifier"]; + } + + if ([resource createdTime] != nil) { + NSDate *createdTime = [resource createdTime]; + int time = (int)[createdTime timeIntervalSince1970]; + [resourceDictionary setObject: @(time) forKey: @"createdTime"]; + } + + if ([resource modifiedTime] != nil) { + NSDate *createdTime = [resource modifiedTime]; + int time = (int)[createdTime timeIntervalSince1970]; + [resourceDictionary setObject: @(time) forKey: @"modifiedTime"]; + } + + if ([resource publicUrl] != nil) { + [resourceDictionary setObject: [resource publicUrl] forKey: @"publicUrl"]; + } + + if ([resource stats] != nil) { + NSMutableDictionary *resourceStats = [NSMutableDictionary dictionary]; + if ([[resource stats] liked] != nil) { + [resourceStats setObject: [[resource stats] liked] forKey: @"liked"]; + } + if ([[resource stats] disliked] != nil) { + [resourceStats setObject: [[resource stats] disliked] forKey: @"disliked"]; + } + if ([[resource stats] used] != nil) { + [resourceStats setObject: [[resource stats] used] forKey: @"used"]; + } + if ([[resource stats] viewed] != nil) { + [resourceStats setObject: [[resource stats] viewed] forKey: @"viewed"]; + } + [resourceDictionary setObject: resourceStats forKey: @"stats"]; + } + + if ([resource content] != nil) { + [resourceDictionary setObject: [resource content] forKey: @"content"]; + } + + SIQArticleRatedType ratedType = [resource ratedType]; + if (ratedType == SIQArticleRatedTypeLiked) { + [resourceDictionary setObject: @"liked" forKey:@"ratedType"]; + } + if (ratedType == SIQArticleRatedTypeDisliked) { + [resourceDictionary setObject: @"disliked" forKey:@"ratedType"]; + } + + } + return resourceDictionary; +} + ++ (NSMutableDictionary *)getCategoryObject: (SIQKnowledgeBaseCategory*)category { + NSMutableDictionary *categoryDictionary = [NSMutableDictionary dictionary]; + if([category id] != nil) { + NSString *categoryID = [category id]; + [categoryDictionary setObject: categoryID forKey: @"id"]; + + if ([category name] != nil) { + [categoryDictionary setObject: [category name] forKey: @"name"]; + } + if ([category departmentId] != nil) { + [categoryDictionary setObject: [category departmentId] forKey: @"departmentId"]; + } + if ([category count] != nil) { + [categoryDictionary setObject: [category count] forKey: @"count"]; + } + if ([category childrenCount] != nil) { + [categoryDictionary setObject: [category childrenCount] forKey: @"childrenCount"]; + } + if ([category order] != nil) { + [categoryDictionary setObject: [category order] forKey: @"order"]; + } + if ([category parentCategoryId] != nil) { + [categoryDictionary setObject: [category parentCategoryId] forKey: @"parentCategoryId"]; + } + if ([category resourceModifiedTime] != nil) { + NSDate *modifiedTime = [category resourceModifiedTime]; + int time = (int)[modifiedTime timeIntervalSince1970]; + [categoryDictionary setObject: @(time) forKey: @"resourceModifiedTime"]; + } + } + return categoryDictionary; +} + ++ (NSMutableArray *)getResourceList: (NSArray *) resources +{ + NSMutableArray *resourceArray = [NSMutableArray array]; + + NSInteger i = 0; + for (SIQKnowledgeBaseResource *resource in resources){ + NSMutableDictionary *resourceDict = [NSMutableDictionary dictionary]; + resourceDict = [RNZohoSalesIQ getResourceObject:resource]; + [resourceArray insertObject:resourceDict atIndex:i]; + i = i + 1; + } + return resourceArray; +} + ++ (NSMutableArray *)getCategoryList: (NSArray *) categories +{ + NSMutableArray *categoryArray = [NSMutableArray array]; + + NSInteger i = 0; + for (SIQKnowledgeBaseCategory *category in categories){ + NSMutableDictionary *categoryDict = [NSMutableDictionary dictionary]; + categoryDict = [RNZohoSalesIQ getCategoryObject:category]; + [categoryArray insertObject:categoryDict atIndex:i]; + i = i + 1; + } + return categoryArray; +} + + (NSMutableDictionary *)getDepartmentObject: (SIQDepartment*)argument { NSMutableDictionary *departmentDictionary = [NSMutableDictionary dictionary]; @@ -1141,7 +1372,7 @@ + (void)handleNotificationAction: (NSDictionary *) info response:(NSString *) re }else if ([level isEqual: ERROR_LOG]){ debugLogLevel = SIQDebugLogLevelError; } - + [[ZohoSalesIQ Logger] write: log logLevel: debugLogLevel file:nil line:nil function:nil fileID:nil filePath:nil column:nil success:^(BOOL success) { NSNumber *complete = [NSNumber numberWithBool:success]; callback(@[complete]); @@ -1166,7 +1397,7 @@ + (void)handleNotificationAction: (NSDictionary *) info response:(NSString *) re NSString *currentObject = [orders objectAtIndex:i]; if ([currentObject isEqual: TAB_CONVERSATIONS]){ [sendOrders addObject:[NSNumber numberWithInteger:0]]; - } else if ([currentObject isEqual: TAB_FAQ]){ + } else if ([currentObject isEqual:TAB_FAQ] || [currentObject isEqual:TAB_KNOWLEDGE_BASE]){ [sendOrders addObject:[NSNumber numberWithInteger:1]]; } @@ -1220,6 +1451,121 @@ + (void)handleNotificationAction: (NSDictionary *) info response:(NSString *) re } } +//KnowledgeBase +RCT_EXPORT_METHOD(isKnowledgeBaseEnabled: (NSString*)type callback:(RCTResponseSenderBlock)callback) +{ + if ([type isEqualToString: RESOURCE_ARTICLES]) { + NSNumber *chatEnabled = [NSNumber numberWithBool:[[ZohoSalesIQ KnowledgeBase] isEnabled:SIQResourceTypeArticles]]; + callback(@[chatEnabled]); + } +} + +RCT_EXPORT_METHOD(setKnowledgeBaseVisibility: (NSString*)type enable:(BOOL)enable) +{ + if ([type isEqualToString: RESOURCE_ARTICLES]) { + [[ZohoSalesIQ KnowledgeBase] setVisibility:SIQResourceTypeArticles enable: enable]; + } +} + +RCT_EXPORT_METHOD(categorizeKnowledgeBase: (NSString*)type enable:(BOOL)enable) +{ + if ([type isEqualToString: RESOURCE_ARTICLES]) { + [[ZohoSalesIQ KnowledgeBase] categorize:SIQResourceTypeArticles enable:enable]; + } +} + +RCT_EXPORT_METHOD(combineKnowledgeBaseDepartments: (NSString*)type enable:(BOOL)enable) +{ + if ([type isEqualToString: RESOURCE_ARTICLES]) { + [[ZohoSalesIQ KnowledgeBase] combineDepartments:SIQResourceTypeArticles enable:enable]; + } +} + +RCT_EXPORT_METHOD(setKnowledgeBaseRecentShowLimit:(NSInteger)limit) { + [[ZohoSalesIQ KnowledgeBase] setRecentShowLimit:limit]; +} + +RCT_EXPORT_METHOD(getKnowledgeBaseResourceDepartments: (RCTResponseSenderBlock)callback) { + [[ZohoSalesIQ KnowledgeBase] getResourceDepartmentsWithCompletion:^(id _Nullable error, NSArray * _Nullable departments) { + + if (departments != nil) { + NSMutableArray *departmentsArray = [NSMutableArray array]; + for (SIQResourceDepartment *department in departments) { + NSMutableDictionary *departmentDict = [NSMutableDictionary dictionary]; + departmentDict[@"id"] = department.id; + departmentDict[@"name"] = department.name; + [departmentsArray addObject:departmentDict]; + } + callback(@[[NSNull null], departmentsArray]); + } else { + NSMutableDictionary *errorDictionary = [RNZohoSalesIQ getSIQErrorObject:error]; + callback(@[errorDictionary, [NSNull null]]); + } + }]; +} + +RCT_EXPORT_METHOD(openKnowledgeBase:(NSString *)type articleID:(NSString *)articleID callback:(RCTResponseSenderBlock)callback) { + if ([type isEqualToString: RESOURCE_ARTICLES]) { + [[ZohoSalesIQ KnowledgeBase] open:SIQResourceTypeArticles id:articleID completion:^(BOOL success, id _Nullable error) { + NSNumber *succeeded = [NSNumber numberWithBool:success]; + if (error != nil) { + NSMutableDictionary *errorDictionary = [RNZohoSalesIQ getSIQErrorObject:error]; + callback(@[succeeded, errorDictionary]); + } else { + callback(@[succeeded, [NSNull null]]); + } + }]; + } +} + +RCT_EXPORT_METHOD(getKnowledgeBaseSingleResource:(NSString *)type articleID:(NSString *)articleID callback:(RCTResponseSenderBlock)callback) { + if ([type isEqualToString: RESOURCE_ARTICLES]) { + [[ZohoSalesIQ KnowledgeBase]getSingleResource:SIQResourceTypeArticles id:articleID completion:^(BOOL success, id _Nullable error, SIQKnowledgeBaseResource * _Nullable resource) { + if (error != nil) { + NSMutableDictionary *errorDictionary = [RNZohoSalesIQ getSIQErrorObject:error]; + callback(@[errorDictionary, [NSNull null]]); + } else { + NSMutableDictionary *resourceObject = [NSMutableDictionary dictionary]; + resourceObject = [RNZohoSalesIQ getResourceObject:resource]; + callback(@[[NSNull null], resourceObject]); + } + }]; + } +} + +RCT_EXPORT_METHOD(getKnowledgeBaseResources:(NSString *)type departmentId:(NSString * _Nullable)departmentId parentCategoryId:(NSString * _Nullable)parentCategoryId page:(NSInteger)page limit:(NSInteger)limit searchKey:(NSString * _Nullable)searchKey callback:(RCTResponseSenderBlock)callback) { + if ([type isEqualToString: RESOURCE_ARTICLES]) { + NSInteger limitValue = limit; + NSInteger pageValue = page; + [[ZohoSalesIQ KnowledgeBase] getResources:SIQResourceTypeArticles departmentId:departmentId parentCategoryId:parentCategoryId searchKey:searchKey page:pageValue limit:limitValue completion:^(BOOL success, id _Nullable error, NSArray * _Nullable resources, BOOL moreDataAvailable) { + NSNumber *available = [NSNumber numberWithBool:moreDataAvailable]; + if (error != nil) { + NSMutableDictionary *errorDictionary = [RNZohoSalesIQ getSIQErrorObject:error]; + callback(@[errorDictionary, [NSNull null], available]); + } else { + NSMutableArray *resourceArray = [NSMutableArray array]; + resourceArray = [RNZohoSalesIQ getResourceList:resources]; + callback(@[[NSNull null], resourceArray, available]); + } + }]; + } +} + +RCT_EXPORT_METHOD(getKnowledgeBaseCategories:(NSString *)type departmentId:(NSString * _Nullable)departmentId parentCategoryId:(NSString * _Nullable)parentCategoryId callback:(RCTResponseSenderBlock)callback) { + if ([type isEqualToString: RESOURCE_ARTICLES]) { + [[ZohoSalesIQ KnowledgeBase] getCategories:SIQResourceTypeArticles departmentId:departmentId parentCategoryId:parentCategoryId completion:^(BOOL success, id _Nullable error, NSArray * _Nullable categories) { + if (error != nil) { + NSMutableDictionary *errorDictionary = [RNZohoSalesIQ getSIQErrorObject:error]; + callback(@[errorDictionary, [NSNull null]]); + } else { + NSMutableArray *categoryArray = [NSMutableArray array]; + categoryArray = [RNZohoSalesIQ getCategoryList:categories]; + callback(@[[NSNull null], categoryArray]); + } + }]; + } +} + //MARK:- DELEGATE METHODS - EVENTS - (void)agentsOffline { if (hasListeners) @@ -1364,4 +1710,34 @@ - (void)handleBotTrigger { [self sendEventWithName:BOT_TRIGGER body:[NSNull null]]; } +- (void)handleResourceOpened:(enum SIQResourceType)type resource:(SIQKnowledgeBaseResource * _Nullable)resource { + if (hasListeners) { + NSMutableDictionary *resourceInformation = [self prepareResourceInformation:type resource:resource]; + [self sendEventWithName:EVENT_RESOURCE_OPENED body: resourceInformation]; + } +} + +- (void)handleResourceClosed:(enum SIQResourceType)type resource:(SIQKnowledgeBaseResource * _Nullable)resource { + if (hasListeners) { + NSMutableDictionary *resourceInformation = [self prepareResourceInformation:type resource:resource]; + [self sendEventWithName:EVENT_RESOURCE_CLOSED body: resourceInformation]; + } +} + + +- (void)handleResourceLiked:(enum SIQResourceType)type resource:(SIQKnowledgeBaseResource * _Nullable)resource { + if (hasListeners) { + NSMutableDictionary *resourceInformation = [self prepareResourceInformation:type resource:resource]; + [self sendEventWithName:EVENT_RESOURCE_LIKED body: resourceInformation]; + } +} + + +- (void)handleResourceDisliked:(enum SIQResourceType)type resource:(SIQKnowledgeBaseResource * _Nullable)resource { + if (hasListeners) { + NSMutableDictionary *resourceInformation = [self prepareResourceInformation:type resource:resource]; + [self sendEventWithName:EVENT_RESOURCE_DISLIKED body: resourceInformation]; + } +} + @end diff --git a/ios/RNZohoSalesIQ.podspec b/ios/RNZohoSalesIQ.podspec index 9adc799..3cd5cf7 100644 --- a/ios/RNZohoSalesIQ.podspec +++ b/ios/RNZohoSalesIQ.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "RNZohoSalesIQ" - s.version = "6.0.2" + s.version = "7.0.0" s.summary = "A React-Native module for the SalesIQ Mobilisten SDK" s.description = "A React-Native module for the SalesIQ Mobilisten SDK" s.homepage = "https://zoho.com" diff --git a/package.json b/package.json index 5ba9439..3a4c902 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-zohosalesiq-mobilisten", - "version": "6.1.1", + "version": "7.0.0", "description": "A React Native module for the ZohoSalesIQ Mobilisten SDK", "bugs": { "email": "support@zohosalesiq.com"