@repzo/widget-react-native 1.1.0 → 1.2.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.
Files changed (107) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/README.md +92 -3
  3. package/lib/commonjs/api/client.js +35 -0
  4. package/lib/commonjs/api/client.js.map +1 -1
  5. package/lib/commonjs/i18n.js +14 -0
  6. package/lib/commonjs/i18n.js.map +1 -1
  7. package/lib/commonjs/index.js +2 -1
  8. package/lib/commonjs/index.js.map +1 -1
  9. package/lib/commonjs/locales.js +13 -0
  10. package/lib/commonjs/locales.js.map +1 -1
  11. package/lib/commonjs/push/push-notifications.js +65 -8
  12. package/lib/commonjs/push/push-notifications.js.map +1 -1
  13. package/lib/commonjs/state/widget-store.js +451 -7
  14. package/lib/commonjs/state/widget-store.js.map +1 -1
  15. package/lib/commonjs/types.js +15 -0
  16. package/lib/commonjs/types.js.map +1 -1
  17. package/lib/commonjs/ui/WidgetProvider.js +64 -2
  18. package/lib/commonjs/ui/WidgetProvider.js.map +1 -1
  19. package/lib/commonjs/ui/components/Post.js +1241 -0
  20. package/lib/commonjs/ui/components/Post.js.map +1 -0
  21. package/lib/commonjs/ui/utils/html.js +1 -0
  22. package/lib/commonjs/ui/utils/html.js.map +1 -1
  23. package/lib/commonjs/ui/utils/post-html.js +276 -0
  24. package/lib/commonjs/ui/utils/post-html.js.map +1 -0
  25. package/lib/commonjs/ui/utils/safe-url.js +16 -0
  26. package/lib/commonjs/ui/utils/safe-url.js.map +1 -1
  27. package/lib/module/api/client.js +35 -0
  28. package/lib/module/api/client.js.map +1 -1
  29. package/lib/module/i18n.js +14 -0
  30. package/lib/module/i18n.js.map +1 -1
  31. package/lib/module/index.js +2 -1
  32. package/lib/module/index.js.map +1 -1
  33. package/lib/module/locales.js +13 -0
  34. package/lib/module/locales.js.map +1 -1
  35. package/lib/module/push/push-notifications.js +65 -8
  36. package/lib/module/push/push-notifications.js.map +1 -1
  37. package/lib/module/state/widget-store.js +450 -6
  38. package/lib/module/state/widget-store.js.map +1 -1
  39. package/lib/module/types.js +15 -0
  40. package/lib/module/types.js.map +1 -1
  41. package/lib/module/ui/WidgetProvider.js +65 -3
  42. package/lib/module/ui/WidgetProvider.js.map +1 -1
  43. package/lib/module/ui/components/Post.js +1236 -0
  44. package/lib/module/ui/components/Post.js.map +1 -0
  45. package/lib/module/ui/utils/html.js +1 -1
  46. package/lib/module/ui/utils/html.js.map +1 -1
  47. package/lib/module/ui/utils/post-html.js +271 -0
  48. package/lib/module/ui/utils/post-html.js.map +1 -0
  49. package/lib/module/ui/utils/safe-url.js +15 -0
  50. package/lib/module/ui/utils/safe-url.js.map +1 -1
  51. package/lib/typescript/commonjs/api/client.d.ts +21 -1
  52. package/lib/typescript/commonjs/api/client.d.ts.map +1 -1
  53. package/lib/typescript/commonjs/i18n.d.ts +13 -0
  54. package/lib/typescript/commonjs/i18n.d.ts.map +1 -1
  55. package/lib/typescript/commonjs/index.d.ts +14 -1
  56. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  57. package/lib/typescript/commonjs/locales.d.ts.map +1 -1
  58. package/lib/typescript/commonjs/push/push-notifications.d.ts +26 -2
  59. package/lib/typescript/commonjs/push/push-notifications.d.ts.map +1 -1
  60. package/lib/typescript/commonjs/state/widget-store.d.ts +84 -1
  61. package/lib/typescript/commonjs/state/widget-store.d.ts.map +1 -1
  62. package/lib/typescript/commonjs/types.d.ts +77 -0
  63. package/lib/typescript/commonjs/types.d.ts.map +1 -1
  64. package/lib/typescript/commonjs/ui/WidgetProvider.d.ts.map +1 -1
  65. package/lib/typescript/commonjs/ui/components/Post.d.ts +69 -0
  66. package/lib/typescript/commonjs/ui/components/Post.d.ts.map +1 -0
  67. package/lib/typescript/commonjs/ui/utils/html.d.ts +1 -0
  68. package/lib/typescript/commonjs/ui/utils/html.d.ts.map +1 -1
  69. package/lib/typescript/commonjs/ui/utils/post-html.d.ts +54 -0
  70. package/lib/typescript/commonjs/ui/utils/post-html.d.ts.map +1 -0
  71. package/lib/typescript/commonjs/ui/utils/safe-url.d.ts +6 -0
  72. package/lib/typescript/commonjs/ui/utils/safe-url.d.ts.map +1 -1
  73. package/lib/typescript/module/api/client.d.ts +21 -1
  74. package/lib/typescript/module/api/client.d.ts.map +1 -1
  75. package/lib/typescript/module/i18n.d.ts +13 -0
  76. package/lib/typescript/module/i18n.d.ts.map +1 -1
  77. package/lib/typescript/module/index.d.ts +14 -1
  78. package/lib/typescript/module/index.d.ts.map +1 -1
  79. package/lib/typescript/module/locales.d.ts.map +1 -1
  80. package/lib/typescript/module/push/push-notifications.d.ts +26 -2
  81. package/lib/typescript/module/push/push-notifications.d.ts.map +1 -1
  82. package/lib/typescript/module/state/widget-store.d.ts +84 -1
  83. package/lib/typescript/module/state/widget-store.d.ts.map +1 -1
  84. package/lib/typescript/module/types.d.ts +77 -0
  85. package/lib/typescript/module/types.d.ts.map +1 -1
  86. package/lib/typescript/module/ui/WidgetProvider.d.ts.map +1 -1
  87. package/lib/typescript/module/ui/components/Post.d.ts +69 -0
  88. package/lib/typescript/module/ui/components/Post.d.ts.map +1 -0
  89. package/lib/typescript/module/ui/utils/html.d.ts +1 -0
  90. package/lib/typescript/module/ui/utils/html.d.ts.map +1 -1
  91. package/lib/typescript/module/ui/utils/post-html.d.ts +54 -0
  92. package/lib/typescript/module/ui/utils/post-html.d.ts.map +1 -0
  93. package/lib/typescript/module/ui/utils/safe-url.d.ts +6 -0
  94. package/lib/typescript/module/ui/utils/safe-url.d.ts.map +1 -1
  95. package/package.json +1 -1
  96. package/src/api/client.ts +32 -0
  97. package/src/i18n.ts +14 -0
  98. package/src/index.tsx +17 -0
  99. package/src/locales.ts +13 -0
  100. package/src/push/push-notifications.ts +74 -8
  101. package/src/state/widget-store.ts +429 -6
  102. package/src/types.ts +77 -0
  103. package/src/ui/WidgetProvider.tsx +78 -2
  104. package/src/ui/components/Post.tsx +943 -0
  105. package/src/ui/utils/html.ts +1 -1
  106. package/src/ui/utils/post-html.ts +256 -0
  107. package/src/ui/utils/safe-url.ts +15 -0
@@ -0,0 +1,1241 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.PostBody = PostBody;
7
+ exports.PostOverlay = PostOverlay;
8
+ var _react = require("react");
9
+ var _reactNative = require("react-native");
10
+ var _i18n = require("../../i18n.js");
11
+ var _useKeyboardOverlap = require("../hooks/use-keyboard-overlap.js");
12
+ var _useReduceMotion = require("../hooks/use-reduce-motion.js");
13
+ var _theme = require("../styles/theme.js");
14
+ var _safeUrl = require("../utils/safe-url.js");
15
+ var _postHtml = require("../utils/post-html.js");
16
+ var _rtl = require("../utils/rtl.js");
17
+ var _Avatar = require("./Avatar.js");
18
+ var _icons = require("./icons.js");
19
+ var _jsxRuntime = require("react/jsx-runtime");
20
+ /**
21
+ * A Send post over the host app — the mobile side of Send → Post.
22
+ *
23
+ * Sent as a badge or a snippet, it waits at the bottom of the screen (a pill
24
+ * or a card); tapping opens it in full. Sent as "full" it opens right away. A
25
+ * small post opens as a sheet over the app, a large one as a full screen.
26
+ * The full post has the body (paragraphs, lists, code, a button, social
27
+ * links), what the visitor already replied, and a reply box or reactions.
28
+ * Replies stay under the post and go to the inbox, where the team answers.
29
+ * A reaction answers it: the emoji pops, it says thanks, and goes.
30
+ *
31
+ * "Not now" — the ⌄ button, swiping the top down, tapping outside a sheet, or
32
+ * Android's back — minimizes it back to its pill (or snippet card), where it
33
+ * stays, on later launches too. × closes it for good (the next post loads).
34
+ */
35
+
36
+ function PostOverlay(props) {
37
+ const {
38
+ post,
39
+ expanded,
40
+ onShown
41
+ } = props;
42
+ // Reported once per post per session (the store dedupes).
43
+ (0, _react.useEffect)(() => {
44
+ onShown(post.id);
45
+ }, [onShown, post.id]);
46
+ if (expanded) return /*#__PURE__*/(0, _jsxRuntime.jsx)(PostFull, {
47
+ ...props
48
+ });
49
+ // A snippet waits as its card; a badge — or a full post put away for later — as the pill.
50
+ return post.sentAs === 'snippet' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(PostCard, {
51
+ ...props
52
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(PostBadge, {
53
+ ...props
54
+ });
55
+ }
56
+ function senderLine(post, labels) {
57
+ if (post.sender && post.workspaceName) {
58
+ return (0, _i18n.formatLabel)(labels.from, {
59
+ name: post.sender.name,
60
+ workspace: post.workspaceName
61
+ });
62
+ }
63
+ return post.sender?.name || post.workspaceName || labels.newMessage;
64
+ }
65
+
66
+ /**
67
+ * Slides a card in from below and tells screen-reader users it arrived — the
68
+ * way the in-app notification card does. Holds still under Reduce Motion.
69
+ */
70
+ function useArrival(accessibilityLabel) {
71
+ const reduceMotion = (0, _useReduceMotion.useReduceMotion)();
72
+ // Read as "hold still" until the setting is known (see useReduceMotion).
73
+ const animate = reduceMotion === false;
74
+ const entry = (0, _react.useRef)(new _reactNative.Animated.Value(animate ? 0 : 1)).current;
75
+
76
+ // Runs per mount: a new post, or the card coming back after the messenger.
77
+ (0, _react.useEffect)(() => {
78
+ if (animate) {
79
+ _reactNative.Animated.spring(entry, {
80
+ toValue: 1,
81
+ useNativeDriver: true,
82
+ speed: 16,
83
+ bounciness: 4
84
+ }).start();
85
+ }
86
+ _reactNative.AccessibilityInfo.announceForAccessibility(accessibilityLabel);
87
+ // eslint-disable-next-line react-hooks/exhaustive-deps
88
+ }, []);
89
+ return {
90
+ opacity: entry,
91
+ transform: [{
92
+ translateY: entry.interpolate({
93
+ inputRange: [0, 1],
94
+ outputRange: [_theme.spacing.lg, 0]
95
+ })
96
+ }]
97
+ };
98
+ }
99
+ function CloseButton({
100
+ label,
101
+ onPress,
102
+ color = _theme.palette.textMuted
103
+ }) {
104
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
105
+ onPress: onPress,
106
+ accessibilityRole: "button",
107
+ accessibilityLabel: label,
108
+ hitSlop: 10,
109
+ style: ({
110
+ pressed
111
+ }) => [styles.close, pressed && styles.pressed],
112
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.CloseIcon, {
113
+ size: 14,
114
+ color: color
115
+ })
116
+ });
117
+ }
118
+ function MinimizeButton({
119
+ label,
120
+ onPress
121
+ }) {
122
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
123
+ onPress: onPress,
124
+ accessibilityRole: "button",
125
+ accessibilityLabel: label,
126
+ hitSlop: 10,
127
+ style: ({
128
+ pressed
129
+ }) => [styles.close, pressed && styles.pressed],
130
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.ChevronIcon, {
131
+ size: 16,
132
+ direction: "down",
133
+ color: _theme.palette.textMuted
134
+ })
135
+ });
136
+ }
137
+
138
+ /** How far the top must be dragged down to minimize — or a quick flick. */
139
+ const MINIMIZE_DISTANCE = 120;
140
+ const MINIMIZE_VELOCITY = 0.8;
141
+
142
+ /**
143
+ * Dragging the post's top edge down minimizes it, the way a sheet slides away.
144
+ * Pure JS (PanResponder), like the in-app notification's swipe.
145
+ */
146
+ function useSwipeToMinimize(onMinimize) {
147
+ const {
148
+ height
149
+ } = (0, _reactNative.useWindowDimensions)();
150
+ const reduceMotion = (0, _useReduceMotion.useReduceMotion)();
151
+ const drag = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
152
+ // Read through a ref: the provider re-renders on every store change, and a
153
+ // PanResponder rebuilt mid-drag would restart the gesture.
154
+ const minimizeRef = (0, _react.useRef)(onMinimize);
155
+ minimizeRef.current = onMinimize;
156
+ const handlers = (0, _react.useMemo)(() => {
157
+ const settle = () => _reactNative.Animated.spring(drag, {
158
+ toValue: 0,
159
+ useNativeDriver: true,
160
+ bounciness: 4
161
+ }).start();
162
+ return _reactNative.PanResponder.create({
163
+ onMoveShouldSetPanResponder: (_, gesture) => gesture.dy > 6 && gesture.dy > Math.abs(gesture.dx) * 1.5,
164
+ onPanResponderMove: (_, gesture) => drag.setValue(Math.max(0, gesture.dy)),
165
+ onPanResponderRelease: (_, gesture) => {
166
+ if (gesture.dy < MINIMIZE_DISTANCE && gesture.vy < MINIMIZE_VELOCITY) {
167
+ settle();
168
+ return;
169
+ }
170
+ _reactNative.Animated.timing(drag, {
171
+ toValue: height,
172
+ duration: reduceMotion === false ? 200 : 0,
173
+ useNativeDriver: true
174
+ }).start(() => minimizeRef.current());
175
+ },
176
+ onPanResponderTerminate: settle
177
+ });
178
+ }, [drag, height, reduceMotion]);
179
+ return {
180
+ panHandlers: handlers.panHandlers,
181
+ // Never above where it started.
182
+ translateY: drag.interpolate({
183
+ inputRange: [0, 1],
184
+ outputRange: [0, 1],
185
+ extrapolateLeft: 'clamp'
186
+ }),
187
+ // The dimmed backdrop lightens as the sheet goes.
188
+ backdropOpacity: drag.interpolate({
189
+ inputRange: [0, 400],
190
+ outputRange: [1, 0],
191
+ extrapolate: 'clamp'
192
+ })
193
+ };
194
+ }
195
+
196
+ /** Snippet: a card at the bottom with who it's from and the first words. */
197
+ function PostCard({
198
+ post,
199
+ color,
200
+ insets,
201
+ labels,
202
+ onExpand,
203
+ onInteract
204
+ }) {
205
+ const accessibilityLabel = [senderLine(post, labels), post.snippet].filter(Boolean).join(': ');
206
+ const arrival = useArrival(accessibilityLabel);
207
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
208
+ pointerEvents: "box-none",
209
+ style: [styles.bottomWrap, {
210
+ bottom: insets.bottom + _theme.spacing.md
211
+ }],
212
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Animated.View, {
213
+ style: [styles.card, arrival],
214
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
215
+ onPress: () => onExpand(post.id),
216
+ accessibilityRole: "button",
217
+ accessibilityLabel: accessibilityLabel,
218
+ accessibilityHint: labels.openHint,
219
+ style: ({
220
+ pressed
221
+ }) => [styles.cardBody, pressed && styles.pressed],
222
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
223
+ style: styles.cardHead,
224
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Avatar.Avatar, {
225
+ avatar: post.sender?.avatarUrl,
226
+ name: post.sender?.name ?? post.workspaceName,
227
+ color: color,
228
+ size: 28
229
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
230
+ numberOfLines: 1,
231
+ style: styles.senderText,
232
+ children: senderLine(post, labels)
233
+ })]
234
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
235
+ numberOfLines: 3,
236
+ style: styles.snippet,
237
+ children: post.snippet || labels.newMessage
238
+ })]
239
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
240
+ style: styles.cardClose,
241
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(CloseButton, {
242
+ label: labels.close,
243
+ onPress: () => void onInteract(post.id, 'dismiss')
244
+ })
245
+ })]
246
+ })
247
+ });
248
+ }
249
+
250
+ /** Badge: a small pill — the sender and a dot — that opens the post. */
251
+ function PostBadge({
252
+ post,
253
+ color,
254
+ insets,
255
+ labels,
256
+ onExpand,
257
+ onInteract
258
+ }) {
259
+ const accessibilityLabel = [post.sender?.name || labels.newMessage, post.snippet].filter(Boolean).join(': ');
260
+ const arrival = useArrival(accessibilityLabel);
261
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Animated.View, {
262
+ pointerEvents: "box-none",
263
+ style: [styles.badgeWrap, {
264
+ bottom: insets.bottom + _theme.spacing.md
265
+ }, arrival],
266
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
267
+ onPress: () => onExpand(post.id),
268
+ accessibilityRole: "button",
269
+ accessibilityLabel: accessibilityLabel,
270
+ accessibilityHint: labels.openHint,
271
+ style: ({
272
+ pressed
273
+ }) => [styles.pill, pressed && styles.pressed],
274
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Avatar.Avatar, {
275
+ avatar: post.sender?.avatarUrl,
276
+ name: post.sender?.name ?? post.workspaceName,
277
+ color: color,
278
+ size: 28
279
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
280
+ numberOfLines: 1,
281
+ style: styles.pillText,
282
+ children: post.sender?.name ?? labels.newMessage
283
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
284
+ style: styles.dot
285
+ })]
286
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
287
+ style: styles.pillClose,
288
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(CloseButton, {
289
+ label: labels.close,
290
+ onPress: () => void onInteract(post.id, 'dismiss')
291
+ })
292
+ })]
293
+ });
294
+ }
295
+
296
+ /** The post in full: a sheet (small) or a full screen (large). */
297
+ /** How long iOS gets to present the post before it's treated as blocked (another modal was up). */
298
+ const PRESENT_TIMEOUT_MS = 2500;
299
+ function PostFull({
300
+ post,
301
+ color,
302
+ insets,
303
+ labels,
304
+ onMinimize,
305
+ onCollapse,
306
+ onOpenConversation,
307
+ onInteract
308
+ }) {
309
+ const large = post.format === 'large';
310
+ // KeyboardAvoidingView misplaces the composer inside a modal (see the hook);
311
+ // this is the same lift the messenger's panel uses.
312
+ const keyboard = (0, _useKeyboardOverlap.useKeyboardOverlap)(true, insets.bottom);
313
+ const dismiss = () => void onInteract(post.id, 'dismiss');
314
+ const minimize = () => onMinimize(post.id);
315
+ const swipe = useSwipeToMinimize(minimize);
316
+ const click = href => {
317
+ void onInteract(post.id, 'click', href);
318
+ void (0, _safeUrl.openExternalUrl)(href);
319
+ // A link the app routes in-app would otherwise open behind the post.
320
+ onCollapse(post.id);
321
+ };
322
+
323
+ // iOS presents one modal at a time: if the app had its own up (a Modal, an
324
+ // alert, a modal screen), this one never shows and onShow never fires. Fall
325
+ // back to the pill instead of leaving an invisible post.
326
+ const shown = (0, _react.useRef)(false);
327
+ const collapseRef = (0, _react.useRef)(onCollapse);
328
+ collapseRef.current = onCollapse;
329
+ (0, _react.useEffect)(() => {
330
+ if (_reactNative.Platform.OS !== 'ios') return undefined;
331
+ const timer = setTimeout(() => {
332
+ if (!shown.current) collapseRef.current(post.id);
333
+ }, PRESENT_TIMEOUT_MS);
334
+ return () => clearTimeout(timer);
335
+ }, [post.id]);
336
+ const content = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
337
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
338
+ ...swipe.panHandlers,
339
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
340
+ style: styles.grabber
341
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
342
+ style: styles.fullHead,
343
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Avatar.Avatar, {
344
+ avatar: post.sender?.avatarUrl,
345
+ name: post.sender?.name ?? post.workspaceName,
346
+ color: color,
347
+ size: 32
348
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
349
+ numberOfLines: 1,
350
+ style: [styles.senderText, styles.fullSender],
351
+ children: senderLine(post, labels)
352
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(MinimizeButton, {
353
+ label: labels.minimize,
354
+ onPress: minimize
355
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(CloseButton, {
356
+ label: labels.close,
357
+ onPress: dismiss
358
+ })]
359
+ })]
360
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
361
+ style: large ? styles.flex : styles.fullScroll,
362
+ contentContainerStyle: styles.fullBody,
363
+ keyboardShouldPersistTaps: "handled",
364
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(PostBody, {
365
+ html: post.bodyHtml,
366
+ color: color,
367
+ onLink: click
368
+ }), post.thread.length > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
369
+ style: styles.replies,
370
+ accessibilityLabel: labels.conversation,
371
+ children: [post.thread.map((message, index) => {
372
+ const team = message.from === 'team';
373
+ const previous = post.thread[index - 1];
374
+ // The teammate's name over the first of their answers in a row.
375
+ const showName = team && !!message.senderName && !(previous?.from === 'team' && previous.senderName === message.senderName);
376
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
377
+ style: team ? styles.answerWrap : styles.replyWrap,
378
+ children: [showName ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
379
+ style: styles.replyMeta,
380
+ children: message.senderName
381
+ }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
382
+ style: team ? styles.answer : [styles.reply, {
383
+ backgroundColor: color
384
+ }],
385
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
386
+ style: [styles.replyText, {
387
+ color: team ? _theme.palette.text : (0, _theme.contrastingTextColor)(color)
388
+ }],
389
+ children: message.text
390
+ })
391
+ }), team ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
392
+ style: styles.replyMeta,
393
+ children: labels.sent
394
+ })]
395
+ }, `${message.at}-${index}`);
396
+ }), post.conversationId ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
397
+ onPress: () => onOpenConversation(post.id, post.conversationId),
398
+ accessibilityRole: "button",
399
+ accessibilityLabel: labels.continueChat,
400
+ hitSlop: 8,
401
+ style: ({
402
+ pressed
403
+ }) => [styles.continue, pressed && styles.pressed],
404
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
405
+ style: [styles.continueText, {
406
+ color: (0, _theme.contrastingTextColor)(color) === _theme.palette.ink ? _theme.palette.text : color
407
+ }],
408
+ children: labels.continueChat
409
+ })
410
+ }) : null]
411
+ }) : null]
412
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(PostFooter, {
413
+ post: post,
414
+ color: color,
415
+ labels: labels,
416
+ onInteract: onInteract
417
+ })]
418
+ });
419
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
420
+ visible: true
421
+ // Transparent either way, so swiping a large post down shows the app under it.
422
+ ,
423
+ transparent: true,
424
+ animationType: large ? 'slide' : 'fade',
425
+ presentationStyle: "overFullScreen",
426
+ statusBarTranslucent: true,
427
+ onShow: () => {
428
+ shown.current = true;
429
+ }
430
+ // Back is "not now", not ×.
431
+ ,
432
+ onRequestClose: minimize,
433
+ children: large ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
434
+ accessibilityViewIsModal: true
435
+ // VoiceOver's two-finger scrub: "not now".
436
+ ,
437
+ onAccessibilityEscape: minimize,
438
+ style: [styles.flex, styles.page, {
439
+ paddingTop: insets.top,
440
+ paddingBottom: insets.bottom,
441
+ transform: [{
442
+ translateY: swipe.translateY
443
+ }]
444
+ }],
445
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
446
+ onLayout: keyboard.onLayout,
447
+ style: [styles.flex, {
448
+ paddingBottom: keyboard.overlap
449
+ }],
450
+ children: content
451
+ })
452
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
453
+ style: styles.flex,
454
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
455
+ style: [_reactNative.StyleSheet.absoluteFill, styles.backdropTint, {
456
+ opacity: swipe.backdropOpacity
457
+ }]
458
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
459
+ style: [styles.backdrop, {
460
+ paddingTop: insets.top + _theme.spacing.xl,
461
+ paddingBottom: insets.bottom + _theme.spacing.md
462
+ }],
463
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
464
+ style: _reactNative.StyleSheet.absoluteFill,
465
+ onPress: minimize,
466
+ accessibilityRole: "button",
467
+ accessibilityLabel: labels.minimize
468
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
469
+ onLayout: keyboard.onLayout,
470
+ pointerEvents: "box-none",
471
+ style: [styles.sheetArea, {
472
+ paddingBottom: keyboard.overlap
473
+ }],
474
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
475
+ accessibilityViewIsModal: true,
476
+ onAccessibilityEscape: minimize,
477
+ style: [styles.sheet, {
478
+ transform: [{
479
+ translateY: swipe.translateY
480
+ }]
481
+ }],
482
+ children: content
483
+ })
484
+ })]
485
+ })]
486
+ })
487
+ });
488
+ }
489
+ function PostFooter({
490
+ post,
491
+ color,
492
+ labels,
493
+ onInteract
494
+ }) {
495
+ const [text, setText] = (0, _react.useState)('');
496
+ const [sending, setSending] = (0, _react.useState)(false);
497
+ const [failed, setFailed] = (0, _react.useState)(false);
498
+ const [focused, setFocused] = (0, _react.useState)(false);
499
+ if (post.replyType === 'reaction' && post.reactions.length > 0) {
500
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(PostReactions, {
501
+ post: post,
502
+ color: color,
503
+ labels: labels,
504
+ onInteract: onInteract
505
+ });
506
+ }
507
+ if (post.replyType !== 'text') return null;
508
+ const value = text.trim();
509
+ const canSend = !!value && !sending;
510
+ const send = async () => {
511
+ if (!canSend) return;
512
+ setSending(true);
513
+ setFailed(false);
514
+ try {
515
+ await onInteract(post.id, 'reply', value);
516
+ setText('');
517
+ } catch {
518
+ setFailed(true);
519
+ } finally {
520
+ setSending(false);
521
+ }
522
+ };
523
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
524
+ style: styles.footer,
525
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
526
+ style: [styles.composer, focused && styles.composerFocused],
527
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
528
+ value: text,
529
+ onChangeText: setText,
530
+ onFocus: () => setFocused(true),
531
+ onBlur: () => setFocused(false),
532
+ placeholder: labels.replyPlaceholder,
533
+ placeholderTextColor: _theme.palette.textSubtle,
534
+ accessibilityLabel: labels.replyPlaceholder,
535
+ maxLength: 2000,
536
+ multiline: true,
537
+ editable: !sending,
538
+ style: styles.input
539
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
540
+ onPress: send,
541
+ disabled: !canSend,
542
+ accessibilityRole: "button",
543
+ accessibilityLabel: labels.send,
544
+ accessibilityState: {
545
+ disabled: !canSend
546
+ }
547
+ // 36pt circle + 4pt on every side = a 44pt target.
548
+ ,
549
+ hitSlop: 4,
550
+ style: ({
551
+ pressed
552
+ }) => [styles.send, canSend ? {
553
+ backgroundColor: color
554
+ } : styles.sendDisabled, pressed && canSend && styles.pressed],
555
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.ArrowUpIcon, {
556
+ size: 18,
557
+ color: canSend ? (0, _theme.contrastingTextColor)(color) : _theme.palette.textSubtle
558
+ })
559
+ })]
560
+ }), failed ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
561
+ accessibilityRole: "alert",
562
+ style: styles.error,
563
+ children: labels.replyFailed
564
+ }) : null]
565
+ });
566
+ }
567
+
568
+ /**
569
+ * The reaction row. A tap pops the emoji and throws a few copies of it (the
570
+ * shared reaction burst), then thanks them; the store takes the post away a
571
+ * moment later. Another tap in that moment changes their pick.
572
+ */
573
+ function PostReactions({
574
+ post,
575
+ color,
576
+ labels,
577
+ onInteract
578
+ }) {
579
+ const [thanked, setThanked] = (0, _react.useState)(false);
580
+ const react = emoji => {
581
+ if (!thanked) {
582
+ setThanked(true);
583
+ _reactNative.AccessibilityInfo.announceForAccessibility(labels.thanks);
584
+ }
585
+ void onInteract(post.id, 'reaction', emoji);
586
+ };
587
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
588
+ style: styles.footer,
589
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
590
+ style: styles.reactions,
591
+ accessibilityRole: "radiogroup",
592
+ accessibilityLabel: labels.reactions,
593
+ children: post.reactions.map(emoji => /*#__PURE__*/(0, _jsxRuntime.jsx)(ReactionChip, {
594
+ emoji: emoji,
595
+ color: color,
596
+ picked: post.reacted === emoji,
597
+ dimmed: !!post.reacted && post.reacted !== emoji,
598
+ onPress: () => react(emoji)
599
+ }, emoji))
600
+ }), thanked && post.reacted ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
601
+ accessibilityLiveRegion: "polite",
602
+ style: styles.thanks,
603
+ children: labels.thanks
604
+ }) : null]
605
+ });
606
+ }
607
+
608
+ /** Where the burst's copies fly: a fan above the chip. */
609
+ const SPARKS = [-70, -40, -12, 12, 40, 70].map((degrees, index) => {
610
+ const angle = (degrees - 90) * Math.PI / 180;
611
+ const distance = index % 2 ? 54 : 44;
612
+ return {
613
+ x: Math.cos(angle) * distance,
614
+ y: Math.sin(angle) * distance
615
+ };
616
+ });
617
+ function ReactionChip({
618
+ emoji,
619
+ color,
620
+ picked,
621
+ dimmed,
622
+ onPress
623
+ }) {
624
+ const reduceMotion = (0, _useReduceMotion.useReduceMotion)();
625
+ const scale = (0, _react.useRef)(new _reactNative.Animated.Value(1)).current;
626
+ const spark = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
627
+ const [bursting, setBursting] = (0, _react.useState)(false);
628
+ const press = () => {
629
+ onPress();
630
+ if (reduceMotion !== false) return;
631
+ scale.setValue(1);
632
+ _reactNative.Animated.sequence([_reactNative.Animated.timing(scale, {
633
+ toValue: 1.35,
634
+ duration: 120,
635
+ useNativeDriver: true
636
+ }), _reactNative.Animated.spring(scale, {
637
+ toValue: 1,
638
+ useNativeDriver: true,
639
+ speed: 14,
640
+ bounciness: 12
641
+ })]).start();
642
+ spark.setValue(0);
643
+ setBursting(true);
644
+ _reactNative.Animated.timing(spark, {
645
+ toValue: 1,
646
+ duration: 650,
647
+ useNativeDriver: true
648
+ }).start(({
649
+ finished
650
+ }) => {
651
+ if (finished) setBursting(false);
652
+ });
653
+ };
654
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
655
+ onPress: press,
656
+ accessibilityRole: "radio",
657
+ accessibilityState: {
658
+ checked: picked
659
+ },
660
+ accessibilityLabel: emoji,
661
+ style: ({
662
+ pressed
663
+ }) => [styles.reaction, picked && {
664
+ borderColor: color,
665
+ backgroundColor: (0, _theme.withAlpha)(color, 0.12)
666
+ }, dimmed ? styles.reactionDim : null, pressed && styles.pressed],
667
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.Text, {
668
+ style: [styles.reactionEmoji, {
669
+ transform: [{
670
+ scale
671
+ }]
672
+ }],
673
+ children: emoji
674
+ }), bursting ? SPARKS.map((to, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.Text, {
675
+ pointerEvents: "none",
676
+ importantForAccessibility: "no-hide-descendants",
677
+ accessibilityElementsHidden: true,
678
+ style: [styles.spark, {
679
+ opacity: spark.interpolate({
680
+ inputRange: [0, 0.6, 1],
681
+ outputRange: [1, 0.9, 0]
682
+ }),
683
+ transform: [{
684
+ translateX: spark.interpolate({
685
+ inputRange: [0, 1],
686
+ outputRange: [0, to.x]
687
+ })
688
+ }, {
689
+ translateY: spark.interpolate({
690
+ inputRange: [0, 1],
691
+ outputRange: [0, to.y]
692
+ })
693
+ }, {
694
+ scale: spark.interpolate({
695
+ inputRange: [0, 1],
696
+ outputRange: [0.6, 1.1]
697
+ })
698
+ }]
699
+ }],
700
+ children: emoji
701
+ }, index)) : null]
702
+ });
703
+ }
704
+
705
+ // ─── Body ──────────────────────────────────────────────────────────────────
706
+
707
+ const ALIGN_ITEMS = {
708
+ left: 'flex-start',
709
+ center: 'center',
710
+ right: 'flex-end'
711
+ };
712
+
713
+ /** Brand marks drawn as a colored circle and letters — the SDK ships no SVG renderer. */
714
+ const SOCIAL = {
715
+ facebook: {
716
+ label: 'Facebook',
717
+ glyph: 'f',
718
+ color: '#1877F2'
719
+ },
720
+ // A plain X: the math-letter 𝕏 is missing from some Android fonts.
721
+ x: {
722
+ label: 'X',
723
+ glyph: 'X',
724
+ color: '#000000'
725
+ },
726
+ linkedin: {
727
+ label: 'LinkedIn',
728
+ glyph: 'in',
729
+ color: '#0A66C2'
730
+ },
731
+ instagram: {
732
+ label: 'Instagram',
733
+ glyph: 'IG',
734
+ color: '#E4405F'
735
+ },
736
+ youtube: {
737
+ label: 'YouTube',
738
+ glyph: '▶',
739
+ color: '#FF0000'
740
+ }
741
+ };
742
+ function Runs({
743
+ runs,
744
+ color,
745
+ onLink
746
+ }) {
747
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
748
+ children: runs.map((run, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
749
+ onPress: run.href ? () => onLink(run.href) : undefined,
750
+ accessibilityRole: run.href ? 'link' : undefined,
751
+ style: [run.bold && styles.bold, run.italic && styles.italic, run.code && styles.inlineCode, run.href ? [styles.link, {
752
+ color: (0, _theme.contrastingTextColor)(color) === _theme.palette.ink ? _theme.palette.text : color
753
+ }] : null],
754
+ children: run.text
755
+ }, index))
756
+ });
757
+ }
758
+
759
+ /** "1." / "•" per item, numbering each nested list on its own. */
760
+ function listMarkers(levels) {
761
+ const counts = [];
762
+ return levels.map(({
763
+ depth,
764
+ ordered
765
+ }) => {
766
+ counts.length = depth + 1;
767
+ counts[depth] = (counts[depth] ?? 0) + 1;
768
+ if (ordered) return `${counts[depth]}.`;
769
+ return depth === 0 ? '•' : '◦';
770
+ });
771
+ }
772
+ function PostBody({
773
+ html,
774
+ color,
775
+ onLink
776
+ }) {
777
+ // The provider re-renders on every store change (typing, new messages).
778
+ const blocks = (0, _react.useMemo)(() => (0, _postHtml.parsePostHtml)(html), [html]);
779
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
780
+ style: styles.body,
781
+ children: blocks.map((block, index) => {
782
+ if (block.type === 'paragraph') {
783
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
784
+ style: styles.paragraph,
785
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(Runs, {
786
+ runs: block.runs,
787
+ color: color,
788
+ onLink: onLink
789
+ })
790
+ }, index);
791
+ }
792
+ if (block.type === 'list') {
793
+ const markers = listMarkers(block.levels);
794
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
795
+ style: styles.list,
796
+ children: block.items.map((item, itemIndex) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
797
+ style: [styles.listItem, {
798
+ paddingStart: (block.levels[itemIndex]?.depth ?? 0) * _theme.spacing.lg
799
+ }],
800
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
801
+ style: styles.marker,
802
+ children: markers[itemIndex]
803
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
804
+ style: [styles.paragraph, styles.listText],
805
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(Runs, {
806
+ runs: item,
807
+ color: color,
808
+ onLink: onLink
809
+ })
810
+ })]
811
+ }, itemIndex))
812
+ }, index);
813
+ }
814
+ if (block.type === 'code') {
815
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
816
+ style: styles.codeBlock,
817
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
818
+ style: styles.codeText,
819
+ children: block.text
820
+ })
821
+ }, index);
822
+ }
823
+ if (block.type === 'button') {
824
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
825
+ style: {
826
+ alignItems: ALIGN_ITEMS[block.align]
827
+ },
828
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
829
+ onPress: () => onLink(block.href),
830
+ accessibilityRole: "link",
831
+ style: ({
832
+ pressed
833
+ }) => [styles.button, {
834
+ backgroundColor: color
835
+ }, pressed && styles.pressed],
836
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
837
+ style: [styles.buttonText, {
838
+ color: (0, _theme.contrastingTextColor)(color)
839
+ }],
840
+ children: block.label
841
+ })
842
+ })
843
+ }, index);
844
+ }
845
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
846
+ style: [styles.social, {
847
+ justifyContent: ALIGN_ITEMS[block.align]
848
+ }],
849
+ children: block.links.map(link => {
850
+ const mark = SOCIAL[link.network];
851
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
852
+ onPress: () => onLink(link.href),
853
+ accessibilityRole: "link",
854
+ accessibilityLabel: mark.label,
855
+ hitSlop: 6,
856
+ style: ({
857
+ pressed
858
+ }) => [styles.socialMark, {
859
+ backgroundColor: mark.color
860
+ }, pressed && styles.pressed],
861
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
862
+ style: styles.socialGlyph,
863
+ children: mark.glyph
864
+ })
865
+ }, `${link.network}-${link.href}`);
866
+ })
867
+ }, index);
868
+ })
869
+ });
870
+ }
871
+ const CLOSE_SIZE = 32;
872
+ const LINE_HEIGHT = 1.45;
873
+ const styles = _reactNative.StyleSheet.create({
874
+ flex: {
875
+ flex: 1
876
+ },
877
+ pressed: {
878
+ opacity: 0.85
879
+ },
880
+ close: {
881
+ width: CLOSE_SIZE,
882
+ height: CLOSE_SIZE,
883
+ borderRadius: _theme.radius.sm,
884
+ alignItems: 'center',
885
+ justifyContent: 'center'
886
+ },
887
+ bottomWrap: {
888
+ position: 'absolute',
889
+ left: 0,
890
+ right: 0,
891
+ paddingHorizontal: _theme.spacing.lg,
892
+ alignItems: 'center',
893
+ zIndex: 1000,
894
+ elevation: 1000
895
+ },
896
+ card: {
897
+ width: '100%',
898
+ maxWidth: 480,
899
+ borderRadius: _theme.radius.xl,
900
+ backgroundColor: _theme.palette.white,
901
+ borderWidth: _reactNative.StyleSheet.hairlineWidth,
902
+ borderColor: _theme.palette.border,
903
+ ..._theme.shadows.floating
904
+ },
905
+ cardBody: {
906
+ paddingVertical: _theme.spacing.md,
907
+ paddingStart: _theme.spacing.lg,
908
+ // Room for the × in the corner.
909
+ paddingEnd: CLOSE_SIZE + _theme.spacing.lg,
910
+ gap: _theme.spacing.sm,
911
+ borderRadius: _theme.radius.xl
912
+ },
913
+ cardHead: {
914
+ flexDirection: 'row',
915
+ alignItems: 'center',
916
+ gap: _theme.spacing.sm
917
+ },
918
+ cardClose: {
919
+ position: 'absolute',
920
+ top: _theme.spacing.sm,
921
+ end: _theme.spacing.sm
922
+ },
923
+ senderText: {
924
+ flexShrink: 1,
925
+ color: _theme.palette.textMuted,
926
+ fontSize: _theme.fontSize.meta,
927
+ fontWeight: '600',
928
+ textAlign: _rtl.START_ALIGN
929
+ },
930
+ snippet: {
931
+ color: _theme.palette.text,
932
+ fontSize: _theme.fontSize.body,
933
+ lineHeight: _theme.fontSize.body * LINE_HEIGHT,
934
+ textAlign: _rtl.START_ALIGN
935
+ },
936
+ badgeWrap: {
937
+ position: 'absolute',
938
+ end: _theme.spacing.lg,
939
+ flexDirection: 'row',
940
+ alignItems: 'center',
941
+ gap: _theme.spacing.xs,
942
+ zIndex: 1000,
943
+ elevation: 1000
944
+ },
945
+ pill: {
946
+ flexDirection: 'row',
947
+ alignItems: 'center',
948
+ gap: _theme.spacing.sm,
949
+ minHeight: 44,
950
+ paddingVertical: _theme.spacing.xs,
951
+ paddingStart: _theme.spacing.xs,
952
+ paddingEnd: _theme.spacing.md,
953
+ borderRadius: _theme.radius.pill,
954
+ backgroundColor: _theme.palette.white,
955
+ borderWidth: _reactNative.StyleSheet.hairlineWidth,
956
+ borderColor: _theme.palette.border,
957
+ ..._theme.shadows.floating
958
+ },
959
+ pillText: {
960
+ maxWidth: 180,
961
+ color: _theme.palette.text,
962
+ fontSize: _theme.fontSize.meta,
963
+ fontWeight: '600'
964
+ },
965
+ dot: {
966
+ width: 8,
967
+ height: 8,
968
+ borderRadius: 4,
969
+ backgroundColor: _theme.palette.danger
970
+ },
971
+ pillClose: {
972
+ borderRadius: _theme.radius.pill,
973
+ backgroundColor: _theme.palette.white,
974
+ borderWidth: _reactNative.StyleSheet.hairlineWidth,
975
+ borderColor: _theme.palette.border,
976
+ ..._theme.shadows.card
977
+ },
978
+ backdrop: {
979
+ flex: 1,
980
+ paddingHorizontal: _theme.spacing.md
981
+ },
982
+ backdropTint: {
983
+ backgroundColor: 'rgba(15, 23, 42, 0.45)'
984
+ },
985
+ sheetArea: {
986
+ flex: 1,
987
+ justifyContent: 'flex-end'
988
+ },
989
+ sheet: {
990
+ maxHeight: '100%',
991
+ borderRadius: _theme.radius.xl,
992
+ backgroundColor: _theme.palette.white,
993
+ overflow: 'hidden',
994
+ ..._theme.shadows.floating
995
+ },
996
+ page: {
997
+ backgroundColor: _theme.palette.white
998
+ },
999
+ grabber: {
1000
+ alignSelf: 'center',
1001
+ width: 36,
1002
+ height: 5,
1003
+ borderRadius: 3,
1004
+ marginTop: _theme.spacing.sm,
1005
+ backgroundColor: _theme.palette.border
1006
+ },
1007
+ fullHead: {
1008
+ flexDirection: 'row',
1009
+ alignItems: 'center',
1010
+ gap: _theme.spacing.sm,
1011
+ paddingStart: _theme.spacing.lg,
1012
+ paddingEnd: _theme.spacing.sm,
1013
+ paddingTop: _theme.spacing.xs,
1014
+ paddingBottom: _theme.spacing.md
1015
+ },
1016
+ fullSender: {
1017
+ flex: 1
1018
+ },
1019
+ fullScroll: {
1020
+ flexGrow: 0,
1021
+ flexShrink: 1
1022
+ },
1023
+ fullBody: {
1024
+ paddingHorizontal: _theme.spacing.lg,
1025
+ paddingBottom: _theme.spacing.lg,
1026
+ gap: _theme.spacing.lg
1027
+ },
1028
+ body: {
1029
+ gap: _theme.spacing.md
1030
+ },
1031
+ paragraph: {
1032
+ color: _theme.palette.text,
1033
+ fontSize: _theme.fontSize.body,
1034
+ lineHeight: _theme.fontSize.body * LINE_HEIGHT,
1035
+ textAlign: _rtl.START_ALIGN
1036
+ },
1037
+ bold: {
1038
+ fontWeight: '700'
1039
+ },
1040
+ italic: {
1041
+ fontStyle: 'italic'
1042
+ },
1043
+ inlineCode: {
1044
+ fontFamily: _reactNative.Platform.select({
1045
+ ios: 'Menlo',
1046
+ default: 'monospace'
1047
+ }),
1048
+ fontSize: _theme.fontSize.meta,
1049
+ backgroundColor: _theme.palette.surfaceAlt
1050
+ },
1051
+ link: {
1052
+ textDecorationLine: 'underline',
1053
+ fontWeight: '600'
1054
+ },
1055
+ list: {
1056
+ gap: _theme.spacing.xs
1057
+ },
1058
+ listItem: {
1059
+ flexDirection: 'row',
1060
+ gap: _theme.spacing.sm
1061
+ },
1062
+ marker: {
1063
+ minWidth: _theme.spacing.lg,
1064
+ color: _theme.palette.textMuted,
1065
+ fontSize: _theme.fontSize.body,
1066
+ lineHeight: _theme.fontSize.body * LINE_HEIGHT
1067
+ },
1068
+ listText: {
1069
+ flex: 1
1070
+ },
1071
+ codeBlock: {
1072
+ borderRadius: _theme.radius.md,
1073
+ backgroundColor: _theme.palette.surfaceAlt,
1074
+ padding: _theme.spacing.md
1075
+ },
1076
+ codeText: {
1077
+ color: _theme.palette.text,
1078
+ fontFamily: _reactNative.Platform.select({
1079
+ ios: 'Menlo',
1080
+ default: 'monospace'
1081
+ }),
1082
+ fontSize: _theme.fontSize.meta,
1083
+ lineHeight: _theme.fontSize.meta * LINE_HEIGHT
1084
+ },
1085
+ button: {
1086
+ minHeight: 44,
1087
+ paddingHorizontal: _theme.spacing.lg,
1088
+ borderRadius: _theme.radius.md,
1089
+ alignItems: 'center',
1090
+ justifyContent: 'center'
1091
+ },
1092
+ buttonText: {
1093
+ fontSize: _theme.fontSize.body,
1094
+ fontWeight: '700'
1095
+ },
1096
+ social: {
1097
+ flexDirection: 'row',
1098
+ flexWrap: 'wrap',
1099
+ gap: _theme.spacing.sm
1100
+ },
1101
+ socialMark: {
1102
+ width: 36,
1103
+ height: 36,
1104
+ borderRadius: 18,
1105
+ alignItems: 'center',
1106
+ justifyContent: 'center'
1107
+ },
1108
+ socialGlyph: {
1109
+ color: _theme.palette.white,
1110
+ fontSize: _theme.fontSize.meta,
1111
+ fontWeight: '800'
1112
+ },
1113
+ replies: {
1114
+ gap: _theme.spacing.sm
1115
+ },
1116
+ replyWrap: {
1117
+ alignItems: 'flex-end',
1118
+ gap: _theme.spacing.xs / 2
1119
+ },
1120
+ answerWrap: {
1121
+ alignItems: 'flex-start',
1122
+ gap: _theme.spacing.xs / 2
1123
+ },
1124
+ answer: {
1125
+ maxWidth: '85%',
1126
+ paddingHorizontal: _theme.spacing.md,
1127
+ paddingVertical: _theme.spacing.sm,
1128
+ borderRadius: _theme.radius.xl,
1129
+ borderBottomStartRadius: _theme.spacing.xs,
1130
+ backgroundColor: _theme.palette.surfaceAlt
1131
+ },
1132
+ continue: {
1133
+ alignSelf: 'center',
1134
+ paddingVertical: _theme.spacing.xs,
1135
+ paddingHorizontal: _theme.spacing.sm,
1136
+ marginTop: _theme.spacing.xs
1137
+ },
1138
+ continueText: {
1139
+ fontSize: _theme.fontSize.meta,
1140
+ fontWeight: '600'
1141
+ },
1142
+ reply: {
1143
+ maxWidth: '85%',
1144
+ paddingHorizontal: _theme.spacing.md,
1145
+ paddingVertical: _theme.spacing.sm,
1146
+ borderRadius: _theme.radius.xl,
1147
+ borderBottomEndRadius: _theme.spacing.xs
1148
+ },
1149
+ replyText: {
1150
+ fontSize: _theme.fontSize.body,
1151
+ lineHeight: _theme.fontSize.body * LINE_HEIGHT
1152
+ },
1153
+ replyMeta: {
1154
+ color: _theme.palette.textSubtle,
1155
+ fontSize: _theme.fontSize.caption
1156
+ },
1157
+ footer: {
1158
+ borderTopWidth: _reactNative.StyleSheet.hairlineWidth,
1159
+ borderTopColor: _theme.palette.border,
1160
+ backgroundColor: _theme.palette.white,
1161
+ paddingHorizontal: _theme.spacing.md,
1162
+ paddingVertical: _theme.spacing.sm,
1163
+ gap: _theme.spacing.xs
1164
+ },
1165
+ // Matches the messenger's composer (ChatView.styles.ts).
1166
+ composer: {
1167
+ flexDirection: 'row',
1168
+ alignItems: 'flex-end',
1169
+ gap: _theme.spacing.sm,
1170
+ minHeight: 48,
1171
+ backgroundColor: _theme.palette.surfaceAlt,
1172
+ borderRadius: 24,
1173
+ borderWidth: 1,
1174
+ borderColor: _theme.palette.border,
1175
+ paddingStart: _theme.spacing.lg,
1176
+ paddingEnd: _theme.spacing.xs + 2,
1177
+ paddingVertical: _theme.spacing.xs + 2
1178
+ },
1179
+ composerFocused: {
1180
+ backgroundColor: _theme.palette.white
1181
+ },
1182
+ input: {
1183
+ flex: 1,
1184
+ maxHeight: 120,
1185
+ padding: 0,
1186
+ paddingTop: 6,
1187
+ paddingBottom: 6,
1188
+ color: _theme.palette.ink,
1189
+ fontSize: _theme.fontSize.input,
1190
+ lineHeight: _theme.fontSize.input * 1.4,
1191
+ textAlign: _rtl.START_ALIGN
1192
+ },
1193
+ send: {
1194
+ width: 36,
1195
+ height: 36,
1196
+ borderRadius: 18,
1197
+ alignItems: 'center',
1198
+ justifyContent: 'center'
1199
+ },
1200
+ sendDisabled: {
1201
+ backgroundColor: _theme.palette.surfaceAlt
1202
+ },
1203
+ error: {
1204
+ color: _theme.palette.danger,
1205
+ fontSize: _theme.fontSize.caption
1206
+ },
1207
+ reactions: {
1208
+ flexDirection: 'row',
1209
+ justifyContent: 'center',
1210
+ gap: _theme.spacing.sm,
1211
+ paddingVertical: _theme.spacing.xs
1212
+ },
1213
+ reaction: {
1214
+ width: 48,
1215
+ height: 48,
1216
+ borderRadius: 24,
1217
+ borderWidth: 1,
1218
+ borderColor: _theme.palette.border,
1219
+ backgroundColor: _theme.palette.white,
1220
+ alignItems: 'center',
1221
+ justifyContent: 'center'
1222
+ },
1223
+ reactionDim: {
1224
+ opacity: 0.5
1225
+ },
1226
+ reactionEmoji: {
1227
+ fontSize: 24
1228
+ },
1229
+ spark: {
1230
+ position: 'absolute',
1231
+ fontSize: 16
1232
+ },
1233
+ thanks: {
1234
+ textAlign: 'center',
1235
+ color: _theme.palette.text,
1236
+ fontSize: _theme.fontSize.meta,
1237
+ fontWeight: '600',
1238
+ paddingBottom: _theme.spacing.xs
1239
+ }
1240
+ });
1241
+ //# sourceMappingURL=Post.js.map