├── .gitignore ├── .npmignore ├── .prettierrc.json ├── .vscode └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── RNZendeskChat.d.ts ├── RNZendeskChat.podspec ├── android ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── taskrabbit │ └── zendesk │ ├── RNZendeskChatModule.java │ └── RNZendeskChatPackage.java ├── index.js ├── ios ├── RNZendeskChat.xcodeproj │ └── project.pbxproj ├── RNZendeskChatModule.h └── RNZendeskChatModule.m ├── package.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependency directory 2 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 3 | node_modules 4 | package-lock.json 5 | yarn-error.log 6 | ios/RNZendeskChat/RNZendeskChat.xcodeproj/xcuserdata 7 | ios/RNZendeskChat/RNZendeskChat.xcodeproj/project.xcworkspace 8 | 9 | # Xcode 10 | # 11 | build/ 12 | *.pbxuser 13 | !default.pbxuser 14 | *.mode1v3 15 | !default.mode1v3 16 | *.mode2v3 17 | !default.mode2v3 18 | *.perspectivev3 19 | !default.perspectivev3 20 | *.xcworkspace 21 | !Scripts/xctool/xctool.xcworkspace 22 | !default.xcworkspace 23 | xcuserdata 24 | *.xccheckout 25 | *.moved-aside 26 | DerivedData 27 | *.hmap 28 | *.ipa 29 | *.xcuserstate 30 | project.xcworkspace 31 | 32 | # OSX 33 | # 34 | .DS_Store 35 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .vscode/* 2 | .gitignore 3 | *.log 4 | .prettierrc.json 5 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": [ 3 | { 4 | "files": "package.json", 5 | "options": { "parser": "json" } 6 | } 7 | ], 8 | "proseWrap": "never", 9 | "trailingComma": "all", 10 | "useTabs": true 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "autodetect", 4 | "barthelemy", 5 | "bot", 6 | "bot's", 7 | "chua", 8 | "cocoapod", 9 | "cocoapods", 10 | "fbartho", 11 | "gabrielperales", 12 | "jrichardlai", 13 | "kachanovskyi", 14 | "perales", 15 | "zanechua", 16 | "zendesk", 17 | "zendesk's", 18 | "zopim" 19 | ], 20 | "files.exclude": { 21 | "**/.classpath": true, 22 | "**/.project": true, 23 | "**/.settings": true, 24 | "**/.factorypath": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | This project hasn't kept a changelog historically, and it would be infeasible to retroactively create one. 4 | 5 | Instead, for the near future, releases will be encapsulated by a [milestone](https://github.com/taskrabbit/react-native-zendesk-chat/milestones) and [Github Releases](https://github.com/taskrabbit/react-native-zendesk-chat/releases) 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) TaskRabbit, Inc. and its affiliates. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-zendesk-chat 2 | 3 | Simple module that supports displaying Zendesk Chat within a React Native Application. 4 | 5 | This library assumes you're familiar with Zendesk's Official Documentation: [iOS](https://developer.zendesk.com/embeddables/docs/chat-sdk-v-2-for-ios/introduction) and [Android](https://developer.zendesk.com/embeddables/docs/chat-sdk-v-2-for-android/introduction). 6 | 7 | ## VERSIONS 8 | 9 | - For **Zendesk Chat v2** use version >= 0.4.0 (this requires RN 0.59 or later!) 10 | - For RN version >= 0.59 use version >= 0.3.0 (Zendesk Chat v1) 11 | - For RN version < 0.59 use version <= 0.2.2 (Zendesk Chat v1) 12 | 13 | ## Known Issues 14 | 15 | ## Getting Started 16 | 17 | With npm: 18 | 19 | `npm install react-native-zendesk-chat --save` 20 | 21 | or with yarn: 22 | 23 | `yarn add react-native-zendesk-chat` 24 | 25 | ### QuickStart & Usage 26 | 27 | 1. Setup Native Dependencies
**iOS** If you're on react-native >= 0.60 and you have Cocoapods setup, then you just need to: 28 | 29 | ```bash 30 | $ yarn install # and see if there are any errors 31 | $ (cd ios; pod install) # and see if there are any errors 32 | # -- you may need to do `pod install --repo-update` 33 | ``` 34 | 35 | If you're on older react-native versions, please see the [Advanced Setup](#advanced-setup) section below 36 | 37 | **Android** If you're on react-native >= 0.60, Android should autodetect this dependency. If you're on 0.59, you may need to call `react-native link` 38 | 39 | 2. Call the JS Initializer: 40 | 41 | ```javascript 42 | import ZendeskChat from "react-native-zendesk-chat"; 43 | 44 | // Once in your application: 45 | ZendeskChat.init("YOUR_ZENDESK_ACCOUNT_KEY"); 46 | 47 | // Optionally specify the appId provided by Zendesk 48 | ZendeskChat.init("YOUR_ZENDESK_ACCOUNT_KEY", "APP_ID_PROVIDED_BY_ZENDESK"); 49 | ``` 50 | 51 | 3. Show the Chat UI 52 | 53 | ```javascript 54 | // On button press, when you want to show chat: 55 | ZendeskChat.startChat({ 56 | name: user.full_name, 57 | email: user.email, 58 | phone: user.mobile_phone, 59 | tags: ["tag1", "tag2"], 60 | department: "Your department", 61 | // The behaviorFlags are optional, and each default to 'true' if omitted 62 | behaviorFlags: { 63 | showAgentAvailability: true, 64 | showChatTranscriptPrompt: true, 65 | showPreChatForm: true, 66 | showOfflineForm: true, 67 | }, 68 | // The preChatFormOptions are optional & each defaults to "optional" if omitted 69 | preChatFormOptions: { 70 | name: !user.full_name ? "required" : "optional", 71 | email: "optional", 72 | phone: "optional", 73 | department: "required", 74 | }, 75 | localizedDismissButtonTitle: "Dismiss", 76 | }); 77 | ``` 78 | 79 | 80 | ### Obtaining the `YOUR_ZENDESK_ACCOUNT_KEY` 81 | To optain your zendesk account key see the instructions in [Initializing the SDK](https://api.zopim.com/web-sdk/#initializing-the-sdk) in the Zendesk SDK. 82 | 83 | To get your account key, follow these steps: 84 | 85 | 1. In the Zendesk Chat Dashboard, click on your profile in the upper right corner and click on the 'Check Connection' option: 86 | ![status_dropdown](https://api.zopim.com/web-sdk/images/status_dropdown.png) 87 | 1. In the dialog, copy the account key value 88 | ![account_key](https://api.zopim.com/web-sdk/images/account_key.png) 89 | 90 | 91 | ### Styling 92 | 93 | Changing the UI Styling is mostly achieved through native techniques. 94 | 95 | On Android, this is the [official documentation](https://developer.zendesk.com/embeddables/docs/android-unified-sdk/customize_the_look#how-theming-works) -- and an example might be adding these [3 lines to your app theme](https://github.com/zendesk/sdk_demo_app_android/blob/ae4c551f78911e983b0aac06967628f46be15e54/app/src/main/res/values/styles.xml#L5-L7) 96 | 97 | While on iOS, the options are more minimal -- check the [official doc page](https://developer.zendesk.com/embeddables/docs/chat-sdk-v-2-for-ios/customize_the_look#styling-the-chat-screen) 98 | 99 | ### Migrating 100 | 101 | _From react-native-zendesk-chat <= 0.3.0_ 102 | 103 | To migrate from previous versions of the library, you should probably remove all integration steps you applied, and start over from the [Quick Start](#quickstart--usage). 104 | 105 | The JS API calls are very similar, with mostly additive changes. 106 | 107 | ### Advanced Setup 108 | 109 | Advanced users, or users running on older versions of react-native may want to initialize things in native. 110 | 111 | #### iOS: Manually Setting up with Cocoapods 112 | 113 | If you're on iOS < 0.60, you may need to manually install the cocoapod: 114 | 115 | Add a reference to your Podfile: 116 | 117 | ```Podfile 118 | pod 'RNZendeskChat', :git => 'https://github.com/taskrabbit/react-native-zendesk-chat.git' 119 | ``` 120 | 121 | then run pod install: `(cd ios; pod install)` 122 | 123 | or manually: 124 | 125 | In Xcode, drag and drop `node_modules/react-native-zendesk-chat/RNZendeskChat.m` and `node_modules/react-native-zendesk-chat/RNZendeskChat.h` into your project. 126 | 127 | #### iOS: Configure `ZDCChat` in `AppDelegate.m`: 128 | 129 | ```objective-c 130 | #import 131 | 132 | // ... 133 | 134 | // Inside the appropriate appDidFinishLaunching method 135 | [ZDCChat initializeWithAccountKey:@"YOUR_ZENDESK_ACCOUNT_KEY" appId:"YOUR_ZENDESK_APP_ID"]; 136 | 137 | // And access other interesting APIs 138 | ``` 139 | 140 | #### Android: Manual Setup & Configuration 141 | 142 | If you're on react-native < 0.60, you should be able to call `react-native link`. 143 | 144 | If this doesn't work, then you may need to do a complete manual install as follows: 145 | 146 | 1. Open up `android/app/main/java/[...]/MainApplication.java` 147 | 148 | - Add `import com.taskrabbit.zendesk.*;` to the imports at the top of the file 149 | - Add `new RNZendeskChatPackage(this)` to the list returned by the `getPackages()` method 150 | 151 | 2. Append the following lines to `android/settings.gradle`: 152 | 153 | ```gradle 154 | include ':react-native-zendesk-chat' 155 | project(':react-native-zendesk-chat').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-zendesk-chat/android') 156 | ``` 157 | 158 | 3. Insert the following lines inside the dependencies block in `android/app/build.gradle`: 159 | 160 | For RN >= 0.60: 161 | 162 | ```gradle 163 | dependencies { 164 | // 165 | api group: 'com.zendesk', name: 'chat', version: '2.2.0' 166 | api group: 'com.zendesk', name: 'messaging', version: '4.3.1' 167 | ``` 168 | also in project build.gradle 169 | 170 | Add ```gradle 171 | maven { url 'https://zendesk.jfrog.io/zendesk/repo' }``` 172 | 173 | For RN < 0.60: 174 | 175 | ```gradle 176 | compile project(':react-native-zendesk-chat') 177 | ``` 178 | 179 | 4. Configure `Chat` in `android/app/main/java/[...]/MainActivity.java` 180 | 181 | ```java 182 | // Note: there is a JS method to do this -- prefer doing that! -- This is for advanced users only. 183 | 184 | // Call this once in your Activity's bootup lifecycle 185 | Chat.INSTANCE.init(mReactContext, key, appId); 186 | ``` 187 | 188 | ## Contributing 189 | 190 | - Pull Requests are encouraged! 191 | - Be respectful! 192 | - The trunk branch of this repo is called `main` 193 | 194 | ## License 195 | 196 | React Native Zendesk Chat is MIT licensed, as found in the [LICENSE](https://github.com/taskrabbit/react-native-zendesk-chat/LICENSE) file. 197 | -------------------------------------------------------------------------------- /RNZendeskChat.d.ts: -------------------------------------------------------------------------------- 1 | declare module "react-native-zendesk-chat" { 2 | interface VisitorInfoOptions { 3 | name?: string; 4 | email?: string; 5 | phone?: string; 6 | } 7 | 8 | interface MessagingOptionsCommon { 9 | /** Set this to set the bot's displayName */ 10 | botName?: string; 11 | } 12 | interface MessagingOptions_iOS extends MessagingOptionsCommon { 13 | /** Will be loaded using [UIImage imageWithName:…] ) */ 14 | botAvatarName?: string; 15 | /** Will be loaded using [UIImage imageWithData:[NSData dataWithContentsOfUrl:…]] */ 16 | botAvatarUrl?: string; 17 | } 18 | interface MessagingOptions_Android extends MessagingOptionsCommon { 19 | /** Will be loaded from your native asset resources */ 20 | botAvatarDrawableId?: number; 21 | } 22 | 23 | /** Current default is "optional" */ 24 | type PreChatFormFieldOptionVisibility = "hidden" | "optional" | "required"; 25 | 26 | interface StartChatOptions extends VisitorInfoOptions { 27 | department?: string; 28 | tags?: string[]; 29 | 30 | behaviorFlags?: { 31 | /** 32 | * if omitted, the form is enabled 33 | * 34 | * @warning Android: If you enable preChat form with `required` or `optional` for keys 35 | * Then any visitor info you provide statically as VisitorInfoOptions will be ignored. 36 | * This is documented upstream here: https://support.zendesk.com/hc/en-us/articles/360055343673 37 | */ 38 | showPreChatForm?: boolean; 39 | /** if omitted, the prompt is enabled */ 40 | showChatTranscriptPrompt?: boolean; 41 | /** if omitted, the form is enabled */ 42 | showOfflineForm?: boolean; 43 | /** if omitted, the agent availability message is enabled */ 44 | showAgentAvailability?: boolean; 45 | }; 46 | 47 | /** 48 | * If omitted, the preChatForm will be left as default in Zendesk, be explicit if you want control. 49 | * 50 | * @warning Android: If you provide the preChat form with `required` or `optional` for keys 51 | * Then any visitor info you provide statically as VisitorInfoOptions will be ignored. 52 | * This is documented upstream here: https://support.zendesk.com/hc/en-us/articles/360055343673 53 | */ 54 | preChatFormOptions?: { 55 | /** Should we ask the user for a contact email? */ 56 | email?: PreChatFormFieldOptionVisibility; 57 | /** Should we ask the user their name? */ 58 | name?: PreChatFormFieldOptionVisibility; 59 | /** Should we ask the user for their phone number? */ 60 | phone?: PreChatFormFieldOptionVisibility; 61 | /** Should we ask the user which department? */ 62 | department?: PreChatFormFieldOptionVisibility; 63 | }; 64 | 65 | /** 66 | * Configure the Chat-Bot (if any) 67 | */ 68 | messagingOptions?: MessagingOptions_iOS & MessagingOptions_Android; 69 | 70 | /** 71 | * If not provided, this will be "Close" -- not localized! 72 | * 73 | * -- iOS Only (Android: shows just a Back Button) 74 | */ 75 | localizedDismissButtonTitle?: string; 76 | } 77 | 78 | class RNZendeskChatModuleImpl { 79 | /** 80 | * Must be called before calling startChat/setVisitorInfo 81 | * - (Advanced users may configure this natively instead of calling this from JS) 82 | */ 83 | init: (zendeskAccountKey: string, appId?: string) => void; 84 | 85 | /** 86 | * Presents the Zendesk Chat User Interface 87 | */ 88 | startChat: (options: StartChatOptions) => void; 89 | /** 90 | * Backwards Compatibility! 91 | * - You can pass all these parameters to RNZendeskChatModule.startChat 92 | * - So you should probably prefer that method 93 | */ 94 | setVisitorInfo: (options: VisitorInfoOptions) => void; 95 | 96 | /** 97 | * Configure the token to start receiving Push Notifications 98 | */ 99 | registerPushToken: (token: string) => void; 100 | 101 | /** 102 | * Check if there are available agents 103 | */ 104 | areAgentsOnline: () => Promise; 105 | } 106 | 107 | const RNZendeskChatModule: RNZendeskChatModuleImpl; 108 | 109 | export default RNZendeskChatModule; 110 | } 111 | -------------------------------------------------------------------------------- /RNZendeskChat.podspec: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) 4 | 5 | Pod::Spec.new do |s| 6 | s.name = 'RNZendeskChat' 7 | s.version = package['version'] 8 | s.summary = package['description'] 9 | s.license = package['license'] 10 | s.authors = package['author'] 11 | s.homepage = package['homepage'] 12 | s.platform = :ios, '10' 13 | s.source = { git: 'https://github.com/taskrabbit/react-native-zendesk-chat.git', tag: "v#{s.version}" } 14 | s.source_files = 'ios/*.{h,m}' 15 | s.static_framework = true 16 | 17 | s.framework = 'Foundation' 18 | s.framework = 'UIKit' 19 | 20 | s.dependency 'React-Core' 21 | s.dependency 'ZendeskChatSDK', '~> 2.9' 22 | end 23 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | # Android-specific gitignoreables 2 | 3 | .idea 4 | .gradle 5 | **/*.iml 6 | build/ 7 | .idea 8 | .gradle 9 | local.properties 10 | 11 | # Classpath can differ on developer machines, though I think it's relative currently 12 | .classpath 13 | # Project files can contain developer-specific config? It's unclear, 14 | # and the community debates whether they should be ignored. 15 | # Since they're auto-generated sometimes, this should be okay? 16 | .project 17 | 18 | # I think this is genned by vscode-java extension 19 | org.eclipse.buildship.core.prefs 20 | org.eclipse.jdt.core.prefs -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | def safeExtGet(prop, fallback) { 4 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback 5 | } 6 | 7 | buildscript { 8 | repositories { 9 | jcenter() 10 | google() 11 | } 12 | 13 | dependencies { 14 | classpath 'com.android.tools.build:gradle:3.2.1' 15 | } 16 | } 17 | 18 | android { 19 | compileSdkVersion safeExtGet('compileSdkVersion', 28) 20 | buildToolsVersion safeExtGet('buildToolsVersion', "28.0.3") 21 | 22 | defaultConfig { 23 | minSdkVersion safeExtGet('minSdkVersion', 16) 24 | targetSdkVersion safeExtGet('targetSdkVersion', 28) 25 | versionCode 1 26 | versionName "1.0" 27 | } 28 | } 29 | 30 | repositories { 31 | mavenCentral() 32 | jcenter() 33 | google() 34 | maven { url 'https://zendesk.jfrog.io/zendesk/repo' } 35 | } 36 | 37 | dependencies { 38 | implementation "com.facebook.react:react-native:+" 39 | 40 | api group: 'com.zendesk', name: 'chat', version: safeExtGet('zendeskChatVersion', '3.1.0') 41 | api group: 'com.zendesk', name: 'messaging', version: safeExtGet('zendeskMessagingVersion', '5.1.0') 42 | } 43 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/java/com/taskrabbit/zendesk/RNZendeskChatModule.java: -------------------------------------------------------------------------------- 1 | package com.taskrabbit.zendesk; 2 | 3 | import android.app.Activity; 4 | import android.content.pm.ApplicationInfo; 5 | import android.util.Log; 6 | 7 | import com.facebook.react.bridge.Promise; 8 | import com.facebook.react.bridge.ReactApplicationContext; 9 | import com.facebook.react.bridge.ReactContext; 10 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 11 | import com.facebook.react.bridge.ReactMethod; 12 | import com.facebook.react.bridge.ReadableArray; 13 | import com.facebook.react.bridge.ReadableMap; 14 | import com.facebook.react.bridge.ReadableType; 15 | import com.facebook.react.bridge.WritableNativeMap; 16 | 17 | import zendesk.chat.Account; 18 | import zendesk.chat.AccountStatus; 19 | import zendesk.chat.Chat; 20 | import zendesk.chat.ChatConfiguration; 21 | import zendesk.chat.ChatEngine; 22 | import zendesk.chat.ChatSessionStatus; 23 | import zendesk.chat.ChatState; 24 | import zendesk.chat.ObservationScope; 25 | import zendesk.chat.Observer; 26 | import zendesk.chat.ProfileProvider; 27 | import zendesk.chat.PreChatFormFieldStatus; 28 | import zendesk.chat.PushNotificationsProvider; 29 | import zendesk.chat.VisitorInfo; 30 | import zendesk.messaging.MessagingActivity; 31 | import zendesk.messaging.MessagingConfiguration; 32 | import com.zendesk.service.ErrorResponse; 33 | import com.zendesk.service.ZendeskCallback; 34 | 35 | import java.lang.String; 36 | import java.util.ArrayList; 37 | 38 | public class RNZendeskChatModule extends ReactContextBaseJavaModule { 39 | private static final String TAG = "[RNZendeskChatModule]"; 40 | 41 | private ArrayList currentUserTags = new ArrayList(); 42 | 43 | private ReadableMap pendingVisitorInfo = null; 44 | private ObservationScope observationScope = null; 45 | 46 | // private class Converters { 47 | public static ArrayList getArrayListOfStrings(ReadableMap options, String key, String functionHint) { 48 | ArrayList result = new ArrayList(); 49 | 50 | if (!options.hasKey(key)) { 51 | return result; 52 | } 53 | if (options.getType(key) != ReadableType.Array) { 54 | Log.e(RNZendeskChatModule.TAG, "wrong type for key '" + key + "' when processing " + functionHint 55 | + ", expected an Array of Strings."); 56 | return result; 57 | } 58 | ReadableArray arr = options.getArray(key); 59 | for (int i = 0; i < arr.size(); i++) { 60 | if (arr.isNull(i)) { 61 | continue; 62 | } 63 | if (arr.getType(i) != ReadableType.String) { 64 | Log.e(RNZendeskChatModule.TAG, "wrong type for key '" + key + "[" + i + "]' when processing " 65 | + functionHint + ", expected entry to be a String."); 66 | } 67 | result.add(arr.getString(i)); 68 | } 69 | return result; 70 | } 71 | 72 | public static String getStringOrNull(ReadableMap options, String key, String functionHint) { 73 | if (!options.hasKey(key)) { 74 | return null; 75 | } 76 | if (options.getType(key) != ReadableType.String) { 77 | Log.e(RNZendeskChatModule.TAG, 78 | "wrong type for key '" + key + "' when processing " + functionHint + ", expected a String."); 79 | return null; 80 | } 81 | return options.getString(key); 82 | } 83 | 84 | public static int getIntOrDefault(ReadableMap options, String key, String functionHint, int defaultValue) { 85 | if (!options.hasKey(key)) { 86 | return defaultValue; 87 | } 88 | if (options.getType(key) != ReadableType.String) { 89 | Log.e(RNZendeskChatModule.TAG, 90 | "wrong type for key '" + key + "' when processing " + functionHint + ", expected an Integer."); 91 | return defaultValue; 92 | } 93 | return options.getInt(key); 94 | } 95 | 96 | public static boolean getBooleanOrDefault(ReadableMap options, String key, String functionHint, 97 | boolean defaultValue) { 98 | if (!options.hasKey(key)) { 99 | return defaultValue; 100 | } 101 | if (options.getType(key) != ReadableType.Boolean) { 102 | Log.e(RNZendeskChatModule.TAG, 103 | "wrong type for key '" + key + "' when processing " + functionHint + ", expected a Boolean."); 104 | return defaultValue; 105 | } 106 | return options.getBoolean(key); 107 | } 108 | 109 | public static PreChatFormFieldStatus getFieldStatusOrDefault(ReadableMap options, String key, 110 | PreChatFormFieldStatus defaultValue) { 111 | if (!options.hasKey(key)) { 112 | return defaultValue; 113 | } 114 | if (options.getType(key) != ReadableType.String) { 115 | Log.e(RNZendeskChatModule.TAG, "wrong type for key '" + key 116 | + "' when processing startChat(preChatFormOptions), expected one of ('required' | 'optional' | 'hidden')."); 117 | return defaultValue; 118 | } 119 | switch (options.getString(key)) { 120 | case "required": 121 | return PreChatFormFieldStatus.REQUIRED; 122 | case "optional": 123 | return PreChatFormFieldStatus.OPTIONAL; 124 | case "hidden": 125 | return PreChatFormFieldStatus.HIDDEN; 126 | default: 127 | Log.e(RNZendeskChatModule.TAG, "wrong type for key '" + key 128 | + "' when processing startChat(preChatFormOptions), expected one of ('required' | 'optional' | 'hidden')."); 129 | return defaultValue; 130 | } 131 | } 132 | 133 | public static ReadableMap getReadableMap(ReadableMap options, String key, String functionHint) { 134 | if (!options.hasKey(key)) { 135 | return new WritableNativeMap(); 136 | } 137 | if (options.getType(key) != ReadableType.Map) { 138 | Log.e(RNZendeskChatModule.TAG, 139 | "wrong type for key '" + key + "' when processing " + functionHint + ", expected a config hash."); 140 | return new WritableNativeMap(); 141 | } 142 | return options.getMap(key); 143 | } 144 | 145 | private void selectVisitorInfoFieldIfPreChatFormHidden(String key, WritableNativeMap output, ReadableMap input, ReadableMap shouldInclude) { 146 | if ((!input.hasKey(key) || input.getType(key) != ReadableType.String) 147 | || (shouldInclude.hasKey(key) && shouldInclude.getType(key) == ReadableType.String && !"hidden".equals(shouldInclude.getString(key))) ) { 148 | return; 149 | } 150 | 151 | String value = input.getString(key); 152 | if (((mReactContext.getApplicationInfo().flags 153 | & ApplicationInfo.FLAG_DEBUGGABLE) == 0)) { 154 | // We don't want other Apps to monitor our app's production logs for this debug information. 155 | // If you patch the app to enable it yourself, that's your choice! 156 | value = ""; 157 | } 158 | 159 | Log.d(TAG, "selectVisitorInfo to set later " + key + " '" + value + "'"); 160 | output.putString(key, input.getString(key)); 161 | } 162 | // } 163 | 164 | private ReactContext mReactContext; 165 | 166 | public RNZendeskChatModule(ReactApplicationContext reactContext) { 167 | super(reactContext); 168 | mReactContext = reactContext; 169 | } 170 | 171 | @Override 172 | public String getName() { 173 | return "RNZendeskChatModule"; 174 | } 175 | 176 | @ReactMethod 177 | public void setVisitorInfo(ReadableMap options) { 178 | _setVisitorInfo(options); 179 | } 180 | private boolean _setVisitorInfo(ReadableMap options) { 181 | boolean anyValuesWereSet = false; 182 | VisitorInfo.Builder builder = VisitorInfo.builder(); 183 | 184 | String name = getStringOrNull(options, "name", "visitorInfo"); 185 | if (name != null) { 186 | builder = builder.withName(name); 187 | anyValuesWereSet = true; 188 | } 189 | String email = getStringOrNull(options, "email", "visitorInfo"); 190 | if (email != null) { 191 | builder = builder.withEmail(email); 192 | anyValuesWereSet = true; 193 | } 194 | String phone = getStringOrNull(options, "phone", "visitorInfo"); 195 | if (phone != null) { 196 | builder = builder.withPhoneNumber(phone); 197 | anyValuesWereSet = true; 198 | } 199 | 200 | VisitorInfo visitorInfo = builder.build(); 201 | 202 | if (Chat.INSTANCE.providers() == null) { 203 | Log.e(TAG, 204 | "Zendesk Internals are undefined -- did you forget to call RNZendeskModule.init()?"); 205 | return false; 206 | } 207 | 208 | Chat.INSTANCE.providers().profileProvider().setVisitorInfo(visitorInfo, null); 209 | 210 | return anyValuesWereSet; 211 | } 212 | 213 | // Unfortunately, react-native: android doesn't support the following 214 | // - Java's Method Overloading 215 | // - automatically providing null to undefined parameters (like iOS) 216 | // 217 | // As a result, we need to guarantee this is always called with 2 parameters from JS 218 | // 219 | // This method has been renamed to make that clear, and index.js is adding the 220 | // correct interface for init() at runtime 221 | @ReactMethod 222 | public void _initWith2Args(String key, String appId) { 223 | if (appId != null) { 224 | Chat.INSTANCE.init(mReactContext, key, appId); 225 | } else { 226 | Chat.INSTANCE.init(mReactContext, key); 227 | } 228 | Log.d(TAG, "Chat.INSTANCE was properly initialized from JS."); 229 | } 230 | 231 | private ChatConfiguration.Builder loadBehaviorFlags(ChatConfiguration.Builder b, ReadableMap options) { 232 | boolean defaultValue = true; 233 | String logHint = "startChat(behaviorFlags)"; 234 | 235 | return b.withPreChatFormEnabled(getBooleanOrDefault(options, "showPreChatForm", logHint, defaultValue)) 236 | .withTranscriptEnabled(getBooleanOrDefault(options, "showChatTranscriptPrompt", logHint, defaultValue)) 237 | .withOfflineFormEnabled(getBooleanOrDefault(options, "showOfflineForm", logHint, defaultValue)) 238 | .withAgentAvailabilityEnabled( 239 | getBooleanOrDefault(options, "showAgentAvailability", logHint, defaultValue)); 240 | } 241 | 242 | private ChatConfiguration.Builder loadPreChatFormConfiguration(ChatConfiguration.Builder b, ReadableMap options) { 243 | PreChatFormFieldStatus defaultValue = PreChatFormFieldStatus.OPTIONAL; 244 | return b.withNameFieldStatus(getFieldStatusOrDefault(options, "name", defaultValue)) 245 | .withEmailFieldStatus(getFieldStatusOrDefault(options, "email", defaultValue)) 246 | .withPhoneFieldStatus(getFieldStatusOrDefault(options, "phone", defaultValue)) 247 | .withDepartmentFieldStatus(getFieldStatusOrDefault(options, "department", defaultValue)); 248 | } 249 | // Produces a ReadableMap suitable for passing to setVisitorInfo that only has the fields that won't be asked by the preChatForm 250 | private ReadableMap hiddenVisitorInfoData(ReadableMap allVisitorInfo, ReadableMap preChatFormOptions) { 251 | WritableNativeMap output = new WritableNativeMap(); 252 | selectVisitorInfoFieldIfPreChatFormHidden("email", output, allVisitorInfo, preChatFormOptions); 253 | selectVisitorInfoFieldIfPreChatFormHidden("name", output, allVisitorInfo, preChatFormOptions); 254 | selectVisitorInfoFieldIfPreChatFormHidden("phone", output, allVisitorInfo, preChatFormOptions); 255 | return output; 256 | } 257 | 258 | private void loadTags(ReadableMap options) { 259 | // ZendeskChat Android treats the tags persistently, so you have to add/remove 260 | // as things change -- aka doing a diff :-( 261 | // ZendeskChat iOS just lets you override the full array so this isn't 262 | // necessary on that side. 263 | if (Chat.INSTANCE.providers() == null) { 264 | Log.e(TAG, 265 | "Zendesk Internals are undefined -- did you forget to call RNZendeskModule.init()?"); 266 | return; 267 | } 268 | 269 | ProfileProvider profileProvider = Chat.INSTANCE.providers().profileProvider(); 270 | ArrayList activeTags = (ArrayList) currentUserTags.clone(); 271 | 272 | ArrayList allProvidedTags = RNZendeskChatModule.getArrayListOfStrings(options, "tags", "startChat"); 273 | ArrayList newlyIntroducedTags = (ArrayList) allProvidedTags.clone(); 274 | 275 | newlyIntroducedTags.remove(activeTags); // Now just includes tags to add 276 | currentUserTags.removeAll(allProvidedTags); // Now just includes tags to delete 277 | 278 | if (!currentUserTags.isEmpty()) { 279 | profileProvider.removeVisitorTags(currentUserTags, null); 280 | } 281 | if (!newlyIntroducedTags.isEmpty()) { 282 | profileProvider.addVisitorTags(newlyIntroducedTags, null); 283 | } 284 | 285 | currentUserTags = allProvidedTags; 286 | } 287 | 288 | private MessagingConfiguration.Builder loadBotSettings(ReadableMap options, 289 | MessagingConfiguration.Builder builder) { 290 | if (options == null) { 291 | return builder; 292 | } 293 | String botName = getStringOrNull(options, "botName", "loadBotSettings"); 294 | if (botName != null) { 295 | builder = builder.withBotLabelString(botName); 296 | } 297 | int avatarDrawable = getIntOrDefault(options, "botAvatarDrawableId", "loadBotSettings", -1); 298 | if (avatarDrawable != -1) { 299 | builder = builder.withBotAvatarDrawable(avatarDrawable); 300 | } 301 | 302 | return builder; 303 | } 304 | 305 | @ReactMethod 306 | public void startChat(ReadableMap options) { 307 | if (Chat.INSTANCE.providers() == null) { 308 | Log.e(TAG, 309 | "Zendesk Internals are undefined -- did you forget to call RNZendeskModule.init()?"); 310 | return; 311 | } 312 | pendingVisitorInfo = null; 313 | boolean didSetVisitorInfo = _setVisitorInfo(options); 314 | 315 | ReadableMap flagHash = RNZendeskChatModule.getReadableMap(options, "behaviorFlags", "startChat"); 316 | 317 | boolean showPreChatForm = getBooleanOrDefault(flagHash, "showPreChatForm", "startChat(behaviorFlags)", true); 318 | boolean needsToSetVisitorInfoAfterChatStart = showPreChatForm && didSetVisitorInfo; 319 | 320 | ChatConfiguration.Builder chatBuilder = loadBehaviorFlags(ChatConfiguration.builder(), flagHash); 321 | if (showPreChatForm) { 322 | ReadableMap preChatFormOptions = getReadableMap(options, "preChatFormOptions", "startChat"); 323 | chatBuilder = loadPreChatFormConfiguration(chatBuilder, preChatFormOptions); 324 | pendingVisitorInfo = hiddenVisitorInfoData(options, preChatFormOptions); 325 | } 326 | ChatConfiguration chatConfig = chatBuilder.build(); 327 | 328 | String department = RNZendeskChatModule.getStringOrNull(options, "department", "startChat"); 329 | if (department != null) { 330 | Chat.INSTANCE.providers().chatProvider().setDepartment(department, null); 331 | } 332 | 333 | loadTags(options); 334 | 335 | MessagingConfiguration.Builder messagingBuilder = loadBotSettings( 336 | getReadableMap(options, "messagingOptions", "startChat"), MessagingActivity.builder()); 337 | 338 | if (needsToSetVisitorInfoAfterChatStart) { 339 | setupChatStartObserverToSetVisitorInfo(); 340 | } 341 | 342 | Activity activity = getCurrentActivity(); 343 | if (activity != null) { 344 | messagingBuilder.withEngines(ChatEngine.engine()).show(activity, chatConfig); 345 | } else { 346 | Log.e(TAG, "Could not load getCurrentActivity -- no UI can be displayed without it."); 347 | } 348 | } 349 | 350 | @ReactMethod 351 | public void registerPushToken(String token) { 352 | PushNotificationsProvider pushProvider = Chat.INSTANCE.providers().pushNotificationsProvider(); 353 | 354 | if (pushProvider != null) { 355 | pushProvider.registerPushToken(token); 356 | } 357 | } 358 | 359 | // https://support.zendesk.com/hc/en-us/articles/360055343673 360 | public void setupChatStartObserverToSetVisitorInfo(){ 361 | // Create a temporary observation scope until the chat is started. 362 | observationScope = new ObservationScope(); 363 | Chat.INSTANCE.providers().chatProvider().observeChatState(observationScope, new Observer() { 364 | @Override 365 | public void update(ChatState chatState) { 366 | ChatSessionStatus chatStatus = chatState.getChatSessionStatus(); 367 | // Status achieved after the PreChatForm is completed 368 | if (chatStatus == ChatSessionStatus.STARTED) { 369 | observationScope.cancel(); // Once the chat is started disable the observation 370 | observationScope = null; // Clean things up to avoid confusion. 371 | if (pendingVisitorInfo == null) { return; } 372 | 373 | // Update the information MID chat here. All info but Department can be updated 374 | // Add here the code to set the selected visitor info *after* the preChatForm is complete 375 | _setVisitorInfo(pendingVisitorInfo); 376 | pendingVisitorInfo = null; 377 | 378 | Log.d(TAG, "Set the VisitorInfo after chat start"); 379 | } else { 380 | // There are few other statuses that you can observe but they are unused in this example 381 | Log.d(TAG, "[observerSetup] - ChatSessionUpdate -> (unused) status : " + chatStatus.toString()); 382 | } 383 | } 384 | }); 385 | } 386 | 387 | @ReactMethod 388 | public void areAgentsOnline(final Promise promise) { 389 | Chat.INSTANCE.providers().accountProvider().getAccount(new ZendeskCallback() { 390 | @Override 391 | public void onSuccess(Account account) { 392 | AccountStatus status = account.getStatus(); 393 | 394 | switch (status) { 395 | case ONLINE: 396 | promise.resolve(true); 397 | break; 398 | 399 | default: 400 | promise.resolve(false); 401 | break; 402 | } 403 | } 404 | 405 | @Override 406 | public void onError(ErrorResponse errorResponse) { 407 | promise.reject("no-available-zendesk-account", "DevError: Not connected to Zendesk or network issue"); 408 | } 409 | }); 410 | } 411 | } 412 | -------------------------------------------------------------------------------- /android/src/main/java/com/taskrabbit/zendesk/RNZendeskChatPackage.java: -------------------------------------------------------------------------------- 1 | package com.taskrabbit.zendesk; 2 | 3 | import com.facebook.react.ReactPackage; 4 | import com.facebook.react.bridge.JavaScriptModule; 5 | import com.facebook.react.bridge.NativeModule; 6 | import com.facebook.react.bridge.ReactApplicationContext; 7 | import com.facebook.react.uimanager.ViewManager; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Collections; 11 | import java.util.List; 12 | 13 | public class RNZendeskChatPackage implements ReactPackage { 14 | 15 | @Override 16 | public List createNativeModules( 17 | ReactApplicationContext reactContext) { 18 | List modules = new ArrayList<>(); 19 | 20 | modules.add(new RNZendeskChatModule(reactContext)); 21 | return modules; 22 | } 23 | 24 | public List> createJSModules() { 25 | return Collections.emptyList(); 26 | } 27 | 28 | @Override 29 | public List createViewManagers(ReactApplicationContext reactApplicationContext) { 30 | return Collections.emptyList(); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { NativeModules } from "react-native"; 2 | 3 | const RNZendeskChatModule = NativeModules.RNZendeskChatModule; 4 | 5 | // react-native doesn't support method overloading for Java or Objective-C 6 | // So this code implements the init method but makes sure to 7 | // always call it with two defined parameters, passing null for the second as needed 8 | // Reference: https://github.com/facebook/react-native/blob/07d090dbc6c46b8f3760dbd25dbe0540c18cb3f3/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaModuleWrapper.java#L85-L86 9 | 10 | RNZendeskChatModule.init = (key, appId) => { 11 | return RNZendeskChatModule._initWith2Args(key, appId || null); 12 | }; 13 | 14 | /** 15 | * TypeScript Documentation for this Module describes the available methods & parameters 16 | * 17 | * @see { ./RNZendeskChat.d.ts } 18 | */ 19 | export default RNZendeskChatModule; 20 | -------------------------------------------------------------------------------- /ios/RNZendeskChat.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B6462ECD1C603E5C0010294B /* RNZendeskChatModule.m in Sources */ = {isa = PBXBuildFile; fileRef = B6462ECC1C603E5C0010294B /* RNZendeskChatModule.m */; }; 11 | /* End PBXBuildFile section */ 12 | 13 | /* Begin PBXCopyFilesBuildPhase section */ 14 | B6462EBD1C603E340010294B /* 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 | B6462EBF1C603E340010294B /* libRNZendeskChat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNZendeskChat.a; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | B6462ECB1C603E5C0010294B /* RNZendeskChatModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNZendeskChatModule.h; sourceTree = ""; }; 28 | B6462ECC1C603E5C0010294B /* RNZendeskChatModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNZendeskChatModule.m; sourceTree = ""; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | B6462EBC1C603E340010294B /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXFrameworksBuildPhase section */ 40 | 41 | /* Begin PBXGroup section */ 42 | B6462EB61C603E340010294B = { 43 | isa = PBXGroup; 44 | children = ( 45 | B6462ECB1C603E5C0010294B /* RNZendeskChatModule.h */, 46 | B6462ECC1C603E5C0010294B /* RNZendeskChatModule.m */, 47 | B6462EC01C603E340010294B /* Products */, 48 | ); 49 | sourceTree = ""; 50 | }; 51 | B6462EC01C603E340010294B /* Products */ = { 52 | isa = PBXGroup; 53 | children = ( 54 | B6462EBF1C603E340010294B /* libRNZendeskChat.a */, 55 | ); 56 | name = Products; 57 | sourceTree = ""; 58 | }; 59 | /* End PBXGroup section */ 60 | 61 | /* Begin PBXNativeTarget section */ 62 | B6462EBE1C603E340010294B /* RNZendeskChat */ = { 63 | isa = PBXNativeTarget; 64 | buildConfigurationList = B6462EC81C603E340010294B /* Build configuration list for PBXNativeTarget "RNZendeskChat" */; 65 | buildPhases = ( 66 | B6462EBB1C603E340010294B /* Sources */, 67 | B6462EBC1C603E340010294B /* Frameworks */, 68 | B6462EBD1C603E340010294B /* CopyFiles */, 69 | ); 70 | buildRules = ( 71 | ); 72 | dependencies = ( 73 | ); 74 | name = RNZendeskChat; 75 | productName = RNZendeskChat; 76 | productReference = B6462EBF1C603E340010294B /* libRNZendeskChat.a */; 77 | productType = "com.apple.product-type.library.static"; 78 | }; 79 | /* End PBXNativeTarget section */ 80 | 81 | /* Begin PBXProject section */ 82 | B6462EB71C603E340010294B /* Project object */ = { 83 | isa = PBXProject; 84 | attributes = { 85 | LastUpgradeCheck = 0720; 86 | ORGANIZATIONNAME = TaskRabbit; 87 | TargetAttributes = { 88 | B6462EBE1C603E340010294B = { 89 | CreatedOnToolsVersion = 7.2; 90 | }; 91 | }; 92 | }; 93 | buildConfigurationList = B6462EBA1C603E340010294B /* Build configuration list for PBXProject "RNZendeskChat" */; 94 | compatibilityVersion = "Xcode 3.2"; 95 | developmentRegion = English; 96 | hasScannedForEncodings = 0; 97 | knownRegions = ( 98 | en, 99 | ); 100 | mainGroup = B6462EB61C603E340010294B; 101 | productRefGroup = B6462EC01C603E340010294B /* Products */; 102 | projectDirPath = ""; 103 | projectRoot = ""; 104 | targets = ( 105 | B6462EBE1C603E340010294B /* RNZendeskChat */, 106 | ); 107 | }; 108 | /* End PBXProject section */ 109 | 110 | /* Begin PBXSourcesBuildPhase section */ 111 | B6462EBB1C603E340010294B /* Sources */ = { 112 | isa = PBXSourcesBuildPhase; 113 | buildActionMask = 2147483647; 114 | files = ( 115 | B6462ECD1C603E5C0010294B /* RNZendeskChatModule.m in Sources */, 116 | ); 117 | runOnlyForDeploymentPostprocessing = 0; 118 | }; 119 | /* End PBXSourcesBuildPhase section */ 120 | 121 | /* Begin XCBuildConfiguration section */ 122 | B6462EC61C603E340010294B /* 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_INT_CONVERSION = YES; 136 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 137 | CLANG_WARN_UNREACHABLE_CODE = YES; 138 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 139 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 140 | COPY_PHASE_STRIP = NO; 141 | DEBUG_INFORMATION_FORMAT = dwarf; 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_WARN_64_TO_32_BIT_CONVERSION = YES; 153 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 154 | GCC_WARN_UNDECLARED_SELECTOR = YES; 155 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 156 | GCC_WARN_UNUSED_FUNCTION = YES; 157 | GCC_WARN_UNUSED_VARIABLE = YES; 158 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 159 | MTL_ENABLE_DEBUG_INFO = YES; 160 | ONLY_ACTIVE_ARCH = YES; 161 | SDKROOT = iphoneos; 162 | }; 163 | name = Debug; 164 | }; 165 | B6462EC71C603E340010294B /* Release */ = { 166 | isa = XCBuildConfiguration; 167 | buildSettings = { 168 | ALWAYS_SEARCH_USER_PATHS = NO; 169 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 170 | CLANG_CXX_LIBRARY = "libc++"; 171 | CLANG_ENABLE_MODULES = YES; 172 | CLANG_ENABLE_OBJC_ARC = YES; 173 | CLANG_WARN_BOOL_CONVERSION = YES; 174 | CLANG_WARN_CONSTANT_CONVERSION = YES; 175 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 176 | CLANG_WARN_EMPTY_BODY = YES; 177 | CLANG_WARN_ENUM_CONVERSION = YES; 178 | CLANG_WARN_INT_CONVERSION = YES; 179 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 180 | CLANG_WARN_UNREACHABLE_CODE = YES; 181 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 182 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 183 | COPY_PHASE_STRIP = NO; 184 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 185 | ENABLE_NS_ASSERTIONS = NO; 186 | ENABLE_STRICT_OBJC_MSGSEND = YES; 187 | GCC_C_LANGUAGE_STANDARD = gnu99; 188 | GCC_NO_COMMON_BLOCKS = YES; 189 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 190 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 191 | GCC_WARN_UNDECLARED_SELECTOR = YES; 192 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 193 | GCC_WARN_UNUSED_FUNCTION = YES; 194 | GCC_WARN_UNUSED_VARIABLE = YES; 195 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 196 | MTL_ENABLE_DEBUG_INFO = NO; 197 | SDKROOT = iphoneos; 198 | VALIDATE_PRODUCT = YES; 199 | }; 200 | name = Release; 201 | }; 202 | B6462EC91C603E340010294B /* Debug */ = { 203 | isa = XCBuildConfiguration; 204 | buildSettings = { 205 | HEADER_SEARCH_PATHS = ( 206 | "$(inherited)", 207 | "$(SRCROOT)/../../../React/**", 208 | "$(SRCROOT)/../../react-native/React/**", 209 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 210 | ); 211 | OTHER_LDFLAGS = "-ObjC"; 212 | PRODUCT_NAME = "$(TARGET_NAME)"; 213 | SKIP_INSTALL = YES; 214 | }; 215 | name = Debug; 216 | }; 217 | B6462ECA1C603E340010294B /* Release */ = { 218 | isa = XCBuildConfiguration; 219 | buildSettings = { 220 | HEADER_SEARCH_PATHS = ( 221 | "$(inherited)", 222 | "$(SRCROOT)/../../../React/**", 223 | "$(SRCROOT)/../../react-native/React/**", 224 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 225 | ); 226 | OTHER_LDFLAGS = "-ObjC"; 227 | PRODUCT_NAME = "$(TARGET_NAME)"; 228 | SKIP_INSTALL = YES; 229 | }; 230 | name = Release; 231 | }; 232 | /* End XCBuildConfiguration section */ 233 | 234 | /* Begin XCConfigurationList section */ 235 | B6462EBA1C603E340010294B /* Build configuration list for PBXProject "RNZendeskChat" */ = { 236 | isa = XCConfigurationList; 237 | buildConfigurations = ( 238 | B6462EC61C603E340010294B /* Debug */, 239 | B6462EC71C603E340010294B /* Release */, 240 | ); 241 | defaultConfigurationIsVisible = 0; 242 | defaultConfigurationName = Release; 243 | }; 244 | B6462EC81C603E340010294B /* Build configuration list for PBXNativeTarget "RNZendeskChat" */ = { 245 | isa = XCConfigurationList; 246 | buildConfigurations = ( 247 | B6462EC91C603E340010294B /* Debug */, 248 | B6462ECA1C603E340010294B /* Release */, 249 | ); 250 | defaultConfigurationIsVisible = 0; 251 | defaultConfigurationName = Release; 252 | }; 253 | /* End XCConfigurationList section */ 254 | }; 255 | rootObject = B6462EB71C603E340010294B /* Project object */; 256 | } 257 | -------------------------------------------------------------------------------- /ios/RNZendeskChatModule.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | 5 | @interface RNZendeskChatModule : NSObject 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /ios/RNZendeskChatModule.m: -------------------------------------------------------------------------------- 1 | // 2 | // RNZendeskChat.m 3 | // Tasker 4 | // 5 | // Created by Jean-Richard Lai on 11/23/15. 6 | // 7 | 8 | 9 | #import "RNZendeskChatModule.h" 10 | 11 | #import 12 | #import 13 | 14 | #import 15 | #import 16 | #import 17 | #import 18 | 19 | 20 | @implementation RCTConvert (ZDKChatFormFieldStatus) 21 | 22 | RCT_ENUM_CONVERTER(ZDKFormFieldStatus, 23 | (@{ 24 | @"required": @(ZDKFormFieldStatusRequired), 25 | @"optional": @(ZDKFormFieldStatusOptional), 26 | @"hidden": @(ZDKFormFieldStatusHidden), 27 | }), 28 | ZDKFormFieldStatusOptional, 29 | integerValue); 30 | 31 | @end 32 | 33 | @interface RNZendeskChatModule () 34 | @end 35 | 36 | @implementation RNZendeskChatModule 37 | // Backwards compatibility with the unnecessary setVisitorInfo method 38 | ZDKChatAPIConfiguration *_visitorAPIConfig; 39 | 40 | 41 | RCT_EXPORT_MODULE(RNZendeskChatModule); 42 | 43 | RCT_EXPORT_METHOD(setVisitorInfo:(NSDictionary *)options) { 44 | if (!NSThread.isMainThread) { 45 | dispatch_async(dispatch_get_main_queue(), ^{ 46 | [self setVisitorInfo:options]; 47 | }); 48 | return; 49 | } 50 | 51 | ZDKChat.instance.configuration = _visitorAPIConfig = [self applyVisitorInfo:options intoConfig: _visitorAPIConfig ?: [[ZDKChatAPIConfiguration alloc] init]]; 52 | } 53 | 54 | - (ZDKChatAPIConfiguration*)applyVisitorInfo:(NSDictionary*)options intoConfig:(ZDKChatAPIConfiguration*)config { 55 | if (options[@"department"]) { 56 | config.department = options[@"department"]; 57 | } 58 | if (options[@"tags"]) { 59 | config.tags = options[@"tags"]; 60 | } 61 | config.visitorInfo = [[ZDKVisitorInfo alloc] initWithName:options[@"name"] 62 | email:options[@"email"] 63 | phoneNumber:options[@"phone"]]; 64 | 65 | NSLog(@"[RNZendeskChatModule] Applied visitor info: department: %@ tags: %@, email: %@, name: %@, phone: %@", config.department, config.tags, config.visitorInfo.email, config.visitorInfo.name, config.visitorInfo.phoneNumber); 66 | return config; 67 | } 68 | 69 | #define RNZDKConfigHashErrorLog(options, what)\ 70 | if (!!options) {\ 71 | NSLog(@"[RNZendeskChatModule] Invalid %@ -- expected a config hash", what);\ 72 | } 73 | 74 | - (ZDKMessagingConfiguration *)messagingConfigurationFromConfig:(NSDictionary*)options { 75 | ZDKMessagingConfiguration *config = [[ZDKMessagingConfiguration alloc] init]; 76 | if (!options || ![options isKindOfClass:NSDictionary.class]) { 77 | RNZDKConfigHashErrorLog(options, @"MessagingConfiguration config options"); 78 | return config; 79 | } 80 | if (options[@"botName"]) { 81 | config.name = options[@"botName"]; 82 | } 83 | 84 | if (options[@"botAvatarName"]) { 85 | config.botAvatar = [UIImage imageNamed:@"botAvatarName"]; 86 | } else if (options[@"botAvatarUrl"]) { 87 | config.botAvatar = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:options[@"botAvatarUrl"]]]]; 88 | } 89 | 90 | return config; 91 | } 92 | 93 | - (ZDKChatFormConfiguration * _Nullable)preChatFormConfigurationFromConfig:(NSDictionary*)options { 94 | if (!options || ![options isKindOfClass:NSDictionary.class]) { 95 | RNZDKConfigHashErrorLog(options, @"pre-Chat-Form Configuration Options"); 96 | return nil; 97 | } 98 | #define ParseFormFieldStatus(key)\ 99 | ZDKFormFieldStatus key = [RCTConvert ZDKFormFieldStatus:options[@"" #key]] 100 | ParseFormFieldStatus(name); 101 | ParseFormFieldStatus(email); 102 | ParseFormFieldStatus(phone); 103 | ParseFormFieldStatus(department); 104 | #undef ParseFormFieldStatus 105 | return [[ZDKChatFormConfiguration alloc] initWithName:name 106 | email:email 107 | phoneNumber:phone 108 | department:department]; 109 | } 110 | - (ZDKChatConfiguration *)chatConfigurationFromConfig:(NSDictionary*)options { 111 | options = options ?: @{}; 112 | 113 | ZDKChatConfiguration* config = [[ZDKChatConfiguration alloc] init]; 114 | if (![options isKindOfClass:NSDictionary.class]){ 115 | RNZDKConfigHashErrorLog(options, @"Chat Configuration Options"); 116 | return config; 117 | } 118 | NSDictionary * behaviorFlags = options[@"behaviorFlags"]; 119 | if (!behaviorFlags || ![behaviorFlags isKindOfClass:NSDictionary.class]) { 120 | RNZDKConfigHashErrorLog(behaviorFlags, @"BehaviorFlags -- expected a config hash"); 121 | behaviorFlags = NSDictionary.dictionary; 122 | } 123 | 124 | #define ParseBehaviorFlag(key, target)\ 125 | config.target = [RCTConvert BOOL: behaviorFlags[@"" #key] ?: @YES] 126 | ParseBehaviorFlag(showPreChatForm, isPreChatFormEnabled); 127 | ParseBehaviorFlag(showChatTranscriptPrompt, isChatTranscriptPromptEnabled); 128 | ParseBehaviorFlag(showOfflineForm, isOfflineFormEnabled); 129 | ParseBehaviorFlag(showAgentAvailability, isAgentAvailabilityEnabled); 130 | #undef ParseBehaviorFlag 131 | 132 | if (config.isPreChatFormEnabled) { 133 | ZDKChatFormConfiguration * formConfig = [self preChatFormConfigurationFromConfig:options[@"preChatFormOptions"]]; 134 | if (!!formConfig) { 135 | // Zendesk Swift Code crashes if you provide a nil form 136 | config.preChatFormConfiguration = formConfig; 137 | } 138 | } 139 | return config; 140 | } 141 | 142 | RCT_EXPORT_METHOD(startChat:(NSDictionary *)options) { 143 | if (!options || ![options isKindOfClass: NSDictionary.class]) { 144 | if (!!options){ 145 | NSLog(@"[RNZendeskChatModule] Invalid JS startChat Configuration Options -- expected a config hash"); 146 | } 147 | options = NSDictionary.dictionary; 148 | } 149 | 150 | dispatch_sync(dispatch_get_main_queue(), ^{ 151 | 152 | ZDKChat.instance.configuration = [self applyVisitorInfo:options 153 | intoConfig: _visitorAPIConfig ?: [[ZDKChatAPIConfiguration alloc] init]]; 154 | 155 | ZDKChatConfiguration * chatConfig = [self chatConfigurationFromConfig:options]; 156 | 157 | NSError *error = nil; 158 | NSArray *engines = @[ 159 | [ZDKChatEngine engineAndReturnError:&error] 160 | ]; 161 | if (!!error) { 162 | NSLog(@"[RNZendeskChatModule] Internal Error loading ZDKChatEngine %@", error); 163 | return; 164 | } 165 | 166 | ZDKMessagingConfiguration *messagingConfig = [self messagingConfigurationFromConfig: options[@"messagingOptions"]]; 167 | 168 | UIViewController *viewController = [ZDKMessaging.instance buildUIWithEngines:engines 169 | configs:@[chatConfig, messagingConfig] 170 | error:&error]; 171 | if (!!error) { 172 | NSLog(@"[RNZendeskChatModule] Internal Error building ZDKMessagingUI %@",error); 173 | return; 174 | } 175 | 176 | viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle: options[@"localizedDismissButtonTitle"] ?: @"Close" 177 | style: UIBarButtonItemStylePlain 178 | target: self 179 | action: @selector(dismissChatUI)]; 180 | 181 | UINavigationController *chatController = [[UINavigationController alloc] initWithRootViewController: viewController]; 182 | [RCTPresentedViewController() presentViewController:chatController animated:YES completion:nil]; 183 | }); 184 | } 185 | 186 | - (void) dismissChatUI { 187 | [RCTPresentedViewController() dismissViewControllerAnimated:YES completion:nil]; 188 | } 189 | 190 | RCT_EXPORT_METHOD(_initWith2Args:(NSString *)zenDeskKey appId:(NSString *)appId) { 191 | if (appId) { 192 | [ZDKChat initializeWithAccountKey:zenDeskKey appId:appId queue:dispatch_get_main_queue()]; 193 | } else { 194 | [ZDKChat initializeWithAccountKey:zenDeskKey queue:dispatch_get_main_queue()]; 195 | } 196 | } 197 | 198 | RCT_EXPORT_METHOD(registerPushToken:(NSString *)token) { 199 | dispatch_sync(dispatch_get_main_queue(), ^{ 200 | [ZDKChat registerPushTokenString:token]; 201 | }); 202 | } 203 | 204 | RCT_EXPORT_METHOD(areAgentsOnline: 205 | (RCTPromiseResolveBlock) resolve 206 | rejecter: (RCTPromiseRejectBlock) reject) { 207 | 208 | [ZDKChat.accountProvider getAccount:^(ZDKChatAccount *account, NSError *error) { 209 | if (account) { 210 | switch (account.accountStatus) { 211 | case ZDKChatAccountStatusOnline: 212 | resolve(@YES); 213 | break; 214 | 215 | default: 216 | resolve(@NO); 217 | break; 218 | } 219 | } else { 220 | reject(@"no-available-zendesk-account", @"DevError: Not connected to Zendesk or network issue", error); 221 | } 222 | }]; 223 | } 224 | 225 | @end 226 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-zendesk-chat", 3 | "version": "0.4.1", 4 | "description": "React Native Wrapper around Zopim Zendesk Chat", 5 | "main": "index.js", 6 | "types": "RNZendeskChat.d.ts", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/taskrabbit/react-native-zendesk-chat" 10 | }, 11 | "scripts": { 12 | "prettier": "prettier", 13 | "prettier:all": "prettier --write '**/*.json' '**/*.js' '**/*.ts' ", 14 | "lint": "eslint" 15 | }, 16 | "keywords": [ 17 | "zendesk", 18 | "chat", 19 | "react-native", 20 | "react", 21 | "ios", 22 | "android", 23 | "react-component" 24 | ], 25 | "homepage": "https://github.com/taskrabbit/react-native-zendesk-chat.git", 26 | "bugs": "https://github.com/taskrabbit/react-native-zendesk-chat/issues", 27 | "author": "jrichardlai", 28 | "contributors": [ 29 | "Brian Leonard ", 30 | "Frederic Barthelemy ", 31 | "Gabriel Perales ", 32 | "Jean-Richard Lai ", 33 | "Jordan Smith ", 34 | "Michael Kachanovskyi ", 35 | "Robert Murray ", 36 | "Zane Chua " 37 | ], 38 | "license": "MIT", 39 | "devDependencies": { "prettier": "2.0.x", "eslint": "7.3.x" }, 40 | "peerDependencies": { 41 | "react": "^16.11.0", 42 | "react-native": "0.60.x | 0.61.x | 0.62.x | 0.63.x" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@babel/code-frame@^7.0.0": 6 | version "7.10.3" 7 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.3.tgz#324bcfd8d35cd3d47dae18cde63d752086435e9a" 8 | integrity sha512-fDx9eNW0qz0WkUeqL6tXEXzVlPh6Y5aCDEZesl0xBGA8ndRukX91Uk44ZqnkECp01NAZUdCAl+aiQNGi0k88Eg== 9 | dependencies: 10 | "@babel/highlight" "^7.10.3" 11 | 12 | "@babel/helper-validator-identifier@^7.10.3": 13 | version "7.10.3" 14 | resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.3.tgz#60d9847f98c4cea1b279e005fdb7c28be5412d15" 15 | integrity sha512-bU8JvtlYpJSBPuj1VUmKpFGaDZuLxASky3LhaKj3bmpSTY6VWooSM8msk+Z0CZoErFye2tlABF6yDkT3FOPAXw== 16 | 17 | "@babel/highlight@^7.10.3": 18 | version "7.10.3" 19 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.3.tgz#c633bb34adf07c5c13156692f5922c81ec53f28d" 20 | integrity sha512-Ih9B/u7AtgEnySE2L2F0Xm0GaM729XqqLfHkalTsbjXGyqmf/6M0Cu0WpvqueUlW+xk88BHw9Nkpj49naU+vWw== 21 | dependencies: 22 | "@babel/helper-validator-identifier" "^7.10.3" 23 | chalk "^2.0.0" 24 | js-tokens "^4.0.0" 25 | 26 | "@types/color-name@^1.1.1": 27 | version "1.1.1" 28 | resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" 29 | integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== 30 | 31 | acorn-jsx@^5.2.0: 32 | version "5.2.0" 33 | resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe" 34 | integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ== 35 | 36 | acorn@^7.2.0: 37 | version "7.3.1" 38 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.3.1.tgz#85010754db53c3fbaf3b9ea3e083aa5c5d147ffd" 39 | integrity sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA== 40 | 41 | ajv@^6.10.0, ajv@^6.10.2: 42 | version "6.12.2" 43 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" 44 | integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== 45 | dependencies: 46 | fast-deep-equal "^3.1.1" 47 | fast-json-stable-stringify "^2.0.0" 48 | json-schema-traverse "^0.4.1" 49 | uri-js "^4.2.2" 50 | 51 | ansi-colors@^3.2.1: 52 | version "3.2.4" 53 | resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" 54 | integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== 55 | 56 | ansi-regex@^4.1.0: 57 | version "4.1.0" 58 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" 59 | integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== 60 | 61 | ansi-regex@^5.0.0: 62 | version "5.0.0" 63 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" 64 | integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== 65 | 66 | ansi-styles@^3.2.0, ansi-styles@^3.2.1: 67 | version "3.2.1" 68 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 69 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 70 | dependencies: 71 | color-convert "^1.9.0" 72 | 73 | ansi-styles@^4.1.0: 74 | version "4.2.1" 75 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" 76 | integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== 77 | dependencies: 78 | "@types/color-name" "^1.1.1" 79 | color-convert "^2.0.1" 80 | 81 | argparse@^1.0.7: 82 | version "1.0.10" 83 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" 84 | integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== 85 | dependencies: 86 | sprintf-js "~1.0.2" 87 | 88 | astral-regex@^1.0.0: 89 | version "1.0.0" 90 | resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" 91 | integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== 92 | 93 | balanced-match@^1.0.0: 94 | version "1.0.0" 95 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 96 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 97 | 98 | brace-expansion@^1.1.7: 99 | version "1.1.11" 100 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 101 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 102 | dependencies: 103 | balanced-match "^1.0.0" 104 | concat-map "0.0.1" 105 | 106 | callsites@^3.0.0: 107 | version "3.1.0" 108 | resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" 109 | integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== 110 | 111 | chalk@^2.0.0: 112 | version "2.4.2" 113 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" 114 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== 115 | dependencies: 116 | ansi-styles "^3.2.1" 117 | escape-string-regexp "^1.0.5" 118 | supports-color "^5.3.0" 119 | 120 | chalk@^4.0.0: 121 | version "4.1.0" 122 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" 123 | integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== 124 | dependencies: 125 | ansi-styles "^4.1.0" 126 | supports-color "^7.1.0" 127 | 128 | color-convert@^1.9.0: 129 | version "1.9.3" 130 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 131 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== 132 | dependencies: 133 | color-name "1.1.3" 134 | 135 | color-convert@^2.0.1: 136 | version "2.0.1" 137 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" 138 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 139 | dependencies: 140 | color-name "~1.1.4" 141 | 142 | color-name@1.1.3: 143 | version "1.1.3" 144 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 145 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= 146 | 147 | color-name@~1.1.4: 148 | version "1.1.4" 149 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" 150 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 151 | 152 | concat-map@0.0.1: 153 | version "0.0.1" 154 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 155 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 156 | 157 | cross-spawn@^7.0.2: 158 | version "7.0.3" 159 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" 160 | integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== 161 | dependencies: 162 | path-key "^3.1.0" 163 | shebang-command "^2.0.0" 164 | which "^2.0.1" 165 | 166 | debug@^4.0.1: 167 | version "4.1.1" 168 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" 169 | integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== 170 | dependencies: 171 | ms "^2.1.1" 172 | 173 | deep-is@^0.1.3: 174 | version "0.1.3" 175 | resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" 176 | integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= 177 | 178 | doctrine@^3.0.0: 179 | version "3.0.0" 180 | resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" 181 | integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== 182 | dependencies: 183 | esutils "^2.0.2" 184 | 185 | emoji-regex@^7.0.1: 186 | version "7.0.3" 187 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" 188 | integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== 189 | 190 | enquirer@^2.3.5: 191 | version "2.3.5" 192 | resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.5.tgz#3ab2b838df0a9d8ab9e7dff235b0e8712ef92381" 193 | integrity sha512-BNT1C08P9XD0vNg3J475yIUG+mVdp9T6towYFHUv897X0KoHBjB1shyrNmhmtHWKP17iSWgo7Gqh7BBuzLZMSA== 194 | dependencies: 195 | ansi-colors "^3.2.1" 196 | 197 | escape-string-regexp@^1.0.5: 198 | version "1.0.5" 199 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 200 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 201 | 202 | eslint-scope@^5.1.0: 203 | version "5.1.0" 204 | resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.0.tgz#d0f971dfe59c69e0cada684b23d49dbf82600ce5" 205 | integrity sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w== 206 | dependencies: 207 | esrecurse "^4.1.0" 208 | estraverse "^4.1.1" 209 | 210 | eslint-utils@^2.0.0: 211 | version "2.1.0" 212 | resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" 213 | integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== 214 | dependencies: 215 | eslint-visitor-keys "^1.1.0" 216 | 217 | eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.2.0: 218 | version "1.3.0" 219 | resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" 220 | integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== 221 | 222 | eslint@7.3.x: 223 | version "7.3.1" 224 | resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.3.1.tgz#76392bd7e44468d046149ba128d1566c59acbe19" 225 | integrity sha512-cQC/xj9bhWUcyi/RuMbRtC3I0eW8MH0jhRELSvpKYkWep3C6YZ2OkvcvJVUeO6gcunABmzptbXBuDoXsjHmfTA== 226 | dependencies: 227 | "@babel/code-frame" "^7.0.0" 228 | ajv "^6.10.0" 229 | chalk "^4.0.0" 230 | cross-spawn "^7.0.2" 231 | debug "^4.0.1" 232 | doctrine "^3.0.0" 233 | enquirer "^2.3.5" 234 | eslint-scope "^5.1.0" 235 | eslint-utils "^2.0.0" 236 | eslint-visitor-keys "^1.2.0" 237 | espree "^7.1.0" 238 | esquery "^1.2.0" 239 | esutils "^2.0.2" 240 | file-entry-cache "^5.0.1" 241 | functional-red-black-tree "^1.0.1" 242 | glob-parent "^5.0.0" 243 | globals "^12.1.0" 244 | ignore "^4.0.6" 245 | import-fresh "^3.0.0" 246 | imurmurhash "^0.1.4" 247 | is-glob "^4.0.0" 248 | js-yaml "^3.13.1" 249 | json-stable-stringify-without-jsonify "^1.0.1" 250 | levn "^0.4.1" 251 | lodash "^4.17.14" 252 | minimatch "^3.0.4" 253 | natural-compare "^1.4.0" 254 | optionator "^0.9.1" 255 | progress "^2.0.0" 256 | regexpp "^3.1.0" 257 | semver "^7.2.1" 258 | strip-ansi "^6.0.0" 259 | strip-json-comments "^3.1.0" 260 | table "^5.2.3" 261 | text-table "^0.2.0" 262 | v8-compile-cache "^2.0.3" 263 | 264 | espree@^7.1.0: 265 | version "7.1.0" 266 | resolved "https://registry.yarnpkg.com/espree/-/espree-7.1.0.tgz#a9c7f18a752056735bf1ba14cb1b70adc3a5ce1c" 267 | integrity sha512-dcorZSyfmm4WTuTnE5Y7MEN1DyoPYy1ZR783QW1FJoenn7RailyWFsq/UL6ZAAA7uXurN9FIpYyUs3OfiIW+Qw== 268 | dependencies: 269 | acorn "^7.2.0" 270 | acorn-jsx "^5.2.0" 271 | eslint-visitor-keys "^1.2.0" 272 | 273 | esprima@^4.0.0: 274 | version "4.0.1" 275 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 276 | integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== 277 | 278 | esquery@^1.2.0: 279 | version "1.3.1" 280 | resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" 281 | integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== 282 | dependencies: 283 | estraverse "^5.1.0" 284 | 285 | esrecurse@^4.1.0: 286 | version "4.2.1" 287 | resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" 288 | integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== 289 | dependencies: 290 | estraverse "^4.1.0" 291 | 292 | estraverse@^4.1.0, estraverse@^4.1.1: 293 | version "4.3.0" 294 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" 295 | integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== 296 | 297 | estraverse@^5.1.0: 298 | version "5.1.0" 299 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642" 300 | integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw== 301 | 302 | esutils@^2.0.2: 303 | version "2.0.3" 304 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" 305 | integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== 306 | 307 | fast-deep-equal@^3.1.1: 308 | version "3.1.3" 309 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" 310 | integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== 311 | 312 | fast-json-stable-stringify@^2.0.0: 313 | version "2.1.0" 314 | resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" 315 | integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== 316 | 317 | fast-levenshtein@^2.0.6: 318 | version "2.0.6" 319 | resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" 320 | integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= 321 | 322 | file-entry-cache@^5.0.1: 323 | version "5.0.1" 324 | resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" 325 | integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== 326 | dependencies: 327 | flat-cache "^2.0.1" 328 | 329 | flat-cache@^2.0.1: 330 | version "2.0.1" 331 | resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" 332 | integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== 333 | dependencies: 334 | flatted "^2.0.0" 335 | rimraf "2.6.3" 336 | write "1.0.3" 337 | 338 | flatted@^2.0.0: 339 | version "2.0.2" 340 | resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" 341 | integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== 342 | 343 | fs.realpath@^1.0.0: 344 | version "1.0.0" 345 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 346 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 347 | 348 | functional-red-black-tree@^1.0.1: 349 | version "1.0.1" 350 | resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" 351 | integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= 352 | 353 | glob-parent@^5.0.0: 354 | version "5.1.1" 355 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" 356 | integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== 357 | dependencies: 358 | is-glob "^4.0.1" 359 | 360 | glob@^7.1.3: 361 | version "7.1.6" 362 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" 363 | integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== 364 | dependencies: 365 | fs.realpath "^1.0.0" 366 | inflight "^1.0.4" 367 | inherits "2" 368 | minimatch "^3.0.4" 369 | once "^1.3.0" 370 | path-is-absolute "^1.0.0" 371 | 372 | globals@^12.1.0: 373 | version "12.4.0" 374 | resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" 375 | integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== 376 | dependencies: 377 | type-fest "^0.8.1" 378 | 379 | has-flag@^3.0.0: 380 | version "3.0.0" 381 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 382 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= 383 | 384 | has-flag@^4.0.0: 385 | version "4.0.0" 386 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" 387 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== 388 | 389 | ignore@^4.0.6: 390 | version "4.0.6" 391 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" 392 | integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== 393 | 394 | import-fresh@^3.0.0: 395 | version "3.2.1" 396 | resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" 397 | integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== 398 | dependencies: 399 | parent-module "^1.0.0" 400 | resolve-from "^4.0.0" 401 | 402 | imurmurhash@^0.1.4: 403 | version "0.1.4" 404 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" 405 | integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= 406 | 407 | inflight@^1.0.4: 408 | version "1.0.6" 409 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 410 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 411 | dependencies: 412 | once "^1.3.0" 413 | wrappy "1" 414 | 415 | inherits@2: 416 | version "2.0.4" 417 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 418 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 419 | 420 | is-extglob@^2.1.1: 421 | version "2.1.1" 422 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 423 | integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= 424 | 425 | is-fullwidth-code-point@^2.0.0: 426 | version "2.0.0" 427 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" 428 | integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= 429 | 430 | is-glob@^4.0.0, is-glob@^4.0.1: 431 | version "4.0.1" 432 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" 433 | integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== 434 | dependencies: 435 | is-extglob "^2.1.1" 436 | 437 | isexe@^2.0.0: 438 | version "2.0.0" 439 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 440 | integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= 441 | 442 | js-tokens@^4.0.0: 443 | version "4.0.0" 444 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 445 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== 446 | 447 | js-yaml@^3.13.1: 448 | version "3.14.0" 449 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" 450 | integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== 451 | dependencies: 452 | argparse "^1.0.7" 453 | esprima "^4.0.0" 454 | 455 | json-schema-traverse@^0.4.1: 456 | version "0.4.1" 457 | resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" 458 | integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== 459 | 460 | json-stable-stringify-without-jsonify@^1.0.1: 461 | version "1.0.1" 462 | resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" 463 | integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= 464 | 465 | levn@^0.4.1: 466 | version "0.4.1" 467 | resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" 468 | integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== 469 | dependencies: 470 | prelude-ls "^1.2.1" 471 | type-check "~0.4.0" 472 | 473 | lodash@^4.17.14: 474 | version "4.17.19" 475 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" 476 | integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== 477 | 478 | minimatch@^3.0.4: 479 | version "3.0.4" 480 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 481 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 482 | dependencies: 483 | brace-expansion "^1.1.7" 484 | 485 | minimist@^1.2.5: 486 | version "1.2.5" 487 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" 488 | integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== 489 | 490 | mkdirp@^0.5.1: 491 | version "0.5.5" 492 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" 493 | integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== 494 | dependencies: 495 | minimist "^1.2.5" 496 | 497 | ms@^2.1.1: 498 | version "2.1.2" 499 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 500 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 501 | 502 | natural-compare@^1.4.0: 503 | version "1.4.0" 504 | resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" 505 | integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= 506 | 507 | once@^1.3.0: 508 | version "1.4.0" 509 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 510 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 511 | dependencies: 512 | wrappy "1" 513 | 514 | optionator@^0.9.1: 515 | version "0.9.1" 516 | resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" 517 | integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== 518 | dependencies: 519 | deep-is "^0.1.3" 520 | fast-levenshtein "^2.0.6" 521 | levn "^0.4.1" 522 | prelude-ls "^1.2.1" 523 | type-check "^0.4.0" 524 | word-wrap "^1.2.3" 525 | 526 | parent-module@^1.0.0: 527 | version "1.0.1" 528 | resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" 529 | integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== 530 | dependencies: 531 | callsites "^3.0.0" 532 | 533 | path-is-absolute@^1.0.0: 534 | version "1.0.1" 535 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 536 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 537 | 538 | path-key@^3.1.0: 539 | version "3.1.1" 540 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" 541 | integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== 542 | 543 | prelude-ls@^1.2.1: 544 | version "1.2.1" 545 | resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" 546 | integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== 547 | 548 | prettier@2.0.x: 549 | version "2.0.5" 550 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4" 551 | integrity sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg== 552 | 553 | progress@^2.0.0: 554 | version "2.0.3" 555 | resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" 556 | integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== 557 | 558 | punycode@^2.1.0: 559 | version "2.1.1" 560 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" 561 | integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== 562 | 563 | regexpp@^3.1.0: 564 | version "3.1.0" 565 | resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" 566 | integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== 567 | 568 | resolve-from@^4.0.0: 569 | version "4.0.0" 570 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" 571 | integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== 572 | 573 | rimraf@2.6.3: 574 | version "2.6.3" 575 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" 576 | integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== 577 | dependencies: 578 | glob "^7.1.3" 579 | 580 | semver@^7.2.1: 581 | version "7.3.2" 582 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" 583 | integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== 584 | 585 | shebang-command@^2.0.0: 586 | version "2.0.0" 587 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" 588 | integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== 589 | dependencies: 590 | shebang-regex "^3.0.0" 591 | 592 | shebang-regex@^3.0.0: 593 | version "3.0.0" 594 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" 595 | integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== 596 | 597 | slice-ansi@^2.1.0: 598 | version "2.1.0" 599 | resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" 600 | integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== 601 | dependencies: 602 | ansi-styles "^3.2.0" 603 | astral-regex "^1.0.0" 604 | is-fullwidth-code-point "^2.0.0" 605 | 606 | sprintf-js@~1.0.2: 607 | version "1.0.3" 608 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 609 | integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= 610 | 611 | string-width@^3.0.0: 612 | version "3.1.0" 613 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" 614 | integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== 615 | dependencies: 616 | emoji-regex "^7.0.1" 617 | is-fullwidth-code-point "^2.0.0" 618 | strip-ansi "^5.1.0" 619 | 620 | strip-ansi@^5.1.0: 621 | version "5.2.0" 622 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" 623 | integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== 624 | dependencies: 625 | ansi-regex "^4.1.0" 626 | 627 | strip-ansi@^6.0.0: 628 | version "6.0.0" 629 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" 630 | integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== 631 | dependencies: 632 | ansi-regex "^5.0.0" 633 | 634 | strip-json-comments@^3.1.0: 635 | version "3.1.0" 636 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.0.tgz#7638d31422129ecf4457440009fba03f9f9ac180" 637 | integrity sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w== 638 | 639 | supports-color@^5.3.0: 640 | version "5.5.0" 641 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 642 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 643 | dependencies: 644 | has-flag "^3.0.0" 645 | 646 | supports-color@^7.1.0: 647 | version "7.1.0" 648 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" 649 | integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== 650 | dependencies: 651 | has-flag "^4.0.0" 652 | 653 | table@^5.2.3: 654 | version "5.4.6" 655 | resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" 656 | integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== 657 | dependencies: 658 | ajv "^6.10.2" 659 | lodash "^4.17.14" 660 | slice-ansi "^2.1.0" 661 | string-width "^3.0.0" 662 | 663 | text-table@^0.2.0: 664 | version "0.2.0" 665 | resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" 666 | integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= 667 | 668 | type-check@^0.4.0, type-check@~0.4.0: 669 | version "0.4.0" 670 | resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" 671 | integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== 672 | dependencies: 673 | prelude-ls "^1.2.1" 674 | 675 | type-fest@^0.8.1: 676 | version "0.8.1" 677 | resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" 678 | integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== 679 | 680 | uri-js@^4.2.2: 681 | version "4.2.2" 682 | resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" 683 | integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== 684 | dependencies: 685 | punycode "^2.1.0" 686 | 687 | v8-compile-cache@^2.0.3: 688 | version "2.1.1" 689 | resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745" 690 | integrity sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ== 691 | 692 | which@^2.0.1: 693 | version "2.0.2" 694 | resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" 695 | integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== 696 | dependencies: 697 | isexe "^2.0.0" 698 | 699 | word-wrap@^1.2.3: 700 | version "1.2.3" 701 | resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" 702 | integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== 703 | 704 | wrappy@1: 705 | version "1.0.2" 706 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 707 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 708 | 709 | write@1.0.3: 710 | version "1.0.3" 711 | resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" 712 | integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== 713 | dependencies: 714 | mkdirp "^0.5.1" 715 | --------------------------------------------------------------------------------