@smartspace/chat-ui 1.13.1-pr.259.3c073ae → 1.13.1-pr.260.2e65c1d
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/dist/index.d.ts +8 -14
- package/dist/index.js +52 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -238,6 +238,14 @@ type MessageThread = {
|
|
|
238
238
|
totalMessages: number;
|
|
239
239
|
pinned: boolean;
|
|
240
240
|
workSpaceId: string;
|
|
241
|
+
/**
|
|
242
|
+
* Monotonic version of when this summary was emitted (epoch ms). Used by
|
|
243
|
+
* `applyThreadToCache` to reject stale writes — e.g. a SignalR summary
|
|
244
|
+
* landing after a fresher SSE thread frame because the server's DB write
|
|
245
|
+
* lagged Redis. Mappers derive this from `lastUpdatedAt`; client-side
|
|
246
|
+
* writers like `ensureDraftThread` use `Date.now()`.
|
|
247
|
+
*/
|
|
248
|
+
summaryEmittedAt: number;
|
|
241
249
|
};
|
|
242
250
|
type ThreadsResponse = {
|
|
243
251
|
data: MessageThread[];
|
|
@@ -969,20 +977,6 @@ declare function mapMentionUserDtoToModel(dto: MentionUserDto): MentionUser;
|
|
|
969
977
|
declare function mapWorkspaceDtoToModel(dto: WorkspaceDto): Workspace;
|
|
970
978
|
declare const mapWorkspacesDtoToModels: (arr: WorkspacesListItemDto[]) => Workspace[];
|
|
971
979
|
|
|
972
|
-
/**
|
|
973
|
-
* Write a freshly-observed thread (from SignalR or an SSE thread frame)
|
|
974
|
-
* directly into the relevant query caches so subscribers paint without a
|
|
975
|
-
* refetch roundtrip.
|
|
976
|
-
*
|
|
977
|
-
* - Merges into `threadsKeys.detail(workspaceId, thread.id)`.
|
|
978
|
-
* - Splices into every threads-list cache for the workspace, handling both
|
|
979
|
-
* finite `ThreadsResponse` and infinite `{ pages, pageParams }` shapes.
|
|
980
|
-
*
|
|
981
|
-
* Returns `true` when the thread was found in at least one list cache.
|
|
982
|
-
* Callers that need to surface brand-new threads (e.g. another user just
|
|
983
|
-
* created one) can fall back to invalidating the list queries when this
|
|
984
|
-
* returns `false`.
|
|
985
|
-
*/
|
|
986
980
|
declare function applyThreadToCache(qc: QueryClient, thread: MessageThread): boolean;
|
|
987
981
|
/**
|
|
988
982
|
* Invalidate every threads-list cache for a workspace. Use as a fallback when
|
package/dist/index.js
CHANGED
|
@@ -3233,7 +3233,20 @@ var threadsKeys = {
|
|
|
3233
3233
|
};
|
|
3234
3234
|
|
|
3235
3235
|
// src/domains/threads/cache.ts
|
|
3236
|
+
function isStaleSummary(incoming, existing) {
|
|
3237
|
+
if (!existing) return false;
|
|
3238
|
+
if (typeof existing.summaryEmittedAt !== "number") return false;
|
|
3239
|
+
if (typeof incoming.summaryEmittedAt !== "number") return false;
|
|
3240
|
+
if (incoming.summaryEmittedAt >= existing.summaryEmittedAt) return false;
|
|
3241
|
+
return existing.isFlowRunning === false && incoming.isFlowRunning === true;
|
|
3242
|
+
}
|
|
3236
3243
|
function applyThreadToCache(qc, thread) {
|
|
3244
|
+
const existingDetail = qc.getQueryData(
|
|
3245
|
+
threadsKeys.detail(thread.workSpaceId, thread.id)
|
|
3246
|
+
);
|
|
3247
|
+
if (isStaleSummary(thread, existingDetail)) {
|
|
3248
|
+
return false;
|
|
3249
|
+
}
|
|
3237
3250
|
qc.setQueryData(
|
|
3238
3251
|
threadsKeys.detail(thread.workSpaceId, thread.id),
|
|
3239
3252
|
(old) => ({ ...old ?? thread, ...thread })
|
|
@@ -3254,6 +3267,7 @@ function applyThreadToCache(qc, thread) {
|
|
|
3254
3267
|
if (!page?.data) return page;
|
|
3255
3268
|
const idx2 = page.data.findIndex((t) => t.id === thread.id);
|
|
3256
3269
|
if (idx2 === -1) return page;
|
|
3270
|
+
if (isStaleSummary(thread, page.data[idx2])) return page;
|
|
3257
3271
|
changed = true;
|
|
3258
3272
|
foundInList = true;
|
|
3259
3273
|
const nextData2 = page.data.slice();
|
|
@@ -3266,6 +3280,7 @@ function applyThreadToCache(qc, thread) {
|
|
|
3266
3280
|
if (!list2.data) return old;
|
|
3267
3281
|
const idx = list2.data.findIndex((t) => t.id === thread.id);
|
|
3268
3282
|
if (idx === -1) return old;
|
|
3283
|
+
if (isStaleSummary(thread, list2.data[idx])) return old;
|
|
3269
3284
|
foundInList = true;
|
|
3270
3285
|
const nextData = list2.data.slice();
|
|
3271
3286
|
nextData[idx] = { ...nextData[idx], ...thread };
|
|
@@ -3335,36 +3350,40 @@ var {
|
|
|
3335
3350
|
messageThreadsGetMessageThreadWorkspacesWorkspaceIdMessagethreadsIdResponse: threadResponseSchema
|
|
3336
3351
|
} = ChatZod;
|
|
3337
3352
|
function mapThreadDtoToModel(dto) {
|
|
3353
|
+
const lastUpdatedAt = utcDate(dto.lastUpdatedAt);
|
|
3338
3354
|
return {
|
|
3339
3355
|
id: dto.id,
|
|
3340
3356
|
createdAt: utcDate(dto.createdAt),
|
|
3341
3357
|
createdBy: dto.createdBy ?? "",
|
|
3342
3358
|
createdByUserId: dto.createdByUserId,
|
|
3343
3359
|
isFlowRunning: dto.isFlowRunning,
|
|
3344
|
-
lastUpdatedAt
|
|
3360
|
+
lastUpdatedAt,
|
|
3345
3361
|
lastUpdatedByUserId: dto.lastUpdatedByUserId,
|
|
3346
3362
|
name: dto.name ?? "",
|
|
3347
3363
|
totalMessages: dto.totalMessages,
|
|
3348
3364
|
pinned: dto.favorited,
|
|
3349
|
-
workSpaceId: dto.workSpaceId
|
|
3365
|
+
workSpaceId: dto.workSpaceId,
|
|
3366
|
+
summaryEmittedAt: lastUpdatedAt.getTime()
|
|
3350
3367
|
};
|
|
3351
3368
|
}
|
|
3352
3369
|
function mapThreadsResponseDtoToModel(dto) {
|
|
3353
3370
|
return { data: dto.data.map(mapThreadDtoToModel), total: dto.total };
|
|
3354
3371
|
}
|
|
3355
3372
|
function mapSignalRThreadSummaryToModel(summary) {
|
|
3373
|
+
const lastUpdatedAt = utcDate(summary.lastUpdatedAt);
|
|
3356
3374
|
return {
|
|
3357
3375
|
id: summary.id,
|
|
3358
3376
|
createdAt: utcDate(summary.createdAt),
|
|
3359
3377
|
createdBy: summary.createdBy ?? "",
|
|
3360
3378
|
createdByUserId: summary.createdByUserId,
|
|
3361
3379
|
isFlowRunning: summary.isFlowRunning,
|
|
3362
|
-
lastUpdatedAt
|
|
3380
|
+
lastUpdatedAt,
|
|
3363
3381
|
lastUpdatedByUserId: summary.lastUpdatedByUserId,
|
|
3364
3382
|
name: summary.name ?? "",
|
|
3365
3383
|
totalMessages: summary.totalMessages,
|
|
3366
3384
|
pinned: summary.favorited,
|
|
3367
|
-
workSpaceId: summary.workSpaceId
|
|
3385
|
+
workSpaceId: summary.workSpaceId,
|
|
3386
|
+
summaryEmittedAt: lastUpdatedAt.getTime()
|
|
3368
3387
|
};
|
|
3369
3388
|
}
|
|
3370
3389
|
var threadDetailOptions = ({
|
|
@@ -3540,7 +3559,13 @@ function useSendMessage() {
|
|
|
3540
3559
|
toast.error("There was an error posting your message");
|
|
3541
3560
|
throw err;
|
|
3542
3561
|
}
|
|
3543
|
-
qc.setQueryData(messagesKeys.list(threadId), [
|
|
3562
|
+
qc.setQueryData(messagesKeys.list(threadId), (old = []) => {
|
|
3563
|
+
const withoutOptimistic = old.filter((m) => !m.optimistic);
|
|
3564
|
+
if (withoutOptimistic.some((m) => m.id === realMessage.id)) {
|
|
3565
|
+
return withoutOptimistic;
|
|
3566
|
+
}
|
|
3567
|
+
return [...withoutOptimistic, realMessage];
|
|
3568
|
+
});
|
|
3544
3569
|
qc.setQueryData(
|
|
3545
3570
|
threadsKeys.detail(workspaceId, threadId),
|
|
3546
3571
|
(old) => old ? { ...old, isFlowRunning: true } : old
|
|
@@ -19804,6 +19829,10 @@ function MessageList({
|
|
|
19804
19829
|
const messagesEndRef = useRef(null);
|
|
19805
19830
|
const prevMessageCountRef = useRef(0);
|
|
19806
19831
|
const hasInitialScrollRef = useRef(false);
|
|
19832
|
+
const everHadMessagesRef = useRef({
|
|
19833
|
+
threadId: "",
|
|
19834
|
+
had: false
|
|
19835
|
+
});
|
|
19807
19836
|
const isMobile = useIsMobile();
|
|
19808
19837
|
const { data: activeWorkspace } = useWorkspace(workspaceId);
|
|
19809
19838
|
const [isAtBottom, setIsAtBottom] = useState(true);
|
|
@@ -19875,8 +19904,15 @@ function MessageList({
|
|
|
19875
19904
|
ro.observe(content);
|
|
19876
19905
|
return () => ro.disconnect();
|
|
19877
19906
|
}, [isAtBottom, scrollToBottom]);
|
|
19907
|
+
const safeMessages = messages ?? [];
|
|
19908
|
+
if (everHadMessagesRef.current.threadId !== threadId) {
|
|
19909
|
+
everHadMessagesRef.current = { threadId, had: safeMessages.length > 0 };
|
|
19910
|
+
} else if (safeMessages.length > 0) {
|
|
19911
|
+
everHadMessagesRef.current.had = true;
|
|
19912
|
+
}
|
|
19913
|
+
const hadMessagesBefore = everHadMessagesRef.current.had;
|
|
19878
19914
|
const isLoading = isChoosingThread || (threadPending || threadFetching) && !thread || (messagesPending || messagesFetching) && messages === void 0;
|
|
19879
|
-
if (isLoading) {
|
|
19915
|
+
if (isLoading && !hadMessagesBefore) {
|
|
19880
19916
|
return /* @__PURE__ */ jsx(
|
|
19881
19917
|
"div",
|
|
19882
19918
|
{
|
|
@@ -19892,7 +19928,7 @@ function MessageList({
|
|
|
19892
19928
|
}
|
|
19893
19929
|
);
|
|
19894
19930
|
}
|
|
19895
|
-
if (threadError || messagesError) {
|
|
19931
|
+
if ((threadError || messagesError) && !hadMessagesBefore) {
|
|
19896
19932
|
return /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center p-6", children: /* @__PURE__ */ jsxs("div", { className: "w-full max-w-md space-y-3", children: [
|
|
19897
19933
|
threadError && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-destructive", children: [
|
|
19898
19934
|
/* @__PURE__ */ jsx(AlertTriangle, { className: "h-4 w-4" }),
|
|
@@ -19904,8 +19940,16 @@ function MessageList({
|
|
|
19904
19940
|
] })
|
|
19905
19941
|
] }) });
|
|
19906
19942
|
}
|
|
19907
|
-
const safeMessages = messages ?? [];
|
|
19908
19943
|
if (safeMessages.length === 0) {
|
|
19944
|
+
if (hadMessagesBefore) {
|
|
19945
|
+
return /* @__PURE__ */ jsx(
|
|
19946
|
+
"div",
|
|
19947
|
+
{
|
|
19948
|
+
className: `ss-chat__body flex-shrink-10 flex-1 overflow-y-auto ${hostBg}`,
|
|
19949
|
+
"data-ss-layer": "message-list"
|
|
19950
|
+
}
|
|
19951
|
+
);
|
|
19952
|
+
}
|
|
19909
19953
|
return /* @__PURE__ */ jsxs("div", { className: "flex overflow-auto flex-shrink-10 flex-col p-8 text-center", children: [
|
|
19910
19954
|
/* @__PURE__ */ jsx("h3", { className: "text-lg font-medium mb-2", children: activeWorkspace?.name ?? "No messages yet" }),
|
|
19911
19955
|
activeWorkspace?.firstPrompt && /* @__PURE__ */ jsx("div", { className: "max-w-3xl mx-auto p-4", children: /* @__PURE__ */ jsx(MessageMarkdown, { value: activeWorkspace.firstPrompt }) })
|