@zellio/chat-react-native 0.0.0-stage → 1.0.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.
Files changed (58) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +239 -2
  3. package/ZellioChatReactNative.podspec +30 -0
  4. package/android/build.gradle +62 -0
  5. package/android/src/main/AndroidManifest.xml +1 -0
  6. package/android/src/main/java/ai/zellio/chat/reactnative/ZellioChatModule.kt +214 -0
  7. package/android/src/main/java/ai/zellio/chat/reactnative/ZellioChatPackage.kt +25 -0
  8. package/app.plugin.js +4 -0
  9. package/ios/RNZellioChat.h +5 -0
  10. package/ios/RNZellioChat.mm +116 -0
  11. package/ios/ZCRNBridge.swift +217 -0
  12. package/ios/vendor/resources/PrivacyInfo.xcprivacy +99 -0
  13. package/ios/vendor/swift/ZellioChat/Model/ChatModel.swift +261 -0
  14. package/ios/vendor/swift/ZellioChat/Platform/Platform.swift +124 -0
  15. package/ios/vendor/swift/ZellioChat/Platform/URLSessionTransport.swift +175 -0
  16. package/ios/vendor/swift/ZellioChat/Preview/Previews.swift +62 -0
  17. package/ios/vendor/swift/ZellioChat/Public/InAppBanner.swift +155 -0
  18. package/ios/vendor/swift/ZellioChat/Public/PublicTypes.swift +141 -0
  19. package/ios/vendor/swift/ZellioChat/Public/Runtime.swift +406 -0
  20. package/ios/vendor/swift/ZellioChat/Public/ZellioChat.swift +209 -0
  21. package/ios/vendor/swift/ZellioChat/Theme/ChatTheme.swift +185 -0
  22. package/ios/vendor/swift/ZellioChat/Thread/AttachmentPicker.swift +354 -0
  23. package/ios/vendor/swift/ZellioChat/Thread/ComposerBar.swift +355 -0
  24. package/ios/vendor/swift/ZellioChat/Thread/ThreadCells.swift +776 -0
  25. package/ios/vendor/swift/ZellioChat/Thread/ThreadViewController.swift +703 -0
  26. package/ios/vendor/swift/ZellioChat/Views/ChatRootView.swift +154 -0
  27. package/ios/vendor/swift/ZellioChat/Views/Components.swift +335 -0
  28. package/ios/vendor/swift/ZellioChat/Views/HomeView.swift +413 -0
  29. package/ios/vendor/swift/ZellioChatCore/ChatModels.swift +373 -0
  30. package/ios/vendor/swift/ZellioChatCore/ConfigModels.swift +389 -0
  31. package/ios/vendor/swift/ZellioChatCore/Engine.swift +325 -0
  32. package/ios/vendor/swift/ZellioChatCore/Errors.swift +158 -0
  33. package/ios/vendor/swift/ZellioChatCore/Generated/ChatStringTable.swift +125 -0
  34. package/ios/vendor/swift/ZellioChatCore/Outbox.swift +223 -0
  35. package/ios/vendor/swift/ZellioChatCore/Push.swift +74 -0
  36. package/ios/vendor/swift/ZellioChatCore/Realtime.swift +328 -0
  37. package/ios/vendor/swift/ZellioChatCore/Session.swift +210 -0
  38. package/ios/vendor/swift/ZellioChatCore/Storage.swift +201 -0
  39. package/ios/vendor/swift/ZellioChatCore/Strings.swift +104 -0
  40. package/ios/vendor/swift/ZellioChatCore/Theme.swift +238 -0
  41. package/ios/vendor/swift/ZellioChatCore/ThreadController.swift +319 -0
  42. package/ios/vendor/swift/ZellioChatCore/Timeline.swift +157 -0
  43. package/ios/vendor/swift/ZellioChatCore/Transport.swift +236 -0
  44. package/ios/vendor/swift/ZellioChatCore/Util.swift +137 -0
  45. package/lib/NativeZellioChat.d.ts +34 -0
  46. package/lib/NativeZellioChat.js +4 -0
  47. package/lib/index.d.ts +37 -0
  48. package/lib/index.js +241 -0
  49. package/lib/types.d.ts +54 -0
  50. package/lib/types.js +2 -0
  51. package/lib/version.d.ts +4 -0
  52. package/lib/version.js +7 -0
  53. package/package.json +75 -4
  54. package/plugin/build/index.js +73 -0
  55. package/src/NativeZellioChat.ts +30 -0
  56. package/src/index.ts +237 -0
  57. package/src/types.ts +56 -0
  58. package/src/version.ts +5 -0
package/src/index.ts ADDED
@@ -0,0 +1,237 @@
1
+ import NativeZellioChat from './NativeZellioChat';
2
+ import type {
3
+ ZellioChatConfig,
4
+ ZellioChatEvent,
5
+ ZellioContext,
6
+ ZellioPushEnvironment,
7
+ ZellioPushPayload,
8
+ ZellioSubscription,
9
+ ZellioTokenProvider,
10
+ } from './types';
11
+ import { WRAPPER } from './version';
12
+
13
+ export type * from './types';
14
+ export { VERSION } from './version';
15
+
16
+ /** Native asks for a token at most this long before it gives up. */
17
+ export const TOKEN_TIMEOUT_MS = 15_000;
18
+
19
+ let tokenProvider: ZellioTokenProvider | null = null;
20
+ let tokenSubscription: ZellioSubscription | null = null;
21
+
22
+ function reportMisuse(call: string, error: unknown): void {
23
+ // Only the call name and the error kind. Never the arguments: they can hold a token.
24
+ const kind = error instanceof Error ? error.name : 'unknown';
25
+ console.warn(`[ZellioChat] ${call} failed (${kind})`);
26
+ }
27
+
28
+ function guard(call: string, run: () => void): void {
29
+ try {
30
+ run();
31
+ } catch (error) {
32
+ reportMisuse(call, error);
33
+ }
34
+ }
35
+
36
+ /** Call once at app start. Cheap: no network on the calling thread, no UI. */
37
+ export function configure(config: ZellioChatConfig): void {
38
+ guard('configure', () => {
39
+ const { appKey, theme, ...rest } = config;
40
+ NativeZellioChat.configure(
41
+ appKey,
42
+ withoutUndefined({
43
+ ...rest,
44
+ themeMode: theme?.mode,
45
+ primaryColor: theme?.primaryColor,
46
+ customerMessageColor: theme?.customerMessageColor,
47
+ surfaceColor: theme?.surfaceColor,
48
+ wrapper: WRAPPER,
49
+ }),
50
+ );
51
+ });
52
+ }
53
+
54
+ /** The bridge turns `undefined` into null. Native code reads "missing" more simply than "null". */
55
+ function withoutUndefined(values: Record<string, string | boolean | undefined>): Record<string, string | boolean> {
56
+ const out: Record<string, string | boolean> = {};
57
+ for (const [key, value] of Object.entries(values)) {
58
+ if (value !== undefined) out[key] = value;
59
+ }
60
+ return out;
61
+ }
62
+
63
+ async function answerTokenRequest(requestId: string): Promise<void> {
64
+ const provider = tokenProvider;
65
+ if (provider === null) {
66
+ NativeZellioChat.resolveToken(requestId, null, 'no_provider');
67
+ return;
68
+ }
69
+ let timer: ReturnType<typeof setTimeout> | undefined;
70
+ try {
71
+ const token = await Promise.race([
72
+ provider(),
73
+ new Promise<never>((_, reject) => {
74
+ timer = setTimeout(() => reject(new Error('timeout')), TOKEN_TIMEOUT_MS);
75
+ }),
76
+ ]);
77
+ if (typeof token === 'string' && token.length > 0) {
78
+ NativeZellioChat.resolveToken(requestId, token, null);
79
+ } else {
80
+ NativeZellioChat.resolveToken(requestId, null, 'no_token');
81
+ }
82
+ } catch (error) {
83
+ const timedOut = error instanceof Error && error.message === 'timeout';
84
+ NativeZellioChat.resolveToken(requestId, null, timedOut ? 'timeout' : 'provider_error');
85
+ } finally {
86
+ if (timer !== undefined) clearTimeout(timer);
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Marks a signed-in customer. `tokenProvider` returns a JWT from your server. The SDK calls it when it needs a
92
+ * session and again if the server rejects the token. The token is never logged. Calling `identify` again
93
+ * replaces the provider.
94
+ */
95
+ export function identify(provider: ZellioTokenProvider): void {
96
+ guard('identify', () => {
97
+ tokenProvider = provider;
98
+ if (tokenSubscription === null) {
99
+ tokenSubscription = NativeZellioChat.onTokenRequest((request) => {
100
+ void answerTokenRequest(request.requestId);
101
+ });
102
+ }
103
+ NativeZellioChat.identify();
104
+ });
105
+ }
106
+
107
+ /** Ends the session, deletes all local chat data and removes the push token from the server. */
108
+ export function logout(): void {
109
+ guard('logout', () => {
110
+ tokenProvider = null;
111
+ NativeZellioChat.logout();
112
+ });
113
+ }
114
+
115
+ /** Shows chat on top of the app. */
116
+ export function present(): void {
117
+ guard('present', () => NativeZellioChat.present());
118
+ }
119
+
120
+ /** Opens one conversation, or a new one when `conversationId` is omitted. */
121
+ export function presentConversation(conversationId?: string | null): void {
122
+ guard('presentConversation', () => NativeZellioChat.presentConversation(conversationId ?? null));
123
+ }
124
+
125
+ /** Closes chat. */
126
+ export function dismiss(): void {
127
+ guard('dismiss', () => NativeZellioChat.dismiss());
128
+ }
129
+
130
+ /** Tells agents what the customer looks at. It is sent with the next messages. `null` clears it. */
131
+ export function setContext(context: ZellioContext | null): void {
132
+ guard('setContext', () => {
133
+ NativeZellioChat.setContext(context === null ? null : withoutUndefined({ ...context }));
134
+ });
135
+ }
136
+
137
+ /** The number of unread agent messages, across all conversations. Resolves 0 if the SDK is not ready. */
138
+ export async function getUnreadCount(): Promise<number> {
139
+ try {
140
+ return await NativeZellioChat.getUnreadCount();
141
+ } catch (error) {
142
+ reportMisuse('getUnreadCount', error);
143
+ return 0;
144
+ }
145
+ }
146
+
147
+ /** Calls back now with the current count and on every change. Call `remove()` to stop. */
148
+ export function addUnreadListener(callback: (count: number) => void): ZellioSubscription {
149
+ let active = true;
150
+ const native = NativeZellioChat.onUnread((event) => {
151
+ if (active) callback(event.count);
152
+ });
153
+ void getUnreadCount().then((count) => {
154
+ if (active) callback(count);
155
+ });
156
+ return {
157
+ remove() {
158
+ if (!active) return;
159
+ active = false;
160
+ native.remove();
161
+ },
162
+ };
163
+ }
164
+
165
+ /** Chat events for your own analytics. Call `remove()` to stop. */
166
+ export function addEventListener(callback: (event: ZellioChatEvent) => void): ZellioSubscription {
167
+ const native = NativeZellioChat.onEvent((event) => {
168
+ switch (event.name) {
169
+ case 'chatOpened':
170
+ case 'chatClosed':
171
+ case 'messageSent':
172
+ case 'messageReceived':
173
+ callback({ type: event.name });
174
+ break;
175
+ case 'error':
176
+ callback({ type: 'error', code: event.code ?? 'unknown' });
177
+ break;
178
+ default:
179
+ break;
180
+ }
181
+ });
182
+ return {
183
+ remove() {
184
+ native.remove();
185
+ },
186
+ };
187
+ }
188
+
189
+ /** Gives the SDK the push token: the APNs token as a hex string on iOS, the FCM token on Android. */
190
+ export function setPushToken(token: string, environment?: ZellioPushEnvironment): void {
191
+ guard('setPushToken', () => NativeZellioChat.setPushToken(token, environment ?? null));
192
+ }
193
+
194
+ function toJson(payload: ZellioPushPayload | null | undefined): string | null {
195
+ if (payload === null || payload === undefined) return null;
196
+ try {
197
+ return JSON.stringify(payload);
198
+ } catch {
199
+ return null;
200
+ }
201
+ }
202
+
203
+ /** True only if the payload has the `zellio` key. */
204
+ export function isZellioPush(payload: ZellioPushPayload | null | undefined): boolean {
205
+ const json = toJson(payload);
206
+ if (json === null) return false;
207
+ try {
208
+ return NativeZellioChat.isZellioPush(json);
209
+ } catch (error) {
210
+ reportMisuse('isZellioPush', error);
211
+ return false;
212
+ }
213
+ }
214
+
215
+ /** Refreshes the unread state. Resolves true if the push was ours. */
216
+ export async function handlePush(payload: ZellioPushPayload | null | undefined): Promise<boolean> {
217
+ const json = toJson(payload);
218
+ if (json === null) return false;
219
+ try {
220
+ return await NativeZellioChat.handlePush(json);
221
+ } catch (error) {
222
+ reportMisuse('handlePush', error);
223
+ return false;
224
+ }
225
+ }
226
+
227
+ /** Opens the conversation that the push names. Resolves true if the push was ours. */
228
+ export async function handleNotificationTap(payload: ZellioPushPayload | null | undefined): Promise<boolean> {
229
+ const json = toJson(payload);
230
+ if (json === null) return false;
231
+ try {
232
+ return await NativeZellioChat.handleNotificationTap(json);
233
+ } catch (error) {
234
+ reportMisuse('handleNotificationTap', error);
235
+ return false;
236
+ }
237
+ }
package/src/types.ts ADDED
@@ -0,0 +1,56 @@
1
+ export type ZellioLocale = 'auto' | 'ar' | 'en';
2
+ export type ZellioThemeMode = 'auto' | 'light' | 'dark';
3
+ export type ZellioLogLevel = 'none' | 'error' | 'warn' | 'info' | 'debug';
4
+
5
+ /** Colors are `#RRGGBB`. A color that would make text hard to read is adjusted by the native SDK. */
6
+ export interface ZellioTheme {
7
+ mode?: ZellioThemeMode;
8
+ primaryColor?: string;
9
+ customerMessageColor?: string;
10
+ surfaceColor?: string;
11
+ }
12
+
13
+ export interface ZellioChatConfig {
14
+ /** The app key from Zellio Studio. */
15
+ appKey: string;
16
+ /** Default: https://app.zellio.ai */
17
+ baseUrl?: string;
18
+ /** Default: `auto` (the Studio language policy, then the device language). */
19
+ locale?: ZellioLocale;
20
+ theme?: ZellioTheme;
21
+ /** Default: `error` on iOS and `warn` on Android. Chat text and tokens are never logged. */
22
+ logLevel?: ZellioLogLevel;
23
+ /** The small banner for a new message while chat is closed. Default: true. */
24
+ showsInAppBanner?: boolean;
25
+ /** false: the banner says "New message" and never shows the text. Default: true. */
26
+ bannerShowsPreview?: boolean;
27
+ }
28
+
29
+ /** What the customer is looking at. Agents see it. `url` must be https. */
30
+ export interface ZellioContext {
31
+ screen?: string;
32
+ itemType?: string;
33
+ itemId?: string;
34
+ itemTitle?: string;
35
+ url?: string;
36
+ }
37
+
38
+ /** Events for your own analytics. They never carry message text. */
39
+ export type ZellioChatEvent =
40
+ | { type: 'chatOpened' }
41
+ | { type: 'chatClosed' }
42
+ | { type: 'messageSent' }
43
+ | { type: 'messageReceived' }
44
+ | { type: 'error'; code: string };
45
+
46
+ /** Returns the identity JWT from your server, or null when nobody is signed in. */
47
+ export type ZellioTokenProvider = () => Promise<string | null>;
48
+
49
+ export interface ZellioSubscription {
50
+ remove(): void;
51
+ }
52
+
53
+ export type ZellioPushEnvironment = 'sandbox' | 'production';
54
+
55
+ /** The data of a push message, as your push library gives it to you. */
56
+ export type ZellioPushPayload = Record<string, unknown>;
package/src/version.ts ADDED
@@ -0,0 +1,5 @@
1
+ /** Kept equal to package.json by `npm run check:version` (runs before every pack). */
2
+ export const VERSION = '1.0.0';
3
+
4
+ /** Sent to the native SDK as `wrapper`, so the x-zee-sdk-wrapper header names this package. */
5
+ export const WRAPPER = `react-native/${VERSION}`;