@repzo/widget-react-native 1.0.4 → 1.0.6
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/lib/commonjs/api/client.js +8 -0
- package/lib/commonjs/api/client.js.map +1 -1
- package/lib/commonjs/i18n.js +14 -0
- package/lib/commonjs/i18n.js.map +1 -1
- package/lib/commonjs/locales.js +13 -0
- package/lib/commonjs/locales.js.map +1 -1
- package/lib/commonjs/state/widget-store.js +44 -4
- package/lib/commonjs/state/widget-store.js.map +1 -1
- package/lib/commonjs/types.js +3 -0
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/ui/WidgetProvider.js +93 -64
- package/lib/commonjs/ui/WidgetProvider.js.map +1 -1
- package/lib/commonjs/ui/components/Gradient.js +18 -2
- package/lib/commonjs/ui/components/Gradient.js.map +1 -1
- package/lib/commonjs/ui/components/RatingCard.js +347 -0
- package/lib/commonjs/ui/components/RatingCard.js.map +1 -0
- package/lib/commonjs/ui/hooks/use-top-inset.js +22 -0
- package/lib/commonjs/ui/hooks/use-top-inset.js.map +1 -0
- package/lib/commonjs/ui/screens/chat/ChatView.js +30 -2
- package/lib/commonjs/ui/screens/chat/ChatView.js.map +1 -1
- package/lib/commonjs/ui/screens/home/HomeView.js +42 -9
- package/lib/commonjs/ui/screens/home/HomeView.js.map +1 -1
- package/lib/commonjs/ui/screens/home/HomeView.styles.js +5 -3
- package/lib/commonjs/ui/screens/home/HomeView.styles.js.map +1 -1
- package/lib/module/api/client.js +8 -0
- package/lib/module/api/client.js.map +1 -1
- package/lib/module/i18n.js +14 -0
- package/lib/module/i18n.js.map +1 -1
- package/lib/module/locales.js +13 -0
- package/lib/module/locales.js.map +1 -1
- package/lib/module/state/widget-store.js +44 -4
- package/lib/module/state/widget-store.js.map +1 -1
- package/lib/module/types.js +3 -0
- package/lib/module/types.js.map +1 -1
- package/lib/module/ui/WidgetProvider.js +96 -67
- package/lib/module/ui/WidgetProvider.js.map +1 -1
- package/lib/module/ui/components/Gradient.js +18 -2
- package/lib/module/ui/components/Gradient.js.map +1 -1
- package/lib/module/ui/components/RatingCard.js +341 -0
- package/lib/module/ui/components/RatingCard.js.map +1 -0
- package/lib/module/ui/hooks/use-top-inset.js +17 -0
- package/lib/module/ui/hooks/use-top-inset.js.map +1 -0
- package/lib/module/ui/screens/chat/ChatView.js +31 -3
- package/lib/module/ui/screens/chat/ChatView.js.map +1 -1
- package/lib/module/ui/screens/home/HomeView.js +44 -12
- package/lib/module/ui/screens/home/HomeView.js.map +1 -1
- package/lib/module/ui/screens/home/HomeView.styles.js +4 -2
- package/lib/module/ui/screens/home/HomeView.styles.js.map +1 -1
- package/lib/typescript/commonjs/api/client.d.ts +7 -1
- package/lib/typescript/commonjs/api/client.d.ts.map +1 -1
- package/lib/typescript/commonjs/i18n.d.ts +13 -0
- package/lib/typescript/commonjs/i18n.d.ts.map +1 -1
- package/lib/typescript/commonjs/locales.d.ts.map +1 -1
- package/lib/typescript/commonjs/state/widget-store.d.ts +18 -1
- package/lib/typescript/commonjs/state/widget-store.d.ts.map +1 -1
- package/lib/typescript/commonjs/types.d.ts +17 -0
- package/lib/typescript/commonjs/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/WidgetProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/Gradient.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/RatingCard.d.ts +57 -0
- package/lib/typescript/commonjs/ui/components/RatingCard.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/hooks/use-top-inset.d.ts +11 -0
- package/lib/typescript/commonjs/ui/hooks/use-top-inset.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/screens/chat/ChatView.d.ts +1 -1
- package/lib/typescript/commonjs/ui/screens/chat/ChatView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/chat/ChatView.types.d.ts +11 -1
- package/lib/typescript/commonjs/ui/screens/chat/ChatView.types.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/home/HomeView.d.ts +7 -0
- package/lib/typescript/commonjs/ui/screens/home/HomeView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/home/HomeView.styles.d.ts +3 -1
- package/lib/typescript/commonjs/ui/screens/home/HomeView.styles.d.ts.map +1 -1
- package/lib/typescript/module/api/client.d.ts +7 -1
- package/lib/typescript/module/api/client.d.ts.map +1 -1
- package/lib/typescript/module/i18n.d.ts +13 -0
- package/lib/typescript/module/i18n.d.ts.map +1 -1
- package/lib/typescript/module/locales.d.ts.map +1 -1
- package/lib/typescript/module/state/widget-store.d.ts +18 -1
- package/lib/typescript/module/state/widget-store.d.ts.map +1 -1
- package/lib/typescript/module/types.d.ts +17 -0
- package/lib/typescript/module/types.d.ts.map +1 -1
- package/lib/typescript/module/ui/WidgetProvider.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/Gradient.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/RatingCard.d.ts +57 -0
- package/lib/typescript/module/ui/components/RatingCard.d.ts.map +1 -0
- package/lib/typescript/module/ui/hooks/use-top-inset.d.ts +11 -0
- package/lib/typescript/module/ui/hooks/use-top-inset.d.ts.map +1 -0
- package/lib/typescript/module/ui/screens/chat/ChatView.d.ts +1 -1
- package/lib/typescript/module/ui/screens/chat/ChatView.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/chat/ChatView.types.d.ts +11 -1
- package/lib/typescript/module/ui/screens/chat/ChatView.types.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/home/HomeView.d.ts +7 -0
- package/lib/typescript/module/ui/screens/home/HomeView.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/home/HomeView.styles.d.ts +3 -1
- package/lib/typescript/module/ui/screens/home/HomeView.styles.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/api/client.ts +13 -0
- package/src/i18n.ts +14 -0
- package/src/locales.ts +13 -0
- package/src/state/widget-store.ts +53 -4
- package/src/types.ts +19 -0
- package/src/ui/WidgetProvider.tsx +54 -4
- package/src/ui/components/Gradient.tsx +19 -2
- package/src/ui/components/RatingCard.tsx +355 -0
- package/src/ui/hooks/use-top-inset.ts +16 -0
- package/src/ui/screens/chat/ChatView.tsx +35 -2
- package/src/ui/screens/chat/ChatView.types.ts +11 -0
- package/src/ui/screens/home/HomeView.styles.ts +4 -2
- package/src/ui/screens/home/HomeView.tsx +36 -8
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { TeamAvatar } from '../../../types';
|
|
2
2
|
/** Props and label bundle for the chat thread. */
|
|
3
3
|
import type { DraftAttachment, PickResult } from '../../../attachments/pickers';
|
|
4
|
-
import type { AgentUiAction, AttachmentSource, Message, OutgoingAttachment } from '../../../types';
|
|
4
|
+
import type { AgentUiAction, AttachmentSource, ConversationRatingResult, Message, OutgoingAttachment } from '../../../types';
|
|
5
5
|
import type { AgentUiBlockLabels } from '../../components/AgentUiBlocks';
|
|
6
6
|
import type { ImageViewerLabels } from '../../components/ImageViewer';
|
|
7
|
+
import type { RatingCardLabels } from '../../components/RatingCard';
|
|
7
8
|
export interface ChatLabels {
|
|
8
9
|
back: string;
|
|
9
10
|
loading: string;
|
|
@@ -27,6 +28,7 @@ export interface ChatLabels {
|
|
|
27
28
|
procedureSend: string;
|
|
28
29
|
procedureSent: string;
|
|
29
30
|
blocks: AgentUiBlockLabels;
|
|
31
|
+
rating: RatingCardLabels;
|
|
30
32
|
attach: AttachLabels;
|
|
31
33
|
viewer: ImageViewerLabels;
|
|
32
34
|
}
|
|
@@ -79,6 +81,14 @@ export interface ChatViewProps {
|
|
|
79
81
|
name?: string;
|
|
80
82
|
phone?: string;
|
|
81
83
|
}) => Promise<void>;
|
|
84
|
+
/** Answers a rating card; rejects with the server error (409 locked, 410 expired). */
|
|
85
|
+
onRateConversation?: (messageId: string, ratingId: string, data: {
|
|
86
|
+
rating: number;
|
|
87
|
+
reason?: string;
|
|
88
|
+
comment?: string;
|
|
89
|
+
}) => Promise<ConversationRatingResult>;
|
|
90
|
+
/** The widget's default language — rating reasons fall back to it. */
|
|
91
|
+
defaultLocale?: string;
|
|
82
92
|
onLoadMore: () => void;
|
|
83
93
|
onTyping: (isTyping: boolean) => void;
|
|
84
94
|
onBack: () => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatView.types.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/screens/chat/ChatView.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,kDAAkD;AAElD,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAChF,OAAO,KAAK,EACV,aAAa,EACb,gBAAgB,EAChB,OAAO,EACP,kBAAkB,EACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"ChatView.types.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/screens/chat/ChatView.types.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,6 +12,13 @@
|
|
|
12
12
|
* the visitor already has; nothing here assumes a field the backend does not
|
|
13
13
|
* send today.
|
|
14
14
|
*/
|
|
15
|
+
import type { WidgetConfig } from '../../../types';
|
|
15
16
|
import type { HomeViewProps } from './HomeView.types';
|
|
17
|
+
/**
|
|
18
|
+
* The admin's explicit black/white toggle always wins. Older configs can omit
|
|
19
|
+
* it, and defaulting to dark text over a dark brand header is unreadable, so
|
|
20
|
+
* derive something legible from the background instead.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveHeaderTextColor(config: WidgetConfig | null, fallbackColor: string): string;
|
|
16
23
|
export declare function HomeView({ config, color, visitorName, conversationCount, recentConversations, unreadByConversation, teamAgents, isOnline, offlineMessage, startConversationLabel, replyTimeLabel, allowNewConversations, showPreviousConversations, labels, onClose, onStartConversation, onViewConversations, onOpenConversation, }: HomeViewProps): import("react").JSX.Element;
|
|
17
24
|
//# sourceMappingURL=HomeView.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HomeView.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/screens/home/HomeView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;
|
|
1
|
+
{"version":3,"file":"HomeView.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/screens/home/HomeView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAcH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AA2BnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAQjG;AAED,wBAAgB,QAAQ,CAAC,EACvB,MAAM,EACN,KAAK,EACL,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,UAAU,EACV,QAAQ,EACR,cAAc,EACd,sBAAsB,EACtB,cAAc,EACd,qBAAqB,EACrB,yBAAyB,EACzB,MAAM,EACN,OAAO,EACP,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,GACnB,EAAE,aAAa,+BAmNf"}
|
|
@@ -16,6 +16,9 @@ export declare const START_CARD_LIFT = 28;
|
|
|
16
16
|
export declare const START_CARD_MIN_HEIGHT = 76;
|
|
17
17
|
export declare const HEADER_FADE_HEIGHT = 90;
|
|
18
18
|
export declare const CLOSE_BUTTON_SIZE = 28;
|
|
19
|
+
/** The logo keeps its own shape at this height, up to the web widget's max width. */
|
|
20
|
+
export declare const LOGO_HEIGHT = 40;
|
|
21
|
+
export declare const LOGO_MAX_WIDTH = 152;
|
|
19
22
|
export declare const CLOSE_ICON_SIZE = 14;
|
|
20
23
|
export declare const styles: {
|
|
21
24
|
contentContainer: {
|
|
@@ -47,7 +50,6 @@ export declare const styles: {
|
|
|
47
50
|
minHeight: number;
|
|
48
51
|
};
|
|
49
52
|
logo: {
|
|
50
|
-
width: number;
|
|
51
53
|
height: number;
|
|
52
54
|
};
|
|
53
55
|
closeButton: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HomeView.styles.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/screens/home/HomeView.styles.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,iFAAiF;AACjF,eAAO,MAAM,qBAAqB,KAAK,CAAC;AACxC,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,eAAO,MAAM,qBAAqB,KAAK,CAAC;AACxC,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC,eAAO,MAAM,MAAM
|
|
1
|
+
{"version":3,"file":"HomeView.styles.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/screens/home/HomeView.styles.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,iFAAiF;AACjF,eAAO,MAAM,qBAAqB,KAAK,CAAC;AACxC,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,eAAO,MAAM,qBAAqB,KAAK,CAAC;AACxC,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC,qFAAqF;AACrF,eAAO,MAAM,WAAW,KAAK,CAAC;AAC9B,eAAO,MAAM,cAAc,MAAM,CAAC;AAClC,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyNjB,CAAC"}
|
package/package.json
CHANGED
package/src/api/client.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { SecureStorage, TOKEN_KEY } from '../storage';
|
|
|
12
12
|
import type {
|
|
13
13
|
AgentUiAction,
|
|
14
14
|
Conversation,
|
|
15
|
+
ConversationRatingResult,
|
|
15
16
|
InitResponse,
|
|
16
17
|
Message,
|
|
17
18
|
OutboundMessage,
|
|
@@ -285,6 +286,18 @@ export class WidgetApiClient {
|
|
|
285
286
|
);
|
|
286
287
|
}
|
|
287
288
|
|
|
289
|
+
/** Answers (or changes) the rating card posted after a teammate closed the chat. */
|
|
290
|
+
rateConversation(
|
|
291
|
+
conversationId: string,
|
|
292
|
+
ratingId: string,
|
|
293
|
+
data: { rating: number; reason?: string | null; comment?: string | null }
|
|
294
|
+
): Promise<ConversationRatingResult> {
|
|
295
|
+
return this.request(
|
|
296
|
+
`/api/widget/conversations/${encodeURIComponent(conversationId)}/ratings/${encodeURIComponent(ratingId)}`,
|
|
297
|
+
{ method: 'POST', body: data }
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
|
|
288
301
|
identify(data: {
|
|
289
302
|
email?: string;
|
|
290
303
|
name?: string;
|
package/src/i18n.ts
CHANGED
|
@@ -118,6 +118,20 @@ export const DEFAULT_LABELS = {
|
|
|
118
118
|
messageSeen: 'Seen',
|
|
119
119
|
waitingForTeammate: 'Waiting for a teammate',
|
|
120
120
|
joinedConversation: 'joined the conversation',
|
|
121
|
+
// Conversation rating card, posted after a teammate closes the chat.
|
|
122
|
+
ratingQuestion: 'How would you rate this conversation?',
|
|
123
|
+
ratingReasonPrompt: 'What went wrong?',
|
|
124
|
+
ratingCommentPlaceholder: 'Tell us more (optional)',
|
|
125
|
+
ratingSubmit: 'Submit',
|
|
126
|
+
ratingThanks: 'Thanks for your feedback!',
|
|
127
|
+
ratingLocked: 'This rating can no longer be changed',
|
|
128
|
+
ratingExpired: 'This conversation can no longer be rated',
|
|
129
|
+
ratingFailed: "Couldn't save your rating. Please try again.",
|
|
130
|
+
rating1: 'Terrible',
|
|
131
|
+
rating2: 'Bad',
|
|
132
|
+
rating3: 'OK',
|
|
133
|
+
rating4: 'Great',
|
|
134
|
+
rating5: 'Amazing',
|
|
121
135
|
// Forms.
|
|
122
136
|
procedureSend: 'Send details',
|
|
123
137
|
procedureSent: 'Sent',
|
package/src/locales.ts
CHANGED
|
@@ -74,6 +74,19 @@ const ar: LabelMap = {
|
|
|
74
74
|
messageSeen: 'تمت المشاهدة',
|
|
75
75
|
waitingForTeammate: 'بانتظار أحد أعضاء الفريق',
|
|
76
76
|
joinedConversation: 'انضم إلى المحادثة',
|
|
77
|
+
ratingQuestion: 'كيف تقيّم هذه المحادثة؟',
|
|
78
|
+
ratingReasonPrompt: 'ما الذي لم يكن جيداً؟',
|
|
79
|
+
ratingCommentPlaceholder: 'أخبرنا المزيد (اختياري)',
|
|
80
|
+
ratingSubmit: 'إرسال',
|
|
81
|
+
ratingThanks: 'شكراً على ملاحظاتك!',
|
|
82
|
+
ratingLocked: 'لم يعد بالإمكان تغيير هذا التقييم',
|
|
83
|
+
ratingExpired: 'لم يعد بالإمكان تقييم هذه المحادثة',
|
|
84
|
+
ratingFailed: 'تعذّر حفظ تقييمك. حاول مرة أخرى.',
|
|
85
|
+
rating1: 'سيئ جداً',
|
|
86
|
+
rating2: 'سيئ',
|
|
87
|
+
rating3: 'مقبول',
|
|
88
|
+
rating4: 'جيد',
|
|
89
|
+
rating5: 'ممتاز',
|
|
77
90
|
procedureSend: 'إرسال التفاصيل',
|
|
78
91
|
procedureSent: 'تم الإرسال',
|
|
79
92
|
fieldRequired: '{field} مطلوب',
|
|
@@ -22,6 +22,7 @@ import type {
|
|
|
22
22
|
AgentUiAction,
|
|
23
23
|
BootOptions,
|
|
24
24
|
Conversation,
|
|
25
|
+
ConversationRatingResult,
|
|
25
26
|
Message,
|
|
26
27
|
OutgoingAttachment,
|
|
27
28
|
RepzoWidgetErrorCode,
|
|
@@ -35,7 +36,7 @@ import type {
|
|
|
35
36
|
} from '../types';
|
|
36
37
|
import { setOpenUrlHandler } from '../ui/utils/safe-url';
|
|
37
38
|
|
|
38
|
-
const SDK_VERSION = '@repzo/widget-react-native@1.0.
|
|
39
|
+
const SDK_VERSION = '@repzo/widget-react-native@1.0.5';
|
|
39
40
|
|
|
40
41
|
/** The platform this session reports — mobile sessions get the Mobile SDKs overrides. */
|
|
41
42
|
function sessionPlatform(): 'ios' | 'android' | 'web' {
|
|
@@ -489,7 +490,7 @@ export class WidgetStore {
|
|
|
489
490
|
loading: false,
|
|
490
491
|
error: null,
|
|
491
492
|
errorCode: null,
|
|
492
|
-
config: result.config,
|
|
493
|
+
config: this.hydrateConfig(result.config),
|
|
493
494
|
identityId: result.identityId,
|
|
494
495
|
conversations,
|
|
495
496
|
// Also seeds the launcher badge, so a reply that landed while the app
|
|
@@ -512,7 +513,7 @@ export class WidgetStore {
|
|
|
512
513
|
},
|
|
513
514
|
}));
|
|
514
515
|
|
|
515
|
-
this.persistConfig(result.config);
|
|
516
|
+
this.persistConfig(this.hydrateConfig(result.config));
|
|
516
517
|
this.connectRealtime(result.token);
|
|
517
518
|
this.watchAppState();
|
|
518
519
|
this.loadOutboundMessage();
|
|
@@ -558,13 +559,28 @@ export class WidgetStore {
|
|
|
558
559
|
cached.platform === sessionPlatform() &&
|
|
559
560
|
!this.state.config
|
|
560
561
|
) {
|
|
561
|
-
this.setState({ config: cached.config });
|
|
562
|
+
this.setState({ config: this.hydrateConfig(cached.config) });
|
|
562
563
|
}
|
|
563
564
|
} catch {
|
|
564
565
|
// Corrupt cache — the network config overwrites it shortly.
|
|
565
566
|
}
|
|
566
567
|
}
|
|
567
568
|
|
|
569
|
+
/**
|
|
570
|
+
* The server sends the home logo and background image as paths relative to
|
|
571
|
+
* Workstation (`/api/media/:id/download`), which a React Native Image can't
|
|
572
|
+
* load. Resolved against the boot `baseUrl` they become full links (public
|
|
573
|
+
* media redirects to the CDN). Absolute URLs pass through unchanged.
|
|
574
|
+
*/
|
|
575
|
+
private hydrateConfig(config: WidgetConfig): WidgetConfig {
|
|
576
|
+
if (!config) return config;
|
|
577
|
+
return {
|
|
578
|
+
...config,
|
|
579
|
+
homeLogoUrl: this.client.resolveUrl(config.homeLogoUrl),
|
|
580
|
+
homeBackgroundImageUrl: this.client.resolveUrl(config.homeBackgroundImageUrl),
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
|
|
568
584
|
private persistConfig(config: WidgetConfig): void {
|
|
569
585
|
if (!this.bootConfig) return;
|
|
570
586
|
const entry: CachedConfig = {
|
|
@@ -1835,6 +1851,39 @@ export class WidgetStore {
|
|
|
1835
1851
|
await this.client.identify(data);
|
|
1836
1852
|
}
|
|
1837
1853
|
|
|
1854
|
+
/**
|
|
1855
|
+
* Answers a rating card in the thread on screen and mirrors the answer onto
|
|
1856
|
+
* its message, so the card keeps it across a re-render. Rejects with the
|
|
1857
|
+
* server's error (409 locked, 410 expired) for the card to show.
|
|
1858
|
+
*/
|
|
1859
|
+
async rateConversation(
|
|
1860
|
+
messageId: string,
|
|
1861
|
+
ratingId: string,
|
|
1862
|
+
data: { rating: number; reason?: string | null; comment?: string | null }
|
|
1863
|
+
): Promise<ConversationRatingResult> {
|
|
1864
|
+
const conversationId = this.state.activeConversationId;
|
|
1865
|
+
if (!conversationId) throw new RepzoWidgetError('No conversation is open', 0, 'http');
|
|
1866
|
+
const result = await this.client.rateConversation(conversationId, ratingId, data);
|
|
1867
|
+
if (this.state.activeConversationId === conversationId) {
|
|
1868
|
+
this.setState((current) => ({
|
|
1869
|
+
messages: current.messages.map((message) =>
|
|
1870
|
+
message.id === messageId
|
|
1871
|
+
? {
|
|
1872
|
+
...message,
|
|
1873
|
+
metadata: {
|
|
1874
|
+
...message.metadata,
|
|
1875
|
+
rating: result.rating,
|
|
1876
|
+
ratingReason: result.reason,
|
|
1877
|
+
ratingComment: result.comment,
|
|
1878
|
+
},
|
|
1879
|
+
}
|
|
1880
|
+
: message
|
|
1881
|
+
),
|
|
1882
|
+
}));
|
|
1883
|
+
}
|
|
1884
|
+
return result;
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1838
1887
|
/** Fire-and-forget visitor typing ping, throttled well under the 30/min cap. */
|
|
1839
1888
|
notifyTyping(isTyping: boolean): void {
|
|
1840
1889
|
const conversationId = this.state.activeConversationId;
|
package/src/types.ts
CHANGED
|
@@ -480,6 +480,25 @@ export interface RepzoWidgetStorageAdapter {
|
|
|
480
480
|
* - `identity_verification_failed` — `userHash` missing/wrong while the
|
|
481
481
|
* channel enforces identity verification.
|
|
482
482
|
*/
|
|
483
|
+
/** A bad-rating reason on the rating card, labelled per language. */
|
|
484
|
+
export interface RatingReason {
|
|
485
|
+
id: string;
|
|
486
|
+
/** Locale code → label. */
|
|
487
|
+
labels: Record<string, string>;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/** The visitor's answer to a conversation rating card. */
|
|
491
|
+
export interface ConversationRatingResult {
|
|
492
|
+
id: string;
|
|
493
|
+
/** 1 (Terrible) … 5 (Amazing). */
|
|
494
|
+
rating: number;
|
|
495
|
+
/** Id of the reason picked after a bad rating (1–2), if any. */
|
|
496
|
+
reason: string | null;
|
|
497
|
+
comment: string | null;
|
|
498
|
+
/** A comment was left: the rating can't change any more. */
|
|
499
|
+
locked: boolean;
|
|
500
|
+
}
|
|
501
|
+
|
|
483
502
|
export type RepzoWidgetErrorCode =
|
|
484
503
|
| 'network'
|
|
485
504
|
| 'http'
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
Modal,
|
|
13
13
|
Platform,
|
|
14
14
|
Pressable,
|
|
15
|
+
StatusBar,
|
|
15
16
|
StyleSheet,
|
|
16
17
|
Text,
|
|
17
18
|
View,
|
|
@@ -35,13 +36,22 @@ import { InAppNotification } from './components/InAppNotification';
|
|
|
35
36
|
import { OutboundMessageCard } from './components/OutboundMessageCard';
|
|
36
37
|
import { WidgetPanel } from './components/WidgetPanel';
|
|
37
38
|
import { useKeyboardOverlap } from './hooks/use-keyboard-overlap';
|
|
39
|
+
import { TopInsetContext } from './hooks/use-top-inset';
|
|
38
40
|
import { useWidgetInsets } from './hooks/use-widget-insets';
|
|
39
41
|
import { ChatView } from './screens/chat/ChatView';
|
|
40
42
|
import { ConversationsList } from './screens/conversations/ConversationsList';
|
|
41
|
-
import { HomeView } from './screens/home/HomeView';
|
|
43
|
+
import { HomeView, resolveHeaderTextColor } from './screens/home/HomeView';
|
|
42
44
|
import { PreChatForm } from './screens/pre-chat/PreChatForm';
|
|
43
45
|
import { globalStyles } from './styles/global';
|
|
44
|
-
import {
|
|
46
|
+
import {
|
|
47
|
+
buildTheme,
|
|
48
|
+
contrastingTextColor,
|
|
49
|
+
palette,
|
|
50
|
+
radius,
|
|
51
|
+
spacing,
|
|
52
|
+
fontSize,
|
|
53
|
+
withAlpha,
|
|
54
|
+
} from './styles/theme';
|
|
45
55
|
import { pickRecentConversations, pickTeamAgents } from './utils/conversations';
|
|
46
56
|
|
|
47
57
|
export interface RepzoWidgetProviderProps {
|
|
@@ -192,6 +202,21 @@ export function RepzoWidgetProvider({
|
|
|
192
202
|
: null;
|
|
193
203
|
const isHomeSurface = state.view === 'home';
|
|
194
204
|
|
|
205
|
+
// Home and chat paint their brand header under the status bar; every other
|
|
206
|
+
// screen (and an outbound card stacked above home) keeps a plain white top.
|
|
207
|
+
const outboundVisible =
|
|
208
|
+
!!state.outboundMessage && !blockingError && state.view !== 'chat' && state.view !== 'prechat';
|
|
209
|
+
const headerOwnsTopInset =
|
|
210
|
+
!blockingError &&
|
|
211
|
+
!(state.loading && !state.config) &&
|
|
212
|
+
!outboundVisible &&
|
|
213
|
+
(state.view === 'chat' || isHomeSurface);
|
|
214
|
+
const topBarColor = !headerOwnsTopInset
|
|
215
|
+
? palette.ink
|
|
216
|
+
: state.view === 'chat'
|
|
217
|
+
? contrastingTextColor(theme.primaryColor)
|
|
218
|
+
: resolveHeaderTextColor(localizedConfig, theme.primaryColor);
|
|
219
|
+
|
|
195
220
|
const renderContent = () => {
|
|
196
221
|
if (state.loading && !state.config) {
|
|
197
222
|
return (
|
|
@@ -270,6 +295,7 @@ export function RepzoWidgetProvider({
|
|
|
270
295
|
teamAvatars={teamAvatars}
|
|
271
296
|
replyTimeLabel={theme.replyTimeLabel}
|
|
272
297
|
locale={state.locale}
|
|
298
|
+
defaultLocale={state.config?.defaultLocale}
|
|
273
299
|
prefillText={state.prefillText}
|
|
274
300
|
offlineMessage={theme.offlineMessage}
|
|
275
301
|
labels={{
|
|
@@ -295,6 +321,17 @@ export function RepzoWidgetProvider({
|
|
|
295
321
|
procedureSend: labels.procedureSend,
|
|
296
322
|
procedureSent: labels.procedureSent,
|
|
297
323
|
blocks: blockLabels,
|
|
324
|
+
rating: {
|
|
325
|
+
question: labels.ratingQuestion,
|
|
326
|
+
reasonPrompt: labels.ratingReasonPrompt,
|
|
327
|
+
commentPlaceholder: labels.ratingCommentPlaceholder,
|
|
328
|
+
submit: labels.ratingSubmit,
|
|
329
|
+
thanks: labels.ratingThanks,
|
|
330
|
+
locked: labels.ratingLocked,
|
|
331
|
+
expired: labels.ratingExpired,
|
|
332
|
+
failed: labels.ratingFailed,
|
|
333
|
+
scale: [labels.rating1, labels.rating2, labels.rating3, labels.rating4, labels.rating5],
|
|
334
|
+
},
|
|
298
335
|
attach: attachLabels,
|
|
299
336
|
viewer: {
|
|
300
337
|
close: labels.close,
|
|
@@ -306,6 +343,7 @@ export function RepzoWidgetProvider({
|
|
|
306
343
|
onPickAttachments={pickAttachments}
|
|
307
344
|
onSendMessage={handleSendMessage}
|
|
308
345
|
onIdentifyVisitor={(data) => store.identifyVisitor(data)}
|
|
346
|
+
onRateConversation={(messageId, ratingId, data) => store.rateConversation(messageId, ratingId, data)}
|
|
309
347
|
onLoadMore={() => store.loadMoreMessages()}
|
|
310
348
|
onTyping={(isTyping) => store.notifyTyping(isTyping)}
|
|
311
349
|
onBack={handleBack}
|
|
@@ -405,8 +443,19 @@ export function RepzoWidgetProvider({
|
|
|
405
443
|
animationType="slide"
|
|
406
444
|
presentationStyle={Platform.OS === 'ios' ? presentationStyle : undefined}
|
|
407
445
|
onRequestClose={handleClose}
|
|
408
|
-
|
|
409
|
-
|
|
446
|
+
// Edge to edge on every Android version, so the insets below are the
|
|
447
|
+
// only top spacing (a non-translucent modal used to add a second one).
|
|
448
|
+
statusBarTranslucent>
|
|
449
|
+
<StatusBar
|
|
450
|
+
barStyle={topBarColor === palette.white ? 'light-content' : 'dark-content'}
|
|
451
|
+
animated
|
|
452
|
+
/>
|
|
453
|
+
<View
|
|
454
|
+
style={[
|
|
455
|
+
globalStyles.screen,
|
|
456
|
+
{ paddingTop: headerOwnsTopInset ? 0 : topInset, paddingBottom: insets.bottom },
|
|
457
|
+
]}>
|
|
458
|
+
<TopInsetContext.Provider value={headerOwnsTopInset ? topInset : 0}>
|
|
410
459
|
<View
|
|
411
460
|
onLayout={keyboard.onLayout}
|
|
412
461
|
style={[globalStyles.fill, { paddingBottom: keyboard.overlap }]}>
|
|
@@ -463,6 +512,7 @@ export function RepzoWidgetProvider({
|
|
|
463
512
|
</WidgetPanel>
|
|
464
513
|
</WidgetErrorBoundary>
|
|
465
514
|
</View>
|
|
515
|
+
</TopInsetContext.Provider>
|
|
466
516
|
</View>
|
|
467
517
|
</Modal>
|
|
468
518
|
</>
|
|
@@ -104,6 +104,22 @@ function GradientFill({ colors, bands }: { colors: string[]; bands?: number }) {
|
|
|
104
104
|
);
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Alpha at evenly spaced points down the fade. An eased curve instead of a
|
|
109
|
+
* straight line: a linear ramp shows a visible edge where it starts and goes
|
|
110
|
+
* opaque too late, so the brand colour reads as a band rather than melting
|
|
111
|
+
* into the page. Shared with the web widget's fade so both look the same.
|
|
112
|
+
*/
|
|
113
|
+
const FADE_STOPS = [0, 0.04, 0.12, 0.26, 0.46, 0.68, 0.86, 0.96, 1];
|
|
114
|
+
|
|
115
|
+
function fadeAlphaAt(progress: number): number {
|
|
116
|
+
const scaled = Math.min(Math.max(progress, 0), 1) * (FADE_STOPS.length - 1);
|
|
117
|
+
const index = Math.min(Math.floor(scaled), FADE_STOPS.length - 2);
|
|
118
|
+
const from = FADE_STOPS[index] as number;
|
|
119
|
+
const to = FADE_STOPS[index + 1] as number;
|
|
120
|
+
return from + (to - from) * (scaled - index);
|
|
121
|
+
}
|
|
122
|
+
|
|
107
123
|
/**
|
|
108
124
|
* Washes the bottom of a header out into the page colour: a straight ramp from
|
|
109
125
|
* fully transparent at the top to opaque at the bottom edge, the same
|
|
@@ -129,7 +145,8 @@ export function FadeOverlay({
|
|
|
129
145
|
<View style={[styles.fadeOverlay, { height }, style]} pointerEvents="none">
|
|
130
146
|
{NativeGradient ? (
|
|
131
147
|
<NativeGradient
|
|
132
|
-
colors={
|
|
148
|
+
colors={FADE_STOPS.map((alpha) => withAlpha(color, alpha))}
|
|
149
|
+
locations={FADE_STOPS.map((_, index) => index / (FADE_STOPS.length - 1))}
|
|
133
150
|
start={VERTICAL.start}
|
|
134
151
|
end={VERTICAL.end}
|
|
135
152
|
style={StyleSheet.absoluteFill}
|
|
@@ -144,7 +161,7 @@ export function FadeOverlay({
|
|
|
144
161
|
// body colour, with no hairline where the two meet.
|
|
145
162
|
<View
|
|
146
163
|
key={index}
|
|
147
|
-
style={[styles.band, { backgroundColor: withAlpha(color, (index + 1) / all.length) }]}
|
|
164
|
+
style={[styles.band, { backgroundColor: withAlpha(color, fadeAlphaAt((index + 1) / all.length)) }]}
|
|
148
165
|
/>
|
|
149
166
|
))
|
|
150
167
|
)}
|