@burdenoff/microfe-conversations 2026.830.2 → 2026.928.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/dist/conversations/hooks/useConversationQueries.js +66 -40
- package/dist/conversations/hooks/useConversationQueries.js.map +1 -1
- package/dist/conversations/pages/ConversationDetailPage.js +316 -276
- package/dist/conversations/pages/ConversationDetailPage.js.map +1 -1
- package/dist/conversations/utils/mappers.js +21 -12
- package/dist/conversations/utils/mappers.js.map +1 -1
- package/dist/conversations/utils/readReceiptCache.js +45 -0
- package/dist/conversations/utils/readReceiptCache.js.map +1 -0
- package/dist/generated/wspace-operations.js +90 -63
- package/dist/generated/wspace-operations.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { useConversations as e } from "../providers/ConversationsProvider.js";
|
|
2
|
-
import { useArchiveConversationMutation as t, useCreateConversationMutation as n, useGetConversationQuery as r, useGetConversationsQuery as i, useGetWorkspaceDirectoryQuery as a, useLeaveConversationMutation as o,
|
|
3
|
-
import { mapConversation as
|
|
4
|
-
import {
|
|
2
|
+
import { useArchiveConversationMutation as t, useCreateConversationMutation as n, useGetConversationQuery as r, useGetConversationsQuery as i, useGetWorkspaceDirectoryQuery as a, useLeaveConversationMutation as o, useMarkConversationAsReadMutation as s, useMuteConversationMutation as c, usePinConversationMutation as l, useSearchConversationsQuery as u, useUnarchiveConversationMutation as d, useUnmuteConversationMutation as f, useUnpinConversationMutation as p, useUpdateConversationByHookMutation as m } from "../../generated/wspace-operations.js";
|
|
3
|
+
import { mapConversation as h } from "../utils/mappers.js";
|
|
4
|
+
import { applyMarkConversationAsReadToCache as g } from "../utils/readReceiptCache.js";
|
|
5
|
+
import { useCallback as _, useEffect as v, useMemo as y, useState as b } from "react";
|
|
5
6
|
//#region src/conversations/hooks/useConversationQueries.ts
|
|
6
|
-
function
|
|
7
|
+
function x(t) {
|
|
7
8
|
let { workspaceId: n } = e(), r = i({
|
|
8
9
|
variables: {
|
|
9
10
|
first: t?.first ?? 25,
|
|
@@ -16,7 +17,7 @@ function y(t) {
|
|
|
16
17
|
}
|
|
17
18
|
},
|
|
18
19
|
skip: !n
|
|
19
|
-
}), a =
|
|
20
|
+
}), a = y(() => r.data?.conversations.edges.map((e) => h(e.node)) ?? [], [r.data]), o = r.data?.conversations.pageInfo.hasNextPage ?? !1;
|
|
20
21
|
return {
|
|
21
22
|
conversations: a,
|
|
22
23
|
loading: r.loading,
|
|
@@ -40,13 +41,13 @@ function y(t) {
|
|
|
40
41
|
}
|
|
41
42
|
};
|
|
42
43
|
}
|
|
43
|
-
function
|
|
44
|
+
function S(t) {
|
|
44
45
|
let { workspaceId: n } = e(), i = r({
|
|
45
46
|
variables: { id: t },
|
|
46
47
|
skip: !n || !t
|
|
47
48
|
});
|
|
48
49
|
return {
|
|
49
|
-
conversation: i.data?.conversation ?
|
|
50
|
+
conversation: i.data?.conversation ? h(i.data.conversation) : null,
|
|
50
51
|
members: i.data?.conversation?.members.edges.map((e) => e.node).filter((e) => !!e) ?? [],
|
|
51
52
|
membersTotalCount: i.data?.conversation?.members.totalCount ?? 0,
|
|
52
53
|
loading: i.loading,
|
|
@@ -56,8 +57,8 @@ function b(t) {
|
|
|
56
57
|
}
|
|
57
58
|
};
|
|
58
59
|
}
|
|
59
|
-
function
|
|
60
|
-
let { workspaceId: n } = e(), r = t.trim(), i =
|
|
60
|
+
function C(t) {
|
|
61
|
+
let { workspaceId: n } = e(), r = t.trim(), i = u({
|
|
61
62
|
variables: {
|
|
62
63
|
query: r,
|
|
63
64
|
first: 25
|
|
@@ -65,25 +66,25 @@ function x(t) {
|
|
|
65
66
|
skip: !n || r.length < 2
|
|
66
67
|
});
|
|
67
68
|
return {
|
|
68
|
-
conversations: i.data?.searchConversations.edges.map((e) =>
|
|
69
|
+
conversations: i.data?.searchConversations.edges.map((e) => h(e.node)) ?? [],
|
|
69
70
|
loading: i.loading,
|
|
70
71
|
error: i.error ?? null
|
|
71
72
|
};
|
|
72
73
|
}
|
|
73
|
-
var
|
|
74
|
-
function
|
|
75
|
-
let { workspaceId: n } = e(), [r, i] =
|
|
76
|
-
|
|
74
|
+
var w = 25, T = 250;
|
|
75
|
+
function E(t) {
|
|
76
|
+
let { workspaceId: n } = e(), [r, i] = b(t.trim());
|
|
77
|
+
v(() => {
|
|
77
78
|
let e = t.trim(), n = setTimeout(() => {
|
|
78
79
|
i(e);
|
|
79
|
-
},
|
|
80
|
+
}, T);
|
|
80
81
|
return () => clearTimeout(n);
|
|
81
82
|
}, [t]);
|
|
82
83
|
let o = a({
|
|
83
84
|
variables: {
|
|
84
85
|
filter: r ? { search: r } : void 0,
|
|
85
86
|
pagination: {
|
|
86
|
-
limit:
|
|
87
|
+
limit: w,
|
|
87
88
|
offset: 0
|
|
88
89
|
}
|
|
89
90
|
},
|
|
@@ -91,16 +92,16 @@ function w(t) {
|
|
|
91
92
|
fetchPolicy: "cache-and-network"
|
|
92
93
|
});
|
|
93
94
|
return {
|
|
94
|
-
members:
|
|
95
|
+
members: y(() => o.data?.workspaceDirectory.edges ?? [], [o.data]),
|
|
95
96
|
totalCount: o.data?.workspaceDirectory.totalCount ?? 0,
|
|
96
97
|
loading: o.loading,
|
|
97
98
|
error: o.error ?? null
|
|
98
99
|
};
|
|
99
100
|
}
|
|
100
|
-
function
|
|
101
|
+
function D() {
|
|
101
102
|
let { workspaceId: t } = e(), [r, i] = n();
|
|
102
103
|
return {
|
|
103
|
-
createConversation:
|
|
104
|
+
createConversation: _(async (e) => {
|
|
104
105
|
if (!t) throw Error("Workspace context is required to create a conversation");
|
|
105
106
|
try {
|
|
106
107
|
let { data: n, error: i } = await r({ variables: { input: {
|
|
@@ -112,7 +113,7 @@ function T() {
|
|
|
112
113
|
} } });
|
|
113
114
|
if (i) throw i;
|
|
114
115
|
let a = n?.createConversation.conversation;
|
|
115
|
-
return a ?
|
|
116
|
+
return a ? h(a) : null;
|
|
116
117
|
} catch (e) {
|
|
117
118
|
throw e instanceof Error ? e : /* @__PURE__ */ Error("Failed to create conversation");
|
|
118
119
|
}
|
|
@@ -121,10 +122,35 @@ function T() {
|
|
|
121
122
|
error: i.error ?? null
|
|
122
123
|
};
|
|
123
124
|
}
|
|
124
|
-
function
|
|
125
|
-
let [e,
|
|
125
|
+
function O() {
|
|
126
|
+
let [e, t] = s();
|
|
126
127
|
return {
|
|
127
|
-
|
|
128
|
+
markConversationAsRead: _(async (t) => {
|
|
129
|
+
try {
|
|
130
|
+
let { data: n, error: r } = await e({
|
|
131
|
+
variables: {
|
|
132
|
+
conversationId: t.conversationId,
|
|
133
|
+
messageId: t.messageId
|
|
134
|
+
},
|
|
135
|
+
update: (e, t) => {
|
|
136
|
+
let n = t.data?.markConversationAsRead;
|
|
137
|
+
n && g(e, n);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
if (r) throw r;
|
|
141
|
+
return !!n?.markConversationAsRead;
|
|
142
|
+
} catch (e) {
|
|
143
|
+
throw e instanceof Error ? e : /* @__PURE__ */ Error("Failed to mark conversation as read");
|
|
144
|
+
}
|
|
145
|
+
}, [e]),
|
|
146
|
+
loading: t.loading,
|
|
147
|
+
error: t.error ?? null
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function k() {
|
|
151
|
+
let [e, n] = t(), [r, i] = d(), [a, s] = l(), [u, m] = p(), [h, g] = c(), [v, y] = f(), [b, x] = o();
|
|
152
|
+
return {
|
|
153
|
+
archiveConversation: _(async (t) => {
|
|
128
154
|
let { data: n } = await e({
|
|
129
155
|
variables: { id: t },
|
|
130
156
|
refetchQueries: ["GetConversations"],
|
|
@@ -132,7 +158,7 @@ function E() {
|
|
|
132
158
|
});
|
|
133
159
|
return n?.archiveConversation.id === t;
|
|
134
160
|
}, [e]),
|
|
135
|
-
unarchiveConversation:
|
|
161
|
+
unarchiveConversation: _(async (e) => {
|
|
136
162
|
let { data: t } = await r({
|
|
137
163
|
variables: { id: e },
|
|
138
164
|
refetchQueries: ["GetConversations"],
|
|
@@ -140,23 +166,23 @@ function E() {
|
|
|
140
166
|
});
|
|
141
167
|
return t?.unarchiveConversation.id === e;
|
|
142
168
|
}, [r]),
|
|
143
|
-
pinConversation:
|
|
169
|
+
pinConversation: _(async (e) => {
|
|
144
170
|
let { data: t } = await a({ variables: { conversationId: e } });
|
|
145
171
|
return !!t?.pinConversation?.id;
|
|
146
172
|
}, [a]),
|
|
147
|
-
unpinConversation:
|
|
148
|
-
let { data: t } = await
|
|
173
|
+
unpinConversation: _(async (e) => {
|
|
174
|
+
let { data: t } = await u({ variables: { conversationId: e } });
|
|
149
175
|
return !!t?.unpinConversation?.id;
|
|
150
|
-
}, [
|
|
151
|
-
muteConversation:
|
|
152
|
-
let { data: t } = await
|
|
176
|
+
}, [u]),
|
|
177
|
+
muteConversation: _(async (e) => {
|
|
178
|
+
let { data: t } = await h({ variables: { conversationId: e } });
|
|
153
179
|
return !!t?.muteConversation?.id;
|
|
154
|
-
}, [
|
|
155
|
-
unmuteConversation:
|
|
180
|
+
}, [h]),
|
|
181
|
+
unmuteConversation: _(async (e) => {
|
|
156
182
|
let { data: t } = await v({ variables: { conversationId: e } });
|
|
157
183
|
return !!t?.unmuteConversation?.id;
|
|
158
184
|
}, [v]),
|
|
159
|
-
leaveConversation:
|
|
185
|
+
leaveConversation: _(async (e) => {
|
|
160
186
|
let { data: t } = await b({
|
|
161
187
|
variables: { conversationId: e },
|
|
162
188
|
refetchQueries: ["GetConversations"],
|
|
@@ -164,17 +190,17 @@ function E() {
|
|
|
164
190
|
});
|
|
165
191
|
return t?.leaveConversation.success ?? !1;
|
|
166
192
|
}, [b]),
|
|
167
|
-
loading: n.loading || i.loading ||
|
|
168
|
-
error: n.error ?? i.error ??
|
|
193
|
+
loading: n.loading || i.loading || s.loading || m.loading || g.loading || y.loading || x.loading,
|
|
194
|
+
error: n.error ?? i.error ?? s.error ?? m.error ?? g.error ?? y.error ?? x.error ?? null
|
|
169
195
|
};
|
|
170
196
|
}
|
|
171
|
-
function
|
|
172
|
-
let [e, t] =
|
|
197
|
+
function A() {
|
|
198
|
+
let [e, t] = m();
|
|
173
199
|
return {
|
|
174
|
-
updateConversation:
|
|
200
|
+
updateConversation: _(async (t) => {
|
|
175
201
|
try {
|
|
176
202
|
let { data: n } = await e({ variables: { input: t } }), r = n?.updateConversation?.conversation;
|
|
177
|
-
return r ?
|
|
203
|
+
return r ? h(r) : null;
|
|
178
204
|
} catch (e) {
|
|
179
205
|
throw e instanceof Error ? e : /* @__PURE__ */ Error("Failed to update conversation");
|
|
180
206
|
}
|
|
@@ -184,6 +210,6 @@ function D() {
|
|
|
184
210
|
};
|
|
185
211
|
}
|
|
186
212
|
//#endregion
|
|
187
|
-
export {
|
|
213
|
+
export { k as useConversationActions, D as useCreateConversation, S as useGetConversation, x as useGetConversations, O as useMarkConversationAsRead, C as useSearchConversations, A as useUpdateConversation, E as useWorkspaceDirectory };
|
|
188
214
|
|
|
189
215
|
//# sourceMappingURL=useConversationQueries.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useConversationQueries.js","names":[],"sources":["../../../src/conversations/hooks/useConversationQueries.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from \"react\";\nimport {\n type GetConversationQuery,\n type GetWorkspaceDirectoryQuery,\n useArchiveConversationMutation,\n useCreateConversationMutation,\n useGetConversationQuery,\n useGetConversationsQuery,\n useGetWorkspaceDirectoryQuery,\n useMuteConversationMutation,\n useLeaveConversationMutation,\n usePinConversationMutation,\n useSearchConversationsQuery,\n useUnarchiveConversationMutation,\n useUnmuteConversationMutation,\n useUnpinConversationMutation,\n useUpdateConversationByHookMutation,\n} from \"../../generated/wspace-operations\";\nimport type * as WspaceTypes from \"../../generated/wspace-types\";\nimport type {\n Conversation,\n ConversationState,\n ConversationType,\n ConversationVisibility,\n} from \"../types\";\nimport { useConversations } from \"../providers/ConversationsProvider\";\nimport { mapConversation } from \"../utils/mappers\";\n\ninterface UseGetConversationsOptions {\n first?: number;\n after?: string;\n types?: ConversationType[];\n /**\n * Conversation lifecycle states to include. When omitted the backend\n * returns ACTIVE conversations by default (filtering is union, not\n * exclusion, so `[\"ACTIVE\"]` and `undefined` are interchangeable for the\n * default inbox). Pass `[\"ARCHIVED\"]` to drive the archived tab.\n */\n states?: ConversationState[];\n hasUnread?: boolean;\n isPinned?: boolean;\n}\n\ninterface UseGetConversationsResult {\n conversations: Conversation[];\n loading: boolean;\n error: Error | null;\n hasNextPage: boolean;\n fetchMore: () => void;\n refetch: () => void;\n}\n\n/**\n * Hook: Fetch a paginated list of conversations.\n */\nexport function useGetConversations(\n options?: UseGetConversationsOptions,\n): UseGetConversationsResult {\n const { workspaceId } = useConversations();\n const queryResult = useGetConversationsQuery({\n variables: {\n first: options?.first ?? 25,\n after: options?.after,\n filter: {\n types: options?.types as WspaceTypes.InputMaybe<\n WspaceTypes.ConversationType[]\n >,\n states: options?.states as WspaceTypes.InputMaybe<\n WspaceTypes.ConversationState[]\n >,\n hasUnread: options?.hasUnread,\n isPinned: options?.isPinned,\n },\n },\n skip: !workspaceId,\n });\n\n const conversations = useMemo(\n () =>\n queryResult.data?.conversations.edges.map(\n (edge: { node: Record<string, unknown> }) => mapConversation(edge.node),\n ) ?? [],\n [queryResult.data],\n );\n\n const hasNextPage =\n queryResult.data?.conversations.pageInfo.hasNextPage ?? false;\n\n return {\n conversations,\n loading: queryResult.loading,\n error: queryResult.error ?? null,\n hasNextPage,\n fetchMore: () => {\n const endCursor = queryResult.data?.conversations.pageInfo.endCursor;\n if (!endCursor || !hasNextPage) return;\n void queryResult.fetchMore({\n variables: {\n first: options?.first ?? 25,\n after: endCursor,\n filter: {\n types: options?.types as WspaceTypes.InputMaybe<\n WspaceTypes.ConversationType[]\n >,\n states: options?.states as WspaceTypes.InputMaybe<\n WspaceTypes.ConversationState[]\n >,\n hasUnread: options?.hasUnread,\n isPinned: options?.isPinned,\n },\n },\n });\n },\n refetch: () => {\n void queryResult.refetch();\n },\n };\n}\n\ninterface UseGetConversationResult {\n conversation: Conversation | null;\n members: ConversationMember[];\n membersTotalCount: number;\n loading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\nexport type ConversationMember = NonNullable<\n GetConversationQuery[\"conversation\"]\n>[\"members\"][\"edges\"][number][\"node\"];\ntype ConversationMemberEdge = NonNullable<\n GetConversationQuery[\"conversation\"]\n>[\"members\"][\"edges\"][number];\n\n/**\n * Hook: Fetch a single conversation by ID.\n */\nexport function useGetConversation(\n conversationId: string,\n): UseGetConversationResult {\n const { workspaceId } = useConversations();\n const queryResult = useGetConversationQuery({\n variables: { id: conversationId },\n skip: !workspaceId || !conversationId,\n });\n\n return {\n conversation: queryResult.data?.conversation\n ? mapConversation(queryResult.data.conversation)\n : null,\n members:\n queryResult.data?.conversation?.members.edges\n .map((edge: ConversationMemberEdge) => edge.node)\n .filter(\n (member: ConversationMember | null): member is ConversationMember =>\n Boolean(member),\n ) ?? [],\n membersTotalCount: queryResult.data?.conversation?.members.totalCount ?? 0,\n loading: queryResult.loading,\n error: queryResult.error ?? null,\n refetch: () => {\n void queryResult.refetch();\n },\n };\n}\n\ninterface UseSearchConversationsResult {\n conversations: Conversation[];\n loading: boolean;\n error: Error | null;\n}\n\n/**\n * Hook: Search conversations by query string.\n */\nexport function useSearchConversations(\n query: string,\n): UseSearchConversationsResult {\n const { workspaceId } = useConversations();\n const trimmedQuery = query.trim();\n const queryResult = useSearchConversationsQuery({\n variables: { query: trimmedQuery, first: 25 },\n skip: !workspaceId || trimmedQuery.length < 2,\n });\n\n return {\n conversations:\n queryResult.data?.searchConversations.edges.map(\n (edge: { node: Record<string, unknown> }) => mapConversation(edge.node),\n ) ?? [],\n loading: queryResult.loading,\n error: queryResult.error ?? null,\n };\n}\n\n/**\n * A workspace member surfaced by the directory, resolved to a friendly\n * identity. `id` is the member's user id (used for `participantIds`).\n */\nexport type WorkspaceDirectoryMember =\n GetWorkspaceDirectoryQuery[\"workspaceDirectory\"][\"edges\"][number];\n\ninterface UseWorkspaceDirectoryResult {\n members: WorkspaceDirectoryMember[];\n totalCount: number;\n loading: boolean;\n error: Error | null;\n}\n\nconst DIRECTORY_PAGE_SIZE = 25;\nconst DIRECTORY_SEARCH_DEBOUNCE_MS = 250;\n\n/**\n * Hook: Search the current workspace's member directory (server-side,\n * debounced). Backed by the conversations `workspaceDirectory` query, which is\n * a thin, identity-resolved wrapper over wspace-workspace-svc. An empty search\n * lists the first page of members so the picker shows suggestions before the\n * user types.\n */\nexport function useWorkspaceDirectory(\n search: string,\n): UseWorkspaceDirectoryResult {\n const { workspaceId } = useConversations();\n const [debouncedSearch, setDebouncedSearch] = useState(search.trim());\n\n useEffect(() => {\n const trimmed = search.trim();\n const handle = setTimeout(() => {\n setDebouncedSearch(trimmed);\n }, DIRECTORY_SEARCH_DEBOUNCE_MS);\n return () => clearTimeout(handle);\n }, [search]);\n\n const queryResult = useGetWorkspaceDirectoryQuery({\n variables: {\n filter: debouncedSearch ? { search: debouncedSearch } : undefined,\n pagination: { limit: DIRECTORY_PAGE_SIZE, offset: 0 },\n },\n skip: !workspaceId,\n fetchPolicy: \"cache-and-network\",\n });\n\n const members = useMemo(\n () => queryResult.data?.workspaceDirectory.edges ?? [],\n [queryResult.data],\n );\n\n return {\n members,\n totalCount: queryResult.data?.workspaceDirectory.totalCount ?? 0,\n loading: queryResult.loading,\n error: queryResult.error ?? null,\n };\n}\n\ninterface CreateConversationInput {\n type: ConversationType;\n title?: string;\n description?: string;\n participantIds: string[];\n visibility?: ConversationVisibility;\n}\n\ninterface UseCreateConversationResult {\n createConversation: (\n input: CreateConversationInput,\n ) => Promise<Conversation | null>;\n loading: boolean;\n error: Error | null;\n}\n\n/**\n * Hook: Create a new conversation.\n */\nexport function useCreateConversation(): UseCreateConversationResult {\n const { workspaceId } = useConversations();\n const [createConversationMutation, mutationState] =\n useCreateConversationMutation();\n\n const createConversation = useCallback(\n async (input: CreateConversationInput): Promise<Conversation | null> => {\n if (!workspaceId) {\n throw new Error(\n \"Workspace context is required to create a conversation\",\n );\n }\n\n try {\n const { data, error } = await createConversationMutation({\n variables: {\n input: {\n workspaceId,\n type: input.type as WspaceTypes.ConversationType,\n title: input.title,\n description: input.description,\n participantIds: input.participantIds,\n },\n },\n });\n if (error) throw error;\n\n const conversation = data?.createConversation.conversation;\n return conversation ? mapConversation(conversation) : null;\n } catch (err) {\n throw err instanceof Error\n ? err\n : new Error(\"Failed to create conversation\");\n }\n },\n [createConversationMutation, workspaceId],\n );\n\n return {\n createConversation,\n loading: mutationState.loading,\n error: mutationState.error ?? null,\n };\n}\n\ninterface UseUpdateConversationResult {\n archiveConversation: (id: string) => Promise<boolean>;\n unarchiveConversation: (id: string) => Promise<boolean>;\n pinConversation: (id: string) => Promise<boolean>;\n unpinConversation: (id: string) => Promise<boolean>;\n muteConversation: (id: string) => Promise<boolean>;\n unmuteConversation: (id: string) => Promise<boolean>;\n leaveConversation: (id: string) => Promise<boolean>;\n loading: boolean;\n error: Error | null;\n}\n\n/**\n * Hook: Conversation mutation operations.\n */\nexport function useConversationActions(): UseUpdateConversationResult {\n const [archiveConversationMutation, archiveState] =\n useArchiveConversationMutation();\n const [unarchiveConversationMutation, unarchiveState] =\n useUnarchiveConversationMutation();\n const [pinConversationMutation, pinState] = usePinConversationMutation();\n const [unpinConversationMutation, unpinState] =\n useUnpinConversationMutation();\n const [muteConversationMutation, muteState] = useMuteConversationMutation();\n const [unmuteConversationMutation, unmuteState] =\n useUnmuteConversationMutation();\n const [leaveConversationMutation, leaveState] =\n useLeaveConversationMutation();\n\n const archiveConversation = useCallback(\n async (id: string): Promise<boolean> => {\n // refetch GetConversations so the row drops out of the inbox (and the\n // Archived tab gains it) without waiting for a manual refresh.\n const { data } = await archiveConversationMutation({\n variables: { id },\n refetchQueries: [\"GetConversations\"],\n awaitRefetchQueries: true,\n });\n return data?.archiveConversation.id === id;\n },\n [archiveConversationMutation],\n );\n\n const unarchiveConversation = useCallback(\n async (id: string): Promise<boolean> => {\n const { data } = await unarchiveConversationMutation({\n variables: { id },\n refetchQueries: [\"GetConversations\"],\n awaitRefetchQueries: true,\n });\n return data?.unarchiveConversation.id === id;\n },\n [unarchiveConversationMutation],\n );\n\n const pinConversation = useCallback(\n async (id: string): Promise<boolean> => {\n const { data } = await pinConversationMutation({\n variables: { conversationId: id },\n });\n return Boolean(data?.pinConversation?.id);\n },\n [pinConversationMutation],\n );\n\n const unpinConversation = useCallback(\n async (id: string): Promise<boolean> => {\n const { data } = await unpinConversationMutation({\n variables: { conversationId: id },\n });\n return Boolean(data?.unpinConversation?.id);\n },\n [unpinConversationMutation],\n );\n\n const muteConversation = useCallback(\n async (id: string): Promise<boolean> => {\n const { data } = await muteConversationMutation({\n variables: { conversationId: id },\n });\n return Boolean(data?.muteConversation?.id);\n },\n [muteConversationMutation],\n );\n\n const unmuteConversation = useCallback(\n async (id: string): Promise<boolean> => {\n const { data } = await unmuteConversationMutation({\n variables: { conversationId: id },\n });\n return Boolean(data?.unmuteConversation?.id);\n },\n [unmuteConversationMutation],\n );\n\n const leaveConversation = useCallback(\n async (id: string): Promise<boolean> => {\n const { data } = await leaveConversationMutation({\n variables: { conversationId: id },\n refetchQueries: [\"GetConversations\"],\n awaitRefetchQueries: true,\n });\n return data?.leaveConversation.success ?? false;\n },\n [leaveConversationMutation],\n );\n\n return {\n archiveConversation,\n unarchiveConversation,\n pinConversation,\n unpinConversation,\n muteConversation,\n unmuteConversation,\n leaveConversation,\n loading:\n archiveState.loading ||\n unarchiveState.loading ||\n pinState.loading ||\n unpinState.loading ||\n muteState.loading ||\n unmuteState.loading ||\n leaveState.loading,\n error:\n (archiveState.error as Error | undefined) ??\n (unarchiveState.error as Error | undefined) ??\n (pinState.error as Error | undefined) ??\n (unpinState.error as Error | undefined) ??\n (muteState.error as Error | undefined) ??\n (unmuteState.error as Error | undefined) ??\n (leaveState.error as Error | undefined) ??\n null,\n };\n}\n\ninterface UseUpdateConversationOptions {\n updateConversation: (input: {\n id: string;\n title?: string;\n description?: string;\n avatarUrl?: string;\n settings?: Record<string, unknown>;\n }) => Promise<Conversation | null>;\n loading: boolean;\n error: Error | null;\n}\n\nexport function useUpdateConversation(): UseUpdateConversationOptions {\n const [updateConversationMutation, mutationState] =\n useUpdateConversationByHookMutation();\n\n const updateConversation = useCallback(\n async (input: {\n id: string;\n title?: string;\n description?: string;\n avatarUrl?: string;\n settings?: Record<string, unknown>;\n }): Promise<Conversation | null> => {\n try {\n const { data } = await updateConversationMutation({\n variables: { input },\n });\n const typedData = data as\n | {\n updateConversation?: {\n conversation?: Record<string, unknown> | null;\n };\n }\n | undefined;\n const conversation = typedData?.updateConversation?.conversation;\n return conversation\n ? mapConversation(conversation as Record<string, unknown>)\n : null;\n } catch (err) {\n throw err instanceof Error\n ? err\n : new Error(\"Failed to update conversation\");\n }\n },\n [updateConversationMutation],\n );\n\n return {\n updateConversation,\n loading: mutationState.loading,\n error: mutationState.error ?? null,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useConversationQueries.js","names":[],"sources":["../../../src/conversations/hooks/useConversationQueries.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { type ApolloCache, type FetchResult } from \"@apollo/client\";\nimport {\n type GetConversationQuery,\n type GetWorkspaceDirectoryQuery,\n type MarkConversationAsReadMutation,\n useArchiveConversationMutation,\n useCreateConversationMutation,\n useGetConversationQuery,\n useGetConversationsQuery,\n useGetWorkspaceDirectoryQuery,\n useMarkConversationAsReadMutation,\n useMuteConversationMutation,\n useLeaveConversationMutation,\n usePinConversationMutation,\n useSearchConversationsQuery,\n useUnarchiveConversationMutation,\n useUnmuteConversationMutation,\n useUnpinConversationMutation,\n useUpdateConversationByHookMutation,\n} from \"../../generated/wspace-operations\";\nimport type * as WspaceTypes from \"../../generated/wspace-types\";\nimport type {\n Conversation,\n ConversationState,\n ConversationType,\n ConversationVisibility,\n} from \"../types\";\nimport { useConversations } from \"../providers/ConversationsProvider\";\nimport { mapConversation } from \"../utils/mappers\";\nimport { applyMarkConversationAsReadToCache } from \"../utils/readReceiptCache\";\n\ninterface UseGetConversationsOptions {\n first?: number;\n after?: string;\n types?: ConversationType[];\n /**\n * Conversation lifecycle states to include. When omitted the backend\n * returns ACTIVE conversations by default (filtering is union, not\n * exclusion, so `[\"ACTIVE\"]` and `undefined` are interchangeable for the\n * default inbox). Pass `[\"ARCHIVED\"]` to drive the archived tab.\n */\n states?: ConversationState[];\n hasUnread?: boolean;\n isPinned?: boolean;\n}\n\ninterface UseGetConversationsResult {\n conversations: Conversation[];\n loading: boolean;\n error: Error | null;\n hasNextPage: boolean;\n fetchMore: () => void;\n refetch: () => void;\n}\n\n/**\n * Hook: Fetch a paginated list of conversations.\n */\nexport function useGetConversations(\n options?: UseGetConversationsOptions,\n): UseGetConversationsResult {\n const { workspaceId } = useConversations();\n const queryResult = useGetConversationsQuery({\n variables: {\n first: options?.first ?? 25,\n after: options?.after,\n filter: {\n types: options?.types as WspaceTypes.InputMaybe<\n WspaceTypes.ConversationType[]\n >,\n states: options?.states as WspaceTypes.InputMaybe<\n WspaceTypes.ConversationState[]\n >,\n hasUnread: options?.hasUnread,\n isPinned: options?.isPinned,\n },\n },\n skip: !workspaceId,\n });\n\n const conversations = useMemo(\n () =>\n queryResult.data?.conversations.edges.map(\n (edge: { node: Record<string, unknown> }) => mapConversation(edge.node),\n ) ?? [],\n [queryResult.data],\n );\n\n const hasNextPage =\n queryResult.data?.conversations.pageInfo.hasNextPage ?? false;\n\n return {\n conversations,\n loading: queryResult.loading,\n error: queryResult.error ?? null,\n hasNextPage,\n fetchMore: () => {\n const endCursor = queryResult.data?.conversations.pageInfo.endCursor;\n if (!endCursor || !hasNextPage) return;\n void queryResult.fetchMore({\n variables: {\n first: options?.first ?? 25,\n after: endCursor,\n filter: {\n types: options?.types as WspaceTypes.InputMaybe<\n WspaceTypes.ConversationType[]\n >,\n states: options?.states as WspaceTypes.InputMaybe<\n WspaceTypes.ConversationState[]\n >,\n hasUnread: options?.hasUnread,\n isPinned: options?.isPinned,\n },\n },\n });\n },\n refetch: () => {\n void queryResult.refetch();\n },\n };\n}\n\ninterface UseGetConversationResult {\n conversation: Conversation | null;\n members: ConversationMember[];\n membersTotalCount: number;\n loading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\nexport type ConversationMember = NonNullable<\n GetConversationQuery[\"conversation\"]\n>[\"members\"][\"edges\"][number][\"node\"];\ntype ConversationMemberEdge = NonNullable<\n GetConversationQuery[\"conversation\"]\n>[\"members\"][\"edges\"][number];\n\n/**\n * Hook: Fetch a single conversation by ID.\n */\nexport function useGetConversation(\n conversationId: string,\n): UseGetConversationResult {\n const { workspaceId } = useConversations();\n const queryResult = useGetConversationQuery({\n variables: { id: conversationId },\n skip: !workspaceId || !conversationId,\n });\n\n return {\n conversation: queryResult.data?.conversation\n ? mapConversation(queryResult.data.conversation)\n : null,\n members:\n queryResult.data?.conversation?.members.edges\n .map((edge: ConversationMemberEdge) => edge.node)\n .filter(\n (member: ConversationMember | null): member is ConversationMember =>\n Boolean(member),\n ) ?? [],\n membersTotalCount: queryResult.data?.conversation?.members.totalCount ?? 0,\n loading: queryResult.loading,\n error: queryResult.error ?? null,\n refetch: () => {\n void queryResult.refetch();\n },\n };\n}\n\ninterface UseSearchConversationsResult {\n conversations: Conversation[];\n loading: boolean;\n error: Error | null;\n}\n\n/**\n * Hook: Search conversations by query string.\n */\nexport function useSearchConversations(\n query: string,\n): UseSearchConversationsResult {\n const { workspaceId } = useConversations();\n const trimmedQuery = query.trim();\n const queryResult = useSearchConversationsQuery({\n variables: { query: trimmedQuery, first: 25 },\n skip: !workspaceId || trimmedQuery.length < 2,\n });\n\n return {\n conversations:\n queryResult.data?.searchConversations.edges.map(\n (edge: { node: Record<string, unknown> }) => mapConversation(edge.node),\n ) ?? [],\n loading: queryResult.loading,\n error: queryResult.error ?? null,\n };\n}\n\n/**\n * A workspace member surfaced by the directory, resolved to a friendly\n * identity. `id` is the member's user id (used for `participantIds`).\n */\nexport type WorkspaceDirectoryMember =\n GetWorkspaceDirectoryQuery[\"workspaceDirectory\"][\"edges\"][number];\n\ninterface UseWorkspaceDirectoryResult {\n members: WorkspaceDirectoryMember[];\n totalCount: number;\n loading: boolean;\n error: Error | null;\n}\n\nconst DIRECTORY_PAGE_SIZE = 25;\nconst DIRECTORY_SEARCH_DEBOUNCE_MS = 250;\n\n/**\n * Hook: Search the current workspace's member directory (server-side,\n * debounced). Backed by the conversations `workspaceDirectory` query, which is\n * a thin, identity-resolved wrapper over wspace-workspace-svc. An empty search\n * lists the first page of members so the picker shows suggestions before the\n * user types.\n */\nexport function useWorkspaceDirectory(\n search: string,\n): UseWorkspaceDirectoryResult {\n const { workspaceId } = useConversations();\n const [debouncedSearch, setDebouncedSearch] = useState(search.trim());\n\n useEffect(() => {\n const trimmed = search.trim();\n const handle = setTimeout(() => {\n setDebouncedSearch(trimmed);\n }, DIRECTORY_SEARCH_DEBOUNCE_MS);\n return () => clearTimeout(handle);\n }, [search]);\n\n const queryResult = useGetWorkspaceDirectoryQuery({\n variables: {\n filter: debouncedSearch ? { search: debouncedSearch } : undefined,\n pagination: { limit: DIRECTORY_PAGE_SIZE, offset: 0 },\n },\n skip: !workspaceId,\n fetchPolicy: \"cache-and-network\",\n });\n\n const members = useMemo(\n () => queryResult.data?.workspaceDirectory.edges ?? [],\n [queryResult.data],\n );\n\n return {\n members,\n totalCount: queryResult.data?.workspaceDirectory.totalCount ?? 0,\n loading: queryResult.loading,\n error: queryResult.error ?? null,\n };\n}\n\ninterface CreateConversationInput {\n type: ConversationType;\n title?: string;\n description?: string;\n participantIds: string[];\n visibility?: ConversationVisibility;\n}\n\ninterface UseCreateConversationResult {\n createConversation: (\n input: CreateConversationInput,\n ) => Promise<Conversation | null>;\n loading: boolean;\n error: Error | null;\n}\n\n/**\n * Hook: Create a new conversation.\n */\nexport function useCreateConversation(): UseCreateConversationResult {\n const { workspaceId } = useConversations();\n const [createConversationMutation, mutationState] =\n useCreateConversationMutation();\n\n const createConversation = useCallback(\n async (input: CreateConversationInput): Promise<Conversation | null> => {\n if (!workspaceId) {\n throw new Error(\n \"Workspace context is required to create a conversation\",\n );\n }\n\n try {\n const { data, error } = await createConversationMutation({\n variables: {\n input: {\n workspaceId,\n type: input.type as WspaceTypes.ConversationType,\n title: input.title,\n description: input.description,\n participantIds: input.participantIds,\n },\n },\n });\n if (error) throw error;\n\n const conversation = data?.createConversation.conversation;\n return conversation ? mapConversation(conversation) : null;\n } catch (err) {\n throw err instanceof Error\n ? err\n : new Error(\"Failed to create conversation\");\n }\n },\n [createConversationMutation, workspaceId],\n );\n\n return {\n createConversation,\n loading: mutationState.loading,\n error: mutationState.error ?? null,\n };\n}\n\ninterface UseMarkConversationAsReadResult {\n markConversationAsRead: (input: {\n conversationId: string;\n messageId?: string;\n }) => Promise<boolean>;\n loading: boolean;\n error: Error | null;\n}\n\n/**\n * Hook: Mark a conversation as read for the current viewer.\n *\n * Pass the newest message the viewer has seen as `messageId`; when omitted\n * the backend marks the whole conversation read. On success the cache update\n * zeroes the viewer-scoped `unreadCount`/`mentionCount` fields on the cached\n * Conversation entity — the list badge reads those top-level fields, which\n * membership normalization alone would leave stale — and drops the\n * conversation from any cached `hasUnread: true` list (see\n * `applyMarkConversationAsReadToCache`).\n */\nexport function useMarkConversationAsRead(): UseMarkConversationAsReadResult {\n const [markConversationAsReadMutation, mutationState] =\n useMarkConversationAsReadMutation();\n\n const markConversationAsRead = useCallback(\n async (input: {\n conversationId: string;\n messageId?: string;\n }): Promise<boolean> => {\n try {\n const { data, error } = await markConversationAsReadMutation({\n variables: {\n conversationId: input.conversationId,\n messageId: input.messageId,\n },\n update: (\n cache: ApolloCache,\n result: FetchResult<MarkConversationAsReadMutation>,\n ) => {\n const membership = result.data?.markConversationAsRead;\n if (!membership) return;\n applyMarkConversationAsReadToCache(cache, membership);\n },\n });\n if (error) throw error;\n return Boolean(data?.markConversationAsRead);\n } catch (err) {\n throw err instanceof Error\n ? err\n : new Error(\"Failed to mark conversation as read\");\n }\n },\n [markConversationAsReadMutation],\n );\n\n return {\n markConversationAsRead,\n loading: mutationState.loading,\n error: mutationState.error ?? null,\n };\n}\n\ninterface UseUpdateConversationResult {\n archiveConversation: (id: string) => Promise<boolean>;\n unarchiveConversation: (id: string) => Promise<boolean>;\n pinConversation: (id: string) => Promise<boolean>;\n unpinConversation: (id: string) => Promise<boolean>;\n muteConversation: (id: string) => Promise<boolean>;\n unmuteConversation: (id: string) => Promise<boolean>;\n leaveConversation: (id: string) => Promise<boolean>;\n loading: boolean;\n error: Error | null;\n}\n\n/**\n * Hook: Conversation mutation operations.\n */\nexport function useConversationActions(): UseUpdateConversationResult {\n const [archiveConversationMutation, archiveState] =\n useArchiveConversationMutation();\n const [unarchiveConversationMutation, unarchiveState] =\n useUnarchiveConversationMutation();\n const [pinConversationMutation, pinState] = usePinConversationMutation();\n const [unpinConversationMutation, unpinState] =\n useUnpinConversationMutation();\n const [muteConversationMutation, muteState] = useMuteConversationMutation();\n const [unmuteConversationMutation, unmuteState] =\n useUnmuteConversationMutation();\n const [leaveConversationMutation, leaveState] =\n useLeaveConversationMutation();\n\n const archiveConversation = useCallback(\n async (id: string): Promise<boolean> => {\n // refetch GetConversations so the row drops out of the inbox (and the\n // Archived tab gains it) without waiting for a manual refresh.\n const { data } = await archiveConversationMutation({\n variables: { id },\n refetchQueries: [\"GetConversations\"],\n awaitRefetchQueries: true,\n });\n return data?.archiveConversation.id === id;\n },\n [archiveConversationMutation],\n );\n\n const unarchiveConversation = useCallback(\n async (id: string): Promise<boolean> => {\n const { data } = await unarchiveConversationMutation({\n variables: { id },\n refetchQueries: [\"GetConversations\"],\n awaitRefetchQueries: true,\n });\n return data?.unarchiveConversation.id === id;\n },\n [unarchiveConversationMutation],\n );\n\n const pinConversation = useCallback(\n async (id: string): Promise<boolean> => {\n const { data } = await pinConversationMutation({\n variables: { conversationId: id },\n });\n return Boolean(data?.pinConversation?.id);\n },\n [pinConversationMutation],\n );\n\n const unpinConversation = useCallback(\n async (id: string): Promise<boolean> => {\n const { data } = await unpinConversationMutation({\n variables: { conversationId: id },\n });\n return Boolean(data?.unpinConversation?.id);\n },\n [unpinConversationMutation],\n );\n\n const muteConversation = useCallback(\n async (id: string): Promise<boolean> => {\n const { data } = await muteConversationMutation({\n variables: { conversationId: id },\n });\n return Boolean(data?.muteConversation?.id);\n },\n [muteConversationMutation],\n );\n\n const unmuteConversation = useCallback(\n async (id: string): Promise<boolean> => {\n const { data } = await unmuteConversationMutation({\n variables: { conversationId: id },\n });\n return Boolean(data?.unmuteConversation?.id);\n },\n [unmuteConversationMutation],\n );\n\n const leaveConversation = useCallback(\n async (id: string): Promise<boolean> => {\n const { data } = await leaveConversationMutation({\n variables: { conversationId: id },\n refetchQueries: [\"GetConversations\"],\n awaitRefetchQueries: true,\n });\n return data?.leaveConversation.success ?? false;\n },\n [leaveConversationMutation],\n );\n\n return {\n archiveConversation,\n unarchiveConversation,\n pinConversation,\n unpinConversation,\n muteConversation,\n unmuteConversation,\n leaveConversation,\n loading:\n archiveState.loading ||\n unarchiveState.loading ||\n pinState.loading ||\n unpinState.loading ||\n muteState.loading ||\n unmuteState.loading ||\n leaveState.loading,\n error:\n (archiveState.error as Error | undefined) ??\n (unarchiveState.error as Error | undefined) ??\n (pinState.error as Error | undefined) ??\n (unpinState.error as Error | undefined) ??\n (muteState.error as Error | undefined) ??\n (unmuteState.error as Error | undefined) ??\n (leaveState.error as Error | undefined) ??\n null,\n };\n}\n\ninterface UseUpdateConversationOptions {\n updateConversation: (input: {\n id: string;\n title?: string;\n description?: string;\n avatarUrl?: string;\n settings?: Record<string, unknown>;\n }) => Promise<Conversation | null>;\n loading: boolean;\n error: Error | null;\n}\n\nexport function useUpdateConversation(): UseUpdateConversationOptions {\n const [updateConversationMutation, mutationState] =\n useUpdateConversationByHookMutation();\n\n const updateConversation = useCallback(\n async (input: {\n id: string;\n title?: string;\n description?: string;\n avatarUrl?: string;\n settings?: Record<string, unknown>;\n }): Promise<Conversation | null> => {\n try {\n const { data } = await updateConversationMutation({\n variables: { input },\n });\n const typedData = data as\n | {\n updateConversation?: {\n conversation?: Record<string, unknown> | null;\n };\n }\n | undefined;\n const conversation = typedData?.updateConversation?.conversation;\n return conversation\n ? mapConversation(conversation as Record<string, unknown>)\n : null;\n } catch (err) {\n throw err instanceof Error\n ? err\n : new Error(\"Failed to update conversation\");\n }\n },\n [updateConversationMutation],\n );\n\n return {\n updateConversation,\n loading: mutationState.loading,\n error: mutationState.error ?? null,\n };\n}\n"],"mappings":"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"}
|