@giovchat/react-native 0.0.0-stage → 0.3.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 (163) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +390 -2
  3. package/dist/GiovChat.d.ts +135 -0
  4. package/dist/GiovChat.d.ts.map +1 -0
  5. package/dist/GiovChat.js +294 -0
  6. package/dist/GiovChat.js.map +1 -0
  7. package/dist/attachments.d.ts +121 -0
  8. package/dist/attachments.d.ts.map +1 -0
  9. package/dist/attachments.js +185 -0
  10. package/dist/attachments.js.map +1 -0
  11. package/dist/brand-assets.d.ts +6 -0
  12. package/dist/brand-assets.d.ts.map +1 -0
  13. package/dist/brand-assets.js +9 -0
  14. package/dist/brand-assets.js.map +1 -0
  15. package/dist/brandingStyle.d.ts +38 -0
  16. package/dist/brandingStyle.d.ts.map +1 -0
  17. package/dist/brandingStyle.js +71 -0
  18. package/dist/brandingStyle.js.map +1 -0
  19. package/dist/client.d.ts +226 -0
  20. package/dist/client.d.ts.map +1 -0
  21. package/dist/client.js +891 -0
  22. package/dist/client.js.map +1 -0
  23. package/dist/color.d.ts +25 -0
  24. package/dist/color.d.ts.map +1 -0
  25. package/dist/color.js +78 -0
  26. package/dist/color.js.map +1 -0
  27. package/dist/components/Attachments.d.ts +13 -0
  28. package/dist/components/Attachments.d.ts.map +1 -0
  29. package/dist/components/Attachments.js +42 -0
  30. package/dist/components/Attachments.js.map +1 -0
  31. package/dist/components/Branding.d.ts +29 -0
  32. package/dist/components/Branding.d.ts.map +1 -0
  33. package/dist/components/Branding.js +71 -0
  34. package/dist/components/Branding.js.map +1 -0
  35. package/dist/components/ChatMarkdown.d.ts +15 -0
  36. package/dist/components/ChatMarkdown.d.ts.map +1 -0
  37. package/dist/components/ChatMarkdown.js +72 -0
  38. package/dist/components/ChatMarkdown.js.map +1 -0
  39. package/dist/components/GiovBotMark.d.ts +6 -0
  40. package/dist/components/GiovBotMark.d.ts.map +1 -0
  41. package/dist/components/GiovBotMark.js +11 -0
  42. package/dist/components/GiovBotMark.js.map +1 -0
  43. package/dist/components/GreetingBubble.d.ts +7 -0
  44. package/dist/components/GreetingBubble.d.ts.map +1 -0
  45. package/dist/components/GreetingBubble.js +12 -0
  46. package/dist/components/GreetingBubble.js.map +1 -0
  47. package/dist/components/Header.d.ts +18 -0
  48. package/dist/components/Header.d.ts.map +1 -0
  49. package/dist/components/Header.js +67 -0
  50. package/dist/components/Header.js.map +1 -0
  51. package/dist/components/MessageBubble.d.ts +23 -0
  52. package/dist/components/MessageBubble.d.ts.map +1 -0
  53. package/dist/components/MessageBubble.js +51 -0
  54. package/dist/components/MessageBubble.js.map +1 -0
  55. package/dist/components/PreChatForm.d.ts +11 -0
  56. package/dist/components/PreChatForm.d.ts.map +1 -0
  57. package/dist/components/PreChatForm.js +72 -0
  58. package/dist/components/PreChatForm.js.map +1 -0
  59. package/dist/components/RatingCard.d.ts +23 -0
  60. package/dist/components/RatingCard.d.ts.map +1 -0
  61. package/dist/components/RatingCard.js +107 -0
  62. package/dist/components/RatingCard.js.map +1 -0
  63. package/dist/components/TypingIndicator.d.ts +9 -0
  64. package/dist/components/TypingIndicator.d.ts.map +1 -0
  65. package/dist/components/TypingIndicator.js +18 -0
  66. package/dist/components/TypingIndicator.js.map +1 -0
  67. package/dist/components/WidgetStyles.d.ts +3 -0
  68. package/dist/components/WidgetStyles.d.ts.map +1 -0
  69. package/dist/components/WidgetStyles.js +9 -0
  70. package/dist/components/WidgetStyles.js.map +1 -0
  71. package/dist/components/bubbleStyles.d.ts +47 -0
  72. package/dist/components/bubbleStyles.d.ts.map +1 -0
  73. package/dist/components/bubbleStyles.js +19 -0
  74. package/dist/components/bubbleStyles.js.map +1 -0
  75. package/dist/components/index.d.ts +11 -0
  76. package/dist/components/index.d.ts.map +1 -0
  77. package/dist/components/index.js +14 -0
  78. package/dist/components/index.js.map +1 -0
  79. package/dist/errors.d.ts +20 -0
  80. package/dist/errors.d.ts.map +1 -0
  81. package/dist/errors.js +26 -0
  82. package/dist/errors.js.map +1 -0
  83. package/dist/i18n.d.ts +96 -0
  84. package/dist/i18n.d.ts.map +1 -0
  85. package/dist/i18n.js +392 -0
  86. package/dist/i18n.js.map +1 -0
  87. package/dist/identity.d.ts +41 -0
  88. package/dist/identity.d.ts.map +1 -0
  89. package/dist/identity.js +41 -0
  90. package/dist/identity.js.map +1 -0
  91. package/dist/index.d.ts +30 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +64 -0
  94. package/dist/index.js.map +1 -0
  95. package/dist/markdown.d.ts +56 -0
  96. package/dist/markdown.d.ts.map +1 -0
  97. package/dist/markdown.js +219 -0
  98. package/dist/markdown.js.map +1 -0
  99. package/dist/outbox.d.ts +68 -0
  100. package/dist/outbox.d.ts.map +1 -0
  101. package/dist/outbox.js +168 -0
  102. package/dist/outbox.js.map +1 -0
  103. package/dist/protocol.d.ts +9 -0
  104. package/dist/protocol.d.ts.map +1 -0
  105. package/dist/protocol.js +39 -0
  106. package/dist/protocol.js.map +1 -0
  107. package/dist/rating.d.ts +41 -0
  108. package/dist/rating.d.ts.map +1 -0
  109. package/dist/rating.js +165 -0
  110. package/dist/rating.js.map +1 -0
  111. package/dist/ratingStore.d.ts +36 -0
  112. package/dist/ratingStore.d.ts.map +1 -0
  113. package/dist/ratingStore.js +55 -0
  114. package/dist/ratingStore.js.map +1 -0
  115. package/dist/styleOverrides.d.ts +45 -0
  116. package/dist/styleOverrides.d.ts.map +1 -0
  117. package/dist/styleOverrides.js +63 -0
  118. package/dist/styleOverrides.js.map +1 -0
  119. package/dist/types.d.ts +156 -0
  120. package/dist/types.d.ts.map +1 -0
  121. package/dist/types.js +3 -0
  122. package/dist/types.js.map +1 -0
  123. package/dist/unread.d.ts +9 -0
  124. package/dist/unread.d.ts.map +1 -0
  125. package/dist/unread.js +49 -0
  126. package/dist/unread.js.map +1 -0
  127. package/dist/useChatSession.d.ts +95 -0
  128. package/dist/useChatSession.d.ts.map +1 -0
  129. package/dist/useChatSession.js +965 -0
  130. package/dist/useChatSession.js.map +1 -0
  131. package/package.json +60 -4
  132. package/src/GiovChat.tsx +640 -0
  133. package/src/attachments.ts +218 -0
  134. package/src/brand-assets.ts +5 -0
  135. package/src/brandingStyle.ts +87 -0
  136. package/src/client.ts +848 -0
  137. package/src/color.ts +74 -0
  138. package/src/components/Attachments.tsx +75 -0
  139. package/src/components/Branding.tsx +94 -0
  140. package/src/components/ChatMarkdown.tsx +111 -0
  141. package/src/components/GiovBotMark.tsx +19 -0
  142. package/src/components/GreetingBubble.tsx +20 -0
  143. package/src/components/Header.tsx +131 -0
  144. package/src/components/MessageBubble.tsx +137 -0
  145. package/src/components/PreChatForm.tsx +183 -0
  146. package/src/components/RatingCard.tsx +219 -0
  147. package/src/components/TypingIndicator.tsx +34 -0
  148. package/src/components/WidgetStyles.tsx +5 -0
  149. package/src/components/bubbleStyles.ts +16 -0
  150. package/src/components/index.ts +14 -0
  151. package/src/errors.ts +33 -0
  152. package/src/i18n.ts +474 -0
  153. package/src/identity.ts +54 -0
  154. package/src/index.ts +85 -0
  155. package/src/markdown.ts +227 -0
  156. package/src/outbox.ts +175 -0
  157. package/src/protocol.ts +36 -0
  158. package/src/rating.ts +169 -0
  159. package/src/ratingStore.ts +65 -0
  160. package/src/styleOverrides.ts +98 -0
  161. package/src/types.ts +170 -0
  162. package/src/unread.ts +39 -0
  163. package/src/useChatSession.ts +841 -0
@@ -0,0 +1,98 @@
1
+ import type { TextStyle, ViewStyle } from "react-native";
2
+ import type { GiovChatBrandingStyle } from "./brandingStyle";
3
+
4
+ /** Typography and colors only; text sizing and layout remain SDK-controlled. */
5
+ export type GiovChatTextStyle = Pick<TextStyle, "fontFamily" | "fontWeight" | "fontStyle"> & {
6
+ color?: string;
7
+ };
8
+
9
+ export type GiovChatBackgroundStyle = { backgroundColor?: string };
10
+
11
+ /** Plain objects only. No arbitrary React Native styles. */
12
+ export interface GiovChatStyleOverrides {
13
+ /** Default font family for all customizable text. Load the font in your app. */
14
+ fontFamily?: string;
15
+ container?: GiovChatBackgroundStyle;
16
+ header?: GiovChatBackgroundStyle;
17
+ headerTitle?: GiovChatTextStyle;
18
+ headerSubtitle?: GiovChatTextStyle;
19
+ agentBubble?: GiovChatBackgroundStyle;
20
+ visitorBubble?: GiovChatBackgroundStyle;
21
+ agentText?: GiovChatTextStyle;
22
+ visitorText?: GiovChatTextStyle;
23
+ composer?: GiovChatBackgroundStyle;
24
+ input?: GiovChatTextStyle & GiovChatBackgroundStyle;
25
+ button?: GiovChatBackgroundStyle;
26
+ buttonText?: GiovChatTextStyle;
27
+ form?: GiovChatBackgroundStyle;
28
+ formTitle?: GiovChatTextStyle;
29
+ label?: GiovChatTextStyle;
30
+ hint?: GiovChatTextStyle;
31
+ errorText?: GiovChatTextStyle;
32
+ /**
33
+ * The "Powered by GiovChat" footer: font family, text colour, link colour and background. It follows
34
+ * your app, but stays on (only the plan removes it) and keeps 4.5:1 contrast. Layout, size and text are fixed.
35
+ */
36
+ branding?: GiovChatBrandingStyle;
37
+ }
38
+
39
+ const backgroundSlots = [
40
+ "container", "header", "agentBubble", "visitorBubble", "composer", "button", "form",
41
+ ] as const;
42
+ const textSlots = [
43
+ "headerTitle", "headerSubtitle", "agentText", "visitorText", "input", "buttonText",
44
+ "formTitle", "label", "hint", "errorText",
45
+ ] as const;
46
+
47
+ export type ResolvedWidgetStyles = Record<(typeof backgroundSlots)[number], ViewStyle> &
48
+ Record<(typeof textSlots)[number], TextStyle> & { branding: GiovChatBrandingStyle };
49
+
50
+ function record(value: unknown): Record<string, unknown> {
51
+ return value !== null && typeof value === "object" && !Array.isArray(value)
52
+ ? value as Record<string, unknown>
53
+ : {};
54
+ }
55
+
56
+ function nonEmptyString(value: unknown): value is string {
57
+ return typeof value === "string" && value.trim().length > 0;
58
+ }
59
+
60
+ /** Rebuild every slot from an allowlist, including for untyped JavaScript callers. */
61
+ export function resolveStyleOverrides(value: unknown): ResolvedWidgetStyles {
62
+ const input = record(value);
63
+ const result = {} as ResolvedWidgetStyles;
64
+ for (const slot of backgroundSlots) {
65
+ const source = record(input[slot]);
66
+ result[slot] = nonEmptyString(source.backgroundColor)
67
+ ? { backgroundColor: source.backgroundColor }
68
+ : {};
69
+ }
70
+ for (const slot of textSlots) {
71
+ const source = record(input[slot]);
72
+ const style: TextStyle = {};
73
+ if (nonEmptyString(input.fontFamily)) style.fontFamily = input.fontFamily;
74
+ if (nonEmptyString(source.fontFamily)) style.fontFamily = source.fontFamily;
75
+ if (nonEmptyString(source.color)) style.color = source.color;
76
+ if (source.fontStyle === "normal" || source.fontStyle === "italic") {
77
+ style.fontStyle = source.fontStyle;
78
+ }
79
+ const weight = source.fontWeight;
80
+ if (weight === "normal" || weight === "bold" ||
81
+ (typeof weight === "string" && /^[1-9]00$/.test(weight)) ||
82
+ (typeof weight === "number" && weight >= 100 && weight <= 900 && weight % 100 === 0)) {
83
+ style.fontWeight = weight as TextStyle["fontWeight"];
84
+ }
85
+ if (slot === "input" && nonEmptyString(source.backgroundColor)) {
86
+ style.backgroundColor = source.backgroundColor;
87
+ }
88
+ result[slot] = style;
89
+ }
90
+ const footer = record(input.branding);
91
+ const branding: GiovChatBrandingStyle = {};
92
+ if (nonEmptyString(input.fontFamily)) branding.fontFamily = input.fontFamily;
93
+ for (const key of ["fontFamily", "color", "brandColor", "backgroundColor"] as const) {
94
+ if (nonEmptyString(footer[key])) branding[key] = footer[key] as string;
95
+ }
96
+ result.branding = branding;
97
+ return result;
98
+ }
package/src/types.ts ADDED
@@ -0,0 +1,170 @@
1
+ import type { GiovAttachment, GiovAttachmentPolicy } from "./attachments";
2
+
3
+ /**
4
+ * Fields the agent has configured as required in the pre-chat form.
5
+ * Mirrors the dashboard "Pre-chat form" settings.
6
+ */
7
+ export interface RequestUserInfo {
8
+ email?: boolean;
9
+ phone?: boolean;
10
+ firstName?: boolean;
11
+ lastName?: boolean;
12
+ }
13
+
14
+ /**
15
+ * Contact information for the current user.
16
+ *
17
+ * Pass this to `GiovChat` via the `contact` prop to automatically identify
18
+ * the visitor and skip (or pre-fill) any required pre-chat fields.
19
+ */
20
+ export interface GiovChatContact {
21
+ /**
22
+ * Your stable user id. On its own it is only a claim: the API stores it for
23
+ * agents but does not link the chat to an existing contact. Pass `userHash`
24
+ * as well to have it verified.
25
+ */
26
+ userId?: string;
27
+ /**
28
+ * HMAC-SHA256 of `userId` (hex) using the workspace's visitor identity
29
+ * secret from Settings → Chat widget → Visitor details. Compute it on YOUR backend;
30
+ * never ship the secret in the app.
31
+ */
32
+ userHash?: string;
33
+ email?: string;
34
+ phone?: string;
35
+ firstName?: string;
36
+ lastName?: string;
37
+ }
38
+
39
+ /** Private visitor credential. Never log it or share it across users. */
40
+ export interface GiovChatSession {
41
+ conversationId: string;
42
+ sessionToken: string;
43
+ }
44
+
45
+ /** Optional host-owned secure persistence. The scope separates API, tenant, identity and reset key. */
46
+ export interface GiovChatSessionStorage {
47
+ load(scope: string): Promise<GiovChatSession | null>;
48
+ save(scope: string, session: GiovChatSession): Promise<void>;
49
+ remove(scope: string): Promise<void>;
50
+ }
51
+
52
+ export type GiovChatConnectionStatus = "connecting" | "connected" | "disconnected";
53
+
54
+ /**
55
+ * Widget configuration returned by `GET /widget/config`.
56
+ * All values are set by the agent in their dashboard.
57
+ */
58
+ export interface GiovWidgetConfig {
59
+ /** Primary brand color (hex or CSS color). */
60
+ primaryColor: string;
61
+ /** Display name shown in the chat header. */
62
+ title: string;
63
+ /** Greeting message shown at the top of the conversation. */
64
+ greetingText: string;
65
+ /** Placeholder text for the message composer. */
66
+ textArea?: string;
67
+ /** Display name for the AI bot, when the GiovBot add-on is enabled. */
68
+ botName?: string;
69
+ /** True when GiovBot is turned on and included in the tenant's plan. */
70
+ botEnabled?: boolean;
71
+ /** The published GiovBot agent's avatar; the header shows the bot while it answers. */
72
+ botAvatarUrl?: string;
73
+ /** What visitors may attach, from the published agent's settings. Absent: images only. */
74
+ attachments?: GiovAttachmentPolicy;
75
+ /** URL of the agent/team avatar image. */
76
+ avatarUrl?: string;
77
+ /** Color theme. `"auto"` follows the device system setting. */
78
+ theme: "light" | "dark" | "auto";
79
+ /** Server-resolved plan policy. Only an explicit false removes GiovChat branding. */
80
+ showBranding?: boolean;
81
+ /** The workspace's privacy policy (https only); the header menu links to it, else to Giov's. */
82
+ privacyPolicyUrl?: string;
83
+ /** Which contact fields the agent requires before starting a chat. */
84
+ requestUserInfo?: RequestUserInfo;
85
+ }
86
+
87
+ /** A single message in a conversation. */
88
+ export interface GiovMessage {
89
+ /** Unique message identifier. */
90
+ id: string;
91
+ /** Who sent the message. */
92
+ role: "visitor" | "agent";
93
+ /** Message body: Chat Markdown, as the web widget renders it. */
94
+ content: string;
95
+ /** Unix timestamp in milliseconds. */
96
+ createdAt: number;
97
+ /** `true` when this message was sent by the AI bot. */
98
+ isBot?: boolean;
99
+ /** System-generated delivery. AI replies may set both this flag and `isBot`. */
100
+ isAutomated?: boolean;
101
+ /** Display name of the human agent, when available. */
102
+ senderName?: string | null;
103
+ /** Photo of the human agent (http(s) only), when available. */
104
+ senderAvatar?: string | null;
105
+ /** Files sent with the message. */
106
+ attachments?: GiovAttachment[];
107
+ /** Local only: "sending" until the server confirms it, "failed" when it could not be delivered. */
108
+ status?: "sending" | "failed";
109
+ /** Local only: the id it was shown under before the server confirmed it (keeps list keys stable). */
110
+ clientMessageId?: string;
111
+ /** Local only: a GiovBot reply still being written. */
112
+ isStreaming?: boolean;
113
+ /** The conversation rating card this message carries (team or GiovBot "How did we do?" request). */
114
+ rating?: GiovRatingBlock;
115
+ /** The visitor's own thumb on this GiovBot answer. */
116
+ feedback?: "up" | "down";
117
+ }
118
+
119
+ /** A score from 1 (Very bad) to 5 (Great). */
120
+ export type GiovRatingScore = 1 | 2 | 3 | 4 | 5;
121
+
122
+ /** The `rating` block of a message (docs/protocol, "Conversation ratings"). */
123
+ export interface GiovRatingBlock {
124
+ requestId: string;
125
+ /** When the card stops accepting answers or changes (epoch ms; the server decides). */
126
+ closesAt: number;
127
+ /** A hosted rating page, when the API sent one. The SDK always answers with the visitor token instead. */
128
+ url?: string;
129
+ score?: GiovRatingScore;
130
+ comment?: string;
131
+ /** `"replaced"`: a newer card took its place; draw nothing. */
132
+ state?: "replaced";
133
+ }
134
+
135
+ /** The rating card the visitor can answer or change right now (see `useGiovRating`). */
136
+ export interface GiovRatingRequest {
137
+ requestId: string;
138
+ /** The message that carries the card. */
139
+ messageId: string;
140
+ closesAt: number;
141
+ url?: string;
142
+ /** True once the visitor has answered (the answer can still change until `closesAt`). */
143
+ answered: boolean;
144
+ score?: GiovRatingScore;
145
+ comment?: string;
146
+ }
147
+
148
+ /** The server's answer to a rating. */
149
+ export interface GiovRatingResult {
150
+ requestId: string;
151
+ score: GiovRatingScore;
152
+ comment?: string;
153
+ closesAt: number;
154
+ ratedAt?: number;
155
+ }
156
+
157
+ /** The visitor's vote on a GiovBot answer. */
158
+ export type GiovFeedbackValue = "up" | "down" | "none";
159
+
160
+ export interface GiovAnswerFeedback {
161
+ messageId: string;
162
+ value: GiovFeedbackValue;
163
+ reason?: string;
164
+ }
165
+
166
+ /** Callback invoked for every incoming message. */
167
+ export type MessageHandler = (message: GiovMessage) => void;
168
+
169
+ /** Callback invoked when the WebSocket connection status changes. */
170
+ export type StatusHandler = (connected: boolean) => void;
package/src/unread.ts ADDED
@@ -0,0 +1,39 @@
1
+ import { useSyncExternalStore } from "react";
2
+
3
+ /**
4
+ * Unread agent replies per mounted chat, so any screen can show a badge without a provider. Each
5
+ * `GiovChat` publishes its own count under its tenant key; the hook sums them (normally one).
6
+ */
7
+ const counts = new Map<symbol, { tenantKey: string; count: number }>();
8
+ const listeners = new Set<() => void>();
9
+ const notify = () => { for (const listener of listeners) { try { listener(); } catch { /* Isolated. */ } } };
10
+
11
+ export function publishUnreadCount(owner: symbol, tenantKey: string, count: number): void {
12
+ const current = counts.get(owner);
13
+ if (current && current.tenantKey === tenantKey && current.count === count) return;
14
+ counts.set(owner, { tenantKey, count });
15
+ notify();
16
+ }
17
+
18
+ export function forgetUnreadCount(owner: symbol): void {
19
+ if (counts.delete(owner)) notify();
20
+ }
21
+
22
+ export function readUnreadCount(tenantKey?: string): number {
23
+ let total = 0;
24
+ for (const entry of counts.values()) if (!tenantKey || entry.tenantKey === tenantKey) total += entry.count;
25
+ return total;
26
+ }
27
+
28
+ function subscribe(listener: () => void): () => void {
29
+ listeners.add(listener);
30
+ return () => { listeners.delete(listener); };
31
+ }
32
+
33
+ /**
34
+ * Agent replies the visitor has not seen because the chat was closed. Counts only while a mounted
35
+ * `GiovChat` stays connected when hidden (`keepConnected`); opening the chat clears it.
36
+ */
37
+ export function useGiovChatUnreadCount(tenantKey?: string): number {
38
+ return useSyncExternalStore(subscribe, () => readUnreadCount(tenantKey), () => 0);
39
+ }