@repzo/widget-react-native 1.2.2 → 1.3.1
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/CHANGELOG.md +88 -0
- package/README.md +43 -33
- package/android/build.gradle +22 -0
- package/android/src/main/java/expo/modules/repzowidget/RepzoVoicePlayer.kt +222 -0
- package/android/src/main/java/expo/modules/repzowidget/RepzoWidgetAudioModule.kt +68 -0
- package/expo-module.config.json +9 -0
- package/ios/RepzoVoicePlayer.swift +175 -0
- package/ios/RepzoWidgetAudio.podspec +26 -0
- package/ios/RepzoWidgetAudioModule.swift +65 -0
- package/lib/commonjs/api/client.js +40 -0
- package/lib/commonjs/api/client.js.map +1 -1
- package/lib/commonjs/attachments/pickers.js +80 -23
- package/lib/commonjs/attachments/pickers.js.map +1 -1
- package/lib/commonjs/audio/native-player.js +97 -0
- package/lib/commonjs/audio/native-player.js.map +1 -0
- package/lib/commonjs/audio/voice.js +34 -7
- package/lib/commonjs/audio/voice.js.map +1 -1
- package/lib/commonjs/i18n.js +2 -0
- package/lib/commonjs/i18n.js.map +1 -1
- package/lib/commonjs/locales.js +1 -0
- package/lib/commonjs/locales.js.map +1 -1
- package/lib/commonjs/state/widget-store.js +238 -40
- package/lib/commonjs/state/widget-store.js.map +1 -1
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/ui/WidgetProvider.js +8 -6
- package/lib/commonjs/ui/WidgetProvider.js.map +1 -1
- package/lib/commonjs/ui/components/ImageViewer.js +20 -37
- package/lib/commonjs/ui/components/ImageViewer.js.map +1 -1
- package/lib/commonjs/ui/components/MessagesSkeleton.js +126 -0
- package/lib/commonjs/ui/components/MessagesSkeleton.js.map +1 -0
- package/lib/commonjs/ui/components/VoiceNote.js +8 -9
- package/lib/commonjs/ui/components/VoiceNote.js.map +1 -1
- package/lib/commonjs/ui/components/ZoomableImage.js +299 -0
- package/lib/commonjs/ui/components/ZoomableImage.js.map +1 -0
- package/lib/commonjs/ui/components/icons.js +22 -16
- package/lib/commonjs/ui/components/icons.js.map +1 -1
- package/lib/commonjs/ui/screens/chat/ChatView.js +42 -18
- package/lib/commonjs/ui/screens/chat/ChatView.js.map +1 -1
- package/lib/commonjs/ui/screens/chat/ChatView.styles.js +17 -9
- package/lib/commonjs/ui/screens/chat/ChatView.styles.js.map +1 -1
- package/lib/commonjs/ui/styles/theme.js +1 -0
- package/lib/commonjs/ui/styles/theme.js.map +1 -1
- package/lib/commonjs/ui/utils/conversations.js +17 -1
- package/lib/commonjs/ui/utils/conversations.js.map +1 -1
- package/lib/module/api/client.js +40 -0
- package/lib/module/api/client.js.map +1 -1
- package/lib/module/attachments/pickers.js +80 -23
- package/lib/module/attachments/pickers.js.map +1 -1
- package/lib/module/audio/native-player.js +93 -0
- package/lib/module/audio/native-player.js.map +1 -0
- package/lib/module/audio/voice.js +32 -6
- package/lib/module/audio/voice.js.map +1 -1
- package/lib/module/i18n.js +2 -0
- package/lib/module/i18n.js.map +1 -1
- package/lib/module/locales.js +1 -0
- package/lib/module/locales.js.map +1 -1
- package/lib/module/state/widget-store.js +238 -40
- package/lib/module/state/widget-store.js.map +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/module/ui/WidgetProvider.js +8 -6
- package/lib/module/ui/WidgetProvider.js.map +1 -1
- package/lib/module/ui/components/ImageViewer.js +21 -38
- package/lib/module/ui/components/ImageViewer.js.map +1 -1
- package/lib/module/ui/components/MessagesSkeleton.js +124 -0
- package/lib/module/ui/components/MessagesSkeleton.js.map +1 -0
- package/lib/module/ui/components/VoiceNote.js +9 -10
- package/lib/module/ui/components/VoiceNote.js.map +1 -1
- package/lib/module/ui/components/ZoomableImage.js +291 -0
- package/lib/module/ui/components/ZoomableImage.js.map +1 -0
- package/lib/module/ui/components/icons.js +21 -15
- package/lib/module/ui/components/icons.js.map +1 -1
- package/lib/module/ui/screens/chat/ChatView.js +44 -20
- package/lib/module/ui/screens/chat/ChatView.js.map +1 -1
- package/lib/module/ui/screens/chat/ChatView.styles.js +17 -9
- package/lib/module/ui/screens/chat/ChatView.styles.js.map +1 -1
- package/lib/module/ui/styles/theme.js +1 -0
- package/lib/module/ui/styles/theme.js.map +1 -1
- package/lib/module/ui/utils/conversations.js +15 -1
- package/lib/module/ui/utils/conversations.js.map +1 -1
- package/lib/typescript/commonjs/api/client.d.ts +27 -1
- package/lib/typescript/commonjs/api/client.d.ts.map +1 -1
- package/lib/typescript/commonjs/attachments/pickers.d.ts +22 -10
- package/lib/typescript/commonjs/attachments/pickers.d.ts.map +1 -1
- package/lib/typescript/commonjs/audio/native-player.d.ts +36 -0
- package/lib/typescript/commonjs/audio/native-player.d.ts.map +1 -0
- package/lib/typescript/commonjs/audio/voice.d.ts +17 -4
- package/lib/typescript/commonjs/audio/voice.d.ts.map +1 -1
- package/lib/typescript/commonjs/i18n.d.ts +1 -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 +38 -3
- 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/ImageViewer.d.ts +2 -2
- package/lib/typescript/commonjs/ui/components/ImageViewer.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/MessagesSkeleton.d.ts +10 -0
- package/lib/typescript/commonjs/ui/components/MessagesSkeleton.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/VoiceNote.d.ts +2 -2
- package/lib/typescript/commonjs/ui/components/VoiceNote.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/ZoomableImage.d.ts +45 -0
- package/lib/typescript/commonjs/ui/components/ZoomableImage.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/icons.d.ts +2 -1
- package/lib/typescript/commonjs/ui/components/icons.d.ts.map +1 -1
- 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.styles.d.ts +14 -9
- package/lib/typescript/commonjs/ui/screens/chat/ChatView.styles.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/chat/ChatView.types.d.ts +4 -0
- package/lib/typescript/commonjs/ui/screens/chat/ChatView.types.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/styles/theme.d.ts +2 -0
- package/lib/typescript/commonjs/ui/styles/theme.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/conversations.d.ts +10 -2
- package/lib/typescript/commonjs/ui/utils/conversations.d.ts.map +1 -1
- package/lib/typescript/module/api/client.d.ts +27 -1
- package/lib/typescript/module/api/client.d.ts.map +1 -1
- package/lib/typescript/module/attachments/pickers.d.ts +22 -10
- package/lib/typescript/module/attachments/pickers.d.ts.map +1 -1
- package/lib/typescript/module/audio/native-player.d.ts +36 -0
- package/lib/typescript/module/audio/native-player.d.ts.map +1 -0
- package/lib/typescript/module/audio/voice.d.ts +17 -4
- package/lib/typescript/module/audio/voice.d.ts.map +1 -1
- package/lib/typescript/module/i18n.d.ts +1 -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 +38 -3
- 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/ImageViewer.d.ts +2 -2
- package/lib/typescript/module/ui/components/ImageViewer.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/MessagesSkeleton.d.ts +10 -0
- package/lib/typescript/module/ui/components/MessagesSkeleton.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/VoiceNote.d.ts +2 -2
- package/lib/typescript/module/ui/components/VoiceNote.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ZoomableImage.d.ts +45 -0
- package/lib/typescript/module/ui/components/ZoomableImage.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/icons.d.ts +2 -1
- package/lib/typescript/module/ui/components/icons.d.ts.map +1 -1
- 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.styles.d.ts +14 -9
- package/lib/typescript/module/ui/screens/chat/ChatView.styles.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/chat/ChatView.types.d.ts +4 -0
- package/lib/typescript/module/ui/screens/chat/ChatView.types.d.ts.map +1 -1
- package/lib/typescript/module/ui/styles/theme.d.ts +2 -0
- package/lib/typescript/module/ui/styles/theme.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/conversations.d.ts +10 -2
- package/lib/typescript/module/ui/utils/conversations.d.ts.map +1 -1
- package/package.json +10 -3
- package/react-native.config.js +12 -0
- package/src/api/client.ts +66 -2
- package/src/attachments/pickers.ts +87 -25
- package/src/audio/native-player.ts +106 -0
- package/src/audio/voice.ts +30 -6
- package/src/i18n.ts +2 -0
- package/src/locales.ts +1 -0
- package/src/state/widget-store.ts +233 -33
- package/src/types.ts +17 -0
- package/src/ui/WidgetProvider.tsx +16 -7
- package/src/ui/components/ImageViewer.tsx +21 -36
- package/src/ui/components/MessagesSkeleton.tsx +92 -0
- package/src/ui/components/VoiceNote.tsx +6 -14
- package/src/ui/components/ZoomableImage.tsx +250 -0
- package/src/ui/components/icons.tsx +10 -12
- package/src/ui/screens/chat/ChatView.styles.ts +17 -9
- package/src/ui/screens/chat/ChatView.tsx +49 -25
- package/src/ui/screens/chat/ChatView.types.ts +4 -0
- package/src/ui/styles/theme.ts +6 -0
- package/src/ui/utils/conversations.ts +16 -2
|
@@ -49,19 +49,20 @@ import { ImageViewer } from '../../components/ImageViewer';
|
|
|
49
49
|
import { getRatingRequest, RatingCard } from '../../components/RatingCard';
|
|
50
50
|
import { WidgetErrorBoundary } from '../../components/ErrorBoundary';
|
|
51
51
|
import { GradientBackground } from '../../components/Gradient';
|
|
52
|
-
import { ArrowUpIcon, ChatOutlineIcon, ChevronIcon, ClockIcon,
|
|
52
|
+
import { AlertIcon, ArrowUpIcon, ChatOutlineIcon, ChevronIcon, ClockIcon, PlusIcon } from '../../components/icons';
|
|
53
53
|
import { TeamAvatars } from '../../components/TeamAvatars';
|
|
54
54
|
import { Markdown } from '../../components/Markdown';
|
|
55
55
|
import { useReduceMotion } from '../../hooks/use-reduce-motion';
|
|
56
56
|
import { useTopInset } from '../../hooks/use-top-inset';
|
|
57
57
|
import { globalStyles } from '../../styles/global';
|
|
58
58
|
import { contrastingTextColor, mixHex, palette, spacing, withAlpha } from '../../styles/theme';
|
|
59
|
-
import { formatClockTime, formatDayLabel, isSameDay } from '../../utils/conversations';
|
|
59
|
+
import { canRetryMessage, formatClockTime, formatDayLabel, isSameDay } from '../../utils/conversations';
|
|
60
60
|
import { stripHtml } from '../../utils/html';
|
|
61
61
|
import { isHttpUrl, openExternalUrl } from '../../utils/safe-url';
|
|
62
62
|
import { isPlayableAudio } from '../../../audio/voice';
|
|
63
63
|
import { VoiceNote, type VoiceNoteLabels } from '../../components/VoiceNote';
|
|
64
64
|
import { FileCard } from '../../components/FileCard';
|
|
65
|
+
import { MessagesSkeleton } from '../../components/MessagesSkeleton';
|
|
65
66
|
import { backDirection, isRTL } from '../../utils/rtl';
|
|
66
67
|
import { styles } from './ChatView.styles';
|
|
67
68
|
import type { ChatLabels, ChatViewProps } from './ChatView.types';
|
|
@@ -86,6 +87,7 @@ export function ChatView({
|
|
|
86
87
|
attachmentSources = [],
|
|
87
88
|
onPickAttachments,
|
|
88
89
|
onSendMessage,
|
|
90
|
+
onRetryMessage,
|
|
89
91
|
onIdentifyVisitor,
|
|
90
92
|
onRateConversation,
|
|
91
93
|
defaultLocale,
|
|
@@ -267,6 +269,7 @@ export function ChatView({
|
|
|
267
269
|
sent.map((draft) => ({
|
|
268
270
|
name: draft.name,
|
|
269
271
|
url: draft.dataUrl,
|
|
272
|
+
fileUri: draft.fileUri,
|
|
270
273
|
contentType: draft.contentType,
|
|
271
274
|
size: draft.size,
|
|
272
275
|
previewUrl: draft.previewUri,
|
|
@@ -324,6 +327,16 @@ export function ChatView({
|
|
|
324
327
|
[onSendMessage]
|
|
325
328
|
);
|
|
326
329
|
|
|
330
|
+
// The retried bubble moves to the end of the thread; follow it there.
|
|
331
|
+
// A second failure shows on the bubble again.
|
|
332
|
+
const handleRetryMessage = useCallback(
|
|
333
|
+
(messageId: string) => {
|
|
334
|
+
onRetryMessage?.(messageId).catch(() => {});
|
|
335
|
+
scrollToBottom();
|
|
336
|
+
},
|
|
337
|
+
[onRetryMessage, scrollToBottom]
|
|
338
|
+
);
|
|
339
|
+
|
|
327
340
|
// Recomputed only when the list changes — never on a keystroke.
|
|
328
341
|
const thread = useMemo(() => deriveThread(messages), [messages]);
|
|
329
342
|
const locales = useMemo(() => [locale, defaultLocale], [locale, defaultLocale]);
|
|
@@ -331,6 +344,7 @@ export function ChatView({
|
|
|
331
344
|
const headerTextColor = contrastingTextColor(color);
|
|
332
345
|
const onDarkHeader = headerTextColor === palette.white;
|
|
333
346
|
const isEmptyThread = !loading && messages.length === 0;
|
|
347
|
+
const isLoadingThread = loading && messages.length === 0;
|
|
334
348
|
const agentIsActive = agentOnline ?? isOnline;
|
|
335
349
|
// "Waiting for a teammate" until someone replies: no teammate in the header
|
|
336
350
|
// and the visitor spoke last (bot replies before that still count as waiting).
|
|
@@ -439,6 +453,7 @@ export function ChatView({
|
|
|
439
453
|
contentContainerStyle={[
|
|
440
454
|
styles.messagesContent,
|
|
441
455
|
isEmptyThread && styles.messagesContentEmpty,
|
|
456
|
+
isLoadingThread && styles.messagesContentLoading,
|
|
442
457
|
]}
|
|
443
458
|
keyboardShouldPersistTaps="handled"
|
|
444
459
|
// Interactive dismissal drags the keyboard down under the finger while
|
|
@@ -455,9 +470,7 @@ export function ChatView({
|
|
|
455
470
|
</Pressable>
|
|
456
471
|
) : null}
|
|
457
472
|
|
|
458
|
-
{
|
|
459
|
-
<Text style={styles.stateText}>{labels.loading}</Text>
|
|
460
|
-
) : null}
|
|
473
|
+
{isLoadingThread ? <MessagesSkeleton color={color} label={labels.loading} /> : null}
|
|
461
474
|
|
|
462
475
|
{isEmptyThread ? (
|
|
463
476
|
<View style={styles.emptyState}>
|
|
@@ -520,6 +533,7 @@ export function ChatView({
|
|
|
520
533
|
onAgentUiAction={
|
|
521
534
|
thread.agentBlocksShown.has(message.id) ? handleAgentUiAction : undefined
|
|
522
535
|
}
|
|
536
|
+
onRetry={onRetryMessage && canRetryMessage(message) ? handleRetryMessage : undefined}
|
|
523
537
|
/>
|
|
524
538
|
</WidgetErrorBoundary>
|
|
525
539
|
</Fragment>
|
|
@@ -591,7 +605,7 @@ export function ChatView({
|
|
|
591
605
|
styles.attachButton,
|
|
592
606
|
(pressed || sending || picking) && globalStyles.pressed,
|
|
593
607
|
]}>
|
|
594
|
-
<
|
|
608
|
+
<PlusIcon size={20} color={palette.textMuted} />
|
|
595
609
|
</Pressable>
|
|
596
610
|
) : null}
|
|
597
611
|
<TextInput
|
|
@@ -662,6 +676,7 @@ const MessageBubble = memo(function MessageBubble({
|
|
|
662
676
|
onOpenImage,
|
|
663
677
|
onRateConversation,
|
|
664
678
|
locales,
|
|
679
|
+
onRetry,
|
|
665
680
|
}: {
|
|
666
681
|
message: Message;
|
|
667
682
|
color: string;
|
|
@@ -674,6 +689,8 @@ const MessageBubble = memo(function MessageBubble({
|
|
|
674
689
|
onOpenImage: (image: { url: string; name: string }) => void;
|
|
675
690
|
onRateConversation?: ChatViewProps['onRateConversation'];
|
|
676
691
|
locales: Array<string | null | undefined>;
|
|
692
|
+
/** Re-sends this message when it failed. */
|
|
693
|
+
onRetry?: (messageId: string) => void;
|
|
677
694
|
}) {
|
|
678
695
|
const joinedName = message.metadata?.event === 'agent_joined' ? message.metadata?.agentName : null;
|
|
679
696
|
if (message.messageType === 'system' && typeof joinedName === 'string' && joinedName) {
|
|
@@ -771,16 +788,6 @@ const MessageBubble = memo(function MessageBubble({
|
|
|
771
788
|
</View>
|
|
772
789
|
) : null}
|
|
773
790
|
|
|
774
|
-
{message.status === 'failed' ? (
|
|
775
|
-
<Text
|
|
776
|
-
style={[
|
|
777
|
-
styles.failedText,
|
|
778
|
-
{ color: isVisitor ? withAlpha(palette.white, 0.7) : palette.danger },
|
|
779
|
-
]}>
|
|
780
|
-
{labels.failedToSend}
|
|
781
|
-
</Text>
|
|
782
|
-
) : null}
|
|
783
|
-
|
|
784
791
|
{timeLabel ? (
|
|
785
792
|
<Text
|
|
786
793
|
style={[
|
|
@@ -794,6 +801,22 @@ const MessageBubble = memo(function MessageBubble({
|
|
|
794
801
|
|
|
795
802
|
{receiptLabel ? <Text style={styles.receiptText}>{receiptLabel}</Text> : null}
|
|
796
803
|
|
|
804
|
+
{/* Under the bubble, where Sent/Seen would be: in the danger color it
|
|
805
|
+
reads on any brand color, and the whole line is the retry target. */}
|
|
806
|
+
{message.status === 'failed' ? (
|
|
807
|
+
<Pressable
|
|
808
|
+
onPress={onRetry ? () => onRetry(message.id) : undefined}
|
|
809
|
+
disabled={!onRetry}
|
|
810
|
+
accessibilityRole={onRetry ? 'button' : 'text'}
|
|
811
|
+
hitSlop={8}
|
|
812
|
+
style={({ pressed }) => [styles.failedRow, pressed && globalStyles.pressed]}>
|
|
813
|
+
<AlertIcon size={12} color={palette.danger} />
|
|
814
|
+
<Text style={styles.failedText}>
|
|
815
|
+
{onRetry ? `${labels.failedToSend} · ${labels.tapToRetry}` : labels.failedToSend}
|
|
816
|
+
</Text>
|
|
817
|
+
</Pressable>
|
|
818
|
+
) : null}
|
|
819
|
+
|
|
797
820
|
{agentUiBlocks.length > 0 && onAgentUiAction ? (
|
|
798
821
|
<AgentUiBlocks
|
|
799
822
|
blocks={agentUiBlocks}
|
|
@@ -838,18 +861,20 @@ function AttachmentPreview({
|
|
|
838
861
|
onOpenImage: (image: { url: string; name: string }) => void;
|
|
839
862
|
}) {
|
|
840
863
|
// `url` is empty when the server sent a non-http(s) URL (see resolveUrl).
|
|
841
|
-
//
|
|
842
|
-
//
|
|
864
|
+
// The visitor's own attachment points at the picked file until the server
|
|
865
|
+
// copy replaces it (or is empty without one).
|
|
843
866
|
const [imageFailed, setImageFailed] = useState(false);
|
|
844
867
|
const [audioFailed, setAudioFailed] = useState(false);
|
|
845
868
|
const isImage = isImageAttachment(attachment.contentType) && !!attachment.url && !imageFailed;
|
|
846
869
|
const openable = isHttpUrl(attachment.url);
|
|
847
|
-
// Images open in the in-app viewer
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
870
|
+
// Images open in the in-app viewer, a local photo too since it already
|
|
871
|
+
// renders here. Other files (PDF, sheets) need the OS, which can only open
|
|
872
|
+
// the server copy.
|
|
873
|
+
const open = isImage
|
|
874
|
+
? () => onOpenImage({ url: attachment.url, name: attachment.name })
|
|
875
|
+
: openable
|
|
876
|
+
? () => void openExternalUrl(attachment.url)
|
|
877
|
+
: undefined;
|
|
853
878
|
|
|
854
879
|
// Voice notes from the team (and any other audio) play in place, like the
|
|
855
880
|
// web Messenger — when the app has expo-audio; otherwise the file row below.
|
|
@@ -872,7 +897,6 @@ function AttachmentPreview({
|
|
|
872
897
|
return (
|
|
873
898
|
<Pressable
|
|
874
899
|
onPress={open}
|
|
875
|
-
disabled={!openable}
|
|
876
900
|
accessibilityRole="imagebutton"
|
|
877
901
|
accessibilityLabel={attachment.name}>
|
|
878
902
|
<Image
|
|
@@ -21,6 +21,8 @@ export interface ChatLabels {
|
|
|
21
21
|
emptyConversation: string;
|
|
22
22
|
messagePlaceholder: string;
|
|
23
23
|
failedToSend: string;
|
|
24
|
+
/** After `failedToSend` on a bubble that can be re-sent. */
|
|
25
|
+
tapToRetry: string;
|
|
24
26
|
thinking: string;
|
|
25
27
|
team: string;
|
|
26
28
|
statusOnline: string;
|
|
@@ -104,6 +106,8 @@ export interface ChatViewProps {
|
|
|
104
106
|
attachments?: OutgoingAttachment[],
|
|
105
107
|
action?: AgentUiAction
|
|
106
108
|
) => Promise<void>;
|
|
109
|
+
/** Re-sends a failed bubble; rejects when it fails again. Absent: no retry. */
|
|
110
|
+
onRetryMessage?: (messageId: string) => Promise<void>;
|
|
107
111
|
onIdentifyVisitor?: (data: { email?: string; name?: string; phone?: string }) => Promise<void>;
|
|
108
112
|
/** Answers a rating card; rejects with the server error (409 locked, 410 expired). */
|
|
109
113
|
onRateConversation?: (
|
package/src/ui/styles/theme.ts
CHANGED
|
@@ -68,6 +68,8 @@ export interface WidgetTheme {
|
|
|
68
68
|
allowNewConversations: boolean;
|
|
69
69
|
showPreviousConversations: boolean;
|
|
70
70
|
attachmentsEnabled: boolean;
|
|
71
|
+
/** The server's upload cap; null when it only takes files inline in the message. */
|
|
72
|
+
maxAttachmentBytes: number | null;
|
|
71
73
|
preChatEnabled: boolean;
|
|
72
74
|
}
|
|
73
75
|
|
|
@@ -94,6 +96,10 @@ export function buildTheme(config: WidgetConfig | null): WidgetTheme {
|
|
|
94
96
|
allowNewConversations: config?.allowNewConversations ?? true,
|
|
95
97
|
showPreviousConversations: config?.showPreviousConversations ?? true,
|
|
96
98
|
attachmentsEnabled: config?.attachmentsEnabled ?? true,
|
|
99
|
+
maxAttachmentBytes:
|
|
100
|
+
typeof config?.maxAttachmentBytes === 'number' && config.maxAttachmentBytes > 0
|
|
101
|
+
? config.maxAttachmentBytes
|
|
102
|
+
: null,
|
|
97
103
|
preChatEnabled: config?.preChatEnabled ?? false,
|
|
98
104
|
};
|
|
99
105
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Conversation selectors and formatting shared by the Home and Messages
|
|
3
|
-
* spaces
|
|
3
|
+
* spaces (and the store and chat thread, for message helpers), so they can
|
|
4
|
+
* never disagree about ordering or wording.
|
|
4
5
|
* Pure and free of React so they stay trivial to test.
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
8
|
import { formatLabel } from '../../i18n';
|
|
8
|
-
import type { Conversation } from '../../types';
|
|
9
|
+
import type { Conversation, Message } from '../../types';
|
|
9
10
|
|
|
10
11
|
export interface TeamAgent {
|
|
11
12
|
name: string | null;
|
|
@@ -120,3 +121,16 @@ export function formatDayLabel(
|
|
|
120
121
|
return date.toDateString();
|
|
121
122
|
}
|
|
122
123
|
}
|
|
124
|
+
|
|
125
|
+
/** Optimistic visitor bubbles the server hasn't acknowledged (pending/failed). */
|
|
126
|
+
export function isLocalMessage(message: Message): boolean {
|
|
127
|
+
return message.id.startsWith('temp-');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The visitor's own bubble that never reached the server. A `failed` status
|
|
132
|
+
* the server sets on any other message isn't something this device can re-send.
|
|
133
|
+
*/
|
|
134
|
+
export function canRetryMessage(message: Message): boolean {
|
|
135
|
+
return message.status === 'failed' && message.direction === 'inbound' && isLocalMessage(message);
|
|
136
|
+
}
|