@marktiderman/genesis-chat 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +119 -6
- package/dist/{chunk-VEPZROQB.js → chunk-BTG4JWLF.js} +5 -4
- package/dist/chunk-BTG4JWLF.js.map +1 -0
- package/dist/{chunk-AZS3HNLM.js → chunk-IEFULAC2.js} +25 -3
- package/dist/chunk-IEFULAC2.js.map +1 -0
- package/dist/{chunk-JU2FAPBI.js → chunk-XQ2ULNHV.js} +2 -2
- package/dist/{chunk-JU2FAPBI.js.map → chunk-XQ2ULNHV.js.map} +1 -1
- package/dist/native.d.ts +158 -6
- package/dist/native.js +404 -181
- package/dist/native.js.map +1 -1
- package/dist/push.d.ts +2 -1
- package/dist/push.js +1 -1
- package/dist/react.d.ts +6 -2
- package/dist/react.js +1 -1
- package/dist/server.js +1 -1
- package/dist/web.d.ts +6 -2
- package/dist/web.js +6 -18
- package/dist/web.js.map +1 -1
- package/package.json +9 -9
- package/src/native/__tests__/attachment-slot.test.tsx +353 -0
- package/src/native/__tests__/organise-screens.test.tsx +12 -0
- package/src/native/__tests__/push-ids.test.ts +18 -0
- package/src/native/__tests__/push.test.ts +10 -4
- package/src/native/attachment-cards.tsx +249 -11
- package/src/native/copy.ts +15 -0
- package/src/native/index.ts +28 -4
- package/src/native/media.tsx +89 -2
- package/src/native/message-bubble.tsx +78 -12
- package/src/native/push.ts +5 -2
- package/src/native/search-screen.tsx +77 -69
- package/src/native/thread-screen.tsx +23 -6
- package/src/react/error-boundary.tsx +46 -0
- package/src/react/index.tsx +6 -2
- package/src/web/cards.tsx +2 -2
- package/src/web/error-boundary.tsx +2 -30
- package/dist/chunk-AZS3HNLM.js.map +0 -1
- package/dist/chunk-VEPZROQB.js.map +0 -1
package/dist/native.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { cardTitle, callAttachmentKind,
|
|
2
|
-
export { attachmentPreview, cardTitle, messagePreview } from './chunk-
|
|
1
|
+
import { cardTitle, attachmentPreview, callAttachmentKind, ChatErrorBoundary, messagePreview } from './chunk-IEFULAC2.js';
|
|
2
|
+
export { attachmentPreview, cardTitle, messagePreview } from './chunk-IEFULAC2.js';
|
|
3
3
|
import { isHttpsUrl, extractLinks, scheduleLabel, scheduleQuickPicks, isSchedulable, topicKey, snippetParts } from './chunk-BJCE5O7V.js';
|
|
4
|
-
import { useChat, useInbox, usePeople, chatKeys, useScheduled, useAvailability, useTopics, useConversation, usePins, useMessageActions, useFileUrls, useLinkPreviews, useStartConversation, useBroadcast, useSearch } from './chunk-
|
|
5
|
-
import { summarizeReactions,
|
|
4
|
+
import { useChat, useInbox, usePeople, chatKeys, useScheduled, useAvailability, useTopics, useConversation, usePins, useMessageActions, useFileUrls, useLinkPreviews, useStartConversation, useBroadcast, useSearch } from './chunk-XQ2ULNHV.js';
|
|
5
|
+
import { summarizeReactions, parseMentions, startsDay, dayLabel, itemTime, itemSender, startsGroup, activityLine } from './chunk-A4YJX7KT.js';
|
|
6
6
|
import { formatDuration, nextPlaybackRate, ChatError, CHAT_LIMITS, fileExtension, isAllowedExtension, attachmentKindFor, awaySummary, WEEKDAYS, minutesOf, waveformFromMetering, recordingLimitMs, CHAT_BUCKET, randomId } from './chunk-GBJ3YI4E.js';
|
|
7
|
-
export { CHAT_MARK_READ_ACTION, CHAT_PUSH_CATEGORY, CHAT_REPLY_ACTION, PUSH_REPLY_WAIT_MS, chatNotificationActions, chatPath, chatPushCategory, chatPushCopy, chatPushPayload, chatThreadMatch, conversationIdFromData, failedReply, handleChatPushResponse, registerChatNotificationActions, replyClientId, replyClientIdFor } from './chunk-
|
|
8
|
-
import { createContext, memo, useMemo, useRef, useContext, useSyncExternalStore,
|
|
7
|
+
export { CHAT_MARK_READ_ACTION, CHAT_PUSH_CATEGORY, CHAT_REPLY_ACTION, PUSH_REPLY_WAIT_MS, chatNotificationActions, chatPath, chatPushCategory, chatPushCopy, chatPushPayload, chatThreadMatch, conversationIdFromData, failedReply, handleChatPushResponse, registerChatNotificationActions, replyClientId, replyClientIdFor } from './chunk-BTG4JWLF.js';
|
|
8
|
+
import { createContext, memo, useMemo, useState, useCallback, useRef, useContext, useSyncExternalStore, useLayoutEffect, useEffect, useReducer } from 'react';
|
|
9
9
|
import { NativeText, NativeAvatar, NativeImage, NativeIcon, NativeButton, NativeProgress, useToast, NativeSheet, NativeDatePicker, NativeTimePicker, NativeListItem, NativeSkeleton, NativeEmptyState, NativeTextarea, NativeSwitch, NativeCombobox, NativeChipGroup, NativeChip, NativeInput, NativeSearchBar, NativeInlineAlert, NativeFullScreenModal, FULL_SCREEN_DARK, useGenesisModules, NativeSheetScrollView, NativeCheckbox, NativeBadge } from '@marktiderman/genesis-ui-native/components';
|
|
10
10
|
import { InboxPage, SettingsSubPage, getThreadPageStackOptions, formatInboxTime } from '@marktiderman/genesis-ui-native/layouts';
|
|
11
11
|
import { Pressable, Animated, PanResponder, View, Linking, useWindowDimensions, ActivityIndicator, AppState, Modal, AccessibilityInfo, ScrollView, FlatList, TextInput, Platform, KeyboardAvoidingView, Alert } from 'react-native';
|
|
@@ -195,6 +195,12 @@ var chatNativeCopy = {
|
|
|
195
195
|
openLink: "Opens the link",
|
|
196
196
|
openLinkAction: "Open link",
|
|
197
197
|
attachmentCard: "{title}, {subtitle}",
|
|
198
|
+
cardActionFailed: "That didn't work. Try again.",
|
|
199
|
+
cardActionTimedOut: "That's taking too long. Try again.",
|
|
200
|
+
transcribing: "Transcribing\u2026",
|
|
201
|
+
transcriptLabel: "Transcript: {text}",
|
|
202
|
+
showMore: "Show more",
|
|
203
|
+
showLess: "Show less",
|
|
198
204
|
openImage: "Open photo",
|
|
199
205
|
closeViewer: "Close",
|
|
200
206
|
photoOf: "Photo {n} of {count}",
|
|
@@ -281,11 +287,15 @@ var MEDIA_SCRIM = "rgba(0,0,0,0.5)";
|
|
|
281
287
|
var MEDIA_SCRIM_LIGHT = "rgba(0,0,0,0.35)";
|
|
282
288
|
var ON_TINT_FILL = "rgba(255,255,255,0.16)";
|
|
283
289
|
var ON_TINT_FILL_STRONG = "rgba(255,255,255,0.2)";
|
|
284
|
-
|
|
285
|
-
|
|
290
|
+
var BUILT_IN_KINDS = /* @__PURE__ */ new Set(["image", "voice", "video", "file", "link"]);
|
|
291
|
+
function attachmentKindOf(kinds, kind) {
|
|
292
|
+
return kinds && Object.prototype.hasOwnProperty.call(kinds, kind) ? kinds[kind] : void 0;
|
|
293
|
+
}
|
|
294
|
+
function splitCardAttachments(attachments, kinds, slot = false) {
|
|
295
|
+
if (!kinds && !slot) return { cards: [], rest: [...attachments] };
|
|
286
296
|
const cards = [];
|
|
287
297
|
const rest = [];
|
|
288
|
-
for (const a of attachments) (
|
|
298
|
+
for (const a of attachments) (attachmentKindOf(kinds, a.kind) || slot && !BUILT_IN_KINDS.has(a.kind) ? cards : rest).push(a);
|
|
289
299
|
return { cards, rest };
|
|
290
300
|
}
|
|
291
301
|
function isOpenableHref(href) {
|
|
@@ -310,7 +320,63 @@ function cardAction(kind, attachment, message, onOpenAttachment, ctx = { mine: f
|
|
|
310
320
|
const run = onPress ? onPress : onOpenAttachment ? () => onOpenAttachment(attachment, message) : href ? () => void Linking.openURL(href).catch(() => void 0) : null;
|
|
311
321
|
return run ? { label: action.label, run } : null;
|
|
312
322
|
}
|
|
313
|
-
|
|
323
|
+
var IDLE = { pending: false, error: null };
|
|
324
|
+
var CARD_ACTION_TIMEOUT_MS = 3e4;
|
|
325
|
+
function useCardRuns(options) {
|
|
326
|
+
const { failedText, timedOutText = failedText, timeoutMs = CARD_ACTION_TIMEOUT_MS } = typeof options === "string" ? { failedText: options } : options;
|
|
327
|
+
const [states, setStates] = useState({});
|
|
328
|
+
const busy = useRef(/* @__PURE__ */ new Set());
|
|
329
|
+
const run = useCallback(
|
|
330
|
+
async (id, task) => {
|
|
331
|
+
if (busy.current.has(id)) return;
|
|
332
|
+
const failed = (e, text = failedText) => {
|
|
333
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) console.warn("[genesis-chat] an attachment card's action failed:", e);
|
|
334
|
+
setStates((s) => ({ ...s, [id]: { pending: false, error: text } }));
|
|
335
|
+
};
|
|
336
|
+
let result;
|
|
337
|
+
try {
|
|
338
|
+
result = task();
|
|
339
|
+
} catch (e) {
|
|
340
|
+
failed(e);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
if (!result || typeof result.then !== "function") {
|
|
344
|
+
setStates((s) => s[id] ? { ...s, [id]: IDLE } : s);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
busy.current.add(id);
|
|
348
|
+
setStates((s) => ({ ...s, [id]: { pending: true, error: null } }));
|
|
349
|
+
const TIMED_OUT = /* @__PURE__ */ Symbol("timed out");
|
|
350
|
+
let timer;
|
|
351
|
+
const limit = timeoutMs > 0 ? new Promise((resolve) => timer = setTimeout(() => resolve(TIMED_OUT), timeoutMs)) : null;
|
|
352
|
+
try {
|
|
353
|
+
const settled = await (limit ? Promise.race([result, limit]) : result);
|
|
354
|
+
if (settled === TIMED_OUT) {
|
|
355
|
+
failed(new Error(`timed out after ${timeoutMs} ms`), timedOutText);
|
|
356
|
+
void Promise.resolve(result).catch(() => void 0);
|
|
357
|
+
} else setStates((s) => ({ ...s, [id]: IDLE }));
|
|
358
|
+
} catch (e) {
|
|
359
|
+
failed(e);
|
|
360
|
+
} finally {
|
|
361
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
362
|
+
busy.current.delete(id);
|
|
363
|
+
}
|
|
364
|
+
},
|
|
365
|
+
[failedText, timedOutText, timeoutMs]
|
|
366
|
+
);
|
|
367
|
+
const stateOf = useCallback((id) => states[id] ?? IDLE, [states]);
|
|
368
|
+
return { stateOf, run };
|
|
369
|
+
}
|
|
370
|
+
function slotUrl(url) {
|
|
371
|
+
if (!url) return null;
|
|
372
|
+
try {
|
|
373
|
+
const u = new URL(url);
|
|
374
|
+
return (u.protocol === "https:" || u.protocol === "http:") && u.hostname ? url : null;
|
|
375
|
+
} catch {
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
function ChatAttachmentCard({ attachment, message, kind, mine = false, onOpenAttachment, run, state = IDLE, testID = "chat-attachment-card" }) {
|
|
314
380
|
const { copy } = useChatNative();
|
|
315
381
|
const colors = useThemeRoles();
|
|
316
382
|
let title = "";
|
|
@@ -325,31 +391,82 @@ function ChatAttachmentCard({ attachment, message, kind, mine = false, onOpenAtt
|
|
|
325
391
|
title = title || attachment.name || attachment.kind;
|
|
326
392
|
const action = cardAction(kind, attachment, message, onOpenAttachment, ctx);
|
|
327
393
|
const fg = mine ? "text-primary-foreground" : void 0;
|
|
328
|
-
return /* @__PURE__ */ jsxs(
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
394
|
+
return /* @__PURE__ */ jsxs(View, { testID, style: { gap: 4, minWidth: 220 }, children: [
|
|
395
|
+
/* @__PURE__ */ jsxs(
|
|
396
|
+
View,
|
|
397
|
+
{
|
|
398
|
+
style: { flexDirection: "row", alignItems: "center", gap: 10, padding: 10, borderRadius: 12, backgroundColor: mine ? ON_TINT_FILL : colors.groupedBackground },
|
|
399
|
+
children: [
|
|
400
|
+
/* @__PURE__ */ jsxs(
|
|
401
|
+
View,
|
|
402
|
+
{
|
|
403
|
+
accessible: true,
|
|
404
|
+
accessibilityLabel: subtitle ? fill(copy.attachmentCard, { title, subtitle }) : title,
|
|
405
|
+
style: { flexDirection: "row", alignItems: "center", gap: 10, flex: 1 },
|
|
406
|
+
children: [
|
|
407
|
+
/* @__PURE__ */ jsx(View, { style: { width: 40, height: 40, borderRadius: 10, alignItems: "center", justifyContent: "center", backgroundColor: mine ? ON_TINT_FILL_STRONG : colors.fill }, children: /* @__PURE__ */ jsx(NativeIcon, { name: "file", size: 20, color: mine ? colors.surface : colors.tint }) }),
|
|
408
|
+
/* @__PURE__ */ jsxs(View, { style: { flex: 1, minWidth: 0 }, children: [
|
|
409
|
+
/* @__PURE__ */ jsx(NativeText, { numberOfLines: 1, className: fg, style: mine ? { fontWeight: "600", fontSize: 15 } : { fontWeight: "600", fontSize: 15, color: colors.label }, children: title }),
|
|
410
|
+
subtitle ? /* @__PURE__ */ jsx(NativeText, { preset: "caption", numberOfLines: 1, className: fg, style: mine ? { opacity: 0.85 } : { color: colors.secondaryLabel }, children: subtitle }) : null
|
|
411
|
+
] })
|
|
412
|
+
]
|
|
413
|
+
}
|
|
414
|
+
),
|
|
415
|
+
action ? /* @__PURE__ */ jsx(
|
|
416
|
+
NativeButton,
|
|
417
|
+
{
|
|
418
|
+
testID: `${testID}-action`,
|
|
419
|
+
variant: mine ? "secondary" : "default",
|
|
420
|
+
className: "h-11",
|
|
421
|
+
loading: state.pending,
|
|
422
|
+
onPress: () => run ? run(action.run) : action.run(),
|
|
423
|
+
children: action.label
|
|
424
|
+
}
|
|
425
|
+
) : null
|
|
426
|
+
]
|
|
427
|
+
}
|
|
428
|
+
),
|
|
429
|
+
state.error ? /* @__PURE__ */ jsx(CardError, { text: state.error, mine, testID: `${testID}-error` }) : null
|
|
430
|
+
] });
|
|
431
|
+
}
|
|
432
|
+
function CardError({ text, mine, testID }) {
|
|
433
|
+
const colors = useThemeRoles();
|
|
434
|
+
return /* @__PURE__ */ jsx(NativeText, { testID, preset: "caption", accessibilityLiveRegion: "polite", className: mine ? "text-primary-foreground" : void 0, style: mine ? { paddingHorizontal: 4 } : { paddingHorizontal: 4, color: colors.destructive }, children: text });
|
|
435
|
+
}
|
|
436
|
+
function AppAttachment({ attachment, message, kind, url, mine, slot, onOpenAttachment, onEmpty, run, state, testID }) {
|
|
437
|
+
const hidden = slot === null && !kind;
|
|
438
|
+
useLayoutEffect(() => {
|
|
439
|
+
if (slot === null || slot === void 0) onEmpty?.(attachment.id, hidden);
|
|
440
|
+
}, [slot, hidden, attachment.id, onEmpty]);
|
|
441
|
+
const fallback = /* @__PURE__ */ jsx(ChatAttachmentCard, { attachment, message, kind, mine, onOpenAttachment, run, state, testID });
|
|
442
|
+
if (slot === void 0) return fallback;
|
|
443
|
+
if (slot === null) return hidden ? null : fallback;
|
|
444
|
+
const found = cardAction(kind, attachment, message, onOpenAttachment, { mine });
|
|
445
|
+
const action = found ? { label: found.label, run: () => run(found.run) } : null;
|
|
446
|
+
const props = { attachment, message, url: slotUrl(url), mine, kind, action, run, pending: state.pending, error: state.error, fallback, testID };
|
|
447
|
+
return /* @__PURE__ */ jsx(ChatErrorBoundary, { fallback: /* @__PURE__ */ jsx(Reported, { id: attachment.id, empty: false, onEmpty, children: fallback }), resetKeys: [attachment, message, kind, slot], children: /* @__PURE__ */ jsx(SlotProbe, { slot, props, onEmpty }, slotKey(slot)) });
|
|
448
|
+
}
|
|
449
|
+
var slotKeys = /* @__PURE__ */ new WeakMap();
|
|
450
|
+
var nextSlotKey = 0;
|
|
451
|
+
function slotKey(slot) {
|
|
452
|
+
let k = slotKeys.get(slot);
|
|
453
|
+
if (k === void 0) slotKeys.set(slot, k = ++nextSlotKey);
|
|
454
|
+
return k;
|
|
455
|
+
}
|
|
456
|
+
function isPlainFunction(slot) {
|
|
457
|
+
return typeof slot === "function" && !slot.prototype?.isReactComponent;
|
|
458
|
+
}
|
|
459
|
+
function SlotProbe({ slot, props, onEmpty }) {
|
|
460
|
+
const Slot = slot;
|
|
461
|
+
const out = isPlainFunction(slot) ? slot(props) : /* @__PURE__ */ jsx(Slot, { ...props });
|
|
462
|
+
const empty = out === null || out === void 0 || out === false;
|
|
463
|
+
return /* @__PURE__ */ jsx(Reported, { id: props.attachment.id, empty, onEmpty, children: empty ? null : out });
|
|
464
|
+
}
|
|
465
|
+
function Reported({ id, empty, onEmpty, children }) {
|
|
466
|
+
useLayoutEffect(() => {
|
|
467
|
+
onEmpty?.(id, empty);
|
|
468
|
+
}, [id, empty, onEmpty]);
|
|
469
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
353
470
|
}
|
|
354
471
|
function linkHost(url) {
|
|
355
472
|
try {
|
|
@@ -941,7 +1058,9 @@ function fromServer(a, urls) {
|
|
|
941
1058
|
provider: a.provider,
|
|
942
1059
|
playbackId: a.playbackId,
|
|
943
1060
|
status: a.status,
|
|
944
|
-
posterUri: a.posterPath ? urls[a.posterPath] ?? null : null
|
|
1061
|
+
posterUri: a.posterPath ? urls[a.posterPath] ?? null : null,
|
|
1062
|
+
transcript: a.transcript,
|
|
1063
|
+
transcriptStatus: a.transcriptStatus
|
|
945
1064
|
};
|
|
946
1065
|
}
|
|
947
1066
|
function fromPending(clientId, index2, file, preview) {
|
|
@@ -1013,7 +1132,10 @@ function MessageAttachments({ attachments, mine, onOpenImage, testID }) {
|
|
|
1013
1132
|
);
|
|
1014
1133
|
}) }) : null,
|
|
1015
1134
|
others.map(
|
|
1016
|
-
(a, i) => a.kind === "voice" ? /* @__PURE__ */
|
|
1135
|
+
(a, i) => a.kind === "voice" ? /* @__PURE__ */ jsxs(View, { style: { gap: 2 }, children: [
|
|
1136
|
+
/* @__PURE__ */ jsx(VoiceNotePlayer, { id: a.key, uri: a.uri, durationMs: a.durationMs, waveform: a.waveform, mine, testID: `${testID}-voice-${i}` }),
|
|
1137
|
+
/* @__PURE__ */ jsx(VoiceTranscript, { transcript: a.transcript, status: a.transcriptStatus, mine, testID: `${testID}-voice-${i}-transcript` })
|
|
1138
|
+
] }, a.key) : a.kind === "video" && modules.video ? /* @__PURE__ */ jsx(
|
|
1017
1139
|
ChatVideoPlayer,
|
|
1018
1140
|
{
|
|
1019
1141
|
id: a.key,
|
|
@@ -1046,6 +1168,53 @@ function MessageAttachments({ attachments, mine, onOpenImage, testID }) {
|
|
|
1046
1168
|
)
|
|
1047
1169
|
] });
|
|
1048
1170
|
}
|
|
1171
|
+
var TRANSCRIPT_LINES = 3;
|
|
1172
|
+
function transcriptIsLong(text, measuredLines) {
|
|
1173
|
+
if (typeof measuredLines === "number") return measuredLines > TRANSCRIPT_LINES;
|
|
1174
|
+
return text.length > 140 || text.split("\n").length > TRANSCRIPT_LINES;
|
|
1175
|
+
}
|
|
1176
|
+
function VoiceTranscript({ transcript, status, mine, testID }) {
|
|
1177
|
+
const { copy } = useChatNative();
|
|
1178
|
+
const colors = useThemeRoles();
|
|
1179
|
+
const [open, setOpen] = useState(false);
|
|
1180
|
+
const [lines, setLines] = useState(null);
|
|
1181
|
+
const fg = mine ? "text-primary-foreground" : void 0;
|
|
1182
|
+
const text = transcript?.trim();
|
|
1183
|
+
if (status === "pending" && !text) {
|
|
1184
|
+
return /* @__PURE__ */ jsx(NativeText, { testID, preset: "caption", className: fg, style: mine ? { fontStyle: "italic", opacity: 0.85, paddingHorizontal: 8 } : { fontStyle: "italic", color: colors.secondaryLabel, paddingHorizontal: 8 }, children: copy.transcribing });
|
|
1185
|
+
}
|
|
1186
|
+
if (!text) return null;
|
|
1187
|
+
const long = transcriptIsLong(text, lines);
|
|
1188
|
+
const textStyle = mine ? { fontSize: 15, lineHeight: 20 } : { fontSize: 15, lineHeight: 20, color: colors.label };
|
|
1189
|
+
return /* @__PURE__ */ jsxs(View, { testID, style: { paddingHorizontal: 8, alignItems: "flex-start" }, children: [
|
|
1190
|
+
/* @__PURE__ */ jsx(NativeText, { testID: `${testID}-text`, className: fg, numberOfLines: open || !long ? void 0 : TRANSCRIPT_LINES, style: textStyle, children: text }),
|
|
1191
|
+
/* @__PURE__ */ jsx(
|
|
1192
|
+
NativeText,
|
|
1193
|
+
{
|
|
1194
|
+
testID: `${testID}-measure`,
|
|
1195
|
+
"aria-hidden": true,
|
|
1196
|
+
accessibilityElementsHidden: true,
|
|
1197
|
+
importantForAccessibility: "no-hide-descendants",
|
|
1198
|
+
onTextLayout: (e) => setLines(e.nativeEvent.lines.length),
|
|
1199
|
+
style: [textStyle, { position: "absolute", left: 8, right: 8, top: 0, opacity: 0 }],
|
|
1200
|
+
pointerEvents: "none",
|
|
1201
|
+
children: text
|
|
1202
|
+
}
|
|
1203
|
+
),
|
|
1204
|
+
long ? /* @__PURE__ */ jsx(
|
|
1205
|
+
Pressable,
|
|
1206
|
+
{
|
|
1207
|
+
testID: `${testID}-toggle`,
|
|
1208
|
+
accessibilityRole: "button",
|
|
1209
|
+
accessibilityState: { expanded: open },
|
|
1210
|
+
onPress: () => setOpen((o) => !o),
|
|
1211
|
+
hitSlop: { top: 8, bottom: 8 },
|
|
1212
|
+
style: { minHeight: 44, minWidth: 44, justifyContent: "center" },
|
|
1213
|
+
children: /* @__PURE__ */ jsx(NativeText, { preset: "caption", className: fg, style: mine ? { fontWeight: "600" } : { fontWeight: "600", color: colors.tint }, children: open ? copy.showLess : copy.showMore })
|
|
1214
|
+
}
|
|
1215
|
+
) : null
|
|
1216
|
+
] });
|
|
1217
|
+
}
|
|
1049
1218
|
function moderationNote(message, opts) {
|
|
1050
1219
|
if (!message || message.deletedAt) return null;
|
|
1051
1220
|
if (message.moderation === "hidden") return opts.mine && !opts.observer ? "hidden-mine" : "hidden";
|
|
@@ -1087,10 +1256,23 @@ function MessageBubbleImpl(props) {
|
|
|
1087
1256
|
const sender = person(row.senderId);
|
|
1088
1257
|
const senderName = mine ? copy.you : sender?.name ?? "";
|
|
1089
1258
|
const kinds = props.attachmentKinds;
|
|
1259
|
+
const slot = props.attachmentSlot;
|
|
1090
1260
|
const { cards, rest: plain } = useMemo(
|
|
1091
|
-
() => splitCardAttachments(message && !message.deletedAt ? message.attachments : [], kinds),
|
|
1092
|
-
[message, kinds]
|
|
1261
|
+
() => splitCardAttachments(message && !message.deletedAt ? message.attachments : [], kinds, slot !== void 0),
|
|
1262
|
+
[message, kinds, slot]
|
|
1093
1263
|
);
|
|
1264
|
+
const runs = useCardRuns({ failedText: copy.cardActionFailed, timedOutText: copy.cardActionTimedOut, timeoutMs: props.cardActionTimeoutMs });
|
|
1265
|
+
const [empty, setEmpty] = useState(() => /* @__PURE__ */ new Set());
|
|
1266
|
+
const onEmpty = useCallback((id, isEmpty) => {
|
|
1267
|
+
setEmpty((s) => {
|
|
1268
|
+
if (s.has(id) === isEmpty) return s;
|
|
1269
|
+
const next = new Set(s);
|
|
1270
|
+
if (isEmpty) next.add(id);
|
|
1271
|
+
else next.delete(id);
|
|
1272
|
+
return next;
|
|
1273
|
+
});
|
|
1274
|
+
}, []);
|
|
1275
|
+
const shownCards = cards.filter((a) => !empty.has(a.id));
|
|
1094
1276
|
const attachments = useMemo(() => {
|
|
1095
1277
|
if (message) return message.deletedAt ? [] : plain.map((a) => fromServer(a, urls));
|
|
1096
1278
|
return [
|
|
@@ -1103,7 +1285,11 @@ function MessageBubbleImpl(props) {
|
|
|
1103
1285
|
const photos = attachments.filter((a) => a.kind === "image" && a.uri && props.onOpenImage);
|
|
1104
1286
|
const videos = attachments.filter((a) => a.kind === "video" && modules.video && (a.uri || a.playbackId));
|
|
1105
1287
|
const playingVideo = usePlayingVideo(videos.map((a) => a.key));
|
|
1106
|
-
const cardActions = message ? cards.map((a) =>
|
|
1288
|
+
const cardActions = message ? cards.map((a) => {
|
|
1289
|
+
if (empty.has(a.id)) return null;
|
|
1290
|
+
const c = cardAction(attachmentKindOf(kinds, a.kind), a, message, props.onOpenAttachment, { mine });
|
|
1291
|
+
return c ? { label: c.label, run: () => void runs.run(a.id, c.run) } : null;
|
|
1292
|
+
}) : [];
|
|
1107
1293
|
const playingVoice = usePlayingVoiceNote(voices.map((a) => a.key));
|
|
1108
1294
|
const x = useRef(new Animated.Value(0)).current;
|
|
1109
1295
|
const latest = useRef(props);
|
|
@@ -1144,8 +1330,13 @@ function MessageBubbleImpl(props) {
|
|
|
1144
1330
|
const label = [
|
|
1145
1331
|
senderName,
|
|
1146
1332
|
message?.deletedAt ? copy.deleted : displayBody(body),
|
|
1147
|
-
attachments.map(
|
|
1148
|
-
|
|
1333
|
+
attachments.map(
|
|
1334
|
+
(a) => a.kind === "image" ? copy.photo : a.kind === "voice" ? [copy.voice, a.transcript?.trim() ? fill(copy.transcriptLabel, { text: a.transcript.trim() }) : null].filter(Boolean).join(", ") : a.kind === "video" ? copy.video : copy.file
|
|
1335
|
+
).join(", "),
|
|
1336
|
+
shownCards.map((a) => {
|
|
1337
|
+
const s = runs.stateOf(a.id);
|
|
1338
|
+
return [cardTitle(attachmentKindOf(kinds, a.kind), a, { mine }), s.error].filter(Boolean).join(", ");
|
|
1339
|
+
}).join(", "),
|
|
1149
1340
|
preview ? fill(copy.linkPreview, { title: preview.title?.trim() || linkHost(preview.url), site: preview.siteName?.trim() || linkHost(preview.url) }) : null,
|
|
1150
1341
|
time,
|
|
1151
1342
|
message?.editedAt ? copy.edited : null,
|
|
@@ -1196,94 +1387,120 @@ function MessageBubbleImpl(props) {
|
|
|
1196
1387
|
};
|
|
1197
1388
|
const quote = message?.replyToId ?? pending?.input.replyToId ?? null;
|
|
1198
1389
|
const quoted = quote && repliedTo ? repliedTo : null;
|
|
1390
|
+
const invisible = Boolean(message && !message.deletedAt && !body && !attachments.length && !preview && !quote && !moderation && cards.length && !shownCards.length);
|
|
1199
1391
|
const bubbleColor = mine ? colors.tint : colors.surface;
|
|
1200
1392
|
const fg = mine ? "text-primary-foreground" : void 0;
|
|
1201
1393
|
const textStyle = mine ? { fontSize: 17, lineHeight: 22 } : { fontSize: 17, lineHeight: 22, color: colors.label };
|
|
1202
|
-
return /* @__PURE__ */ jsxs(
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1394
|
+
return /* @__PURE__ */ jsxs(
|
|
1395
|
+
View,
|
|
1396
|
+
{
|
|
1397
|
+
testID,
|
|
1398
|
+
accessibilityElementsHidden: invisible || void 0,
|
|
1399
|
+
importantForAccessibility: invisible ? "no-hide-descendants" : void 0,
|
|
1400
|
+
style: { paddingHorizontal: 8, paddingTop: row.groupStart ? 8 : 2, backgroundColor: props.highlighted ? colors.fill : void 0, display: invisible ? "none" : void 0 },
|
|
1401
|
+
children: [
|
|
1402
|
+
row.showName && sender?.name ? /* @__PURE__ */ jsx(NativeText, { preset: "caption", style: { marginLeft: group ? 48 : 12, marginBottom: 2, color: colors.secondaryLabel }, children: sender.name }) : null,
|
|
1403
|
+
/* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", alignItems: "flex-end", justifyContent: mine ? "flex-end" : "flex-start" }, children: [
|
|
1404
|
+
group && !mine ? /* @__PURE__ */ jsx(View, { style: { width: 40, alignItems: "flex-start" }, children: row.showAvatar ? /* @__PURE__ */ jsx(NativeAvatar, { size: 32, src: sender?.avatarUrl ?? null, fallback: initials(sender?.name), alt: sender?.name, recyclingKey: row.senderId ?? void 0 }) : null }) : null,
|
|
1405
|
+
/* @__PURE__ */ jsx(
|
|
1406
|
+
Animated.View,
|
|
1213
1407
|
{
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1408
|
+
style: { maxWidth: "80%", transform: [{ translateX: x }] },
|
|
1409
|
+
...canSwipe ? responder.panHandlers : {},
|
|
1410
|
+
children: /* @__PURE__ */ jsxs(
|
|
1411
|
+
Pressable,
|
|
1412
|
+
{
|
|
1413
|
+
testID: `${testID}-bubble`,
|
|
1414
|
+
accessible: true,
|
|
1415
|
+
accessibilityLabel: label,
|
|
1416
|
+
accessibilityHint: failed ? copy.retry : void 0,
|
|
1417
|
+
accessibilityActions: actions,
|
|
1418
|
+
onAccessibilityAction: (e) => onAction(e.nativeEvent.actionName),
|
|
1419
|
+
onLongPress: message && !message.deletedAt ? () => props.onLongPress?.(message) : void 0,
|
|
1420
|
+
delayLongPress: 350,
|
|
1421
|
+
style: {
|
|
1422
|
+
paddingHorizontal: attachments.length && !body ? 4 : 12,
|
|
1423
|
+
paddingVertical: attachments.length && !body ? 4 : 8,
|
|
1424
|
+
borderRadius: 18,
|
|
1425
|
+
backgroundColor: bubbleColor,
|
|
1426
|
+
opacity: sending ? 0.6 : moderation === "hidden-mine" ? 0.7 : 1,
|
|
1427
|
+
gap: 4
|
|
1428
|
+
},
|
|
1429
|
+
children: [
|
|
1430
|
+
quote ? /* @__PURE__ */ jsxs(
|
|
1431
|
+
View,
|
|
1432
|
+
{
|
|
1433
|
+
testID: `${testID}-quote`,
|
|
1434
|
+
style: { borderLeftWidth: 3, borderLeftColor: mine ? colors.surface : colors.tint, paddingLeft: 8, paddingVertical: 2, opacity: 0.85 },
|
|
1435
|
+
children: [
|
|
1436
|
+
/* @__PURE__ */ jsx(NativeText, { preset: "caption", className: fg, style: mine ? { fontWeight: "600" } : { fontWeight: "600", color: colors.secondaryLabel }, children: quoted ? quoted.senderId === me ? copy.you : person(quoted.senderId)?.name ?? "" : "" }),
|
|
1437
|
+
/* @__PURE__ */ jsx(NativeText, { preset: "caption", numberOfLines: 2, className: fg, style: mine ? void 0 : { color: colors.secondaryLabel }, children: quoted ? attachmentPreview(displayBody(quoted.body), quoted.attachments[0], kinds, Boolean(quoted.deletedAt)) : "\u2026" })
|
|
1438
|
+
]
|
|
1439
|
+
}
|
|
1440
|
+
) : null,
|
|
1441
|
+
attachments.length ? /* @__PURE__ */ jsx(MessageAttachments, { attachments, mine, onOpenImage: props.onOpenImage, testID }) : null,
|
|
1442
|
+
message && cards.length ? cards.map((a, i) => /* @__PURE__ */ jsx(
|
|
1443
|
+
AppAttachment,
|
|
1444
|
+
{
|
|
1445
|
+
attachment: a,
|
|
1446
|
+
message,
|
|
1447
|
+
kind: attachmentKindOf(kinds, a.kind),
|
|
1448
|
+
url: (a.path ? urls[a.path] : null) ?? a.url ?? null,
|
|
1449
|
+
mine,
|
|
1450
|
+
slot,
|
|
1451
|
+
onOpenAttachment: props.onOpenAttachment,
|
|
1452
|
+
onEmpty,
|
|
1453
|
+
run: (task) => void runs.run(a.id, task),
|
|
1454
|
+
state: runs.stateOf(a.id),
|
|
1455
|
+
testID: `${testID}-card-${i}`
|
|
1456
|
+
},
|
|
1457
|
+
a.id
|
|
1458
|
+
)) : null,
|
|
1459
|
+
message?.deletedAt ? /* @__PURE__ */ jsx(NativeText, { className: fg, style: [textStyle, { fontStyle: "italic", opacity: 0.7 }], children: copy.deleted }) : body ? /* @__PURE__ */ jsx(NativeText, { className: fg, style: [textStyle, attachments.length ? { paddingHorizontal: 8, paddingBottom: 4 } : null], children: displayBody(body) }) : null,
|
|
1460
|
+
moderation ? /* @__PURE__ */ jsx(ModerationMarker, { note: moderation, mine, testID: `${testID}-moderation` }) : null,
|
|
1461
|
+
preview ? /* @__PURE__ */ jsx(ChatLinkPreviewCard, { preview, mine, images: props.linkPreviewImages, testID: `${testID}-preview` }) : null
|
|
1462
|
+
]
|
|
1463
|
+
}
|
|
1464
|
+
)
|
|
1248
1465
|
}
|
|
1249
1466
|
)
|
|
1250
|
-
}
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1467
|
+
] }),
|
|
1468
|
+
reactions.length ? /* @__PURE__ */ jsx(View, { style: { flexDirection: "row", flexWrap: "wrap", gap: 4, marginTop: 2, justifyContent: mine ? "flex-end" : "flex-start", marginLeft: group && !mine ? 40 : 0 }, children: reactions.map((r) => /* @__PURE__ */ jsxs(
|
|
1469
|
+
Pressable,
|
|
1470
|
+
{
|
|
1471
|
+
testID: `${testID}-reaction-${r.emoji}`,
|
|
1472
|
+
accessibilityRole: "button",
|
|
1473
|
+
accessibilityState: { selected: r.mine },
|
|
1474
|
+
accessibilityLabel: `${r.emoji} ${r.count}`,
|
|
1475
|
+
disabled: !message || !props.onToggleReaction,
|
|
1476
|
+
onPress: () => message && props.onToggleReaction?.(message, r.emoji, !r.mine),
|
|
1477
|
+
hitSlop: 6,
|
|
1478
|
+
style: {
|
|
1479
|
+
flexDirection: "row",
|
|
1480
|
+
alignItems: "center",
|
|
1481
|
+
gap: 4,
|
|
1482
|
+
minHeight: 32,
|
|
1483
|
+
paddingHorizontal: 10,
|
|
1484
|
+
borderRadius: 16,
|
|
1485
|
+
backgroundColor: r.mine ? colors.fill : colors.surface,
|
|
1486
|
+
borderWidth: r.mine ? 1 : 0,
|
|
1487
|
+
borderColor: colors.tint
|
|
1488
|
+
},
|
|
1489
|
+
children: [
|
|
1490
|
+
/* @__PURE__ */ jsx(NativeText, { style: { fontSize: 15 }, children: r.emoji }),
|
|
1491
|
+
/* @__PURE__ */ jsx(NativeText, { preset: "caption", style: { color: colors.secondaryLabel }, children: String(r.count) })
|
|
1492
|
+
]
|
|
1493
|
+
},
|
|
1494
|
+
r.emoji
|
|
1495
|
+
)) }) : null,
|
|
1496
|
+
row.groupEnd || failed ? /* @__PURE__ */ jsx(View, { style: { flexDirection: "row", justifyContent: mine ? "flex-end" : "flex-start", alignItems: "center", gap: 4, marginTop: 2, marginLeft: group && !mine ? 52 : 12, marginRight: 12 }, children: failed ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1497
|
+
/* @__PURE__ */ jsx(NativeText, { testID: `${testID}-failed`, preset: "caption", style: { color: colors.destructive }, children: pending?.error ?? copy.notSent }),
|
|
1498
|
+
lost ? props.onReattach ? /* @__PURE__ */ jsx(SmallAction, { testID: `${testID}-reattach`, label: copy.attachAgain, onPress: () => props.onReattach?.(pending.clientId, pending.files ?? []), color: colors.tint }) : null : /* @__PURE__ */ jsx(SmallAction, { testID: `${testID}-retry`, label: copy.retry, onPress: () => props.onRetry?.(pending.clientId), color: colors.tint }),
|
|
1499
|
+
/* @__PURE__ */ jsx(SmallAction, { testID: `${testID}-discard`, label: copy.discard, onPress: () => props.onDiscard?.(pending.clientId), color: colors.destructive })
|
|
1500
|
+
] }) : /* @__PURE__ */ jsx(NativeText, { preset: "caption", style: { color: colors.tertiaryLabel }, children: [time, message?.editedAt ? copy.edited : null, sending ? copy.sending : null].filter(Boolean).join(" \xB7 ") }) }) : null
|
|
1501
|
+
]
|
|
1502
|
+
}
|
|
1503
|
+
);
|
|
1287
1504
|
}
|
|
1288
1505
|
function SmallAction({ label, onPress, color, testID }) {
|
|
1289
1506
|
return /* @__PURE__ */ jsx(Pressable, { testID, accessibilityRole: "button", accessibilityLabel: label, onPress, style: { minHeight: 44, minWidth: 44, paddingHorizontal: 8, alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsx(NativeText, { preset: "label", style: { color }, children: label }) });
|
|
@@ -4134,6 +4351,8 @@ function ChatThreadScreen(props) {
|
|
|
4134
4351
|
const { conversationId, threadRootId = null, keyboardVerticalOffset, onOpenAttachment, jumpTo, topic: topicProp, showTopics = false, scheduling = true, availability = true, focused = true, testID = "chat-thread" } = props;
|
|
4135
4352
|
const chat = useChat();
|
|
4136
4353
|
const { userId, videoUpload, attachmentKinds, outbox } = chat;
|
|
4354
|
+
const attachmentSlot = props.slots?.attachment;
|
|
4355
|
+
const cardActionTimeoutMs = props.cardActionTimeoutMs;
|
|
4137
4356
|
const linkPreviewImages = chat.linkPreviewImages === true;
|
|
4138
4357
|
const topicsState = useTopics(threadRootId ? null : conversationId);
|
|
4139
4358
|
const [chosenTopic, setChosenTopic] = useState(null);
|
|
@@ -4214,9 +4433,9 @@ function ChatThreadScreen(props) {
|
|
|
4214
4433
|
}, [modules.haptics, toast, copy.actionFailed]);
|
|
4215
4434
|
const reply = useCallback(
|
|
4216
4435
|
(m) => {
|
|
4217
|
-
setReply(replyTargetFor(m, nameOf(m.senderId),
|
|
4436
|
+
setReply(replyTargetFor(m, nameOf(m.senderId), attachmentPreview(displayBody(m.body), m.attachments[0], attachmentKinds, Boolean(m.deletedAt))));
|
|
4218
4437
|
},
|
|
4219
|
-
[setReply, nameOf]
|
|
4438
|
+
[setReply, nameOf, attachmentKinds]
|
|
4220
4439
|
);
|
|
4221
4440
|
const onAction = useCallback(
|
|
4222
4441
|
(m, action) => {
|
|
@@ -4313,7 +4532,7 @@ function ChatThreadScreen(props) {
|
|
|
4313
4532
|
},
|
|
4314
4533
|
onLanded: (id) => {
|
|
4315
4534
|
const m = byId.get(id);
|
|
4316
|
-
if (m) AccessibilityInfo.announceForAccessibility?.(fill(copy.jumpedTo, { name: nameOf(m.senderId), text:
|
|
4535
|
+
if (m) AccessibilityInfo.announceForAccessibility?.(fill(copy.jumpedTo, { name: nameOf(m.senderId), text: attachmentPreview(displayBody(m.body), m.attachments[0], attachmentKinds, Boolean(m.deletedAt)) }));
|
|
4317
4536
|
},
|
|
4318
4537
|
onGone: () => {
|
|
4319
4538
|
toast.show({ title: copy.messageGone });
|
|
@@ -4352,11 +4571,13 @@ function ChatThreadScreen(props) {
|
|
|
4352
4571
|
linkPreviewImages,
|
|
4353
4572
|
attachmentKinds,
|
|
4354
4573
|
onOpenAttachment,
|
|
4574
|
+
attachmentSlot,
|
|
4575
|
+
cardActionTimeoutMs,
|
|
4355
4576
|
testID: `${testID}-message-${m?.id ?? row.key}`
|
|
4356
4577
|
}
|
|
4357
4578
|
);
|
|
4358
4579
|
},
|
|
4359
|
-
[testID, copy.newMessages, userId, urls, byId, group, reducedMotion, onLongPress, allowed, reply, onSwipeReply, onRetry, onDiscard, react, onOpenImage, picker, reattachable, onReattach, previews, linkPreviewImages, attachmentKinds, onOpenAttachment, observer, highlightedId]
|
|
4580
|
+
[testID, copy.newMessages, userId, urls, byId, group, reducedMotion, onLongPress, allowed, reply, onSwipeReply, onRetry, onDiscard, react, onOpenImage, picker, reattachable, onReattach, previews, linkPreviewImages, attachmentKinds, onOpenAttachment, attachmentSlot, cardActionTimeoutMs, observer, highlightedId]
|
|
4360
4581
|
);
|
|
4361
4582
|
const [videoOpen, setVideoOpen] = useState(false);
|
|
4362
4583
|
const preparedVideo = useRef(null);
|
|
@@ -4949,59 +5170,61 @@ function ChatSearchScreen({ conversationId, onOpen, conversationTitle, debounceM
|
|
|
4949
5170
|
useEffect(() => {
|
|
4950
5171
|
if (announce) AccessibilityInfo.announceForAccessibility?.(announce);
|
|
4951
5172
|
}, [announce]);
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
{
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
/* @__PURE__ */
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
/* @__PURE__ */ jsx(
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5173
|
+
const field = /* @__PURE__ */ jsx(View, { style: { paddingHorizontal: 16, paddingVertical: 8 }, children: /* @__PURE__ */ jsx(
|
|
5174
|
+
NativeSearchBar,
|
|
5175
|
+
{
|
|
5176
|
+
testID: `${testID}-field`,
|
|
5177
|
+
value,
|
|
5178
|
+
onChange: setValue,
|
|
5179
|
+
placeholder: conversationId ? copy.searchThreadPlaceholder : copy.searchPlaceholder,
|
|
5180
|
+
cancelLabel: copy.cancel,
|
|
5181
|
+
showCancel: false
|
|
5182
|
+
}
|
|
5183
|
+
) });
|
|
5184
|
+
const empty = short ? /* @__PURE__ */ jsx(NativeText, { preset: "caption", style: { paddingHorizontal: 20, color: colors.secondaryLabel }, testID: `${testID}-hint`, children: copy.searchHint }) : isSearching ? /* @__PURE__ */ jsxs(View, { testID: `${testID}-loading`, style: { padding: 16, gap: 10 }, children: [
|
|
5185
|
+
/* @__PURE__ */ jsx(NativeSkeleton, { height: 52, className: "w-full" }),
|
|
5186
|
+
/* @__PURE__ */ jsx(NativeSkeleton, { height: 52, className: "w-full" })
|
|
5187
|
+
] }) : /* @__PURE__ */ jsx(NativeEmptyState, { variant: "no-results", title: copy.searchNoResults, body: fill(copy.searchNoResultsBody, { query }), testID: `${testID}-empty` });
|
|
5188
|
+
return /* @__PURE__ */ jsx(SafeAreaView, { testID, edges: ["left", "right"], style: { flex: 1, backgroundColor: colors.groupedBackground }, children: /* @__PURE__ */ jsx(
|
|
5189
|
+
FlatList,
|
|
5190
|
+
{
|
|
5191
|
+
testID: `${testID}-results`,
|
|
5192
|
+
data: short ? [] : hits,
|
|
5193
|
+
keyExtractor: (h) => `${h.messageId}:${h.inTranscript}`,
|
|
5194
|
+
contentInsetAdjustmentBehavior: "automatic",
|
|
5195
|
+
keyboardShouldPersistTaps: "handled",
|
|
5196
|
+
keyboardDismissMode: "on-drag",
|
|
5197
|
+
ListHeaderComponent: field,
|
|
5198
|
+
ListEmptyComponent: empty,
|
|
5199
|
+
renderItem: ({ item: h }) => {
|
|
5200
|
+
const who = nameOf(h.senderId);
|
|
5201
|
+
const where = conversationId ? null : fill(copy.searchIn, { name: titleOf(h.conversationId) });
|
|
5202
|
+
const when = formatInboxTime(new Date(h.createdAt), now());
|
|
5203
|
+
return /* @__PURE__ */ jsxs(
|
|
5204
|
+
HitRow,
|
|
5205
|
+
{
|
|
5206
|
+
testID: `${testID}-hit-${h.messageId}`,
|
|
5207
|
+
accessibilityLabel: [who, where, when, h.inTranscript ? copy.transcript : null, snippetText(h.snippet)].filter(Boolean).join(", "),
|
|
5208
|
+
onPress: () => onOpen(h),
|
|
5209
|
+
children: [
|
|
5210
|
+
/* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", alignItems: "center", gap: 6 }, children: [
|
|
5211
|
+
/* @__PURE__ */ jsx(NativeText, { preset: "label", numberOfLines: 1, style: { flexShrink: 1 }, children: who }),
|
|
5212
|
+
where ? /* @__PURE__ */ jsx(NativeText, { preset: "caption", numberOfLines: 1, style: { flexShrink: 1, color: colors.secondaryLabel }, children: where }) : null,
|
|
5213
|
+
/* @__PURE__ */ jsx(NativeText, { preset: "caption", style: { marginLeft: "auto", color: colors.tertiaryLabel }, children: when })
|
|
5214
|
+
] }),
|
|
5215
|
+
/* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", alignItems: "flex-start", gap: 6 }, children: [
|
|
5216
|
+
h.inTranscript ? /* @__PURE__ */ jsx(NativeBadge, { variant: "muted", testID: `${testID}-transcript-${h.messageId}`, children: /* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", alignItems: "center", gap: 3 }, children: [
|
|
5217
|
+
/* @__PURE__ */ jsx(NativeIcon, { name: "mic", size: 11, color: colors.secondaryLabel }),
|
|
5218
|
+
/* @__PURE__ */ jsx(NativeText, { preset: "caption", style: { color: colors.secondaryLabel }, children: copy.transcript })
|
|
5219
|
+
] }) }) : null,
|
|
5220
|
+
/* @__PURE__ */ jsx(View, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(SearchSnippet, { snippet: h.snippet }) })
|
|
5221
|
+
] })
|
|
5222
|
+
]
|
|
5223
|
+
}
|
|
5224
|
+
);
|
|
5002
5225
|
}
|
|
5003
|
-
|
|
5004
|
-
|
|
5226
|
+
}
|
|
5227
|
+
) });
|
|
5005
5228
|
}
|
|
5006
5229
|
function HitRow({ testID, accessibilityLabel, onPress, children }) {
|
|
5007
5230
|
const colors = useThemeRoles();
|
|
@@ -5021,6 +5244,6 @@ function HitRow({ testID, accessibilityLabel, onPress, children }) {
|
|
|
5021
5244
|
);
|
|
5022
5245
|
}
|
|
5023
5246
|
|
|
5024
|
-
export { AT_BOTTOM_POINTS, ChatAccessError, ChatAttachmentCard, ChatAutoRetry, ChatAvailabilityScreen, ChatAwayLine, ChatBroadcastSheet, ChatComposer, ChatEmojiPicker, ChatEmojiPickerSheet, ChatIconButton, ChatInboxScreen, ChatLinkPreviewCard, ChatMediaViewer, ChatNativeProvider, ChatPhotoViewer, ChatScheduleSheet, ChatScheduledBar, ChatScheduledList, ChatSearchScreen, ChatStartButton, ChatStartSheet, ChatThreadScreen, ChatTopicBar, ChatVideoPlayer, ChatVideoRecorder, DISMISS_DISTANCE, DISMISS_VELOCITY, EMOJI_CATEGORIES, MAX_ZOOM, MessageActionsSheet, MessageAttachments, MessageBubble, ModerationMarker, QUICK_REACTIONS, RECENT_EMOJI_MAX, SearchSnippet, TUS_CHUNK_BYTES, VIDEO_CHUNK_BYTES, VIDEO_HOLD_MS, VIDEO_MIN_MS, VideoUploadTray, VoiceNotePlayer, activePlaybackId, attachmentPaths, availabilityInput, awayLineFor, barsThatFit, base64ToBytes, base64Utf8, broadcastOutcome, broadcastRows, buildThreadRows, canManageTopics, canReactTo, canSend, cardAction, chatNativeCopy, chatOrganiseCopy, chatThreadStackOptions, checkTopicName, chunkedPut, claimPlayback, clearDrafts, clockToDate, composerDisabledReason, composerReducer, createDraftSaver, createTusUpload, createVideoUploads, cycleVideoRate, cycleVoiceNoteRate, dateToClock, defaultScheduleAt, defaultWeek, deviceTimeZone, displayBody, draftKey, emojiEntry, emojiInCategory, emptyComposer, fileFromDataUri, fileSource, filesFromAssets, fill, forgetRecentEmoji, formFrom, formatClock, fullScreenVideo, galleryFor, haptic, hoursFromWeek, idleRecorder, inboxRow, initialVideoRecorder, initials, insertAtSelection, invertedRows, isLocalUri, isOnline, isOpenableHref, isVideoPlaying, linkHost, linkPreviewIds, liveBars, loadDraft, loadRecentEmoji, memoryDraftStorage, messageActionsFor, moderationNote, moderationText, muxPosterUrl, muxStreamUrl, nearBottom, nextOffset, openChatLink, panLimit, pauseChatPlayback, pinchScale, previewToShow, pushRecentEmoji, recentEmojiKey, recordedVideoFile, recorderStep, recordingType, releasePlayback, replyTargetFor, resolveChatCopy, rowIndexOf, rowKey, sameTimeDays, saveRecentEmoji, scheduleInputFrom, scrubSeconds, searchEmoji, shouldDismiss, snippetText, splitCardAttachments, threadListMode, timeZones, toSend, toggleVideo, toggleVideoMute, toggleVoiceNote, topicClosedFor, topicMenuFor, useAwayLine, useChatAutoRetry, useChatComposer, useChatNative, useChatThreadStackOptions, useJumpToMessage, usePlayingVideo, usePlayingVoiceNote, useScheduleSend, useVideoUploads, useVoiceRecorder, videoRecorderStep, videoSource, videoState, videoUploads, voiceNoteFile, waveformBars, weekFromHours, withDay, withTime };
|
|
5247
|
+
export { AT_BOTTOM_POINTS, AppAttachment, BUILT_IN_KINDS, CARD_ACTION_TIMEOUT_MS, CardError, ChatAccessError, ChatAttachmentCard, ChatAutoRetry, ChatAvailabilityScreen, ChatAwayLine, ChatBroadcastSheet, ChatComposer, ChatEmojiPicker, ChatEmojiPickerSheet, ChatIconButton, ChatInboxScreen, ChatLinkPreviewCard, ChatMediaViewer, ChatNativeProvider, ChatPhotoViewer, ChatScheduleSheet, ChatScheduledBar, ChatScheduledList, ChatSearchScreen, ChatStartButton, ChatStartSheet, ChatThreadScreen, ChatTopicBar, ChatVideoPlayer, ChatVideoRecorder, DISMISS_DISTANCE, DISMISS_VELOCITY, EMOJI_CATEGORIES, MAX_ZOOM, MessageActionsSheet, MessageAttachments, MessageBubble, ModerationMarker, QUICK_REACTIONS, RECENT_EMOJI_MAX, SearchSnippet, TRANSCRIPT_LINES, TUS_CHUNK_BYTES, VIDEO_CHUNK_BYTES, VIDEO_HOLD_MS, VIDEO_MIN_MS, VideoUploadTray, VoiceNotePlayer, VoiceTranscript, activePlaybackId, attachmentKindOf, attachmentPaths, availabilityInput, awayLineFor, barsThatFit, base64ToBytes, base64Utf8, broadcastOutcome, broadcastRows, buildThreadRows, canManageTopics, canReactTo, canSend, cardAction, chatNativeCopy, chatOrganiseCopy, chatThreadStackOptions, checkTopicName, chunkedPut, claimPlayback, clearDrafts, clockToDate, composerDisabledReason, composerReducer, createDraftSaver, createTusUpload, createVideoUploads, cycleVideoRate, cycleVoiceNoteRate, dateToClock, defaultScheduleAt, defaultWeek, deviceTimeZone, displayBody, draftKey, emojiEntry, emojiInCategory, emptyComposer, fileFromDataUri, fileSource, filesFromAssets, fill, forgetRecentEmoji, formFrom, formatClock, fullScreenVideo, galleryFor, haptic, hoursFromWeek, idleRecorder, inboxRow, initialVideoRecorder, initials, insertAtSelection, invertedRows, isLocalUri, isOnline, isOpenableHref, isVideoPlaying, linkHost, linkPreviewIds, liveBars, loadDraft, loadRecentEmoji, memoryDraftStorage, messageActionsFor, moderationNote, moderationText, muxPosterUrl, muxStreamUrl, nearBottom, nextOffset, openChatLink, panLimit, pauseChatPlayback, pinchScale, previewToShow, pushRecentEmoji, recentEmojiKey, recordedVideoFile, recorderStep, recordingType, releasePlayback, replyTargetFor, resolveChatCopy, rowIndexOf, rowKey, sameTimeDays, saveRecentEmoji, scheduleInputFrom, scrubSeconds, searchEmoji, shouldDismiss, slotUrl, snippetText, splitCardAttachments, threadListMode, timeZones, toSend, toggleVideo, toggleVideoMute, toggleVoiceNote, topicClosedFor, topicMenuFor, transcriptIsLong, useAwayLine, useCardRuns, useChatAutoRetry, useChatComposer, useChatNative, useChatThreadStackOptions, useJumpToMessage, usePlayingVideo, usePlayingVoiceNote, useScheduleSend, useVideoUploads, useVoiceRecorder, videoRecorderStep, videoSource, videoState, videoUploads, voiceNoteFile, waveformBars, weekFromHours, withDay, withTime };
|
|
5025
5248
|
//# sourceMappingURL=native.js.map
|
|
5026
5249
|
//# sourceMappingURL=native.js.map
|