@marktiderman/genesis-chat 0.2.0 → 0.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.
- package/README.md +82 -7
- package/dist/{chunk-5D5A7I4Q.js → chunk-A4YJX7KT.js} +130 -16
- package/dist/chunk-A4YJX7KT.js.map +1 -0
- package/dist/chunk-AZS3HNLM.js +30 -0
- package/dist/chunk-AZS3HNLM.js.map +1 -0
- package/dist/{chunk-7S6HYNWW.js → chunk-GBJ3YI4E.js} +72 -5
- package/dist/chunk-GBJ3YI4E.js.map +1 -0
- package/dist/{chunk-PZTOY2LY.js → chunk-JU2FAPBI.js} +46 -13
- package/dist/chunk-JU2FAPBI.js.map +1 -0
- package/dist/{client-DMa2j7kz.d.ts → client-DUIG1hum.d.ts} +36 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/native.d.ts +36 -7
- package/dist/native.js +239 -116
- package/dist/native.js.map +1 -1
- package/dist/{outbox-B0y3phE0.d.ts → outbox-B9NAWQ58.d.ts} +74 -6
- package/dist/push.d.ts +2 -2
- package/dist/react.d.ts +48 -21
- package/dist/react.js +3 -3
- package/dist/server.d.ts +43 -2
- package/dist/server.js +115 -15
- package/dist/server.js.map +1 -1
- package/dist/{thread-DWXHKPdw.d.ts → thread-oPra7pkW.d.ts} +1 -1
- package/dist/web.d.ts +31 -11
- package/dist/web.js +97 -48
- package/dist/web.js.map +1 -1
- package/migrations/0003_genesis_chat_search_threads.sql +85 -0
- package/migrations/tests/0002_genesis_chat_coaching.test.sql +7 -3
- package/migrations/tests/0003_genesis_chat_search_threads.test.sql +50 -0
- package/package.json +7 -3
- package/src/__tests__/coaching-core.test.ts +3 -1
- package/src/__tests__/coaching-react.test.tsx +73 -2
- package/src/__tests__/coaching-web.test.tsx +78 -6
- package/src/__tests__/fake-xhr.ts +54 -0
- package/src/__tests__/outbox-hosted.test.ts +269 -0
- package/src/__tests__/review-279.test.ts +41 -0
- package/src/__tests__/review-fixes-web.test.tsx +13 -10
- package/src/__tests__/server-fake-video.test.ts +81 -0
- package/src/__tests__/server-fakes.ts +7 -1
- package/src/__tests__/server-safety.test.ts +17 -4
- package/src/__tests__/web.test.tsx +16 -0
- package/src/core/client.ts +28 -2
- package/src/core/outbox.ts +222 -16
- package/src/core/transfer.ts +71 -0
- package/src/core/types.ts +17 -0
- package/src/index.ts +1 -0
- package/src/native/__tests__/emoji.test.tsx +16 -0
- package/src/native/__tests__/organise-screens.test.tsx +80 -0
- package/src/native/__tests__/video-media.test.tsx +81 -4
- package/src/native/attachment-cards.tsx +22 -6
- package/src/native/inbox-screen.tsx +12 -6
- package/src/native/index.ts +2 -2
- package/src/native/message-actions-sheet.tsx +10 -5
- package/src/native/message-bubble.tsx +3 -11
- package/src/native/modules.ts +2 -0
- package/src/native/schedule-sheet.tsx +23 -9
- package/src/native/search-screen.tsx +26 -5
- package/src/native/thread-screen.tsx +30 -3
- package/src/native/topics.tsx +70 -15
- package/src/native/video-player.tsx +46 -6
- package/src/native/video-recorder.tsx +6 -3
- package/src/native/voice-player.tsx +18 -0
- package/src/react/index.tsx +103 -19
- package/src/react/previews.ts +51 -0
- package/src/server/fake-video.ts +171 -0
- package/src/server/index.ts +1 -0
- package/src/web/attachments.tsx +6 -6
- package/src/web/bubble.tsx +22 -0
- package/src/web/inbox.tsx +7 -6
- package/src/web/memory-client.ts +11 -3
- package/src/web/panel.tsx +2 -2
- package/src/web/schedule.tsx +14 -2
- package/src/web/thread.tsx +36 -8
- package/src/web/video.tsx +28 -34
- package/dist/chunk-5D5A7I4Q.js.map +0 -1
- package/dist/chunk-7S6HYNWW.js.map +0 -1
- package/dist/chunk-PZTOY2LY.js.map +0 -1
package/dist/native.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
+
import { cardTitle, callAttachmentKind, attachmentPreview, messagePreview } from './chunk-AZS3HNLM.js';
|
|
2
|
+
export { attachmentPreview, cardTitle, messagePreview } from './chunk-AZS3HNLM.js';
|
|
1
3
|
import { isHttpsUrl, extractLinks, scheduleLabel, scheduleQuickPicks, isSchedulable, topicKey, snippetParts } from './chunk-BJCE5O7V.js';
|
|
2
|
-
import { useChat, useInbox, usePeople, chatKeys, useScheduled, useAvailability, useTopics, useConversation, usePins, useMessageActions, useFileUrls, useLinkPreviews, useStartConversation, useBroadcast, useSearch } from './chunk-
|
|
3
|
-
import { summarizeReactions, previewText, parseMentions, startsDay, dayLabel, itemTime, itemSender, startsGroup, activityLine } from './chunk-
|
|
4
|
-
import { formatDuration, nextPlaybackRate, ChatError, CHAT_LIMITS, fileExtension, isAllowedExtension, attachmentKindFor, awaySummary, WEEKDAYS, minutesOf, waveformFromMetering, recordingLimitMs, CHAT_BUCKET, randomId } from './chunk-
|
|
4
|
+
import { useChat, useInbox, usePeople, chatKeys, useScheduled, useAvailability, useTopics, useConversation, usePins, useMessageActions, useFileUrls, useLinkPreviews, useStartConversation, useBroadcast, useSearch } from './chunk-JU2FAPBI.js';
|
|
5
|
+
import { summarizeReactions, previewText, parseMentions, startsDay, dayLabel, itemTime, itemSender, startsGroup, activityLine } from './chunk-A4YJX7KT.js';
|
|
6
|
+
import { formatDuration, nextPlaybackRate, ChatError, CHAT_LIMITS, fileExtension, isAllowedExtension, attachmentKindFor, awaySummary, WEEKDAYS, minutesOf, waveformFromMetering, recordingLimitMs, CHAT_BUCKET, randomId } from './chunk-GBJ3YI4E.js';
|
|
5
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-VEPZROQB.js';
|
|
6
8
|
import { createContext, memo, useMemo, useRef, useContext, useSyncExternalStore, useState, useEffect, useCallback, useReducer } from 'react';
|
|
7
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';
|
|
8
10
|
import { InboxPage, SettingsSubPage, getThreadPageStackOptions, formatInboxTime } from '@marktiderman/genesis-ui-native/layouts';
|
|
9
|
-
import {
|
|
10
|
-
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
11
|
-
import { Pressable, Animated, PanResponder, View, Linking, useWindowDimensions, ActivityIndicator, Modal, AccessibilityInfo, ScrollView, FlatList, AppState, TextInput, Platform, KeyboardAvoidingView, Alert } from 'react-native';
|
|
11
|
+
import { Pressable, Animated, PanResponder, View, Linking, useWindowDimensions, ActivityIndicator, AppState, Modal, AccessibilityInfo, ScrollView, FlatList, TextInput, Platform, KeyboardAvoidingView, Alert } from 'react-native';
|
|
12
12
|
import { useThemeRoles, useReducedMotion } from '@marktiderman/genesis-ui-native/hooks';
|
|
13
|
-
import {
|
|
13
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
14
|
+
import { useQueryClient } from '@tanstack/react-query';
|
|
15
|
+
import { useSafeAreaInsets, SafeAreaView } from 'react-native-safe-area-context';
|
|
14
16
|
|
|
15
17
|
// src/native/organise-copy.ts
|
|
16
18
|
var chatOrganiseCopy = {
|
|
@@ -295,10 +297,10 @@ function isOpenableHref(href) {
|
|
|
295
297
|
return false;
|
|
296
298
|
}
|
|
297
299
|
}
|
|
298
|
-
function cardAction(kind, attachment, message, onOpenAttachment) {
|
|
300
|
+
function cardAction(kind, attachment, message, onOpenAttachment, ctx = { mine: false }) {
|
|
299
301
|
let action = null;
|
|
300
302
|
try {
|
|
301
|
-
action = kind
|
|
303
|
+
action = callAttachmentKind(kind, "action", attachment, ctx);
|
|
302
304
|
} catch {
|
|
303
305
|
return null;
|
|
304
306
|
}
|
|
@@ -313,14 +315,15 @@ function ChatAttachmentCard({ attachment, message, kind, mine = false, onOpenAtt
|
|
|
313
315
|
const colors = useThemeRoles();
|
|
314
316
|
let title = "";
|
|
315
317
|
let subtitle = null;
|
|
318
|
+
const ctx = { mine };
|
|
316
319
|
try {
|
|
317
|
-
title = kind
|
|
318
|
-
subtitle = kind
|
|
320
|
+
title = callAttachmentKind(kind, "title", attachment, ctx) ?? "";
|
|
321
|
+
subtitle = callAttachmentKind(kind, "subtitle", attachment, ctx);
|
|
319
322
|
} catch (e) {
|
|
320
323
|
if (typeof __DEV__ !== "undefined" && __DEV__) console.warn(`[genesis-chat] attachment kind "${attachment.kind}" threw:`, e);
|
|
321
324
|
}
|
|
322
325
|
title = title || attachment.name || attachment.kind;
|
|
323
|
-
const action = cardAction(kind, attachment, message, onOpenAttachment);
|
|
326
|
+
const action = cardAction(kind, attachment, message, onOpenAttachment, ctx);
|
|
324
327
|
const fg = mine ? "text-primary-foreground" : void 0;
|
|
325
328
|
return /* @__PURE__ */ jsxs(
|
|
326
329
|
View,
|
|
@@ -450,6 +453,17 @@ function cycleVoiceNoteRate(id) {
|
|
|
450
453
|
c?.cycleRate();
|
|
451
454
|
return Boolean(c);
|
|
452
455
|
}
|
|
456
|
+
function pauseChatPlayback(ids) {
|
|
457
|
+
if (!active) return false;
|
|
458
|
+
if (typeof ids === "function" ? !ids(active.id) : ids && !new Set(ids).has(active.id)) return false;
|
|
459
|
+
const { id, pause } = active;
|
|
460
|
+
try {
|
|
461
|
+
pause();
|
|
462
|
+
} catch {
|
|
463
|
+
}
|
|
464
|
+
releasePlayback(id);
|
|
465
|
+
return true;
|
|
466
|
+
}
|
|
453
467
|
function activePlaybackId() {
|
|
454
468
|
return active?.id ?? null;
|
|
455
469
|
}
|
|
@@ -783,9 +797,14 @@ function ActiveVideo({
|
|
|
783
797
|
const [muted, setMuted] = useState(false);
|
|
784
798
|
const [rate, setRate] = useState(1);
|
|
785
799
|
const ended = useRef(false);
|
|
800
|
+
const fullScreenOpen = useRef(false);
|
|
801
|
+
const setUp = useRef(false);
|
|
786
802
|
useEffect(() => {
|
|
787
|
-
|
|
788
|
-
|
|
803
|
+
const now = setUp.current ? Boolean(player.playing) : true;
|
|
804
|
+
setUp.current = true;
|
|
805
|
+
setPlaying(now);
|
|
806
|
+
setPlayingId(id, now);
|
|
807
|
+
if (now) claimPlayback(id, () => player.pause());
|
|
789
808
|
const subs = [
|
|
790
809
|
player.addListener("playingChange", ((payload) => {
|
|
791
810
|
setPlaying(payload.isPlaying);
|
|
@@ -805,8 +824,21 @@ function ActiveVideo({
|
|
|
805
824
|
if (payload.status === "error") latestBroken.current();
|
|
806
825
|
}))
|
|
807
826
|
];
|
|
827
|
+
const appSub = AppState?.addEventListener?.("change", (next) => {
|
|
828
|
+
if (next === "background" && !fullScreenOpen.current) {
|
|
829
|
+
try {
|
|
830
|
+
player.pause();
|
|
831
|
+
} catch {
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
});
|
|
808
835
|
return () => {
|
|
809
836
|
subs.forEach((s) => s.remove());
|
|
837
|
+
appSub?.remove?.();
|
|
838
|
+
try {
|
|
839
|
+
player.pause();
|
|
840
|
+
} catch {
|
|
841
|
+
}
|
|
810
842
|
setPlayingId(id, false);
|
|
811
843
|
releasePlayback(id);
|
|
812
844
|
};
|
|
@@ -834,7 +866,24 @@ function ActiveVideo({
|
|
|
834
866
|
handles.current = { toggle, fullScreen, mute: toggleMute, rate: cycleRate };
|
|
835
867
|
const { VideoView } = video;
|
|
836
868
|
return /* @__PURE__ */ jsxs(View, { testID: `${testID}-active`, style, children: [
|
|
837
|
-
/* @__PURE__ */ jsx(
|
|
869
|
+
/* @__PURE__ */ jsx(
|
|
870
|
+
VideoView,
|
|
871
|
+
{
|
|
872
|
+
ref: viewRef,
|
|
873
|
+
player,
|
|
874
|
+
nativeControls: false,
|
|
875
|
+
contentFit: "cover",
|
|
876
|
+
allowsPictureInPicture: false,
|
|
877
|
+
onFullscreenEnter: () => {
|
|
878
|
+
fullScreenOpen.current = true;
|
|
879
|
+
},
|
|
880
|
+
onFullscreenExit: () => {
|
|
881
|
+
fullScreenOpen.current = false;
|
|
882
|
+
},
|
|
883
|
+
style: { width: "100%", height: "100%" },
|
|
884
|
+
testID: `${testID}-view`
|
|
885
|
+
}
|
|
886
|
+
),
|
|
838
887
|
/* @__PURE__ */ jsx(
|
|
839
888
|
Pressable,
|
|
840
889
|
{
|
|
@@ -1054,7 +1103,7 @@ function MessageBubbleImpl(props) {
|
|
|
1054
1103
|
const photos = attachments.filter((a) => a.kind === "image" && a.uri && props.onOpenImage);
|
|
1055
1104
|
const videos = attachments.filter((a) => a.kind === "video" && modules.video && (a.uri || a.playbackId));
|
|
1056
1105
|
const playingVideo = usePlayingVideo(videos.map((a) => a.key));
|
|
1057
|
-
const cardActions = message ? cards.map((a) => cardAction(kinds?.[a.kind], a, message, props.onOpenAttachment)) : [];
|
|
1106
|
+
const cardActions = message ? cards.map((a) => cardAction(kinds?.[a.kind], a, message, props.onOpenAttachment, { mine })) : [];
|
|
1058
1107
|
const playingVoice = usePlayingVoiceNote(voices.map((a) => a.key));
|
|
1059
1108
|
const x = useRef(new Animated.Value(0)).current;
|
|
1060
1109
|
const latest = useRef(props);
|
|
@@ -1096,7 +1145,7 @@ function MessageBubbleImpl(props) {
|
|
|
1096
1145
|
senderName,
|
|
1097
1146
|
message?.deletedAt ? copy.deleted : displayBody(body),
|
|
1098
1147
|
attachments.map((a) => a.kind === "image" ? copy.photo : a.kind === "voice" ? copy.voice : a.kind === "video" ? copy.video : copy.file).join(", "),
|
|
1099
|
-
cards.map((a) => cardTitle(kinds?.[a.kind], a)).join(", "),
|
|
1148
|
+
cards.map((a) => cardTitle(kinds?.[a.kind], a, { mine })).join(", "),
|
|
1100
1149
|
preview ? fill(copy.linkPreview, { title: preview.title?.trim() || linkHost(preview.url), site: preview.siteName?.trim() || linkHost(preview.url) }) : null,
|
|
1101
1150
|
time,
|
|
1102
1151
|
message?.editedAt ? copy.edited : null,
|
|
@@ -1239,19 +1288,12 @@ function MessageBubbleImpl(props) {
|
|
|
1239
1288
|
function SmallAction({ label, onPress, color, testID }) {
|
|
1240
1289
|
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 }) });
|
|
1241
1290
|
}
|
|
1242
|
-
function cardTitle(kind, a) {
|
|
1243
|
-
try {
|
|
1244
|
-
return kind?.title(a) || a.name || a.kind;
|
|
1245
|
-
} catch {
|
|
1246
|
-
return a.name || a.kind;
|
|
1247
|
-
}
|
|
1248
|
-
}
|
|
1249
1291
|
var MessageBubble = memo(MessageBubbleImpl);
|
|
1250
1292
|
function inboxRow(item, ctx) {
|
|
1251
1293
|
const other = item.otherUserId ? ctx.person(item.otherUserId) : void 0;
|
|
1252
1294
|
const title = item.title ?? other?.name ?? ctx.copy.untitled;
|
|
1253
1295
|
const last = item.lastMessage;
|
|
1254
|
-
const text = last ?
|
|
1296
|
+
const text = last ? attachmentPreview(displayBody(last.body), last.attachment, ctx.kinds, last.deleted) : "";
|
|
1255
1297
|
const preview = last && text && last.senderId === ctx.me && last.kind !== "system" ? `${ctx.copy.youPrefix}${text}` : text;
|
|
1256
1298
|
return {
|
|
1257
1299
|
id: item.conversationId,
|
|
@@ -1265,7 +1307,7 @@ function inboxRow(item, ctx) {
|
|
|
1265
1307
|
};
|
|
1266
1308
|
}
|
|
1267
1309
|
function ChatInboxScreen({ onOpen, onStart, kind, kinds, excludeKinds, archived, pageProps, testID = "chat-inbox" }) {
|
|
1268
|
-
const { client, userId } = useChat();
|
|
1310
|
+
const { client, userId, attachmentKinds } = useChat();
|
|
1269
1311
|
const queryClient = useQueryClient();
|
|
1270
1312
|
const { copy, person: known } = useChatNative();
|
|
1271
1313
|
const inbox = useInbox({ kind, kinds, excludeKinds, archived });
|
|
@@ -1286,7 +1328,7 @@ function ChatInboxScreen({ onOpen, onStart, kind, kinds, excludeKinds, archived,
|
|
|
1286
1328
|
void queryClient.invalidateQueries({ queryKey: chatKeys.unread() });
|
|
1287
1329
|
}, [queryClient]);
|
|
1288
1330
|
const items = inbox.items.map((item) => ({
|
|
1289
|
-
...inboxRow(item, { me: userId, copy, person }),
|
|
1331
|
+
...inboxRow(item, { me: userId, copy, person, kinds: attachmentKinds }),
|
|
1290
1332
|
onPress: () => onOpen(item.conversationId),
|
|
1291
1333
|
swipeActions: [
|
|
1292
1334
|
...item.unread > 0 ? [{ id: "read", label: copy.markRead, onPress: () => void client.markRead(item.conversationId).then(settle, settle) }] : [],
|
|
@@ -1414,6 +1456,7 @@ function ChatVideoRecorder(props) {
|
|
|
1414
1456
|
}
|
|
1415
1457
|
function RecorderBody({ camera, onClose, limitMs, onStart, onTick, onRecorded, now = Date.now, testID }) {
|
|
1416
1458
|
const { copy, modules } = useChatNative();
|
|
1459
|
+
const insets = useSafeAreaInsets();
|
|
1417
1460
|
const colors = useThemeRoles();
|
|
1418
1461
|
const [state, setState] = useState(() => initialVideoRecorder());
|
|
1419
1462
|
const stateRef = useRef(state);
|
|
@@ -1516,7 +1559,7 @@ function RecorderBody({ camera, onClose, limitMs, onStart, onTick, onRecorded, n
|
|
|
1516
1559
|
},
|
|
1517
1560
|
state.cameraKey
|
|
1518
1561
|
) : null,
|
|
1519
|
-
/* @__PURE__ */ jsxs(
|
|
1562
|
+
/* @__PURE__ */ jsxs(View, { style: { flex: 1, justifyContent: "space-between", paddingTop: insets.top, paddingBottom: insets.bottom, paddingLeft: insets.left, paddingRight: insets.right }, children: [
|
|
1520
1563
|
/* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", paddingHorizontal: 8, paddingTop: 4 }, children: [
|
|
1521
1564
|
/* @__PURE__ */ jsx(
|
|
1522
1565
|
Pressable,
|
|
@@ -2176,35 +2219,27 @@ function ChatScheduleSheet({ open, onOpenChange, onSchedule, now, testID = "chat
|
|
|
2176
2219
|
setCustom(false);
|
|
2177
2220
|
onSchedule(when);
|
|
2178
2221
|
};
|
|
2179
|
-
return /* @__PURE__ */ jsx(
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
/* @__PURE__ */
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
/* @__PURE__ */ jsx(NativeButton, { testID: `${testID}-confirm`, onPress: () => ok && choose(at), disabled: !ok, children: copy.scheduleConfirm })
|
|
2201
|
-
] }) : /* @__PURE__ */ jsxs(View, { children: [
|
|
2202
|
-
picks.map((p, i) => /* @__PURE__ */ jsx(NativeListItem, { testID: `${testID}-pick-${i}`, title: p.label, leading: /* @__PURE__ */ jsx(NativeIcon, { name: "clock" }), onPress: () => choose(p.at) }, p.label)),
|
|
2203
|
-
/* @__PURE__ */ jsx(NativeListItem, { testID: `${testID}-pick-custom`, title: copy.schedulePick, leading: /* @__PURE__ */ jsx(NativeIcon, { name: "calendar" }), showChevron: true, onPress: () => setCustom(true) })
|
|
2204
|
-
] })
|
|
2205
|
-
] })
|
|
2206
|
-
}
|
|
2207
|
-
);
|
|
2222
|
+
return /* @__PURE__ */ jsx(NativeSheet, { open, onOpenChange, fitContent: true, testID, children: /* @__PURE__ */ jsxs(View, { style: { paddingVertical: 8 }, children: [
|
|
2223
|
+
/* @__PURE__ */ jsxs(View, { style: { paddingHorizontal: 16, paddingBottom: 8, gap: 4 }, children: [
|
|
2224
|
+
/* @__PURE__ */ jsx(NativeText, { preset: "h4", accessibilityRole: "header", children: copy.scheduleTitle }),
|
|
2225
|
+
/* @__PURE__ */ jsx(NativeText, { preset: "caption", style: { color: colors.secondaryLabel }, children: copy.scheduleHint })
|
|
2226
|
+
] }),
|
|
2227
|
+
custom ? /* @__PURE__ */ jsxs(View, { style: { paddingHorizontal: 16, gap: 12 }, testID: `${testID}-custom`, children: [
|
|
2228
|
+
/* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", gap: 12, minHeight: 44 }, children: [
|
|
2229
|
+
/* @__PURE__ */ jsx(NativeText, { preset: "body", children: copy.scheduleDate }),
|
|
2230
|
+
/* @__PURE__ */ jsx(NativeDatePicker, { testID: `${testID}-date`, value: at, onChange: (d) => setAt((x) => withDay(x, d)) })
|
|
2231
|
+
] }),
|
|
2232
|
+
/* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", gap: 12, minHeight: 44 }, children: [
|
|
2233
|
+
/* @__PURE__ */ jsx(NativeText, { preset: "body", children: copy.scheduleTime }),
|
|
2234
|
+
/* @__PURE__ */ jsx(NativeTimePicker, { testID: `${testID}-time`, value: at, onChange: (d) => setAt((x) => withTime(x, d)) })
|
|
2235
|
+
] }),
|
|
2236
|
+
/* @__PURE__ */ jsx(NativeText, { testID: `${testID}-when`, preset: "caption", accessibilityLiveRegion: "polite", style: { color: ok ? colors.secondaryLabel : colors.destructive }, children: ok ? scheduleLabel(at, clock) : copy.scheduleOutOfRange }),
|
|
2237
|
+
/* @__PURE__ */ jsx(NativeButton, { testID: `${testID}-confirm`, onPress: () => ok && choose(at), disabled: !ok, children: copy.scheduleConfirm })
|
|
2238
|
+
] }) : /* @__PURE__ */ jsxs(View, { children: [
|
|
2239
|
+
picks.map((p, i) => /* @__PURE__ */ jsx(NativeListItem, { testID: `${testID}-pick-${i}`, title: p.label, leading: /* @__PURE__ */ jsx(NativeIcon, { name: "clock" }), onPress: () => choose(p.at) }, p.label)),
|
|
2240
|
+
/* @__PURE__ */ jsx(NativeListItem, { testID: `${testID}-pick-custom`, title: copy.schedulePick, leading: /* @__PURE__ */ jsx(NativeIcon, { name: "calendar" }), showChevron: true, onPress: () => setCustom(true) })
|
|
2241
|
+
] })
|
|
2242
|
+
] }) });
|
|
2208
2243
|
}
|
|
2209
2244
|
function ChatScheduledList({ conversationId, conversationTitle, testID = "chat-scheduled" }) {
|
|
2210
2245
|
const { copy } = useChatNative();
|
|
@@ -2231,12 +2266,20 @@ function ChatScheduledList({ conversationId, conversationTitle, testID = "chat-s
|
|
|
2231
2266
|
s.id
|
|
2232
2267
|
)) });
|
|
2233
2268
|
}
|
|
2269
|
+
function scheduledActionLabel(item, copy, kinds) {
|
|
2270
|
+
const text = attachmentPreview(displayBody(item.body), item.attachments[0]?.kind ?? null, kinds);
|
|
2271
|
+
const chars = Array.from(text);
|
|
2272
|
+
const short = chars.length > 60 ? `${chars.slice(0, 59).join("")}\u2026` : text;
|
|
2273
|
+
if (item.status === "failed") return [copy.scheduledRemove, short].filter(Boolean).join(", ");
|
|
2274
|
+
return [copy.scheduledCancel, short, fill(copy.scheduledFor, { when: scheduleLabel(item.sendAt) })].filter(Boolean).join(", ");
|
|
2275
|
+
}
|
|
2234
2276
|
function ScheduledRow({ item, where, onCancel, testID }) {
|
|
2235
2277
|
const { copy } = useChatNative();
|
|
2236
2278
|
const colors = useThemeRoles();
|
|
2279
|
+
const { attachmentKinds } = useChat();
|
|
2237
2280
|
const failed = item.status === "failed";
|
|
2238
2281
|
const when = failed ? copy.scheduledCouldntSend : scheduleLabel(item.sendAt);
|
|
2239
|
-
const text =
|
|
2282
|
+
const text = attachmentPreview(displayBody(item.body), item.attachments[0]?.kind ?? null, attachmentKinds);
|
|
2240
2283
|
return /* @__PURE__ */ jsxs(View, { testID, style: { flexDirection: "row", alignItems: "center", gap: 12, paddingLeft: 16, paddingRight: 4, paddingVertical: 8, minHeight: 56 }, children: [
|
|
2241
2284
|
/* @__PURE__ */ jsx(NativeIcon, { name: failed ? "warning" : "clock", size: 18, color: failed ? colors.destructive : colors.secondaryLabel }),
|
|
2242
2285
|
/* @__PURE__ */ jsxs(View, { style: { flex: 1, gap: 2 }, accessible: true, accessibilityLabel: [when, where, text, item.error].filter(Boolean).join(", "), children: [
|
|
@@ -2251,7 +2294,7 @@ function ScheduledRow({ item, where, onCancel, testID }) {
|
|
|
2251
2294
|
{
|
|
2252
2295
|
testID: `${testID}-cancel`,
|
|
2253
2296
|
accessibilityRole: "button",
|
|
2254
|
-
accessibilityLabel:
|
|
2297
|
+
accessibilityLabel: scheduledActionLabel(item, copy, attachmentKinds),
|
|
2255
2298
|
onPress: onCancel,
|
|
2256
2299
|
style: { minHeight: 44, minWidth: 44, paddingHorizontal: 12, alignItems: "center", justifyContent: "center" },
|
|
2257
2300
|
children: /* @__PURE__ */ jsx(NativeText, { preset: "label", style: { color: colors.destructive }, children: failed ? copy.scheduledRemove : copy.scheduledCancel })
|
|
@@ -2556,6 +2599,12 @@ function checkTopicName(title, topics, renaming) {
|
|
|
2556
2599
|
const key = topicKey(name);
|
|
2557
2600
|
return topics.some((t) => t.topic === key) ? { error: "exists" } : { key };
|
|
2558
2601
|
}
|
|
2602
|
+
function scrollToShow(chip, view, margin = 8) {
|
|
2603
|
+
if (view.width <= 0) return null;
|
|
2604
|
+
if (chip.x - margin < view.offset) return Math.max(0, chip.x - margin);
|
|
2605
|
+
if (chip.x + chip.width + margin > view.offset + view.width) return Math.max(0, chip.x + chip.width + margin - view.width);
|
|
2606
|
+
return null;
|
|
2607
|
+
}
|
|
2559
2608
|
function ChatTopicBar({ topics, value, onValueChange, canManage = false, onSetTopic, testID = "chat-topics" }) {
|
|
2560
2609
|
const { copy } = useChatNative();
|
|
2561
2610
|
const colors = useThemeRoles();
|
|
@@ -2565,6 +2614,28 @@ function ChatTopicBar({ topics, value, onValueChange, canManage = false, onSetTo
|
|
|
2565
2614
|
const [busy, setBusy] = useState(false);
|
|
2566
2615
|
const [error, setError] = useState(null);
|
|
2567
2616
|
const current = value ? topics.find((t) => t.topic === value) ?? null : null;
|
|
2617
|
+
const scrollRef = useRef(null);
|
|
2618
|
+
const chips = useRef(/* @__PURE__ */ new Map());
|
|
2619
|
+
const view = useRef({ offset: 0, width: 0 });
|
|
2620
|
+
const reveal = useCallback((key) => {
|
|
2621
|
+
const chip = chips.current.get(key ?? ALL);
|
|
2622
|
+
if (!chip) return;
|
|
2623
|
+
const to = scrollToShow(chip, view.current);
|
|
2624
|
+
if (to === null) return;
|
|
2625
|
+
view.current.offset = to;
|
|
2626
|
+
scrollRef.current?.scrollTo?.({ x: to, animated: true });
|
|
2627
|
+
}, []);
|
|
2628
|
+
const latestValue = useRef(value);
|
|
2629
|
+
latestValue.current = value;
|
|
2630
|
+
useEffect(() => reveal(value), [value, reveal]);
|
|
2631
|
+
const trackOffset = (e) => {
|
|
2632
|
+
view.current.offset = e.nativeEvent.contentOffset.x;
|
|
2633
|
+
};
|
|
2634
|
+
const onChipLayout = (key) => (e) => {
|
|
2635
|
+
const { x, width } = e.nativeEvent.layout;
|
|
2636
|
+
chips.current.set(key, { x, width });
|
|
2637
|
+
if (key === (latestValue.current ?? ALL)) reveal(latestValue.current);
|
|
2638
|
+
};
|
|
2568
2639
|
const close = () => {
|
|
2569
2640
|
if (busy) return;
|
|
2570
2641
|
setSheet(null);
|
|
@@ -2606,15 +2677,34 @@ function ChatTopicBar({ topics, value, onValueChange, canManage = false, onSetTo
|
|
|
2606
2677
|
}
|
|
2607
2678
|
};
|
|
2608
2679
|
return /* @__PURE__ */ jsxs(View, { testID, style: { flexDirection: "row", alignItems: "center", backgroundColor: colors.groupedBackground, paddingLeft: 8 }, children: [
|
|
2609
|
-
/* @__PURE__ */ jsx(
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2680
|
+
/* @__PURE__ */ jsx(
|
|
2681
|
+
ScrollView,
|
|
2682
|
+
{
|
|
2683
|
+
ref: scrollRef,
|
|
2684
|
+
horizontal: true,
|
|
2685
|
+
showsHorizontalScrollIndicator: false,
|
|
2686
|
+
accessibilityLabel: copy.topics,
|
|
2687
|
+
contentContainerStyle: { paddingVertical: 4, paddingRight: 8 },
|
|
2688
|
+
style: { flex: 1 },
|
|
2689
|
+
onLayout: (e) => {
|
|
2690
|
+
view.current.width = e.nativeEvent.layout.width;
|
|
2691
|
+
reveal(latestValue.current);
|
|
2692
|
+
},
|
|
2693
|
+
onScroll: trackOffset,
|
|
2694
|
+
onScrollEndDrag: trackOffset,
|
|
2695
|
+
onMomentumScrollEnd: trackOffset,
|
|
2696
|
+
scrollEventThrottle: 64,
|
|
2697
|
+
children: /* @__PURE__ */ jsxs(NativeChipGroup, { mode: "single", value: value ?? ALL, onChange: (v) => onValueChange(v === ALL ? null : v), className: "flex-nowrap", testID: `${testID}-group`, children: [
|
|
2698
|
+
/* @__PURE__ */ jsx(View, { onLayout: onChipLayout(ALL), children: /* @__PURE__ */ jsx(NativeChip, { value: ALL, testID: `${testID}-all`, children: copy.topicAll }) }),
|
|
2699
|
+
topics.map((t) => /* @__PURE__ */ jsx(View, { onLayout: onChipLayout(t.topic), children: /* @__PURE__ */ jsx(NativeChip, { value: t.topic, testID: `${testID}-${t.topic}`, children: /* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", alignItems: "center", gap: 4 }, children: [
|
|
2700
|
+
t.closedAt ? /* @__PURE__ */ jsx(NativeIcon, { name: "lock", size: 13, color: colors.secondaryLabel, label: copy.topicClosed }) : null,
|
|
2701
|
+
/* @__PURE__ */ jsx(NativeText, { preset: "body-sm", numberOfLines: 1, style: { maxWidth: 180, fontWeight: value === t.topic ? "600" : "400", color: value === t.topic ? colors.tint : colors.label }, children: t.title })
|
|
2702
|
+
] }) }) }, t.topic))
|
|
2703
|
+
] })
|
|
2704
|
+
}
|
|
2705
|
+
),
|
|
2616
2706
|
canManage && onSetTopic ? /* @__PURE__ */ jsx(ChatIconButton, { testID: `${testID}-menu`, icon: "more", label: copy.topicOptions, onPress: () => setSheet({ step: "menu" }) }) : null,
|
|
2617
|
-
/* @__PURE__ */ jsx(NativeSheet, { open: sheet !== null, onOpenChange: (o) => o ? void 0 : close(), testID: `${testID}-sheet`, children: sheet?.step === "name" ? /* @__PURE__ */ jsxs(View, { style: { padding: 16, gap: 12 }, children: [
|
|
2707
|
+
/* @__PURE__ */ jsx(NativeSheet, { open: sheet !== null, onOpenChange: (o) => o ? void 0 : close(), fitContent: true, testID: `${testID}-sheet`, children: sheet?.step === "name" ? /* @__PURE__ */ jsxs(View, { style: { padding: 16, gap: 12 }, children: [
|
|
2618
2708
|
/* @__PURE__ */ jsx(NativeText, { preset: "h4", accessibilityRole: "header", children: sheet.renaming ? copy.topicRename : copy.topicNew }),
|
|
2619
2709
|
/* @__PURE__ */ jsx(
|
|
2620
2710
|
NativeInput,
|
|
@@ -3909,7 +3999,7 @@ function messageActionsFor(message, ctx) {
|
|
|
3909
3999
|
return out;
|
|
3910
4000
|
}
|
|
3911
4001
|
var ICONS = { reply: "reply", copy: "copy", pin: "pin", unpin: "pin", delete: "trash" };
|
|
3912
|
-
function MessageActionsSheet({ message, context, onClose, onReact, onAction, testID = "chat-message-actions" }) {
|
|
4002
|
+
function MessageActionsSheet({ message, context, onClose, onReact, onAction, attachmentKinds, testID = "chat-message-actions" }) {
|
|
3913
4003
|
const { copy } = useChatNative();
|
|
3914
4004
|
const colors = useThemeRoles();
|
|
3915
4005
|
const actions = message ? messageActionsFor(message, context) : [];
|
|
@@ -3942,51 +4032,54 @@ function MessageActionsSheet({ message, context, onClose, onReact, onAction, tes
|
|
|
3942
4032
|
onReact(message, emoji, !mineReactions.has(emoji));
|
|
3943
4033
|
}
|
|
3944
4034
|
}
|
|
3945
|
-
) }) : message ?
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
4035
|
+
) }) : message ? (
|
|
4036
|
+
// Room at the top: the iOS page sheet has no grabber, and its rounded corner clipped the caption.
|
|
4037
|
+
/* @__PURE__ */ jsxs(View, { style: { gap: 12, paddingTop: 20, paddingBottom: 8, paddingHorizontal: 16 }, children: [
|
|
4038
|
+
/* @__PURE__ */ jsx(NativeText, { testID: `${testID}-caption`, numberOfLines: 2, preset: "body-sm", style: { color: colors.secondaryLabel, paddingHorizontal: 4 }, children: messagePreview(message, attachmentKinds) }),
|
|
4039
|
+
canReactTo(message, context) ? /* @__PURE__ */ jsxs(View, { accessibilityRole: "toolbar", style: { flexDirection: "row", justifyContent: "space-between" }, children: [
|
|
4040
|
+
QUICK_REACTIONS.map((emoji) => {
|
|
4041
|
+
const on = mineReactions.has(emoji);
|
|
4042
|
+
return /* @__PURE__ */ jsx(
|
|
4043
|
+
Pressable,
|
|
4044
|
+
{
|
|
4045
|
+
testID: `${testID}-react-${emoji}`,
|
|
4046
|
+
accessibilityRole: "button",
|
|
4047
|
+
accessibilityState: { selected: on },
|
|
4048
|
+
accessibilityLabel: fill(copy.react, { emoji }),
|
|
4049
|
+
onPress: () => {
|
|
4050
|
+
onClose();
|
|
4051
|
+
onReact(message, emoji, !on);
|
|
4052
|
+
},
|
|
4053
|
+
style: { width: 48, height: 48, borderRadius: 24, alignItems: "center", justifyContent: "center", backgroundColor: on ? colors.fill : void 0 },
|
|
4054
|
+
children: /* @__PURE__ */ jsx(NativeText, { style: { fontSize: 26, lineHeight: 34 }, children: emoji })
|
|
4055
|
+
},
|
|
4056
|
+
emoji
|
|
4057
|
+
);
|
|
4058
|
+
}),
|
|
4059
|
+
/* @__PURE__ */ jsx(
|
|
3951
4060
|
Pressable,
|
|
3952
4061
|
{
|
|
3953
|
-
testID: `${testID}-react
|
|
4062
|
+
testID: `${testID}-react-more`,
|
|
3954
4063
|
accessibilityRole: "button",
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
emoji
|
|
3965
|
-
);
|
|
3966
|
-
}),
|
|
3967
|
-
/* @__PURE__ */ jsx(
|
|
3968
|
-
Pressable,
|
|
4064
|
+
accessibilityLabel: copy.moreReactions,
|
|
4065
|
+
onPress: () => setPicking(true),
|
|
4066
|
+
style: { width: 48, height: 48, borderRadius: 24, alignItems: "center", justifyContent: "center", backgroundColor: colors.fill },
|
|
4067
|
+
children: /* @__PURE__ */ jsx(NativeIcon, { name: "plus", size: 22, color: colors.secondaryLabel })
|
|
4068
|
+
}
|
|
4069
|
+
)
|
|
4070
|
+
] }) : null,
|
|
4071
|
+
/* @__PURE__ */ jsx(View, { children: actions.map((action) => /* @__PURE__ */ jsx(
|
|
4072
|
+
NativeListItem,
|
|
3969
4073
|
{
|
|
3970
|
-
testID: `${testID}
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
onPress: () =>
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
/* @__PURE__ */ jsx(View, { children: actions.map((action) => /* @__PURE__ */ jsx(
|
|
3980
|
-
NativeListItem,
|
|
3981
|
-
{
|
|
3982
|
-
testID: `${testID}-${action}`,
|
|
3983
|
-
title: labels[action],
|
|
3984
|
-
leading: /* @__PURE__ */ jsx(NativeIcon, { name: ICONS[action], size: 20, color: action === "delete" ? colors.destructive : colors.label }),
|
|
3985
|
-
onPress: () => run(action)
|
|
3986
|
-
},
|
|
3987
|
-
action
|
|
3988
|
-
)) })
|
|
3989
|
-
] }) : null });
|
|
4074
|
+
testID: `${testID}-${action}`,
|
|
4075
|
+
title: labels[action],
|
|
4076
|
+
leading: /* @__PURE__ */ jsx(NativeIcon, { name: ICONS[action], size: 20, color: action === "delete" ? colors.destructive : colors.label }),
|
|
4077
|
+
onPress: () => run(action)
|
|
4078
|
+
},
|
|
4079
|
+
action
|
|
4080
|
+
)) })
|
|
4081
|
+
] })
|
|
4082
|
+
) : null });
|
|
3990
4083
|
}
|
|
3991
4084
|
|
|
3992
4085
|
// src/native/rows.ts
|
|
@@ -4038,7 +4131,7 @@ function nearBottom(m, slack = AT_BOTTOM_POINTS) {
|
|
|
4038
4131
|
return m.contentOffset.y + m.layoutMeasurement.height >= m.contentSize.height - slack;
|
|
4039
4132
|
}
|
|
4040
4133
|
function ChatThreadScreen(props) {
|
|
4041
|
-
const { conversationId, threadRootId = null, keyboardVerticalOffset, onOpenAttachment, jumpTo, topic: topicProp, showTopics = false, scheduling = true, availability = true, testID = "chat-thread" } = props;
|
|
4134
|
+
const { conversationId, threadRootId = null, keyboardVerticalOffset, onOpenAttachment, jumpTo, topic: topicProp, showTopics = false, scheduling = true, availability = true, focused = true, testID = "chat-thread" } = props;
|
|
4042
4135
|
const chat = useChat();
|
|
4043
4136
|
const { userId, videoUpload, attachmentKinds, outbox } = chat;
|
|
4044
4137
|
const linkPreviewImages = chat.linkPreviewImages === true;
|
|
@@ -4099,6 +4192,14 @@ function ChatThreadScreen(props) {
|
|
|
4099
4192
|
const rows = useMemo(() => buildThreadRows(conv.items, { me: userId, firstUnreadId: conv.firstUnreadId, group }), [conv.items, userId, conv.firstUnreadId, group]);
|
|
4100
4193
|
const paths = useMemo(() => conv.items.flatMap((i) => i.type === "message" ? attachmentPaths(i.message.attachments) : []), [conv.items]);
|
|
4101
4194
|
const urls = useFileUrls(paths);
|
|
4195
|
+
const itemsNow = useRef(conv.items);
|
|
4196
|
+
itemsNow.current = conv.items;
|
|
4197
|
+
useEffect(() => {
|
|
4198
|
+
if (focused) return;
|
|
4199
|
+
pauseChatPlayback(
|
|
4200
|
+
(id) => itemsNow.current.some((i) => i.type === "message" ? i.message.attachments.some((a) => a.id === id) : i.type === "pending" && id.startsWith(`${i.pending.clientId}:`))
|
|
4201
|
+
);
|
|
4202
|
+
}, [focused]);
|
|
4102
4203
|
const byId = useMemo(() => new Map(conv.messages.map((m) => [m.id, m])), [conv.messages]);
|
|
4103
4204
|
const linkIds = useMemo(() => linkPreviewIds(conv.messages), [conv.messages]);
|
|
4104
4205
|
const previews = useLinkPreviews(linkIds);
|
|
@@ -4198,7 +4299,13 @@ function ChatThreadScreen(props) {
|
|
|
4198
4299
|
loadUntil,
|
|
4199
4300
|
scrollTo: (index2, id) => {
|
|
4200
4301
|
jumpingTo.current = { id, tries: 0 };
|
|
4201
|
-
|
|
4302
|
+
const scroll = (at) => listRef.current?.scrollToIndex?.({ index: at, animated: !reducedMotion, viewPosition: 0.5 });
|
|
4303
|
+
requestAnimationFrame(() => scroll(index2));
|
|
4304
|
+
if (retryTimer.current) clearTimeout(retryTimer.current);
|
|
4305
|
+
retryTimer.current = setTimeout(() => {
|
|
4306
|
+
const at = rowIndexOf(rowsNow.current, id);
|
|
4307
|
+
if (at >= 0) scroll(at);
|
|
4308
|
+
}, 250);
|
|
4202
4309
|
},
|
|
4203
4310
|
onStart: () => {
|
|
4204
4311
|
atBottom.current = false;
|
|
@@ -4302,7 +4409,7 @@ function ChatThreadScreen(props) {
|
|
|
4302
4409
|
/* @__PURE__ */ jsxs(KeyboardAvoiding, { style: { flex: 1 }, ...keyboardProps, children: [
|
|
4303
4410
|
showTopicBar ? /* @__PURE__ */ jsx(ChatTopicBar, { topics, value: topic ?? null, onValueChange: setChosenTopic, canManage: canManageTopics(conv.role), onSetTopic: topicsState.setTopic, testID: `${testID}-topics` }) : null,
|
|
4304
4411
|
/* @__PURE__ */ jsx(ChatAwayLine, { text: awayLine, testID: `${testID}-away` }),
|
|
4305
|
-
/* @__PURE__ */ jsx(View, { style: { flex: 1 }, children: conv.isLoading ? /* @__PURE__ */ jsx(LoadingThread, { testID: `${testID}-loading` }) : empty ? /* @__PURE__ */ jsx(View, { style: { flex: 1, justifyContent: "center" }, children: /* @__PURE__ */ jsx(NativeEmptyState, { variant: "first-run", title: copy.threadEmptyTitle, body: copy.threadEmptyBody, testID: `${testID}-empty` }) }) : /* @__PURE__ */ jsx(
|
|
4412
|
+
/* @__PURE__ */ jsx(View, { style: { flex: 1 }, children: conv.isLoading ? /* @__PURE__ */ jsx(LoadingThread, { testID: `${testID}-loading` }) : empty ? /* @__PURE__ */ jsx(View, { style: { flex: 1, justifyContent: "center" }, children: /* @__PURE__ */ jsx(NativeEmptyState, { variant: "first-run", title: copy.threadEmptyTitle, body: currentTopic?.closedAt ? copy.topicClosedNotice : copy.threadEmptyBody, testID: `${testID}-empty` }) }) : /* @__PURE__ */ jsx(
|
|
4306
4413
|
List,
|
|
4307
4414
|
{
|
|
4308
4415
|
ref: listRef,
|
|
@@ -4379,6 +4486,7 @@ function ChatThreadScreen(props) {
|
|
|
4379
4486
|
onClose: () => setActionsFor(null),
|
|
4380
4487
|
onReact: react,
|
|
4381
4488
|
onAction,
|
|
4489
|
+
attachmentKinds,
|
|
4382
4490
|
testID: `${testID}-actions`
|
|
4383
4491
|
}
|
|
4384
4492
|
),
|
|
@@ -4869,13 +4977,11 @@ function ChatSearchScreen({ conversationId, onOpen, conversationTitle, debounceM
|
|
|
4869
4977
|
const where = conversationId ? null : fill(copy.searchIn, { name: titleOf(h.conversationId) });
|
|
4870
4978
|
const when = formatInboxTime(new Date(h.createdAt), now());
|
|
4871
4979
|
return /* @__PURE__ */ jsxs(
|
|
4872
|
-
|
|
4980
|
+
HitRow,
|
|
4873
4981
|
{
|
|
4874
4982
|
testID: `${testID}-hit-${h.messageId}`,
|
|
4875
|
-
accessibilityRole: "button",
|
|
4876
4983
|
accessibilityLabel: [who, where, when, h.inTranscript ? copy.transcript : null, snippetText(h.snippet)].filter(Boolean).join(", "),
|
|
4877
4984
|
onPress: () => onOpen(h),
|
|
4878
|
-
style: ({ pressed }) => ({ minHeight: 56, paddingHorizontal: 16, paddingVertical: 10, gap: 4, backgroundColor: pressed ? colors.fill : "transparent" }),
|
|
4879
4985
|
children: [
|
|
4880
4986
|
/* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", alignItems: "center", gap: 6 }, children: [
|
|
4881
4987
|
/* @__PURE__ */ jsx(NativeText, { preset: "label", numberOfLines: 1, style: { flexShrink: 1 }, children: who }),
|
|
@@ -4897,7 +5003,24 @@ function ChatSearchScreen({ conversationId, onOpen, conversationTitle, debounceM
|
|
|
4897
5003
|
)
|
|
4898
5004
|
] });
|
|
4899
5005
|
}
|
|
5006
|
+
function HitRow({ testID, accessibilityLabel, onPress, children }) {
|
|
5007
|
+
const colors = useThemeRoles();
|
|
5008
|
+
const [pressed, setPressed] = useState(false);
|
|
5009
|
+
return /* @__PURE__ */ jsx(
|
|
5010
|
+
Pressable,
|
|
5011
|
+
{
|
|
5012
|
+
testID,
|
|
5013
|
+
accessibilityRole: "button",
|
|
5014
|
+
accessibilityLabel,
|
|
5015
|
+
onPress,
|
|
5016
|
+
onPressIn: () => setPressed(true),
|
|
5017
|
+
onPressOut: () => setPressed(false),
|
|
5018
|
+
style: { minHeight: 56, paddingHorizontal: 16, paddingVertical: 10, gap: 4, backgroundColor: pressed ? colors.fill : "transparent" },
|
|
5019
|
+
children
|
|
5020
|
+
}
|
|
5021
|
+
);
|
|
5022
|
+
}
|
|
4900
5023
|
|
|
4901
|
-
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, 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 };
|
|
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 };
|
|
4902
5025
|
//# sourceMappingURL=native.js.map
|
|
4903
5026
|
//# sourceMappingURL=native.js.map
|