@repzo/widget-react-native 1.0.0 → 1.0.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/README.md +167 -74
- package/lib/commonjs/api/client.js.map +1 -1
- package/lib/commonjs/attachments/pickers.js +493 -0
- package/lib/commonjs/attachments/pickers.js.map +1 -0
- package/lib/commonjs/i18n.js +19 -0
- package/lib/commonjs/i18n.js.map +1 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/locales.js +16 -0
- package/lib/commonjs/locales.js.map +1 -1
- package/lib/commonjs/state/widget-store.js +147 -13
- package/lib/commonjs/state/widget-store.js.map +1 -1
- package/lib/commonjs/types.js +14 -0
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/ui/WidgetProvider.js +53 -8
- package/lib/commonjs/ui/WidgetProvider.js.map +1 -1
- package/lib/commonjs/ui/components/AttachmentChips.js +135 -0
- package/lib/commonjs/ui/components/AttachmentChips.js.map +1 -0
- package/lib/commonjs/ui/components/AttachmentMenu.js +125 -0
- package/lib/commonjs/ui/components/AttachmentMenu.js.map +1 -0
- package/lib/commonjs/ui/components/ImageViewer.js +181 -0
- package/lib/commonjs/ui/components/ImageViewer.js.map +1 -0
- package/lib/commonjs/ui/components/InAppNotification.js +210 -0
- package/lib/commonjs/ui/components/InAppNotification.js.map +1 -0
- package/lib/commonjs/ui/screens/chat/ChatView.js +198 -18
- package/lib/commonjs/ui/screens/chat/ChatView.js.map +1 -1
- package/lib/commonjs/ui/screens/chat/ChatView.styles.js +19 -0
- package/lib/commonjs/ui/screens/chat/ChatView.styles.js.map +1 -1
- package/lib/module/api/client.js.map +1 -1
- package/lib/module/attachments/pickers.js +479 -0
- package/lib/module/attachments/pickers.js.map +1 -0
- package/lib/module/i18n.js +19 -0
- package/lib/module/i18n.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/locales.js +16 -0
- package/lib/module/locales.js.map +1 -1
- package/lib/module/state/widget-store.js +147 -13
- package/lib/module/state/widget-store.js.map +1 -1
- package/lib/module/types.js +14 -0
- package/lib/module/types.js.map +1 -1
- package/lib/module/ui/WidgetProvider.js +53 -8
- package/lib/module/ui/WidgetProvider.js.map +1 -1
- package/lib/module/ui/components/AttachmentChips.js +131 -0
- package/lib/module/ui/components/AttachmentChips.js.map +1 -0
- package/lib/module/ui/components/AttachmentMenu.js +120 -0
- package/lib/module/ui/components/AttachmentMenu.js.map +1 -0
- package/lib/module/ui/components/ImageViewer.js +177 -0
- package/lib/module/ui/components/ImageViewer.js.map +1 -0
- package/lib/module/ui/components/InAppNotification.js +208 -0
- package/lib/module/ui/components/InAppNotification.js.map +1 -0
- package/lib/module/ui/screens/chat/ChatView.js +201 -21
- package/lib/module/ui/screens/chat/ChatView.js.map +1 -1
- package/lib/module/ui/screens/chat/ChatView.styles.js +19 -0
- package/lib/module/ui/screens/chat/ChatView.styles.js.map +1 -1
- package/lib/typescript/commonjs/api/client.d.ts +8 -2
- package/lib/typescript/commonjs/api/client.d.ts.map +1 -1
- package/lib/typescript/commonjs/attachments/pickers.d.ts +81 -0
- package/lib/typescript/commonjs/attachments/pickers.d.ts.map +1 -0
- package/lib/typescript/commonjs/i18n.d.ts +16 -0
- package/lib/typescript/commonjs/i18n.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +1 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/locales.d.ts.map +1 -1
- package/lib/typescript/commonjs/state/widget-store.d.ts +23 -2
- package/lib/typescript/commonjs/state/widget-store.d.ts.map +1 -1
- package/lib/typescript/commonjs/types.d.ts +74 -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/AttachmentChips.d.ts +16 -0
- package/lib/typescript/commonjs/ui/components/AttachmentChips.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/AttachmentMenu.d.ts +28 -0
- package/lib/typescript/commonjs/ui/components/AttachmentMenu.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/ImageViewer.d.ts +23 -0
- package/lib/typescript/commonjs/ui/components/ImageViewer.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/InAppNotification.d.ts +28 -0
- package/lib/typescript/commonjs/ui/components/InAppNotification.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/screens/chat/ChatView.d.ts +3 -2
- package/lib/typescript/commonjs/ui/screens/chat/ChatView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/chat/ChatView.styles.d.ts +18 -0
- 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 +28 -2
- package/lib/typescript/commonjs/ui/screens/chat/ChatView.types.d.ts.map +1 -1
- package/lib/typescript/module/api/client.d.ts +8 -2
- package/lib/typescript/module/api/client.d.ts.map +1 -1
- package/lib/typescript/module/attachments/pickers.d.ts +81 -0
- package/lib/typescript/module/attachments/pickers.d.ts.map +1 -0
- package/lib/typescript/module/i18n.d.ts +16 -0
- package/lib/typescript/module/i18n.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +1 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/locales.d.ts.map +1 -1
- package/lib/typescript/module/state/widget-store.d.ts +23 -2
- package/lib/typescript/module/state/widget-store.d.ts.map +1 -1
- package/lib/typescript/module/types.d.ts +74 -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/AttachmentChips.d.ts +16 -0
- package/lib/typescript/module/ui/components/AttachmentChips.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/AttachmentMenu.d.ts +28 -0
- package/lib/typescript/module/ui/components/AttachmentMenu.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/ImageViewer.d.ts +23 -0
- package/lib/typescript/module/ui/components/ImageViewer.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/InAppNotification.d.ts +28 -0
- package/lib/typescript/module/ui/components/InAppNotification.d.ts.map +1 -0
- package/lib/typescript/module/ui/screens/chat/ChatView.d.ts +3 -2
- package/lib/typescript/module/ui/screens/chat/ChatView.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/chat/ChatView.styles.d.ts +18 -0
- 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 +28 -2
- package/lib/typescript/module/ui/screens/chat/ChatView.types.d.ts.map +1 -1
- package/metro.js +17 -32
- package/package.json +18 -1
- package/src/api/client.ts +2 -2
- package/src/attachments/pickers.ts +515 -0
- package/src/i18n.ts +20 -0
- package/src/index.tsx +4 -0
- package/src/locales.ts +16 -0
- package/src/state/widget-store.ts +152 -16
- package/src/types.ts +79 -0
- package/src/ui/WidgetProvider.tsx +65 -6
- package/src/ui/components/AttachmentChips.tsx +135 -0
- package/src/ui/components/AttachmentMenu.tsx +132 -0
- package/src/ui/components/ImageViewer.tsx +178 -0
- package/src/ui/components/InAppNotification.tsx +225 -0
- package/src/ui/screens/chat/ChatView.styles.ts +19 -0
- package/src/ui/screens/chat/ChatView.tsx +235 -16
- package/src/ui/screens/chat/ChatView.types.ts +38 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useState","ActivityIndicator","Image","Modal","Platform","Pressable","ScrollView","StyleSheet","Text","View","useWidgetInsets","fontSize","palette","spacing","CloseIcon","jsx","_jsx","jsxs","_jsxs","ImageViewer","image","labels","onClose","onOpenExternally","insets","status","setStatus","url","state","visible","transparent","animationType","statusBarTranslucent","onRequestClose","children","style","styles","backdrop","accessibilityViewIsModal","fill","contentContainerStyle","center","maximumZoomScale","minimumZoomScale","centerContent","showsHorizontalScrollIndicator","showsVerticalScrollIndicator","bouncesZoom","source","uri","resizeMode","accessibilityLabel","name","accessibilityIgnoresInvertColors","onLoad","onError","absoluteFill","color","white","failed","failedText","loadFailed","onPress","accessibilityRole","pressed","externalButton","externalLabel","openExternally","header","paddingTop","top","sm","pointerEvents","title","numberOfLines","close","hitSlop","closeButton","size","create","flex","backgroundColor","flexGrow","alignItems","justifyContent","width","height","position","left","right","flexDirection","gap","md","paddingHorizontal","lg","paddingBottom","minWidth","body","fontWeight","borderRadius","opacity","xxl","textAlign","paddingVertical","borderWidth","hairlineWidth","borderColor","meta","OS"],"sourceRoot":"../../../../src","sources":["ui/components/ImageViewer.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The card that floats over the host app when a team reply arrives while the
|
|
5
|
+
* messenger is closed — Intercom's in-app notification. It never times out:
|
|
6
|
+
* tapping it opens the conversation, swiping it left or right dismisses it
|
|
7
|
+
* (the thread stays unread). The store takes it down on its own once the
|
|
8
|
+
* messenger opens or the thread is read.
|
|
9
|
+
*
|
|
10
|
+
* Pure JS gestures (PanResponder), so hosts need no gesture library.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { useEffect, useMemo, useRef } from 'react';
|
|
14
|
+
import { AccessibilityInfo, Animated, PanResponder, Pressable, StyleSheet, Text, View, useWindowDimensions } from 'react-native';
|
|
15
|
+
import { formatLabel } from "../../i18n.js";
|
|
16
|
+
import { useReduceMotion } from "../hooks/use-reduce-motion.js";
|
|
17
|
+
import { fontSize, palette, radius, shadows, spacing, withAlpha } from "../styles/theme.js";
|
|
18
|
+
import { START_ALIGN } from "../utils/rtl.js";
|
|
19
|
+
import { Avatar } from "./Avatar.js";
|
|
20
|
+
|
|
21
|
+
/** Share of the card's width a drag must travel to count as a dismiss. */
|
|
22
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
23
|
+
const DISMISS_DISTANCE_RATIO = 0.35;
|
|
24
|
+
/** A quick flick dismisses even when it travels less than that. */
|
|
25
|
+
const DISMISS_VELOCITY = 0.6;
|
|
26
|
+
export function InAppNotification({
|
|
27
|
+
notification,
|
|
28
|
+
color,
|
|
29
|
+
bottomInset,
|
|
30
|
+
labels,
|
|
31
|
+
onOpen,
|
|
32
|
+
onDismiss
|
|
33
|
+
}) {
|
|
34
|
+
const {
|
|
35
|
+
width
|
|
36
|
+
} = useWindowDimensions();
|
|
37
|
+
const reduceMotion = useReduceMotion();
|
|
38
|
+
// Read as "hold still" until the setting is known (see useReduceMotion).
|
|
39
|
+
const animate = reduceMotion === false;
|
|
40
|
+
const entry = useRef(new Animated.Value(animate ? 0 : 1)).current;
|
|
41
|
+
const dragX = useRef(new Animated.Value(0)).current;
|
|
42
|
+
const preview = notification.preview || (notification.hasAttachments ? labels.attachment : '');
|
|
43
|
+
const sender = notification.senderName ? formatLabel(labels.replyFrom, {
|
|
44
|
+
name: notification.senderName
|
|
45
|
+
}) : labels.newReply;
|
|
46
|
+
const accessibilityLabel = [preview, sender].filter(Boolean).join(', ');
|
|
47
|
+
|
|
48
|
+
// Mounted once per reply (the provider keys it by message id), so this runs
|
|
49
|
+
// for every new card: slide it in and tell screen-reader users it arrived.
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (animate) {
|
|
52
|
+
Animated.spring(entry, {
|
|
53
|
+
toValue: 1,
|
|
54
|
+
useNativeDriver: true,
|
|
55
|
+
speed: 16,
|
|
56
|
+
bounciness: 4
|
|
57
|
+
}).start();
|
|
58
|
+
}
|
|
59
|
+
AccessibilityInfo.announceForAccessibility(accessibilityLabel);
|
|
60
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
61
|
+
}, []);
|
|
62
|
+
const panResponder = useMemo(() => {
|
|
63
|
+
const settle = () => Animated.spring(dragX, {
|
|
64
|
+
toValue: 0,
|
|
65
|
+
useNativeDriver: true,
|
|
66
|
+
bounciness: 6
|
|
67
|
+
}).start();
|
|
68
|
+
return PanResponder.create({
|
|
69
|
+
// Capture so a horizontal drag wins over the Pressable underneath; taps
|
|
70
|
+
// and vertical movement still reach it.
|
|
71
|
+
onMoveShouldSetPanResponderCapture: (_, gesture) => Math.abs(gesture.dx) > 8 && Math.abs(gesture.dx) > Math.abs(gesture.dy) * 1.5,
|
|
72
|
+
onPanResponderMove: (_, gesture) => dragX.setValue(gesture.dx),
|
|
73
|
+
onPanResponderTerminationRequest: () => false,
|
|
74
|
+
onPanResponderRelease: (_, gesture) => {
|
|
75
|
+
const farEnough = Math.abs(gesture.dx) > width * DISMISS_DISTANCE_RATIO;
|
|
76
|
+
const fastEnough = Math.abs(gesture.vx) > DISMISS_VELOCITY;
|
|
77
|
+
if (!farEnough && !fastEnough) {
|
|
78
|
+
settle();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const direction = (gesture.dx || gesture.vx) < 0 ? -1 : 1;
|
|
82
|
+
Animated.timing(dragX, {
|
|
83
|
+
toValue: direction * width,
|
|
84
|
+
duration: animate ? 180 : 0,
|
|
85
|
+
useNativeDriver: true
|
|
86
|
+
}).start(() => onDismiss());
|
|
87
|
+
},
|
|
88
|
+
onPanResponderTerminate: settle
|
|
89
|
+
});
|
|
90
|
+
}, [animate, dragX, onDismiss, width]);
|
|
91
|
+
const handleAccessibilityAction = event => {
|
|
92
|
+
if (event.nativeEvent.actionName === 'dismiss') onDismiss();
|
|
93
|
+
if (event.nativeEvent.actionName === 'activate') onOpen(notification.conversationId);
|
|
94
|
+
};
|
|
95
|
+
const opacity = Animated.multiply(entry, dragX.interpolate({
|
|
96
|
+
inputRange: [-width, 0, width],
|
|
97
|
+
outputRange: [0, 1, 0],
|
|
98
|
+
extrapolate: 'clamp'
|
|
99
|
+
}));
|
|
100
|
+
const translateY = entry.interpolate({
|
|
101
|
+
inputRange: [0, 1],
|
|
102
|
+
outputRange: [spacing.lg, 0]
|
|
103
|
+
});
|
|
104
|
+
return /*#__PURE__*/_jsx(View, {
|
|
105
|
+
pointerEvents: "box-none",
|
|
106
|
+
style: [styles.wrapper, {
|
|
107
|
+
bottom: bottomInset + spacing.md
|
|
108
|
+
}],
|
|
109
|
+
children: /*#__PURE__*/_jsx(Animated.View, {
|
|
110
|
+
...panResponder.panHandlers,
|
|
111
|
+
style: [styles.card, {
|
|
112
|
+
opacity,
|
|
113
|
+
transform: [{
|
|
114
|
+
translateX: dragX
|
|
115
|
+
}, {
|
|
116
|
+
translateY
|
|
117
|
+
}]
|
|
118
|
+
}],
|
|
119
|
+
children: /*#__PURE__*/_jsxs(Pressable, {
|
|
120
|
+
onPress: () => onOpen(notification.conversationId),
|
|
121
|
+
accessibilityRole: "button",
|
|
122
|
+
accessibilityLabel: accessibilityLabel,
|
|
123
|
+
accessibilityHint: labels.openHint,
|
|
124
|
+
accessibilityActions: [{
|
|
125
|
+
name: 'activate'
|
|
126
|
+
}, {
|
|
127
|
+
name: 'dismiss',
|
|
128
|
+
label: labels.dismiss
|
|
129
|
+
}],
|
|
130
|
+
onAccessibilityAction: handleAccessibilityAction,
|
|
131
|
+
style: ({
|
|
132
|
+
pressed
|
|
133
|
+
}) => [styles.row, pressed && styles.pressed],
|
|
134
|
+
children: [/*#__PURE__*/_jsx(Avatar, {
|
|
135
|
+
avatar: notification.senderAvatar,
|
|
136
|
+
name: notification.senderName,
|
|
137
|
+
color: color,
|
|
138
|
+
size: 40,
|
|
139
|
+
style: styles.avatar
|
|
140
|
+
}), /*#__PURE__*/_jsxs(View, {
|
|
141
|
+
style: styles.text,
|
|
142
|
+
children: [preview ? /*#__PURE__*/_jsx(Text, {
|
|
143
|
+
numberOfLines: 2,
|
|
144
|
+
style: styles.preview,
|
|
145
|
+
children: preview
|
|
146
|
+
}) : null, /*#__PURE__*/_jsx(Text, {
|
|
147
|
+
numberOfLines: 1,
|
|
148
|
+
style: styles.sender,
|
|
149
|
+
children: sender
|
|
150
|
+
})]
|
|
151
|
+
})]
|
|
152
|
+
})
|
|
153
|
+
})
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
const styles = StyleSheet.create({
|
|
157
|
+
wrapper: {
|
|
158
|
+
position: 'absolute',
|
|
159
|
+
left: 0,
|
|
160
|
+
right: 0,
|
|
161
|
+
paddingHorizontal: spacing.lg,
|
|
162
|
+
alignItems: 'center',
|
|
163
|
+
// Above the host's navigator even when the provider is mounted before it.
|
|
164
|
+
zIndex: 1000,
|
|
165
|
+
elevation: 1000
|
|
166
|
+
},
|
|
167
|
+
card: {
|
|
168
|
+
width: '100%',
|
|
169
|
+
maxWidth: 480,
|
|
170
|
+
borderRadius: radius.xl,
|
|
171
|
+
backgroundColor: palette.text,
|
|
172
|
+
...shadows.floating
|
|
173
|
+
},
|
|
174
|
+
row: {
|
|
175
|
+
flexDirection: 'row',
|
|
176
|
+
alignItems: 'center',
|
|
177
|
+
gap: spacing.md,
|
|
178
|
+
minHeight: 64,
|
|
179
|
+
paddingVertical: spacing.md,
|
|
180
|
+
paddingHorizontal: spacing.lg,
|
|
181
|
+
borderRadius: radius.xl
|
|
182
|
+
},
|
|
183
|
+
pressed: {
|
|
184
|
+
opacity: 0.85
|
|
185
|
+
},
|
|
186
|
+
avatar: {
|
|
187
|
+
borderWidth: 1,
|
|
188
|
+
borderColor: withAlpha(palette.white, 0.1)
|
|
189
|
+
},
|
|
190
|
+
text: {
|
|
191
|
+
flex: 1,
|
|
192
|
+
minWidth: 0,
|
|
193
|
+
gap: 2
|
|
194
|
+
},
|
|
195
|
+
preview: {
|
|
196
|
+
color: palette.white,
|
|
197
|
+
fontSize: fontSize.body,
|
|
198
|
+
fontWeight: '600',
|
|
199
|
+
lineHeight: 20,
|
|
200
|
+
textAlign: START_ALIGN
|
|
201
|
+
},
|
|
202
|
+
sender: {
|
|
203
|
+
color: withAlpha(palette.white, 0.64),
|
|
204
|
+
fontSize: fontSize.meta,
|
|
205
|
+
textAlign: START_ALIGN
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
//# sourceMappingURL=InAppNotification.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useEffect","useMemo","useRef","AccessibilityInfo","Animated","PanResponder","Pressable","StyleSheet","Text","View","useWindowDimensions","formatLabel","useReduceMotion","fontSize","palette","radius","shadows","spacing","withAlpha","START_ALIGN","Avatar","jsx","_jsx","jsxs","_jsxs","DISMISS_DISTANCE_RATIO","DISMISS_VELOCITY","InAppNotification","notification","color","bottomInset","labels","onOpen","onDismiss","width","reduceMotion","animate","entry","Value","current","dragX","preview","hasAttachments","attachment","sender","senderName","replyFrom","name","newReply","accessibilityLabel","filter","Boolean","join","spring","toValue","useNativeDriver","speed","bounciness","start","announceForAccessibility","panResponder","settle","create","onMoveShouldSetPanResponderCapture","_","gesture","Math","abs","dx","dy","onPanResponderMove","setValue","onPanResponderTerminationRequest","onPanResponderRelease","farEnough","fastEnough","vx","direction","timing","duration","onPanResponderTerminate","handleAccessibilityAction","event","nativeEvent","actionName","conversationId","opacity","multiply","interpolate","inputRange","outputRange","extrapolate","translateY","lg","pointerEvents","style","styles","wrapper","bottom","md","children","panHandlers","card","transform","translateX","onPress","accessibilityRole","accessibilityHint","openHint","accessibilityActions","label","dismiss","onAccessibilityAction","pressed","row","avatar","senderAvatar","size","text","numberOfLines","position","left","right","paddingHorizontal","alignItems","zIndex","elevation","maxWidth","borderRadius","xl","backgroundColor","floating","flexDirection","gap","minHeight","paddingVertical","borderWidth","borderColor","white","flex","minWidth","body","fontWeight","lineHeight","textAlign","meta"],"sourceRoot":"../../../../src","sources":["ui/components/InAppNotification.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -6,24 +6,29 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Mobile differences: older messages load through an explicit "Load earlier
|
|
8
8
|
* messages" control (a scroll-anchored infinite list fights the keyboard), and
|
|
9
|
-
* the
|
|
9
|
+
* the attach button only shows when the host app has a picker to open (see
|
|
10
|
+
* `attachments/pickers.ts`).
|
|
10
11
|
*/
|
|
11
12
|
|
|
12
13
|
import { Fragment, useCallback, useEffect, useRef, useState } from 'react';
|
|
13
|
-
import { Animated, Easing, Image, Pressable, ScrollView, Text, TextInput, View } from 'react-native';
|
|
14
|
+
import { Animated, Easing, Image, Platform, Pressable, ScrollView, Text, TextInput, View } from 'react-native';
|
|
14
15
|
import { formatAgentUiActionMessage, getAgentUiBlocks, getProcedureForm } from "../../../agent-ui-blocks.js";
|
|
16
|
+
import { MAX_ATTACHMENTS, isImageAttachment } from "../../../attachments/pickers.js";
|
|
15
17
|
import { AgentUiBlocks } from "../../components/AgentUiBlocks.js";
|
|
18
|
+
import { AttachmentChips } from "../../components/AttachmentChips.js";
|
|
19
|
+
import { AttachmentMenu, showNativeAttachmentMenu } from "../../components/AttachmentMenu.js";
|
|
16
20
|
import { Avatar } from "../../components/Avatar.js";
|
|
21
|
+
import { ImageViewer } from "../../components/ImageViewer.js";
|
|
17
22
|
import { WidgetErrorBoundary } from "../../components/ErrorBoundary.js";
|
|
18
23
|
import { GradientBackground } from "../../components/Gradient.js";
|
|
19
|
-
import { ArrowUpIcon, ChatOutlineIcon, ChevronIcon } from "../../components/icons.js";
|
|
24
|
+
import { ArrowUpIcon, ChatOutlineIcon, ChevronIcon, PaperclipIcon } from "../../components/icons.js";
|
|
20
25
|
import { Markdown } from "../../components/Markdown.js";
|
|
21
26
|
import { useReduceMotion } from "../../hooks/use-reduce-motion.js";
|
|
22
27
|
import { globalStyles } from "../../styles/global.js";
|
|
23
28
|
import { contrastingTextColor, mixHex, palette, withAlpha } from "../../styles/theme.js";
|
|
24
29
|
import { formatClockTime, formatDayLabel, isSameDay } from "../../utils/conversations.js";
|
|
25
30
|
import { stripHtml } from "../../utils/html.js";
|
|
26
|
-
import { openExternalUrl } from "../../utils/safe-url.js";
|
|
31
|
+
import { isHttpUrl, openExternalUrl } from "../../utils/safe-url.js";
|
|
27
32
|
import { backDirection, isRTL } from "../../utils/rtl.js";
|
|
28
33
|
import { styles } from "./ChatView.styles.js";
|
|
29
34
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
@@ -40,6 +45,8 @@ export function ChatView({
|
|
|
40
45
|
prefillText,
|
|
41
46
|
offlineMessage,
|
|
42
47
|
labels,
|
|
48
|
+
attachmentSources = [],
|
|
49
|
+
onPickAttachments,
|
|
43
50
|
onSendMessage,
|
|
44
51
|
onIdentifyVisitor,
|
|
45
52
|
onLoadMore,
|
|
@@ -49,14 +56,38 @@ export function ChatView({
|
|
|
49
56
|
}) {
|
|
50
57
|
const [input, setInput] = useState(prefillText || '');
|
|
51
58
|
const [composerFocused, setComposerFocused] = useState(false);
|
|
59
|
+
const [drafts, setDrafts] = useState([]);
|
|
60
|
+
const [menuOpen, setMenuOpen] = useState(false);
|
|
61
|
+
const [viewerImage, setViewerImage] = useState(null);
|
|
62
|
+
const [picking, setPicking] = useState(false);
|
|
63
|
+
// Only attachment sends hold the composer; text sends stay fire-and-forget.
|
|
64
|
+
const [sending, setSending] = useState(false);
|
|
65
|
+
const [notice, setNotice] = useState(null);
|
|
52
66
|
const reduceMotion = useReduceMotion();
|
|
53
67
|
const scrollRef = useRef(null);
|
|
54
68
|
const typingStopTimer = useRef(null);
|
|
69
|
+
const noticeTimer = useRef(null);
|
|
70
|
+
const pickerTimer = useRef(null);
|
|
71
|
+
const mounted = useRef(false);
|
|
55
72
|
useEffect(() => {
|
|
56
73
|
if (prefillText) setInput(prefillText);
|
|
57
74
|
}, [prefillText]);
|
|
58
|
-
|
|
59
|
-
|
|
75
|
+
|
|
76
|
+
// The provider keys this screen by the store's thread generation. Pending
|
|
77
|
+
// picks and sends belong to this mount, even before a draft gets a server ID.
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
mounted.current = true;
|
|
80
|
+
return () => {
|
|
81
|
+
mounted.current = false;
|
|
82
|
+
if (typingStopTimer.current) clearTimeout(typingStopTimer.current);
|
|
83
|
+
if (noticeTimer.current) clearTimeout(noticeTimer.current);
|
|
84
|
+
if (pickerTimer.current) clearTimeout(pickerTimer.current);
|
|
85
|
+
};
|
|
86
|
+
}, []);
|
|
87
|
+
const showNotice = useCallback(message => {
|
|
88
|
+
if (noticeTimer.current) clearTimeout(noticeTimer.current);
|
|
89
|
+
setNotice(message);
|
|
90
|
+
if (message) noticeTimer.current = setTimeout(() => setNotice(null), 5000);
|
|
60
91
|
}, []);
|
|
61
92
|
const scrollToBottom = useCallback((animated = true) => {
|
|
62
93
|
scrollRef.current?.scrollToEnd({
|
|
@@ -100,15 +131,101 @@ export function ChatView({
|
|
|
100
131
|
viewportHeight.current = height;
|
|
101
132
|
if (shrank) scrollToBottom(false);
|
|
102
133
|
}, [scrollToBottom]);
|
|
103
|
-
const
|
|
134
|
+
const showAttach = attachmentSources.length > 0 && !!onPickAttachments;
|
|
135
|
+
const canSend = !sending && (input.trim().length > 0 || drafts.length > 0);
|
|
136
|
+
const problemLabel = problem => {
|
|
137
|
+
switch (problem) {
|
|
138
|
+
case 'tooMany':
|
|
139
|
+
return labels.attach.tooMany;
|
|
140
|
+
case 'tooLarge':
|
|
141
|
+
return labels.attach.tooLarge;
|
|
142
|
+
case 'tooLargeTogether':
|
|
143
|
+
return labels.attach.tooLargeTogether;
|
|
144
|
+
case 'cameraDenied':
|
|
145
|
+
return labels.attach.cameraDenied;
|
|
146
|
+
case 'failed':
|
|
147
|
+
return labels.attach.failed;
|
|
148
|
+
default:
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
const handlePick = async source => {
|
|
153
|
+
if (!mounted.current || !onPickAttachments || picking) return;
|
|
154
|
+
setPicking(true);
|
|
155
|
+
try {
|
|
156
|
+
const result = await onPickAttachments(source, drafts);
|
|
157
|
+
if (!mounted.current) return;
|
|
158
|
+
if (result.added.length > 0) {
|
|
159
|
+
setDrafts(current => [...current, ...result.added].slice(0, MAX_ATTACHMENTS));
|
|
160
|
+
}
|
|
161
|
+
showNotice(problemLabel(result.problem));
|
|
162
|
+
} finally {
|
|
163
|
+
if (mounted.current) setPicking(false);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
const menuOptions = attachmentSources.map(source => ({
|
|
167
|
+
key: source,
|
|
168
|
+
label: source === 'photo' ? labels.attach.photoLibrary : source === 'camera' ? labels.attach.takePhoto : labels.attach.file
|
|
169
|
+
}));
|
|
170
|
+
const handleAttachPress = () => {
|
|
171
|
+
if (sending || picking) return;
|
|
172
|
+
if (drafts.length >= MAX_ATTACHMENTS) {
|
|
173
|
+
showNotice(labels.attach.tooMany);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const only = attachmentSources.length === 1 ? attachmentSources[0] : undefined;
|
|
177
|
+
if (only) {
|
|
178
|
+
void handlePick(only);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const native = showNativeAttachmentMenu(menuOptions, labels.attach.cancel, source => {
|
|
182
|
+
if (!mounted.current) return;
|
|
183
|
+
// Presenting a picker while the action sheet is still animating away
|
|
184
|
+
// fails silently on iOS; let it finish first.
|
|
185
|
+
pickerTimer.current = setTimeout(() => void handlePick(source), 350);
|
|
186
|
+
});
|
|
187
|
+
if (!native) setMenuOpen(true);
|
|
188
|
+
};
|
|
189
|
+
const handleMenuSelect = source => {
|
|
190
|
+
setMenuOpen(false);
|
|
191
|
+
void handlePick(source);
|
|
192
|
+
};
|
|
104
193
|
const handleSend = () => {
|
|
105
194
|
const trimmed = input.trim();
|
|
106
|
-
if (!
|
|
195
|
+
if (!canSend) return;
|
|
107
196
|
setInput('');
|
|
108
197
|
if (typingStopTimer.current) clearTimeout(typingStopTimer.current);
|
|
109
198
|
onTyping(false);
|
|
110
|
-
onSendMessage(trimmed);
|
|
111
199
|
scrollToBottom();
|
|
200
|
+
if (drafts.length === 0) {
|
|
201
|
+
// The failed bubble already surfaces a failure to the visitor.
|
|
202
|
+
onSendMessage(trimmed).catch(() => {});
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const sent = drafts;
|
|
206
|
+
setSending(true);
|
|
207
|
+
showNotice(null);
|
|
208
|
+
onSendMessage(trimmed, sent.map(draft => ({
|
|
209
|
+
name: draft.name,
|
|
210
|
+
url: draft.dataUrl,
|
|
211
|
+
contentType: draft.contentType,
|
|
212
|
+
size: draft.size,
|
|
213
|
+
previewUrl: draft.previewUri
|
|
214
|
+
}))).then(() => {
|
|
215
|
+
if (!mounted.current) return;
|
|
216
|
+
const sentIds = new Set(sent.map(draft => draft.id));
|
|
217
|
+
setDrafts(current => current.filter(draft => !sentIds.has(draft.id)));
|
|
218
|
+
}).catch(error => {
|
|
219
|
+
// The visitor has moved to another thread: nothing to restore there.
|
|
220
|
+
if (!mounted.current) return;
|
|
221
|
+
// Chips stay for a retry; put the text back too unless they've
|
|
222
|
+
// already started typing something else.
|
|
223
|
+
setInput(current => current || trimmed);
|
|
224
|
+
const status = error?.status;
|
|
225
|
+
showNotice(status === 413 ? labels.attach.tooLargeTogether : labels.failedToSend);
|
|
226
|
+
}).finally(() => {
|
|
227
|
+
if (mounted.current) setSending(false);
|
|
228
|
+
});
|
|
112
229
|
};
|
|
113
230
|
const handleChangeText = text => {
|
|
114
231
|
setInput(text);
|
|
@@ -125,7 +242,7 @@ export function ChatView({
|
|
|
125
242
|
phone: stringPayload(action.payload.phone)
|
|
126
243
|
});
|
|
127
244
|
}
|
|
128
|
-
onSendMessage(formatAgentUiActionMessage(action), undefined, action);
|
|
245
|
+
onSendMessage(formatAgentUiActionMessage(action), undefined, action).catch(() => {});
|
|
129
246
|
scrollToBottom();
|
|
130
247
|
}, [onIdentifyVisitor, onSendMessage, scrollToBottom]);
|
|
131
248
|
const headerTextColor = contrastingTextColor(color);
|
|
@@ -262,9 +379,10 @@ export function ChatView({
|
|
|
262
379
|
message: message,
|
|
263
380
|
color: color,
|
|
264
381
|
labels: labels,
|
|
382
|
+
onOpenImage: setViewerImage,
|
|
265
383
|
senderLabel: isAgentMessage && (startsRun || startsDay) ? message.senderName || agentName || labels.team : null,
|
|
266
384
|
timeLabel: formatClockTime(message.createdAt, locale),
|
|
267
|
-
onSubmitProcedureForm: shouldShowProcedureForm(messages, message) ? body => onSendMessage(body) : undefined,
|
|
385
|
+
onSubmitProcedureForm: shouldShowProcedureForm(messages, message) ? body => void onSendMessage(body).catch(() => {}) : undefined,
|
|
268
386
|
onAgentUiAction: shouldShowAgentUiBlocks(messages, message) ? handleAgentUiAction : undefined
|
|
269
387
|
})
|
|
270
388
|
})]
|
|
@@ -294,13 +412,44 @@ export function ChatView({
|
|
|
294
412
|
style: styles.offlineText,
|
|
295
413
|
children: offlineMessage
|
|
296
414
|
})
|
|
297
|
-
}) : null, /*#__PURE__*/
|
|
415
|
+
}) : null, /*#__PURE__*/_jsxs(View, {
|
|
298
416
|
style: styles.composerWrapper,
|
|
299
|
-
children: /*#__PURE__*/
|
|
300
|
-
style:
|
|
417
|
+
children: [notice ? /*#__PURE__*/_jsx(Text, {
|
|
418
|
+
style: styles.composerNotice,
|
|
419
|
+
accessibilityRole: "alert",
|
|
420
|
+
accessibilityLiveRegion: "polite",
|
|
421
|
+
children: notice
|
|
422
|
+
}) : null, drafts.length > 0 ? /*#__PURE__*/_jsx(AttachmentChips, {
|
|
423
|
+
attachments: drafts,
|
|
424
|
+
disabled: sending,
|
|
425
|
+
removeLabel: labels.attach.remove,
|
|
426
|
+
onRemove: id => {
|
|
427
|
+
setDrafts(current => current.filter(draft => draft.id !== id));
|
|
428
|
+
showNotice(null);
|
|
429
|
+
}
|
|
430
|
+
}) : null, /*#__PURE__*/_jsxs(View, {
|
|
431
|
+
style: [styles.composer, showAttach && styles.composerWithAttach, composerFocused && styles.composerFocused, composerFocused && {
|
|
301
432
|
borderColor: withAlpha(color, 0.55)
|
|
302
433
|
}],
|
|
303
|
-
children: [/*#__PURE__*/_jsx(
|
|
434
|
+
children: [showAttach ? /*#__PURE__*/_jsx(Pressable, {
|
|
435
|
+
onPress: handleAttachPress,
|
|
436
|
+
disabled: sending || picking,
|
|
437
|
+
accessibilityRole: "button",
|
|
438
|
+
accessibilityLabel: labels.attach.button,
|
|
439
|
+
accessibilityState: {
|
|
440
|
+
disabled: sending || picking
|
|
441
|
+
}
|
|
442
|
+
// 36pt circle + 4pt on every side = a 44pt target.
|
|
443
|
+
,
|
|
444
|
+
hitSlop: 4,
|
|
445
|
+
style: ({
|
|
446
|
+
pressed
|
|
447
|
+
}) => [styles.attachButton, (pressed || sending || picking) && globalStyles.pressed],
|
|
448
|
+
children: /*#__PURE__*/_jsx(PaperclipIcon, {
|
|
449
|
+
size: 20,
|
|
450
|
+
color: palette.textMuted
|
|
451
|
+
})
|
|
452
|
+
}) : null, /*#__PURE__*/_jsx(TextInput, {
|
|
304
453
|
value: input,
|
|
305
454
|
onChangeText: handleChangeText,
|
|
306
455
|
onFocus: () => setComposerFocused(true),
|
|
@@ -328,7 +477,21 @@ export function ChatView({
|
|
|
328
477
|
color: canSend ? contrastingTextColor(color) : palette.textSubtle
|
|
329
478
|
})
|
|
330
479
|
})]
|
|
331
|
-
})
|
|
480
|
+
})]
|
|
481
|
+
}), showAttach && Platform.OS !== 'ios' ? /*#__PURE__*/_jsx(AttachmentMenu, {
|
|
482
|
+
visible: menuOpen,
|
|
483
|
+
options: menuOptions,
|
|
484
|
+
cancelLabel: labels.attach.cancel,
|
|
485
|
+
onSelect: handleMenuSelect,
|
|
486
|
+
onClose: () => setMenuOpen(false)
|
|
487
|
+
}) : null, /*#__PURE__*/_jsx(ImageViewer, {
|
|
488
|
+
image: viewerImage,
|
|
489
|
+
labels: labels.viewer,
|
|
490
|
+
onClose: () => setViewerImage(null),
|
|
491
|
+
onOpenExternally: url => {
|
|
492
|
+
setViewerImage(null);
|
|
493
|
+
void openExternalUrl(url);
|
|
494
|
+
}
|
|
332
495
|
})]
|
|
333
496
|
});
|
|
334
497
|
}
|
|
@@ -339,7 +502,8 @@ function MessageBubble({
|
|
|
339
502
|
senderLabel,
|
|
340
503
|
timeLabel,
|
|
341
504
|
onSubmitProcedureForm,
|
|
342
|
-
onAgentUiAction
|
|
505
|
+
onAgentUiAction,
|
|
506
|
+
onOpenImage
|
|
343
507
|
}) {
|
|
344
508
|
if (message.messageType === 'system') {
|
|
345
509
|
const text = message.bodyPlain || stripHtml(message.body || '') || labels.conversationUpdated;
|
|
@@ -379,7 +543,8 @@ function MessageBubble({
|
|
|
379
543
|
style: [styles.attachments, hasBody && styles.attachmentsSpaced],
|
|
380
544
|
children: message.attachments.map(attachment => /*#__PURE__*/_jsx(AttachmentPreview, {
|
|
381
545
|
attachment: attachment,
|
|
382
|
-
isVisitor: isVisitor
|
|
546
|
+
isVisitor: isVisitor,
|
|
547
|
+
onOpenImage: onOpenImage
|
|
383
548
|
}, attachment.id))
|
|
384
549
|
}) : null, message.status === 'failed' ? /*#__PURE__*/_jsx(Text, {
|
|
385
550
|
style: [styles.failedText, {
|
|
@@ -407,14 +572,24 @@ function MessageBubble({
|
|
|
407
572
|
}
|
|
408
573
|
function AttachmentPreview({
|
|
409
574
|
attachment,
|
|
410
|
-
isVisitor
|
|
575
|
+
isVisitor,
|
|
576
|
+
onOpenImage
|
|
411
577
|
}) {
|
|
412
578
|
// `url` is empty when the server sent a non-http(s) URL (see resolveUrl).
|
|
413
|
-
|
|
414
|
-
|
|
579
|
+
// An unsent attachment shows its local file (or data URL): it renders, but
|
|
580
|
+
// there is nothing to open until the server copy arrives.
|
|
581
|
+
const [imageFailed, setImageFailed] = useState(false);
|
|
582
|
+
const isImage = isImageAttachment(attachment.contentType) && !!attachment.url && !imageFailed;
|
|
583
|
+
const openable = isHttpUrl(attachment.url);
|
|
584
|
+
// Images open in the in-app viewer; other files (PDF, sheets) need the OS.
|
|
585
|
+
const open = !openable ? undefined : isImage ? () => onOpenImage({
|
|
586
|
+
url: attachment.url,
|
|
587
|
+
name: attachment.name
|
|
588
|
+
}) : () => void openExternalUrl(attachment.url);
|
|
415
589
|
if (isImage) {
|
|
416
590
|
return /*#__PURE__*/_jsx(Pressable, {
|
|
417
591
|
onPress: open,
|
|
592
|
+
disabled: !openable,
|
|
418
593
|
accessibilityRole: "imagebutton",
|
|
419
594
|
accessibilityLabel: attachment.name,
|
|
420
595
|
children: /*#__PURE__*/_jsx(Image, {
|
|
@@ -424,11 +599,16 @@ function AttachmentPreview({
|
|
|
424
599
|
style: styles.attachmentImage,
|
|
425
600
|
resizeMode: "cover",
|
|
426
601
|
accessibilityIgnoresInvertColors: true
|
|
602
|
+
// An image that can't load (expired link, deleted file) falls back to
|
|
603
|
+
// the file row below instead of an empty box.
|
|
604
|
+
,
|
|
605
|
+
onError: () => setImageFailed(true)
|
|
427
606
|
})
|
|
428
607
|
});
|
|
429
608
|
}
|
|
430
609
|
return /*#__PURE__*/_jsx(Pressable, {
|
|
431
610
|
onPress: open,
|
|
611
|
+
disabled: !openable,
|
|
432
612
|
accessibilityRole: "button",
|
|
433
613
|
style: [styles.attachmentFile, {
|
|
434
614
|
borderColor: isVisitor ? withAlpha(palette.white, 0.35) : palette.border
|