@adminide-stack/yantra-mobile 12.0.28-alpha.66 → 12.0.28-alpha.68
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/lib/api/stt.js +54 -0
- package/lib/api/stt.js.map +1 -0
- package/lib/components/GatewayConnector/GatewayConnector.js +18 -0
- package/lib/components/GatewayConnector/GatewayConnector.js.map +1 -0
- package/lib/components/NavigationHeader/NavigationHeader.js +214 -0
- package/lib/components/NavigationHeader/NavigationHeader.js.map +1 -0
- package/lib/components/ThinkingIndicator.js +55 -0
- package/lib/components/ThinkingIndicator.js.map +1 -0
- package/lib/compute.js +108 -31
- package/lib/compute.js.map +1 -1
- package/lib/contexts/CdecliConnectionContext.js +47 -0
- package/lib/contexts/CdecliConnectionContext.js.map +1 -0
- package/lib/contexts/GatewayContext.js +1 -1
- package/lib/features/audio-input/AudioRecorderPanel.js +228 -0
- package/lib/features/audio-input/AudioRecorderPanel.js.map +1 -0
- package/lib/hooks/useCdecliAutoConnect.js +41 -18
- package/lib/hooks/useCdecliAutoConnect.js.map +1 -1
- package/lib/hooks/useChatApi.js +158 -41
- package/lib/hooks/useChatApi.js.map +1 -1
- package/lib/hooks/useChatStream.js +59 -16
- package/lib/hooks/useChatStream.js.map +1 -1
- package/lib/hooks/usePrerequisiteIds.js +8 -12
- package/lib/hooks/usePrerequisiteIds.js.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/routes.json +112 -0
- package/lib/screens/Chat/index.js +392 -0
- package/lib/screens/Chat/index.js.map +1 -0
- package/lib/screens/ChatHistory/index.js +56 -0
- package/lib/screens/ChatHistory/index.js.map +1 -0
- package/lib/screens/Home/HomeScreen.js +105 -427
- package/lib/screens/Home/HomeScreen.js.map +1 -1
- package/lib/screens/Home/components/ChatHistoryLanding.js +436 -214
- package/lib/screens/Home/components/ChatHistoryLanding.js.map +1 -1
- package/package.json +4 -4
|
@@ -1,265 +1,487 @@
|
|
|
1
|
-
import {jsxs
|
|
2
|
-
|
|
1
|
+
import {jsx,jsxs}from'react/jsx-runtime';import React from'react';import {useWindowDimensions,useColorScheme,View,StyleSheet,Pressable,ActivityIndicator,SectionList,RefreshControl}from'react-native';import {Feather,MaterialCommunityIcons}from'@expo/vector-icons';import {Text}from'@admin-layout/gluestack-ui-mobile';import {useFocusEffect}from'@react-navigation/native';import {useChatHistorySessionsFromChannels,HISTORY_PAGE_SIZE,getHistoryChannelsQueryVariables,chatHistorySessionsFromChannels}from'../../../hooks/useChatApi.js';import {useApolloClient}from'@apollo/client/index.js';import {GetChannelsByUserWithLastMessageDocument}from'common/graphql';import {usePrerequisiteIds}from'../../../hooks/usePrerequisiteIds.js';import ThinkingIndicator from'../../../components/ThinkingIndicator.js';import {mobileTokens}from'../../../theme/mobileTokens.js';var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
3
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __spreadValues = (a, b) => {
|
|
9
|
+
for (var prop in b || (b = {}))
|
|
10
|
+
if (__hasOwnProp.call(b, prop))
|
|
11
|
+
__defNormalProp(a, prop, b[prop]);
|
|
12
|
+
if (__getOwnPropSymbols)
|
|
13
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
+
if (__propIsEnum.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
}
|
|
17
|
+
return a;
|
|
18
|
+
};
|
|
19
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
+
function truncate(text, max = 96) {
|
|
3
21
|
const cleaned = text.replace(/\s+/g, " ").trim();
|
|
4
22
|
if (cleaned.length <= max) return cleaned;
|
|
5
23
|
return `${cleaned.slice(0, max).trim()}...`;
|
|
6
24
|
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
25
|
+
const WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
26
|
+
function relativeTime(ts, now) {
|
|
27
|
+
var _a;
|
|
28
|
+
const deltaMs = now - ts;
|
|
29
|
+
if (deltaMs < 6e4) return "now";
|
|
30
|
+
const minutes = Math.floor(deltaMs / 6e4);
|
|
31
|
+
if (minutes < 60) return `${minutes}m`;
|
|
32
|
+
const hours = Math.floor(minutes / 60);
|
|
33
|
+
if (hours < 24) return `${hours}h`;
|
|
34
|
+
const days = Math.floor(hours / 24);
|
|
35
|
+
if (days < 7) {
|
|
36
|
+
const d2 = new Date(ts);
|
|
37
|
+
return (_a = WEEKDAYS[d2.getDay()]) != null ? _a : `${days}d`;
|
|
38
|
+
}
|
|
39
|
+
const d = new Date(ts);
|
|
40
|
+
const sameYear = d.getFullYear() === new Date(now).getFullYear();
|
|
41
|
+
const m = d.getMonth() + 1;
|
|
42
|
+
const day = d.getDate();
|
|
43
|
+
return sameYear ? `${m}/${day}` : `${m}/${day}/${String(d.getFullYear()).slice(2)}`;
|
|
10
44
|
}
|
|
11
|
-
function
|
|
12
|
-
|
|
13
|
-
|
|
45
|
+
function bucketFor(ts, now) {
|
|
46
|
+
const a = new Date(ts);
|
|
47
|
+
const b = new Date(now);
|
|
48
|
+
const startOf = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
|
|
49
|
+
const today = startOf(b);
|
|
50
|
+
const yesterday = today - 24 * 60 * 60 * 1e3;
|
|
51
|
+
const weekAgo = today - 7 * 24 * 60 * 60 * 1e3;
|
|
52
|
+
const tsDay = startOf(a);
|
|
53
|
+
if (tsDay >= today) return "today";
|
|
54
|
+
if (tsDay >= yesterday) return "yesterday";
|
|
55
|
+
if (tsDay >= weekAgo) return "week";
|
|
56
|
+
return "earlier";
|
|
14
57
|
}
|
|
15
|
-
function
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
58
|
+
function buildSections(rows, now) {
|
|
59
|
+
const buckets = {
|
|
60
|
+
today: [],
|
|
61
|
+
yesterday: [],
|
|
62
|
+
week: [],
|
|
63
|
+
earlier: []
|
|
64
|
+
};
|
|
65
|
+
for (const row of rows) {
|
|
66
|
+
buckets[bucketFor(row.sortAt, now)].push(row);
|
|
67
|
+
}
|
|
68
|
+
const out = [];
|
|
69
|
+
if (buckets.today.length) out.push({
|
|
70
|
+
key: "today",
|
|
71
|
+
title: "Today",
|
|
72
|
+
data: buckets.today
|
|
73
|
+
});
|
|
74
|
+
if (buckets.yesterday.length) out.push({
|
|
75
|
+
key: "yesterday",
|
|
76
|
+
title: "Yesterday",
|
|
77
|
+
data: buckets.yesterday
|
|
78
|
+
});
|
|
79
|
+
if (buckets.week.length) out.push({
|
|
80
|
+
key: "week",
|
|
81
|
+
title: "This week",
|
|
82
|
+
data: buckets.week
|
|
21
83
|
});
|
|
84
|
+
if (buckets.earlier.length) out.push({
|
|
85
|
+
key: "earlier",
|
|
86
|
+
title: "Earlier",
|
|
87
|
+
data: buckets.earlier
|
|
88
|
+
});
|
|
89
|
+
return out;
|
|
22
90
|
}
|
|
23
|
-
function
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
title: truncatePreview(titleRaw, 44),
|
|
48
|
-
preview: truncatePreview(previewRaw, 72),
|
|
49
|
-
sortAt: new Date((_h = (_g = current == null ? void 0 : current.updatedAt) != null ? _g : current == null ? void 0 : current.createdAt) != null ? _h : 0).getTime()
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
if (rows.length > 0) {
|
|
53
|
-
return rows.reverse();
|
|
54
|
-
}
|
|
55
|
-
const fallbackRows = [];
|
|
56
|
-
const seenChannelIds = /* @__PURE__ */ new Set();
|
|
57
|
-
for (let i = sortedAsc.length - 1; i >= 0; i--) {
|
|
58
|
-
const post = sortedAsc[i];
|
|
59
|
-
const sessionChannelId = String((_k = (_j = (_i = post == null ? void 0 : post.channel) == null ? void 0 : _i.id) != null ? _j : post == null ? void 0 : post.channel) != null ? _k : "");
|
|
60
|
-
if (!sessionChannelId || seenChannelIds.has(sessionChannelId)) continue;
|
|
61
|
-
seenChannelIds.add(sessionChannelId);
|
|
62
|
-
const text = stripModelCostHeader(String((_l = post == null ? void 0 : post.message) != null ? _l : "").trim()) || "New Chat";
|
|
63
|
-
fallbackRows.push({
|
|
64
|
-
id: String((_m = post == null ? void 0 : post.id) != null ? _m : `${sessionChannelId}-${i}`),
|
|
65
|
-
channelId: sessionChannelId,
|
|
66
|
-
title: truncatePreview(text, 44),
|
|
67
|
-
preview: truncatePreview(text, 72),
|
|
68
|
-
sortAt: new Date((_o = (_n = post == null ? void 0 : post.updatedAt) != null ? _n : post == null ? void 0 : post.createdAt) != null ? _o : 0).getTime()
|
|
69
|
-
});
|
|
91
|
+
function buildTheme(isDark) {
|
|
92
|
+
if (isDark) {
|
|
93
|
+
return {
|
|
94
|
+
bg: "#0b1220",
|
|
95
|
+
surface: mobileTokens.color.surface,
|
|
96
|
+
title: "#f1f5f9",
|
|
97
|
+
preview: "#94a3b8",
|
|
98
|
+
meta: "#9aa0b8",
|
|
99
|
+
muted: "#7a809a",
|
|
100
|
+
divider: "rgba(148, 163, 184, 0.12)",
|
|
101
|
+
sectionLabel: "#cbd5e1",
|
|
102
|
+
tileChatBg: "rgba(148, 163, 184, 0.16)",
|
|
103
|
+
tileChatIcon: "#e2e8f0",
|
|
104
|
+
tileSearchBg: "rgba(251, 191, 36, 0.16)",
|
|
105
|
+
tileSearchIcon: "#fde68a",
|
|
106
|
+
pressed: "rgba(148, 163, 184, 0.10)",
|
|
107
|
+
loadMoreText: "#e2e8f0",
|
|
108
|
+
loadMoreBorder: "rgba(148, 163, 184, 0.28)",
|
|
109
|
+
loadMoreBg: "rgba(148, 163, 184, 0.10)",
|
|
110
|
+
refreshTint: "#cbd5e1",
|
|
111
|
+
endDot: "#94a3b8",
|
|
112
|
+
emptyMarkBg: "rgba(148, 163, 184, 0.18)",
|
|
113
|
+
emptyMarkIcon: "#b4bccc"
|
|
114
|
+
};
|
|
70
115
|
}
|
|
71
|
-
return
|
|
116
|
+
return {
|
|
117
|
+
bg: mobileTokens.color.bg,
|
|
118
|
+
surface: mobileTokens.color.surface,
|
|
119
|
+
title: "#0b1424",
|
|
120
|
+
preview: "#525a73",
|
|
121
|
+
meta: "#7b8197",
|
|
122
|
+
muted: "#94a3b8",
|
|
123
|
+
divider: "rgba(15, 23, 42, 0.06)",
|
|
124
|
+
sectionLabel: "#374151",
|
|
125
|
+
tileChatBg: "rgba(15, 23, 42, 0.06)",
|
|
126
|
+
tileChatIcon: "#1f2937",
|
|
127
|
+
tileSearchBg: "rgba(217, 119, 6, 0.10)",
|
|
128
|
+
tileSearchIcon: "#b45309",
|
|
129
|
+
pressed: "rgba(15, 23, 42, 0.05)",
|
|
130
|
+
loadMoreText: "#1f2937",
|
|
131
|
+
loadMoreBorder: "rgba(15, 23, 42, 0.15)",
|
|
132
|
+
loadMoreBg: "rgba(15, 23, 42, 0.04)",
|
|
133
|
+
refreshTint: "#374151",
|
|
134
|
+
endDot: "#9ca3af",
|
|
135
|
+
emptyMarkBg: "rgba(15, 23, 42, 0.07)",
|
|
136
|
+
emptyMarkIcon: "#6b7280"
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function ModeTile({
|
|
140
|
+
mode,
|
|
141
|
+
theme
|
|
142
|
+
}) {
|
|
143
|
+
const isSearch = mode === "deep-search";
|
|
144
|
+
return /* @__PURE__ */ jsx(View, { style: [styles.tile, {
|
|
145
|
+
backgroundColor: isSearch ? theme.tileSearchBg : theme.tileChatBg
|
|
146
|
+
}], children: isSearch ? /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: "lightning-bolt-outline", size: 18, color: theme.tileSearchIcon }) : /* @__PURE__ */ jsx(Feather, { name: "message-circle", size: 18, color: theme.tileChatIcon }) });
|
|
72
147
|
}
|
|
73
148
|
function ChatHistoryRow({
|
|
74
149
|
session,
|
|
75
|
-
onSelectSession
|
|
150
|
+
onSelectSession,
|
|
151
|
+
theme,
|
|
152
|
+
timeLabel
|
|
76
153
|
}) {
|
|
77
|
-
|
|
154
|
+
const titleColor = session.isPlaceholder ? theme.preview : theme.title;
|
|
155
|
+
const titleStyle = [styles.rowTitle, {
|
|
156
|
+
color: titleColor
|
|
157
|
+
}, session.isPlaceholder ? styles.rowTitlePlaceholder : null];
|
|
158
|
+
return /* @__PURE__ */ jsxs(Pressable, { onPress: () => onSelectSession(session.channelId, session.mode), style: ({
|
|
78
159
|
pressed
|
|
79
|
-
}) => [styles.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
160
|
+
}) => [styles.row, pressed && {
|
|
161
|
+
backgroundColor: theme.pressed
|
|
162
|
+
}], accessibilityRole: "button", accessibilityLabel: session.previewForRender ? `Open conversation: ${session.titleForRender}. ${session.previewForRender}. ${timeLabel}` : `Open conversation: ${session.titleForRender}, ${timeLabel}`, children: [
|
|
163
|
+
/* @__PURE__ */ jsx(ModeTile, { mode: session.mode, theme }),
|
|
164
|
+
/* @__PURE__ */ jsxs(View, { style: styles.rowBody, children: [
|
|
165
|
+
/* @__PURE__ */ jsxs(View, { style: styles.rowHeader, children: [
|
|
166
|
+
/* @__PURE__ */ jsx(Text, { style: titleStyle, numberOfLines: 1, ellipsizeMode: "tail", children: session.titleForRender }),
|
|
167
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.rowDate, {
|
|
168
|
+
color: theme.meta
|
|
169
|
+
}], numberOfLines: 1, children: timeLabel })
|
|
84
170
|
] }),
|
|
85
|
-
/* @__PURE__ */ jsx(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
171
|
+
session.previewForRender ? /* @__PURE__ */ jsx(Text, { style: [styles.rowPreview, {
|
|
172
|
+
color: theme.preview
|
|
173
|
+
}], numberOfLines: 1, ellipsizeMode: "tail", children: session.previewForRender }) : null
|
|
174
|
+
] })
|
|
175
|
+
] });
|
|
89
176
|
}
|
|
90
177
|
function ChatHistoryLanding({
|
|
91
|
-
onSelectSession
|
|
92
|
-
onCompose
|
|
178
|
+
onSelectSession
|
|
93
179
|
}) {
|
|
94
180
|
const {
|
|
95
181
|
width: screenWidth
|
|
96
182
|
} = useWindowDimensions();
|
|
97
|
-
const
|
|
183
|
+
const colorScheme = useColorScheme();
|
|
184
|
+
const isDark = colorScheme === "dark";
|
|
185
|
+
const theme = React.useMemo(() => buildTheme(isDark), [isDark]);
|
|
186
|
+
const apollo = useApolloClient();
|
|
98
187
|
const {
|
|
99
|
-
|
|
100
|
-
loading:
|
|
101
|
-
} =
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
188
|
+
orgName,
|
|
189
|
+
loading: idsLoading
|
|
190
|
+
} = usePrerequisiteIds();
|
|
191
|
+
const {
|
|
192
|
+
sessions: liveSessions,
|
|
193
|
+
sessionsLoaded,
|
|
194
|
+
refetch,
|
|
195
|
+
sourceChannelCount
|
|
196
|
+
} = useChatHistorySessionsFromChannels(orgName, {
|
|
197
|
+
skip: !orgName
|
|
110
198
|
});
|
|
111
|
-
const [
|
|
112
|
-
const [
|
|
199
|
+
const [nowTs, setNowTs] = React.useState(() => Date.now());
|
|
200
|
+
const [refreshing, setRefreshing] = React.useState(false);
|
|
201
|
+
const [olderSessions, setOlderSessions] = React.useState([]);
|
|
202
|
+
const [hasMore, setHasMore] = React.useState(false);
|
|
203
|
+
const [loadingMore, setLoadingMore] = React.useState(false);
|
|
113
204
|
React.useEffect(() => {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
205
|
+
if (olderSessions.length > 0) return;
|
|
206
|
+
setHasMore(sourceChannelCount >= HISTORY_PAGE_SIZE);
|
|
207
|
+
}, [sourceChannelCount, olderSessions.length]);
|
|
208
|
+
useFocusEffect(React.useCallback(() => {
|
|
209
|
+
if (!orgName) return;
|
|
210
|
+
void refetch(getHistoryChannelsQueryVariables(orgName)).catch(() => {
|
|
211
|
+
});
|
|
212
|
+
}, [orgName, refetch]));
|
|
213
|
+
const onRefresh = React.useCallback(async () => {
|
|
214
|
+
if (!orgName) return;
|
|
215
|
+
setRefreshing(true);
|
|
216
|
+
try {
|
|
217
|
+
await refetch(getHistoryChannelsQueryVariables(orgName));
|
|
218
|
+
setOlderSessions([]);
|
|
219
|
+
setNowTs(Date.now());
|
|
220
|
+
} catch (e) {
|
|
221
|
+
} finally {
|
|
222
|
+
setRefreshing(false);
|
|
223
|
+
}
|
|
224
|
+
}, [orgName, refetch]);
|
|
225
|
+
const onLoadMore = React.useCallback(async () => {
|
|
226
|
+
var _a, _b;
|
|
227
|
+
if (!orgName || loadingMore || !hasMore) return;
|
|
228
|
+
setLoadingMore(true);
|
|
229
|
+
try {
|
|
230
|
+
const nextSkip = liveSessions.length + olderSessions.length;
|
|
231
|
+
const result = await apollo.query({
|
|
232
|
+
query: GetChannelsByUserWithLastMessageDocument,
|
|
233
|
+
variables: getHistoryChannelsQueryVariables(orgName, {
|
|
234
|
+
skip: nextSkip,
|
|
235
|
+
limit: HISTORY_PAGE_SIZE
|
|
236
|
+
}),
|
|
237
|
+
fetchPolicy: "network-only"
|
|
238
|
+
});
|
|
239
|
+
const rawCount = ((_b = (_a = result.data) == null ? void 0 : _a.channelsByUser) != null ? _b : []).length;
|
|
240
|
+
const nextBatch = chatHistorySessionsFromChannels(result.data);
|
|
241
|
+
if (nextBatch.length === 0) {
|
|
242
|
+
setHasMore(false);
|
|
243
|
+
} else {
|
|
244
|
+
setOlderSessions((prev) => {
|
|
245
|
+
const map = /* @__PURE__ */ new Map();
|
|
246
|
+
for (const s of prev) map.set(s.channelId, s);
|
|
247
|
+
for (const s of nextBatch) map.set(s.channelId, s);
|
|
248
|
+
return Array.from(map.values());
|
|
249
|
+
});
|
|
250
|
+
setHasMore(rawCount >= HISTORY_PAGE_SIZE);
|
|
147
251
|
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
252
|
+
} catch (e) {
|
|
253
|
+
} finally {
|
|
254
|
+
setLoadingMore(false);
|
|
255
|
+
}
|
|
256
|
+
}, [apollo, hasMore, liveSessions.length, loadingMore, olderSessions, orgName]);
|
|
257
|
+
const sessions = React.useMemo(() => {
|
|
258
|
+
const map = /* @__PURE__ */ new Map();
|
|
259
|
+
for (const s of olderSessions) map.set(s.channelId, s);
|
|
260
|
+
for (const s of liveSessions) map.set(s.channelId, s);
|
|
261
|
+
return Array.from(map.values()).sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()).map((s) => __spreadProps(__spreadValues({}, s), {
|
|
262
|
+
id: s.channelId,
|
|
263
|
+
titleForRender: truncate(s.title, 64),
|
|
264
|
+
previewForRender: s.preview ? truncate(s.preview, 96) : "",
|
|
265
|
+
sortAt: s.updatedAt.getTime()
|
|
266
|
+
}));
|
|
267
|
+
}, [liveSessions, olderSessions]);
|
|
268
|
+
const sections = React.useMemo(() => buildSections(sessions, nowTs), [sessions, nowTs]);
|
|
269
|
+
const initialLoading = !sessionsLoaded || idsLoading || !orgName;
|
|
270
|
+
const contentMaxWidth = Math.min(720, Math.max(360, screenWidth - 24));
|
|
271
|
+
const containerStyle = {
|
|
272
|
+
maxWidth: contentMaxWidth,
|
|
273
|
+
alignSelf: "center",
|
|
274
|
+
width: "100%"
|
|
275
|
+
};
|
|
276
|
+
const listFooter = hasMore ? /* @__PURE__ */ jsx(View, { style: [styles.footer, containerStyle], children: /* @__PURE__ */ jsx(Pressable, { onPress: () => void onLoadMore(), disabled: loadingMore, style: ({
|
|
277
|
+
pressed
|
|
278
|
+
}) => [styles.loadMore, {
|
|
279
|
+
backgroundColor: theme.loadMoreBg,
|
|
280
|
+
borderColor: theme.loadMoreBorder
|
|
281
|
+
}, loadingMore && {
|
|
282
|
+
opacity: 0.7
|
|
283
|
+
}, pressed && !loadingMore && {
|
|
284
|
+
opacity: 0.8
|
|
285
|
+
}], accessibilityRole: "button", accessibilityLabel: "Load older conversations", children: loadingMore ? /* @__PURE__ */ jsx(ActivityIndicator, { size: "small", color: theme.loadMoreText }) : /* @__PURE__ */ jsx(Text, { style: [styles.loadMoreText, {
|
|
286
|
+
color: theme.loadMoreText
|
|
287
|
+
}], children: "Load older" }) }) }) : !hasMore && sessions.length > 0 ? /* @__PURE__ */ jsxs(View, { style: [styles.endHint, containerStyle], children: [
|
|
288
|
+
/* @__PURE__ */ jsx(View, { style: [styles.endHintDot, {
|
|
289
|
+
backgroundColor: theme.endDot,
|
|
290
|
+
opacity: 0.5
|
|
291
|
+
}] }),
|
|
292
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.endHintText, {
|
|
293
|
+
color: theme.muted
|
|
294
|
+
}], children: "You're all caught up" })
|
|
295
|
+
] }) : /* @__PURE__ */ jsx(View, { style: {
|
|
296
|
+
height: 24
|
|
297
|
+
} });
|
|
298
|
+
const renderItemSeparator = React.useCallback(() => /* @__PURE__ */ jsx(View, { style: [styles.divider, {
|
|
299
|
+
backgroundColor: theme.divider
|
|
300
|
+
}] }), [theme.divider]);
|
|
301
|
+
return /* @__PURE__ */ jsx(View, { style: [styles.container, {
|
|
302
|
+
backgroundColor: theme.bg
|
|
303
|
+
}], children: initialLoading ? (
|
|
304
|
+
/**
|
|
305
|
+
* Initial-fetch state mirrors the web composer's "Thinking…" pill
|
|
306
|
+
* (see `packages-modules/account/browser/src/pages/chat/ChatSessionPage.tsx`):
|
|
307
|
+
* a thin spinning arc + 12px muted caption, both tinted with
|
|
308
|
+
* `theme.muted` so the indicator reads as a quiet status row
|
|
309
|
+
* rather than a brand-stamped loading screen.
|
|
310
|
+
*/
|
|
311
|
+
/* @__PURE__ */ jsx(View, { style: styles.emptyStateWrap, children: /* @__PURE__ */ jsx(ThinkingIndicator, { color: theme.muted }) })
|
|
312
|
+
) : sessions.length === 0 ? /* @__PURE__ */ jsxs(View, { style: styles.emptyStateWrap, children: [
|
|
313
|
+
/* @__PURE__ */ jsx(View, { style: [styles.emptyMark, {
|
|
314
|
+
backgroundColor: theme.emptyMarkBg
|
|
315
|
+
}], children: /* @__PURE__ */ jsx(Feather, { name: "message-circle", size: 40, color: theme.emptyMarkIcon }) }),
|
|
316
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.emptyTitle, {
|
|
317
|
+
color: theme.title
|
|
318
|
+
}], children: "No conversations yet" }),
|
|
319
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.emptySubtitle, {
|
|
320
|
+
color: theme.muted
|
|
321
|
+
}], children: "Start your first chat to see your conversation history here." })
|
|
322
|
+
] }) : /* @__PURE__ */ jsx(SectionList, { sections, keyExtractor: (item) => item.channelId, stickySectionHeadersEnabled: false, contentContainerStyle: [styles.listContent, containerStyle, {
|
|
323
|
+
paddingBottom: 32
|
|
324
|
+
}], ListFooterComponent: listFooter, ItemSeparatorComponent: renderItemSeparator, refreshControl: /* @__PURE__ */ jsx(RefreshControl, { refreshing, onRefresh: () => void onRefresh(), tintColor: theme.refreshTint, colors: [theme.refreshTint] }), renderSectionHeader: ({
|
|
325
|
+
section
|
|
326
|
+
}) => /* @__PURE__ */ jsx(View, { style: styles.sectionHeader, children: /* @__PURE__ */ jsx(Text, { style: [styles.sectionLabel, {
|
|
327
|
+
color: theme.sectionLabel
|
|
328
|
+
}], children: section.title }) }), renderItem: ({
|
|
329
|
+
item
|
|
330
|
+
}) => /* @__PURE__ */ jsx(ChatHistoryRow, { session: item, onSelectSession, theme, timeLabel: relativeTime(item.sortAt, nowTs) }), showsVerticalScrollIndicator: false }) });
|
|
180
331
|
}
|
|
181
332
|
const styles = StyleSheet.create({
|
|
182
333
|
container: {
|
|
183
|
-
flex: 1,
|
|
184
|
-
backgroundColor: mobileTokens.color.bg
|
|
185
|
-
},
|
|
186
|
-
list: {
|
|
187
334
|
flex: 1
|
|
188
335
|
},
|
|
189
336
|
listContent: {
|
|
190
|
-
paddingHorizontal:
|
|
191
|
-
paddingTop:
|
|
192
|
-
paddingBottom: 100,
|
|
193
|
-
gap: 10
|
|
337
|
+
paddingHorizontal: 20,
|
|
338
|
+
paddingTop: 4
|
|
194
339
|
},
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
340
|
+
/**
|
|
341
|
+
* Section header: sentence case, indigo-tinted, modest size. 28px top padding
|
|
342
|
+
* establishes rhythm between groups without dead air below the label.
|
|
343
|
+
*/
|
|
344
|
+
sectionHeader: {
|
|
345
|
+
paddingTop: 28,
|
|
346
|
+
paddingBottom: 10,
|
|
347
|
+
paddingHorizontal: 2
|
|
201
348
|
},
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
borderWidth: 1,
|
|
208
|
-
borderColor: mobileTokens.color.border
|
|
349
|
+
sectionLabel: {
|
|
350
|
+
fontSize: 12,
|
|
351
|
+
fontWeight: "600",
|
|
352
|
+
letterSpacing: 0.2,
|
|
353
|
+
textTransform: "none"
|
|
209
354
|
},
|
|
210
|
-
|
|
355
|
+
/**
|
|
356
|
+
* Row: leading 40×40 mode tile, two-line body (title + preview), right-aligned
|
|
357
|
+
* date aligned to the title's baseline. 14px gap matches the tile-to-text
|
|
358
|
+
* rhythm in Manus / iOS Mail.
|
|
359
|
+
*/
|
|
360
|
+
row: {
|
|
211
361
|
flexDirection: "row",
|
|
212
362
|
alignItems: "center",
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
gap:
|
|
363
|
+
paddingVertical: 12,
|
|
364
|
+
paddingHorizontal: 2,
|
|
365
|
+
gap: 14
|
|
216
366
|
},
|
|
217
|
-
|
|
218
|
-
|
|
367
|
+
tile: {
|
|
368
|
+
width: 40,
|
|
369
|
+
height: 40,
|
|
370
|
+
borderRadius: 12,
|
|
219
371
|
alignItems: "center",
|
|
372
|
+
justifyContent: "center"
|
|
373
|
+
},
|
|
374
|
+
rowBody: {
|
|
220
375
|
flex: 1,
|
|
221
|
-
minWidth: 0
|
|
222
|
-
gap: 8
|
|
376
|
+
minWidth: 0
|
|
223
377
|
},
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
378
|
+
rowHeader: {
|
|
379
|
+
flexDirection: "row",
|
|
380
|
+
alignItems: "baseline",
|
|
381
|
+
gap: 10
|
|
382
|
+
},
|
|
383
|
+
rowTitle: {
|
|
384
|
+
flex: 1,
|
|
385
|
+
fontSize: 16,
|
|
386
|
+
fontWeight: "600",
|
|
387
|
+
letterSpacing: -0.1,
|
|
388
|
+
lineHeight: 22
|
|
389
|
+
},
|
|
390
|
+
rowTitlePlaceholder: {
|
|
391
|
+
fontWeight: "500",
|
|
392
|
+
fontStyle: "italic"
|
|
393
|
+
},
|
|
394
|
+
rowDate: {
|
|
395
|
+
fontSize: 12.5,
|
|
396
|
+
fontWeight: "500",
|
|
397
|
+
fontVariant: ["tabular-nums"]
|
|
398
|
+
},
|
|
399
|
+
rowPreview: {
|
|
400
|
+
marginTop: 2,
|
|
401
|
+
fontSize: 13.5,
|
|
402
|
+
lineHeight: 19,
|
|
403
|
+
fontWeight: "400",
|
|
404
|
+
letterSpacing: -0.05
|
|
405
|
+
},
|
|
406
|
+
/**
|
|
407
|
+
* Indented hairline: starts past the tile column (40 + 14 = 54) so the rule
|
|
408
|
+
* aligns with the title's leading edge. Mail-style.
|
|
409
|
+
*/
|
|
410
|
+
divider: {
|
|
411
|
+
height: StyleSheet.hairlineWidth,
|
|
412
|
+
marginLeft: 56
|
|
413
|
+
},
|
|
414
|
+
footer: {
|
|
415
|
+
paddingTop: 28,
|
|
416
|
+
paddingBottom: 8,
|
|
417
|
+
alignItems: "center"
|
|
418
|
+
},
|
|
419
|
+
/** Bordered ink-neutral pill. Label is the affordance, no leading icon, no chevron. */
|
|
420
|
+
loadMore: {
|
|
421
|
+
flexDirection: "row",
|
|
228
422
|
alignItems: "center",
|
|
229
423
|
justifyContent: "center",
|
|
230
|
-
|
|
424
|
+
paddingVertical: 10,
|
|
425
|
+
paddingHorizontal: 18,
|
|
426
|
+
borderRadius: 999,
|
|
231
427
|
borderWidth: 1,
|
|
232
|
-
|
|
428
|
+
minWidth: 132
|
|
233
429
|
},
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
430
|
+
loadMoreText: {
|
|
431
|
+
fontSize: 13.5,
|
|
432
|
+
fontWeight: "600",
|
|
433
|
+
letterSpacing: -0.05
|
|
237
434
|
},
|
|
238
|
-
|
|
239
|
-
|
|
435
|
+
endHint: {
|
|
436
|
+
flexDirection: "row",
|
|
437
|
+
alignItems: "center",
|
|
438
|
+
justifyContent: "center",
|
|
439
|
+
gap: 8,
|
|
440
|
+
paddingTop: 32,
|
|
441
|
+
paddingBottom: 12
|
|
240
442
|
},
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
443
|
+
endHintDot: {
|
|
444
|
+
width: 4,
|
|
445
|
+
height: 4,
|
|
446
|
+
borderRadius: 2
|
|
447
|
+
},
|
|
448
|
+
endHintText: {
|
|
449
|
+
fontSize: 12.5,
|
|
450
|
+
fontWeight: "500",
|
|
451
|
+
letterSpacing: 0.1
|
|
452
|
+
},
|
|
453
|
+
/**
|
|
454
|
+
* Empty state: stack a 64×64 indigo-tinted mark over title + subtitle.
|
|
455
|
+
* Generous vertical gap (16) between mark and title; tighter gap (6)
|
|
456
|
+
* between title and subtitle so the pair reads as one unit hanging off
|
|
457
|
+
* the mark, not three independent items.
|
|
458
|
+
*/
|
|
459
|
+
emptyStateWrap: {
|
|
460
|
+
flex: 1,
|
|
461
|
+
minHeight: 420,
|
|
251
462
|
alignItems: "center",
|
|
252
463
|
justifyContent: "center",
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
464
|
+
paddingHorizontal: 32
|
|
465
|
+
},
|
|
466
|
+
emptyMark: {
|
|
467
|
+
width: 72,
|
|
468
|
+
height: 72,
|
|
469
|
+
borderRadius: 36,
|
|
470
|
+
alignItems: "center",
|
|
471
|
+
justifyContent: "center",
|
|
472
|
+
marginBottom: 18
|
|
473
|
+
},
|
|
474
|
+
emptyTitle: {
|
|
475
|
+
fontSize: 20,
|
|
476
|
+
fontWeight: "700",
|
|
477
|
+
letterSpacing: -0.3,
|
|
478
|
+
textAlign: "center",
|
|
479
|
+
marginBottom: 6
|
|
261
480
|
},
|
|
262
|
-
|
|
263
|
-
|
|
481
|
+
emptySubtitle: {
|
|
482
|
+
fontSize: 14,
|
|
483
|
+
lineHeight: 20,
|
|
484
|
+
textAlign: "center",
|
|
485
|
+
maxWidth: 340
|
|
264
486
|
}
|
|
265
487
|
});export{ChatHistoryLanding as default};//# sourceMappingURL=ChatHistoryLanding.js.map
|