@regantis-sdk/react-native-chat 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +157 -0
- package/RegantisReactNativeChat.podspec +17 -0
- package/android/build.gradle +24 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/technology/regantis/chat/RegantisChatNativeModule.java +244 -0
- package/android/src/main/java/technology/regantis/chat/RegantisChatPackage.java +28 -0
- package/index.d.ts +91 -0
- package/index.js +10 -0
- package/ios/RegantisChatNative.h +5 -0
- package/ios/RegantisChatNative.m +191 -0
- package/package.json +35 -0
- package/src/RegantisChat.js +539 -0
- package/src/client.js +630 -0
- package/src/defaults.js +43 -0
- package/src/native.js +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# @regantis/react-native-chat
|
|
2
|
+
|
|
3
|
+
Native Regantis customer support chat for React Native Android and iOS.
|
|
4
|
+
|
|
5
|
+
## Compatibility
|
|
6
|
+
|
|
7
|
+
- React Native 0.77+
|
|
8
|
+
- Android API 24+
|
|
9
|
+
- iOS 15.1+
|
|
10
|
+
- React Native New Architecture and interop-compatible apps
|
|
11
|
+
- No runtime npm dependencies besides `react` and `react-native`
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @regantis/react-native-chat
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
For iOS, install pods after adding the package:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
cd ios
|
|
23
|
+
pod install
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Android is autolinked by React Native.
|
|
27
|
+
|
|
28
|
+
## Basic usage
|
|
29
|
+
|
|
30
|
+
```jsx
|
|
31
|
+
import { RegantisChat } from '@regantis/react-native-chat';
|
|
32
|
+
|
|
33
|
+
export default function SupportScreen() {
|
|
34
|
+
return <RegantisChat apiKey="YOUR_REACT_NATIVE_CHAT_API_KEY" />;
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The SDK downloads the Chat settings and UI texts from the CRM. Theme, colors, logo, agent display, sounds, ratings, transcript, translations and white-label settings do not need to be duplicated in the app.
|
|
39
|
+
|
|
40
|
+
## Logged-in customer
|
|
41
|
+
|
|
42
|
+
Do not put the customer identity signing secret in the mobile app. Generate the identity timestamp and HMAC signature on your server and return them through your authenticated app API.
|
|
43
|
+
|
|
44
|
+
```jsx
|
|
45
|
+
import { RegantisChat } from '@regantis/react-native-chat';
|
|
46
|
+
|
|
47
|
+
export default function SupportScreen({ user, chatIdentity }) {
|
|
48
|
+
return (
|
|
49
|
+
<RegantisChat
|
|
50
|
+
apiKey="YOUR_REACT_NATIVE_CHAT_API_KEY"
|
|
51
|
+
language="ro"
|
|
52
|
+
screen="support"
|
|
53
|
+
customer={{
|
|
54
|
+
id: user.id,
|
|
55
|
+
name: user.name,
|
|
56
|
+
email: user.email,
|
|
57
|
+
telephone: user.telephone,
|
|
58
|
+
identityTimestamp: chatIdentity.timestamp,
|
|
59
|
+
identitySignature: chatIdentity.signature,
|
|
60
|
+
}}
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The signing payload is identical to the web widget:
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
timestamp
|
|
70
|
+
customer_id
|
|
71
|
+
name
|
|
72
|
+
lowercase_email
|
|
73
|
+
telephone
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Use HMAC-SHA256 and a Unix timestamp in seconds. The CRM currently accepts signatures for 10 minutes.
|
|
77
|
+
|
|
78
|
+
## Optional overrides
|
|
79
|
+
|
|
80
|
+
Server configuration remains the default. Per-app overrides are optional:
|
|
81
|
+
|
|
82
|
+
```jsx
|
|
83
|
+
<RegantisChat
|
|
84
|
+
apiKey="YOUR_REACT_NATIVE_CHAT_API_KEY"
|
|
85
|
+
language="en"
|
|
86
|
+
translationLanguage="de"
|
|
87
|
+
screen="settings/support"
|
|
88
|
+
theme={{
|
|
89
|
+
theme_mode: 'dark',
|
|
90
|
+
primary_color: '#0B63E5',
|
|
91
|
+
}}
|
|
92
|
+
texts={{
|
|
93
|
+
placeholder: 'Write a message…',
|
|
94
|
+
}}
|
|
95
|
+
onMessage={message => {}}
|
|
96
|
+
onUnreadChange={count => {}}
|
|
97
|
+
onError={error => {}}
|
|
98
|
+
/>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`language` controls the SDK UI language. `translationLanguage` controls conversation translation when customer translations are enabled in Chat.
|
|
102
|
+
|
|
103
|
+
## Included features
|
|
104
|
+
|
|
105
|
+
- persistent anonymous visitor/session identity
|
|
106
|
+
- optional signed logged-in customer identity
|
|
107
|
+
- contact form and prefilled verified customer flow
|
|
108
|
+
- conversation restore
|
|
109
|
+
- message history and older-history pagination
|
|
110
|
+
- send/receive messages
|
|
111
|
+
- chatbot replies and agent handoff
|
|
112
|
+
- WebSocket realtime updates with HTTP polling fallback
|
|
113
|
+
- typing state
|
|
114
|
+
- customer-side translation selector
|
|
115
|
+
- images and files
|
|
116
|
+
- current-app screenshot attachment
|
|
117
|
+
- native incoming-message sound
|
|
118
|
+
- close/reopen conversation
|
|
119
|
+
- rating and rating comment
|
|
120
|
+
- transcript email flow
|
|
121
|
+
- read state and unread callback
|
|
122
|
+
- light/dark mode
|
|
123
|
+
- basic/advanced color configuration
|
|
124
|
+
- logo, agent photo toggle and white-label behavior
|
|
125
|
+
- CRM-managed localized UI texts
|
|
126
|
+
|
|
127
|
+
The web-only launcher settings such as desktop/mobile corner placement and minimized bubble/bar mode are still returned in the settings object for configuration parity, but they do not change a full-page React Native component because there is no browser launcher to position.
|
|
128
|
+
|
|
129
|
+
## Headless client
|
|
130
|
+
|
|
131
|
+
The UI is optional. The same package exports the client if an app needs its own presentation layer:
|
|
132
|
+
|
|
133
|
+
```js
|
|
134
|
+
import { createRegantisChatClient } from '@regantis/react-native-chat';
|
|
135
|
+
|
|
136
|
+
const chat = createRegantisChatClient({
|
|
137
|
+
apiKey: 'YOUR_REACT_NATIVE_CHAT_API_KEY',
|
|
138
|
+
language: 'ro',
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const unsubscribe = chat.subscribe(state => {
|
|
142
|
+
console.log(state.messages);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
await chat.start();
|
|
146
|
+
await chat.sendMessage('Salut');
|
|
147
|
+
|
|
148
|
+
unsubscribe();
|
|
149
|
+
chat.stop();
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Publishing
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
npm login
|
|
156
|
+
npm publish --access public
|
|
157
|
+
```
|
|
@@ -0,0 +1,17 @@
|
|
|
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 = "RegantisReactNativeChat"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = "https://regantis.technology"
|
|
10
|
+
s.license = { :type => "UNLICENSED" }
|
|
11
|
+
s.author = { "Regantis" => "info@regantis.technology" }
|
|
12
|
+
s.platforms = { :ios => "15.1" }
|
|
13
|
+
s.source = { :git => "https://github.com/regantis/react-native-chat.git", :tag => "#{s.version}" }
|
|
14
|
+
s.source_files = "ios/**/*.{h,m,mm}"
|
|
15
|
+
s.frameworks = "AudioToolbox", "UniformTypeIdentifiers"
|
|
16
|
+
s.dependency "React-Core"
|
|
17
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
apply plugin: "com.android.library"
|
|
2
|
+
|
|
3
|
+
def safeExtGet(prop, fallback) {
|
|
4
|
+
return rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
android {
|
|
8
|
+
namespace "technology.regantis.chat"
|
|
9
|
+
compileSdkVersion safeExtGet("compileSdkVersion", 35)
|
|
10
|
+
|
|
11
|
+
defaultConfig {
|
|
12
|
+
minSdkVersion safeExtGet("minSdkVersion", 24)
|
|
13
|
+
targetSdkVersion safeExtGet("targetSdkVersion", 35)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
repositories {
|
|
18
|
+
google()
|
|
19
|
+
mavenCentral()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
dependencies {
|
|
23
|
+
implementation "com.facebook.react:react-android"
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android" />
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
package technology.regantis.chat;
|
|
2
|
+
|
|
3
|
+
import android.app.Activity;
|
|
4
|
+
import android.content.Context;
|
|
5
|
+
import android.content.Intent;
|
|
6
|
+
import android.content.SharedPreferences;
|
|
7
|
+
import android.graphics.Bitmap;
|
|
8
|
+
import android.graphics.Canvas;
|
|
9
|
+
import android.database.Cursor;
|
|
10
|
+
import android.media.Ringtone;
|
|
11
|
+
import android.media.RingtoneManager;
|
|
12
|
+
import android.net.Uri;
|
|
13
|
+
import android.provider.OpenableColumns;
|
|
14
|
+
import android.view.View;
|
|
15
|
+
import android.webkit.MimeTypeMap;
|
|
16
|
+
|
|
17
|
+
import androidx.annotation.NonNull;
|
|
18
|
+
import androidx.annotation.Nullable;
|
|
19
|
+
|
|
20
|
+
import com.facebook.react.bridge.ActivityEventListener;
|
|
21
|
+
import com.facebook.react.bridge.Arguments;
|
|
22
|
+
import com.facebook.react.bridge.BaseActivityEventListener;
|
|
23
|
+
import com.facebook.react.bridge.Promise;
|
|
24
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
25
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
26
|
+
import com.facebook.react.bridge.ReactMethod;
|
|
27
|
+
import com.facebook.react.bridge.WritableMap;
|
|
28
|
+
|
|
29
|
+
import java.io.File;
|
|
30
|
+
import java.io.FileOutputStream;
|
|
31
|
+
|
|
32
|
+
public class RegantisChatNativeModule extends ReactContextBaseJavaModule {
|
|
33
|
+
private static final String MODULE_NAME = "RegantisChatNative";
|
|
34
|
+
private static final String STORAGE_NAME = "regantis_react_native_chat";
|
|
35
|
+
private static final int PICK_ATTACHMENT_REQUEST = 17841;
|
|
36
|
+
|
|
37
|
+
private final ReactApplicationContext reactContext;
|
|
38
|
+
private Promise pickerPromise;
|
|
39
|
+
|
|
40
|
+
private final ActivityEventListener activityEventListener = new BaseActivityEventListener() {
|
|
41
|
+
@Override
|
|
42
|
+
public void onActivityResult(Activity activity, int requestCode, int resultCode, @Nullable Intent data) {
|
|
43
|
+
if (requestCode != PICK_ATTACHMENT_REQUEST || pickerPromise == null) return;
|
|
44
|
+
|
|
45
|
+
Promise promise = pickerPromise;
|
|
46
|
+
pickerPromise = null;
|
|
47
|
+
|
|
48
|
+
if (resultCode != Activity.RESULT_OK || data == null || data.getData() == null) {
|
|
49
|
+
promise.resolve(null);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
Uri uri = data.getData();
|
|
54
|
+
try {
|
|
55
|
+
int flags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
|
56
|
+
if ((flags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
|
|
57
|
+
reactContext.getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
58
|
+
}
|
|
59
|
+
} catch (Exception ignored) {
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
WritableMap result = Arguments.createMap();
|
|
63
|
+
result.putString("uri", uri.toString());
|
|
64
|
+
result.putString("name", getDisplayName(uri));
|
|
65
|
+
result.putString("type", getMimeType(uri));
|
|
66
|
+
result.putDouble("size", getSize(uri));
|
|
67
|
+
promise.resolve(result);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
public RegantisChatNativeModule(ReactApplicationContext reactContext) {
|
|
72
|
+
super(reactContext);
|
|
73
|
+
this.reactContext = reactContext;
|
|
74
|
+
reactContext.addActivityEventListener(activityEventListener);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@NonNull
|
|
78
|
+
@Override
|
|
79
|
+
public String getName() {
|
|
80
|
+
return MODULE_NAME;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private SharedPreferences storage() {
|
|
84
|
+
return reactContext.getSharedPreferences(STORAGE_NAME, Context.MODE_PRIVATE);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@ReactMethod
|
|
88
|
+
public void getItem(String key, Promise promise) {
|
|
89
|
+
promise.resolve(storage().getString(key, null));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@ReactMethod
|
|
93
|
+
public void setItem(String key, String value, Promise promise) {
|
|
94
|
+
storage().edit().putString(key, value).apply();
|
|
95
|
+
promise.resolve(null);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@ReactMethod
|
|
99
|
+
public void removeItem(String key, Promise promise) {
|
|
100
|
+
storage().edit().remove(key).apply();
|
|
101
|
+
promise.resolve(null);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@ReactMethod
|
|
105
|
+
public void getAppInfo(Promise promise) {
|
|
106
|
+
WritableMap result = Arguments.createMap();
|
|
107
|
+
result.putString("appId", reactContext.getPackageName());
|
|
108
|
+
result.putString("platform", "android");
|
|
109
|
+
try {
|
|
110
|
+
String versionName = reactContext.getPackageManager()
|
|
111
|
+
.getPackageInfo(reactContext.getPackageName(), 0)
|
|
112
|
+
.versionName;
|
|
113
|
+
result.putString("appVersion", versionName == null ? "" : versionName);
|
|
114
|
+
} catch (Exception ignored) {
|
|
115
|
+
result.putString("appVersion", "");
|
|
116
|
+
}
|
|
117
|
+
promise.resolve(result);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@ReactMethod
|
|
121
|
+
public void playIncomingSound() {
|
|
122
|
+
try {
|
|
123
|
+
Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
|
124
|
+
Ringtone ringtone = RingtoneManager.getRingtone(reactContext, soundUri);
|
|
125
|
+
if (ringtone != null) ringtone.play();
|
|
126
|
+
} catch (Exception ignored) {
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@ReactMethod
|
|
131
|
+
public void captureScreenshot(Promise promise) {
|
|
132
|
+
Activity activity = getCurrentActivity();
|
|
133
|
+
if (activity == null) {
|
|
134
|
+
promise.reject("NO_ACTIVITY", "No active Android Activity");
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
activity.runOnUiThread(() -> {
|
|
139
|
+
try {
|
|
140
|
+
View root = activity.getWindow().getDecorView().getRootView();
|
|
141
|
+
int width = root.getWidth();
|
|
142
|
+
int height = root.getHeight();
|
|
143
|
+
if (width < 1 || height < 1) {
|
|
144
|
+
promise.reject("SCREENSHOT_FAILED", "Invalid app window size");
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
|
149
|
+
Canvas canvas = new Canvas(bitmap);
|
|
150
|
+
root.draw(canvas);
|
|
151
|
+
|
|
152
|
+
File directory = new File(reactContext.getCacheDir(), "regantis-chat");
|
|
153
|
+
if (!directory.exists() && !directory.mkdirs()) {
|
|
154
|
+
promise.reject("SCREENSHOT_FAILED", "Unable to create screenshot directory");
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
File file = new File(directory, "screenshot-" + System.currentTimeMillis() + ".png");
|
|
158
|
+
try (FileOutputStream stream = new FileOutputStream(file)) {
|
|
159
|
+
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
|
|
160
|
+
} finally {
|
|
161
|
+
bitmap.recycle();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
WritableMap result = Arguments.createMap();
|
|
165
|
+
result.putString("uri", Uri.fromFile(file).toString());
|
|
166
|
+
result.putString("name", file.getName());
|
|
167
|
+
result.putString("type", "image/png");
|
|
168
|
+
result.putDouble("size", file.length());
|
|
169
|
+
promise.resolve(result);
|
|
170
|
+
} catch (Exception error) {
|
|
171
|
+
promise.reject("SCREENSHOT_FAILED", error);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
@ReactMethod
|
|
177
|
+
public void pickAttachment(String kind, Promise promise) {
|
|
178
|
+
Activity activity = getCurrentActivity();
|
|
179
|
+
if (activity == null) {
|
|
180
|
+
promise.reject("NO_ACTIVITY", "No active Android Activity");
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (pickerPromise != null) {
|
|
184
|
+
promise.reject("PICKER_BUSY", "An attachment picker is already open");
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
|
189
|
+
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
|
190
|
+
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
|
|
191
|
+
if ("image".equals(kind)) {
|
|
192
|
+
intent.setType("image/*");
|
|
193
|
+
} else {
|
|
194
|
+
intent.setType("*/*");
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
pickerPromise = promise;
|
|
198
|
+
try {
|
|
199
|
+
activity.startActivityForResult(intent, PICK_ATTACHMENT_REQUEST);
|
|
200
|
+
} catch (Exception error) {
|
|
201
|
+
pickerPromise = null;
|
|
202
|
+
promise.reject("PICKER_FAILED", error);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private String getDisplayName(Uri uri) {
|
|
207
|
+
String name = null;
|
|
208
|
+
try (Cursor cursor = reactContext.getContentResolver().query(uri, null, null, null, null)) {
|
|
209
|
+
if (cursor != null && cursor.moveToFirst()) {
|
|
210
|
+
int index = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
|
211
|
+
if (index >= 0) name = cursor.getString(index);
|
|
212
|
+
}
|
|
213
|
+
} catch (Exception ignored) {
|
|
214
|
+
}
|
|
215
|
+
if (name == null || name.trim().isEmpty()) {
|
|
216
|
+
String segment = uri.getLastPathSegment();
|
|
217
|
+
name = segment == null || segment.trim().isEmpty() ? "attachment" : segment;
|
|
218
|
+
}
|
|
219
|
+
return name;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
private double getSize(Uri uri) {
|
|
223
|
+
try (Cursor cursor = reactContext.getContentResolver().query(uri, null, null, null, null)) {
|
|
224
|
+
if (cursor != null && cursor.moveToFirst()) {
|
|
225
|
+
int index = cursor.getColumnIndex(OpenableColumns.SIZE);
|
|
226
|
+
if (index >= 0 && !cursor.isNull(index)) return cursor.getLong(index);
|
|
227
|
+
}
|
|
228
|
+
} catch (Exception ignored) {
|
|
229
|
+
}
|
|
230
|
+
return 0;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private String getMimeType(Uri uri) {
|
|
234
|
+
String type = reactContext.getContentResolver().getType(uri);
|
|
235
|
+
if (type != null && !type.trim().isEmpty()) return type;
|
|
236
|
+
|
|
237
|
+
String extension = MimeTypeMap.getFileExtensionFromUrl(uri.toString());
|
|
238
|
+
if (extension != null && !extension.isEmpty()) {
|
|
239
|
+
String guessed = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase());
|
|
240
|
+
if (guessed != null) return guessed;
|
|
241
|
+
}
|
|
242
|
+
return "application/octet-stream";
|
|
243
|
+
}
|
|
244
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
package technology.regantis.chat;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
|
|
5
|
+
import com.facebook.react.ReactPackage;
|
|
6
|
+
import com.facebook.react.bridge.NativeModule;
|
|
7
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
8
|
+
import com.facebook.react.uimanager.ViewManager;
|
|
9
|
+
|
|
10
|
+
import java.util.ArrayList;
|
|
11
|
+
import java.util.Collections;
|
|
12
|
+
import java.util.List;
|
|
13
|
+
|
|
14
|
+
public class RegantisChatPackage implements ReactPackage {
|
|
15
|
+
@NonNull
|
|
16
|
+
@Override
|
|
17
|
+
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
|
|
18
|
+
List<NativeModule> modules = new ArrayList<>();
|
|
19
|
+
modules.add(new RegantisChatNativeModule(reactContext));
|
|
20
|
+
return modules;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@NonNull
|
|
24
|
+
@Override
|
|
25
|
+
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
|
|
26
|
+
return Collections.emptyList();
|
|
27
|
+
}
|
|
28
|
+
}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export type RegantisChatCustomer = {
|
|
5
|
+
id?: string | number;
|
|
6
|
+
customerId?: string | number;
|
|
7
|
+
name?: string;
|
|
8
|
+
email?: string;
|
|
9
|
+
telephone?: string;
|
|
10
|
+
phone?: string;
|
|
11
|
+
identityTimestamp?: string | number;
|
|
12
|
+
identitySignature?: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type RegantisChatTheme = Partial<{
|
|
16
|
+
operator_name: string;
|
|
17
|
+
use_profile_names: 0 | 1;
|
|
18
|
+
minimized_type: 'bubble' | 'bar';
|
|
19
|
+
theme_mode: 'light' | 'dark';
|
|
20
|
+
color_mode: 'theme' | 'advanced';
|
|
21
|
+
theme_color: string;
|
|
22
|
+
primary_color: string;
|
|
23
|
+
chat_background: string;
|
|
24
|
+
chat_background_opacity: number;
|
|
25
|
+
customer_bubble: string;
|
|
26
|
+
customer_text: string;
|
|
27
|
+
agent_bubble: string;
|
|
28
|
+
agent_text: string;
|
|
29
|
+
system_text: string;
|
|
30
|
+
launcher_background: string;
|
|
31
|
+
launcher_icon_color: string;
|
|
32
|
+
desktop_align: 'left' | 'right';
|
|
33
|
+
desktop_side_spacing: number;
|
|
34
|
+
desktop_bottom_spacing: number;
|
|
35
|
+
desktop_visibility: string;
|
|
36
|
+
mobile_enabled: 0 | 1;
|
|
37
|
+
mobile_same_as_desktop: 0 | 1;
|
|
38
|
+
mobile_minimized_type: 'bubble' | 'bar';
|
|
39
|
+
mobile_align: 'left' | 'right';
|
|
40
|
+
mobile_side_spacing: number;
|
|
41
|
+
mobile_bottom_spacing: number;
|
|
42
|
+
mobile_visibility: string;
|
|
43
|
+
show_logo: 0 | 1;
|
|
44
|
+
logo_url: string;
|
|
45
|
+
show_agent_photo: 0 | 1;
|
|
46
|
+
sound_enabled: 0 | 1;
|
|
47
|
+
rating_enabled: 0 | 1;
|
|
48
|
+
transcript_enabled: 0 | 1;
|
|
49
|
+
before_you_go_enabled: 0 | 1;
|
|
50
|
+
white_label: 0 | 1;
|
|
51
|
+
}>;
|
|
52
|
+
|
|
53
|
+
export type RegantisChatProps = {
|
|
54
|
+
apiKey: string;
|
|
55
|
+
serverUrl?: string;
|
|
56
|
+
language?: string;
|
|
57
|
+
translationLanguage?: string;
|
|
58
|
+
customer?: RegantisChatCustomer;
|
|
59
|
+
theme?: RegantisChatTheme;
|
|
60
|
+
texts?: Record<string, string>;
|
|
61
|
+
screen?: string;
|
|
62
|
+
style?: StyleProp<ViewStyle>;
|
|
63
|
+
keyboardVerticalOffset?: number;
|
|
64
|
+
onMessage?: (message: any) => void;
|
|
65
|
+
onUnreadChange?: (count: number) => void;
|
|
66
|
+
onError?: (error: Error) => void;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export class RegantisChatClient {
|
|
70
|
+
constructor(config: RegantisChatProps);
|
|
71
|
+
start(): Promise<any>;
|
|
72
|
+
stop(): void;
|
|
73
|
+
subscribe(listener: (state: any) => void): () => void;
|
|
74
|
+
getState(): any;
|
|
75
|
+
sendMessage(message: string): Promise<any>;
|
|
76
|
+
saveContact(name: string, email: string): Promise<any>;
|
|
77
|
+
uploadAttachment(file: { uri: string; name?: string; type?: string }, kind: 'image' | 'file'): Promise<any>;
|
|
78
|
+
pickAndUpload(kind: 'image' | 'file' | 'screenshot'): Promise<any>;
|
|
79
|
+
setTranslationLanguage(languageCode: string): Promise<void>;
|
|
80
|
+
closeChat(): Promise<any>;
|
|
81
|
+
reopenChat(): Promise<any>;
|
|
82
|
+
rate(rating: 0 | 1): Promise<any>;
|
|
83
|
+
rateComment(comment: string): Promise<any>;
|
|
84
|
+
sendTranscript(email: string): Promise<any>;
|
|
85
|
+
markRead(): Promise<void>;
|
|
86
|
+
setVisible(visible: boolean): void;
|
|
87
|
+
setSoundEnabled(enabled: boolean): void;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const RegantisChat: React.ComponentType<RegantisChatProps>;
|
|
91
|
+
export function createRegantisChatClient(config: RegantisChatProps): RegantisChatClient;
|
package/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const RegantisChat = require('./src/RegantisChat');
|
|
4
|
+
const { RegantisChatClient } = require('./src/client');
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
RegantisChat,
|
|
8
|
+
RegantisChatClient,
|
|
9
|
+
createRegantisChatClient: config => new RegantisChatClient(config),
|
|
10
|
+
};
|