@cstar.help/js 0.17.0 → 0.17.3
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 +17 -12
- package/dist/chat/index.cjs +202 -72
- package/dist/chat/index.d.cts +4 -8
- package/dist/chat/index.d.ts +4 -8
- package/dist/chat/index.js +202 -72
- package/dist/index.cjs +29 -27
- package/dist/index.d.cts +187 -60
- package/dist/index.d.ts +187 -60
- package/dist/index.js +29 -27
- package/package.json +1 -1
package/dist/chat/index.js
CHANGED
|
@@ -1,4 +1,26 @@
|
|
|
1
1
|
// src/chat/realtime.ts
|
|
2
|
+
var channelRemovals = /* @__PURE__ */ new WeakMap();
|
|
3
|
+
var CHANNEL_DRAIN_TIMEOUT_MS = 5e3;
|
|
4
|
+
function removeRealtimeChannel(client, channel) {
|
|
5
|
+
const pending = channelRemovals.get(client) ?? /* @__PURE__ */ new Set();
|
|
6
|
+
channelRemovals.set(client, pending);
|
|
7
|
+
const removal = client.removeChannel(channel);
|
|
8
|
+
pending.add(removal);
|
|
9
|
+
const settled = () => pending.delete(removal);
|
|
10
|
+
void removal.then(settled, settled);
|
|
11
|
+
}
|
|
12
|
+
async function closeRealtimeClient(client) {
|
|
13
|
+
const pending = [...channelRemovals.get(client) ?? []];
|
|
14
|
+
if (pending.length) {
|
|
15
|
+
let timeout;
|
|
16
|
+
const deadline = new Promise((resolve) => {
|
|
17
|
+
timeout = setTimeout(resolve, CHANNEL_DRAIN_TIMEOUT_MS);
|
|
18
|
+
});
|
|
19
|
+
await Promise.race([Promise.allSettled(pending), deadline]);
|
|
20
|
+
clearTimeout(timeout);
|
|
21
|
+
}
|
|
22
|
+
await client.realtime.disconnect();
|
|
23
|
+
}
|
|
2
24
|
var RealtimeManager = class {
|
|
3
25
|
constructor(options) {
|
|
4
26
|
this.client = null;
|
|
@@ -7,6 +29,7 @@ var RealtimeManager = class {
|
|
|
7
29
|
this.refreshTimer = null;
|
|
8
30
|
this.currentToken = null;
|
|
9
31
|
this.destroyed = false;
|
|
32
|
+
this.cleanupPromise = null;
|
|
10
33
|
this.options = options;
|
|
11
34
|
}
|
|
12
35
|
/**
|
|
@@ -28,7 +51,11 @@ var RealtimeManager = class {
|
|
|
28
51
|
"@supabase/supabase-js"
|
|
29
52
|
);
|
|
30
53
|
if (this.destroyed) return false;
|
|
54
|
+
this.currentToken = tokenData.token;
|
|
31
55
|
this.client = createClient(tokenData.realtimeUrl, tokenData.realtimeKey, {
|
|
56
|
+
// Reconnects and heartbeats must refresh from cStar's customer JWT,
|
|
57
|
+
// rather than the dedicated client's empty hosted-auth session.
|
|
58
|
+
accessToken: async () => this.currentToken,
|
|
32
59
|
auth: { persistSession: false, autoRefreshToken: false },
|
|
33
60
|
realtime: { params: { eventsPerSecond: 10 } }
|
|
34
61
|
});
|
|
@@ -69,12 +96,14 @@ var RealtimeManager = class {
|
|
|
69
96
|
* Subscribe to new messages on a ticket via Postgres Changes.
|
|
70
97
|
* Returns an unsubscribe function.
|
|
71
98
|
*/
|
|
72
|
-
subscribeToMessages(ticketId, callback) {
|
|
99
|
+
subscribeToMessages(ticketId, callback, onStatus) {
|
|
73
100
|
if (!this.client) return () => {
|
|
74
101
|
};
|
|
75
102
|
const channelName = `messages:${ticketId}`;
|
|
76
103
|
this.removeChannel(channelName);
|
|
77
|
-
const channel = this.client.channel(channelName)
|
|
104
|
+
const channel = this.client.channel(channelName);
|
|
105
|
+
this.channels.set(channelName, channel);
|
|
106
|
+
channel.on(
|
|
78
107
|
"postgres_changes",
|
|
79
108
|
{
|
|
80
109
|
event: "INSERT",
|
|
@@ -83,6 +112,7 @@ var RealtimeManager = class {
|
|
|
83
112
|
filter: `ticket_id=eq.${ticketId}`
|
|
84
113
|
},
|
|
85
114
|
(payload) => {
|
|
115
|
+
if (this.channels.get(channelName) !== channel) return;
|
|
86
116
|
const row = payload.new;
|
|
87
117
|
if (!row) return;
|
|
88
118
|
const message = {
|
|
@@ -97,9 +127,10 @@ var RealtimeManager = class {
|
|
|
97
127
|
};
|
|
98
128
|
callback(message);
|
|
99
129
|
}
|
|
100
|
-
).subscribe()
|
|
101
|
-
|
|
102
|
-
|
|
130
|
+
).subscribe((status) => {
|
|
131
|
+
if (this.channels.get(channelName) === channel) onStatus?.(status);
|
|
132
|
+
});
|
|
133
|
+
return () => this.removeChannel(channelName, channel);
|
|
103
134
|
}
|
|
104
135
|
/** Get the underlying realtime client (for sharing with PresenceManager). */
|
|
105
136
|
getClient() {
|
|
@@ -117,15 +148,17 @@ var RealtimeManager = class {
|
|
|
117
148
|
get isReady() {
|
|
118
149
|
return this.client !== null && this.currentToken !== null;
|
|
119
150
|
}
|
|
120
|
-
removeChannel(name) {
|
|
151
|
+
removeChannel(name, expected) {
|
|
121
152
|
const existing = this.channels.get(name);
|
|
153
|
+
if (expected && existing !== expected) return;
|
|
122
154
|
if (existing && this.client) {
|
|
123
|
-
this.client.removeChannel(existing);
|
|
124
155
|
this.channels.delete(name);
|
|
156
|
+
removeRealtimeChannel(this.client, existing);
|
|
125
157
|
}
|
|
126
158
|
}
|
|
127
159
|
/** Clean up all channels, timers, and the client. */
|
|
128
160
|
destroy() {
|
|
161
|
+
if (this.destroyed) return this.cleanupPromise ?? Promise.resolve();
|
|
129
162
|
this.destroyed = true;
|
|
130
163
|
if (this.refreshTimer) {
|
|
131
164
|
clearTimeout(this.refreshTimer);
|
|
@@ -135,9 +168,13 @@ var RealtimeManager = class {
|
|
|
135
168
|
this.removeChannel(name);
|
|
136
169
|
}
|
|
137
170
|
this.channels.clear();
|
|
171
|
+
const client = this.client;
|
|
138
172
|
this.client = null;
|
|
139
173
|
this.initPromise = null;
|
|
140
174
|
this.currentToken = null;
|
|
175
|
+
this.cleanupPromise = client ? closeRealtimeClient(client).catch(() => {
|
|
176
|
+
}) : Promise.resolve();
|
|
177
|
+
return this.cleanupPromise;
|
|
141
178
|
}
|
|
142
179
|
};
|
|
143
180
|
|
|
@@ -167,7 +204,10 @@ var PresenceManager = class {
|
|
|
167
204
|
};
|
|
168
205
|
const channelName = `typing:${ticketId}`;
|
|
169
206
|
this.removeChannel(channelName);
|
|
170
|
-
const channel = this.client.channel(`ticket:${ticketId}`)
|
|
207
|
+
const channel = this.client.channel(`ticket:${ticketId}`);
|
|
208
|
+
this.channels.set(channelName, channel);
|
|
209
|
+
channel.on("broadcast", { event: "typing" }, (data) => {
|
|
210
|
+
if (this.channels.get(channelName) !== channel) return;
|
|
171
211
|
const payload = data?.payload;
|
|
172
212
|
if (!payload) return;
|
|
173
213
|
if (payload.agent_id) {
|
|
@@ -179,8 +219,7 @@ var PresenceManager = class {
|
|
|
179
219
|
callback(typingPayload);
|
|
180
220
|
}
|
|
181
221
|
}).subscribe();
|
|
182
|
-
this.
|
|
183
|
-
return () => this.removeChannel(channelName);
|
|
222
|
+
return () => this.removeChannel(channelName, channel);
|
|
184
223
|
}
|
|
185
224
|
/**
|
|
186
225
|
* Send a customer typing indicator.
|
|
@@ -233,11 +272,12 @@ var PresenceManager = class {
|
|
|
233
272
|
} catch {
|
|
234
273
|
}
|
|
235
274
|
}
|
|
236
|
-
removeChannel(name) {
|
|
275
|
+
removeChannel(name, expected) {
|
|
237
276
|
const existing = this.channels.get(name);
|
|
277
|
+
if (expected && existing !== expected) return;
|
|
238
278
|
if (existing && this.client) {
|
|
239
|
-
this.client.removeChannel(existing);
|
|
240
279
|
this.channels.delete(name);
|
|
280
|
+
removeRealtimeChannel(this.client, existing);
|
|
241
281
|
}
|
|
242
282
|
}
|
|
243
283
|
/** Clean up all channels and timers. */
|
|
@@ -399,6 +439,7 @@ var ChatClient = class {
|
|
|
399
439
|
this.authMode = null;
|
|
400
440
|
this.sessionGeneration = 0;
|
|
401
441
|
this.pollingSubscriptions = /* @__PURE__ */ new Set();
|
|
442
|
+
this.realtimeMessageSubscriptions = /* @__PURE__ */ new Map();
|
|
402
443
|
this.stateListeners = /* @__PURE__ */ new Set();
|
|
403
444
|
this.realtimeManager = null;
|
|
404
445
|
this.presenceManager = null;
|
|
@@ -564,11 +605,16 @@ var ChatClient = class {
|
|
|
564
605
|
* @returns Unsubscribe function
|
|
565
606
|
*/
|
|
566
607
|
onMessage(ticketId, callback) {
|
|
567
|
-
const generation = this.sessionGeneration;
|
|
568
608
|
if (this.realtimeReady && this.realtimeManager) {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
609
|
+
this.realtimeMessageSubscriptions.get(ticketId)?.();
|
|
610
|
+
const unsubscribe = this.pollMessages(ticketId, callback, this.realtimeManager);
|
|
611
|
+
this.realtimeMessageSubscriptions.set(ticketId, unsubscribe);
|
|
612
|
+
return () => {
|
|
613
|
+
unsubscribe();
|
|
614
|
+
if (this.realtimeMessageSubscriptions.get(ticketId) === unsubscribe) {
|
|
615
|
+
this.realtimeMessageSubscriptions.delete(ticketId);
|
|
616
|
+
}
|
|
617
|
+
};
|
|
572
618
|
}
|
|
573
619
|
return this.pollMessages(ticketId, callback);
|
|
574
620
|
}
|
|
@@ -603,20 +649,21 @@ var ChatClient = class {
|
|
|
603
649
|
}
|
|
604
650
|
retireSession() {
|
|
605
651
|
const generation = ++this.sessionGeneration;
|
|
606
|
-
if (this.realtimeManager) {
|
|
607
|
-
this.realtimeManager.destroy();
|
|
608
|
-
this.realtimeManager = null;
|
|
609
|
-
}
|
|
610
652
|
if (this.presenceManager) {
|
|
611
653
|
this.presenceManager.destroy();
|
|
612
654
|
this.presenceManager = null;
|
|
613
655
|
}
|
|
656
|
+
if (this.realtimeManager) {
|
|
657
|
+
this.realtimeManager.destroy();
|
|
658
|
+
this.realtimeManager = null;
|
|
659
|
+
}
|
|
614
660
|
this.realtimeReady = false;
|
|
615
661
|
this.realtimeInitPromise = null;
|
|
616
662
|
for (const unsubscribe of this.pollingSubscriptions) {
|
|
617
663
|
unsubscribe();
|
|
618
664
|
}
|
|
619
665
|
this.pollingSubscriptions.clear();
|
|
666
|
+
this.realtimeMessageSubscriptions.clear();
|
|
620
667
|
this.sessionToken = null;
|
|
621
668
|
this.teamId = null;
|
|
622
669
|
this.customerId = null;
|
|
@@ -787,14 +834,9 @@ var ChatClient = class {
|
|
|
787
834
|
}
|
|
788
835
|
/**
|
|
789
836
|
* Resolve a team ID, lazily looking it up from `teamSlug` if no team ID
|
|
790
|
-
* has been cached yet.
|
|
791
|
-
*
|
|
792
|
-
*
|
|
793
|
-
* public data.
|
|
794
|
-
*
|
|
795
|
-
* Closes NEW-v3-05 — `TicketsState(chatClient)` previously errored
|
|
796
|
-
* `"Missing teamId"` when the chat client was teamSlug-only because the
|
|
797
|
-
* tickets sub-resource read `getTeamId()` synchronously and bailed.
|
|
837
|
+
* has been cached yet. Authenticated resources and realtime bootstrap
|
|
838
|
+
* use this after session login so callers do not need to list tickets
|
|
839
|
+
* or set a UUID before opening an existing conversation.
|
|
798
840
|
*/
|
|
799
841
|
async ensureTeamId(signal) {
|
|
800
842
|
const assertCurrent = this.captureSession();
|
|
@@ -846,54 +888,90 @@ var ChatClient = class {
|
|
|
846
888
|
}
|
|
847
889
|
}
|
|
848
890
|
async fetchRealtimeToken(generation) {
|
|
891
|
+
this.assertSession(generation);
|
|
892
|
+
const teamId = await this.ensureTeamId();
|
|
849
893
|
this.assertSession(generation);
|
|
850
894
|
const response = await this.fetch(
|
|
851
895
|
"/api/widget/realtime-token",
|
|
852
896
|
{
|
|
853
897
|
method: "POST",
|
|
854
|
-
body: { teamId
|
|
898
|
+
body: { teamId }
|
|
855
899
|
}
|
|
856
900
|
);
|
|
857
901
|
this.assertSession(generation);
|
|
858
902
|
return response.data;
|
|
859
903
|
}
|
|
860
|
-
pollMessages(ticketId, callback) {
|
|
861
|
-
|
|
904
|
+
pollMessages(ticketId, callback, manager) {
|
|
905
|
+
const seen = /* @__PURE__ */ new Set();
|
|
862
906
|
let initialized = false;
|
|
863
907
|
let active = true;
|
|
864
908
|
let inFlight = false;
|
|
865
|
-
|
|
866
|
-
|
|
909
|
+
let catchUpQueued = false;
|
|
910
|
+
let connected = false;
|
|
911
|
+
let timer;
|
|
912
|
+
let stopRealtime;
|
|
913
|
+
const deliver = (message) => {
|
|
914
|
+
if (!active || seen.has(message.id)) return;
|
|
915
|
+
seen.add(message.id);
|
|
916
|
+
callback(message);
|
|
917
|
+
};
|
|
918
|
+
const stopPolling = () => {
|
|
919
|
+
clearInterval(timer);
|
|
920
|
+
timer = void 0;
|
|
921
|
+
};
|
|
922
|
+
const startPolling = () => {
|
|
923
|
+
if (active && timer === void 0) {
|
|
924
|
+
timer = setInterval(() => void poll(), this.pollingInterval);
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
const poll = async (catchUp = false) => {
|
|
928
|
+
if (!active) return;
|
|
929
|
+
if (inFlight) {
|
|
930
|
+
if (catchUp) catchUpQueued = true;
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
867
933
|
inFlight = true;
|
|
868
934
|
try {
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
const
|
|
875
|
-
const newMessages = lastIdx >= 0 ? messages.slice(lastIdx + 1) : lastMessageId ? [latest] : messages;
|
|
876
|
-
for (const msg of newMessages) {
|
|
935
|
+
do {
|
|
936
|
+
catchUpQueued = false;
|
|
937
|
+
try {
|
|
938
|
+
const messages = await this.messages.list(ticketId);
|
|
939
|
+
if (!active) return;
|
|
940
|
+
for (const message of messages) {
|
|
877
941
|
if (!active) return;
|
|
878
|
-
|
|
942
|
+
if (initialized) deliver(message);
|
|
943
|
+
else seen.add(message.id);
|
|
879
944
|
}
|
|
945
|
+
initialized = true;
|
|
946
|
+
if (connected) stopPolling();
|
|
947
|
+
} catch {
|
|
948
|
+
startPolling();
|
|
880
949
|
}
|
|
881
|
-
|
|
882
|
-
}
|
|
883
|
-
initialized = true;
|
|
884
|
-
} catch {
|
|
950
|
+
} while (active && catchUpQueued);
|
|
885
951
|
} finally {
|
|
886
952
|
inFlight = false;
|
|
887
953
|
}
|
|
888
954
|
};
|
|
889
|
-
poll();
|
|
890
|
-
const timer = setInterval(poll, this.pollingInterval);
|
|
891
955
|
const unsubscribe = () => {
|
|
892
956
|
active = false;
|
|
893
|
-
|
|
957
|
+
stopPolling();
|
|
958
|
+
stopRealtime?.();
|
|
894
959
|
this.pollingSubscriptions.delete(unsubscribe);
|
|
895
960
|
};
|
|
896
961
|
this.pollingSubscriptions.add(unsubscribe);
|
|
962
|
+
void poll();
|
|
963
|
+
startPolling();
|
|
964
|
+
stopRealtime = manager?.subscribeToMessages(ticketId, deliver, (status) => {
|
|
965
|
+
if (!active) return;
|
|
966
|
+
if (status === "SUBSCRIBED") {
|
|
967
|
+
connected = true;
|
|
968
|
+
stopPolling();
|
|
969
|
+
} else if (["CHANNEL_ERROR", "TIMED_OUT", "CLOSED"].includes(status)) {
|
|
970
|
+
connected = false;
|
|
971
|
+
startPolling();
|
|
972
|
+
} else return;
|
|
973
|
+
void poll(true);
|
|
974
|
+
});
|
|
897
975
|
return unsubscribe;
|
|
898
976
|
}
|
|
899
977
|
buildUrl(path, query) {
|
|
@@ -982,8 +1060,12 @@ var ChatCommunityApi = class {
|
|
|
982
1060
|
}
|
|
983
1061
|
/** Create a new community post as the identified customer. */
|
|
984
1062
|
async createPost(params) {
|
|
985
|
-
const
|
|
986
|
-
if (
|
|
1063
|
+
const assertCurrent = this.chat.captureSession();
|
|
1064
|
+
if (this.chat.getAuthMode() === null) {
|
|
1065
|
+
throw new ChatClientAuthError("Sign in or identify a customer before accessing community.");
|
|
1066
|
+
}
|
|
1067
|
+
const teamId = await this.chat.ensureTeamId();
|
|
1068
|
+
assertCurrent();
|
|
987
1069
|
const response = await this.chat.fetch("/api/widget/community", {
|
|
988
1070
|
method: "POST",
|
|
989
1071
|
body: {
|
|
@@ -995,12 +1077,17 @@ var ChatCommunityApi = class {
|
|
|
995
1077
|
customFields: params.customFields
|
|
996
1078
|
}
|
|
997
1079
|
});
|
|
1080
|
+
assertCurrent();
|
|
998
1081
|
return response.data.post;
|
|
999
1082
|
}
|
|
1000
1083
|
/** Update a post. Only the original author can edit. */
|
|
1001
1084
|
async updatePost(postId, params) {
|
|
1002
|
-
const
|
|
1003
|
-
if (
|
|
1085
|
+
const assertCurrent = this.chat.captureSession();
|
|
1086
|
+
if (this.chat.getAuthMode() === null) {
|
|
1087
|
+
throw new ChatClientAuthError("Sign in or identify a customer before accessing community.");
|
|
1088
|
+
}
|
|
1089
|
+
const teamId = await this.chat.ensureTeamId();
|
|
1090
|
+
assertCurrent();
|
|
1004
1091
|
const response = await this.chat.fetch("/api/widget/community", {
|
|
1005
1092
|
method: "POST",
|
|
1006
1093
|
body: {
|
|
@@ -1012,21 +1099,31 @@ var ChatCommunityApi = class {
|
|
|
1012
1099
|
customFields: params.customFields
|
|
1013
1100
|
}
|
|
1014
1101
|
});
|
|
1102
|
+
assertCurrent();
|
|
1015
1103
|
return response.data.post;
|
|
1016
1104
|
}
|
|
1017
1105
|
/** Delete a post (soft delete). Only the original author can delete. */
|
|
1018
1106
|
async deletePost(postId) {
|
|
1019
|
-
const
|
|
1020
|
-
if (
|
|
1107
|
+
const assertCurrent = this.chat.captureSession();
|
|
1108
|
+
if (this.chat.getAuthMode() === null) {
|
|
1109
|
+
throw new ChatClientAuthError("Sign in or identify a customer before accessing community.");
|
|
1110
|
+
}
|
|
1111
|
+
const teamId = await this.chat.ensureTeamId();
|
|
1112
|
+
assertCurrent();
|
|
1021
1113
|
await this.chat.fetch("/api/widget/community", {
|
|
1022
1114
|
method: "POST",
|
|
1023
1115
|
body: { teamId, action: "deletePost", postId }
|
|
1024
1116
|
});
|
|
1117
|
+
assertCurrent();
|
|
1025
1118
|
}
|
|
1026
1119
|
/** Update a comment. Only the original author can edit. */
|
|
1027
1120
|
async updateComment(postId, commentId, params) {
|
|
1028
|
-
const
|
|
1029
|
-
if (
|
|
1121
|
+
const assertCurrent = this.chat.captureSession();
|
|
1122
|
+
if (this.chat.getAuthMode() === null) {
|
|
1123
|
+
throw new ChatClientAuthError("Sign in or identify a customer before accessing community.");
|
|
1124
|
+
}
|
|
1125
|
+
const teamId = await this.chat.ensureTeamId();
|
|
1126
|
+
assertCurrent();
|
|
1030
1127
|
const response = await this.chat.fetch("/api/widget/community", {
|
|
1031
1128
|
method: "POST",
|
|
1032
1129
|
body: {
|
|
@@ -1037,31 +1134,46 @@ var ChatCommunityApi = class {
|
|
|
1037
1134
|
commentBody: params.body
|
|
1038
1135
|
}
|
|
1039
1136
|
});
|
|
1137
|
+
assertCurrent();
|
|
1040
1138
|
return response.data.comment;
|
|
1041
1139
|
}
|
|
1042
1140
|
/** Delete a comment (soft delete). Only the original author can delete. */
|
|
1043
1141
|
async deleteComment(postId, commentId) {
|
|
1044
|
-
const
|
|
1045
|
-
if (
|
|
1142
|
+
const assertCurrent = this.chat.captureSession();
|
|
1143
|
+
if (this.chat.getAuthMode() === null) {
|
|
1144
|
+
throw new ChatClientAuthError("Sign in or identify a customer before accessing community.");
|
|
1145
|
+
}
|
|
1146
|
+
const teamId = await this.chat.ensureTeamId();
|
|
1147
|
+
assertCurrent();
|
|
1046
1148
|
await this.chat.fetch("/api/widget/community", {
|
|
1047
1149
|
method: "POST",
|
|
1048
1150
|
body: { teamId, action: "deleteComment", postId, commentId }
|
|
1049
1151
|
});
|
|
1152
|
+
assertCurrent();
|
|
1050
1153
|
}
|
|
1051
1154
|
/** Toggle vote on a post. Returns new vote state and count. */
|
|
1052
1155
|
async vote(postId) {
|
|
1053
|
-
const
|
|
1054
|
-
if (
|
|
1156
|
+
const assertCurrent = this.chat.captureSession();
|
|
1157
|
+
if (this.chat.getAuthMode() === null) {
|
|
1158
|
+
throw new ChatClientAuthError("Sign in or identify a customer before accessing community.");
|
|
1159
|
+
}
|
|
1160
|
+
const teamId = await this.chat.ensureTeamId();
|
|
1161
|
+
assertCurrent();
|
|
1055
1162
|
const response = await this.chat.fetch("/api/widget/community", {
|
|
1056
1163
|
method: "POST",
|
|
1057
1164
|
body: { teamId, action: "vote", postId }
|
|
1058
1165
|
});
|
|
1166
|
+
assertCurrent();
|
|
1059
1167
|
return response.data;
|
|
1060
1168
|
}
|
|
1061
1169
|
/** Add a comment to a post. Supports 1-level nesting via parentCommentId. */
|
|
1062
1170
|
async comment(postId, params) {
|
|
1063
|
-
const
|
|
1064
|
-
if (
|
|
1171
|
+
const assertCurrent = this.chat.captureSession();
|
|
1172
|
+
if (this.chat.getAuthMode() === null) {
|
|
1173
|
+
throw new ChatClientAuthError("Sign in or identify a customer before accessing community.");
|
|
1174
|
+
}
|
|
1175
|
+
const teamId = await this.chat.ensureTeamId();
|
|
1176
|
+
assertCurrent();
|
|
1065
1177
|
const response = await this.chat.fetch("/api/widget/community", {
|
|
1066
1178
|
method: "POST",
|
|
1067
1179
|
body: {
|
|
@@ -1072,37 +1184,53 @@ var ChatCommunityApi = class {
|
|
|
1072
1184
|
parentCommentId: params.parentCommentId ?? null
|
|
1073
1185
|
}
|
|
1074
1186
|
});
|
|
1187
|
+
assertCurrent();
|
|
1075
1188
|
return response.data.comment;
|
|
1076
1189
|
}
|
|
1077
1190
|
/** Toggle follow on a post. */
|
|
1078
1191
|
async follow(postId) {
|
|
1079
|
-
const
|
|
1080
|
-
if (
|
|
1192
|
+
const assertCurrent = this.chat.captureSession();
|
|
1193
|
+
if (this.chat.getAuthMode() === null) {
|
|
1194
|
+
throw new ChatClientAuthError("Sign in or identify a customer before accessing community.");
|
|
1195
|
+
}
|
|
1196
|
+
const teamId = await this.chat.ensureTeamId();
|
|
1197
|
+
assertCurrent();
|
|
1081
1198
|
const response = await this.chat.fetch("/api/widget/community", {
|
|
1082
1199
|
method: "POST",
|
|
1083
1200
|
body: { teamId, action: "follow", postId }
|
|
1084
1201
|
});
|
|
1202
|
+
assertCurrent();
|
|
1085
1203
|
return response.data;
|
|
1086
1204
|
}
|
|
1087
1205
|
/** Check if the current user has voted on a post. */
|
|
1088
1206
|
async getVoteStatus(postId) {
|
|
1089
|
-
const
|
|
1090
|
-
if (
|
|
1207
|
+
const assertCurrent = this.chat.captureSession();
|
|
1208
|
+
if (this.chat.getAuthMode() === null) {
|
|
1209
|
+
throw new ChatClientAuthError("Sign in or identify a customer before accessing community.");
|
|
1210
|
+
}
|
|
1211
|
+
const teamId = await this.chat.ensureTeamId();
|
|
1212
|
+
assertCurrent();
|
|
1091
1213
|
const response = await this.chat.fetch("/api/widget/community", {
|
|
1092
1214
|
method: "GET",
|
|
1093
1215
|
query: { teamId, postId }
|
|
1094
1216
|
});
|
|
1217
|
+
assertCurrent();
|
|
1095
1218
|
return response.data.voted;
|
|
1096
1219
|
}
|
|
1097
1220
|
/** Check vote status for multiple posts in one request. */
|
|
1098
1221
|
async bulkVoteStatus(postIds) {
|
|
1099
1222
|
if (postIds.length === 0) return {};
|
|
1100
|
-
const
|
|
1101
|
-
if (
|
|
1223
|
+
const assertCurrent = this.chat.captureSession();
|
|
1224
|
+
if (this.chat.getAuthMode() === null) {
|
|
1225
|
+
throw new ChatClientAuthError("Sign in or identify a customer before accessing community.");
|
|
1226
|
+
}
|
|
1227
|
+
const teamId = await this.chat.ensureTeamId();
|
|
1228
|
+
assertCurrent();
|
|
1102
1229
|
const response = await this.chat.fetch("/api/widget/community", {
|
|
1103
1230
|
method: "GET",
|
|
1104
1231
|
query: { teamId, postIds: postIds.join(",") }
|
|
1105
1232
|
});
|
|
1233
|
+
assertCurrent();
|
|
1106
1234
|
return response.data;
|
|
1107
1235
|
}
|
|
1108
1236
|
};
|
|
@@ -1113,13 +1241,14 @@ var MessagesApi = class {
|
|
|
1113
1241
|
/** List messages for a ticket. */
|
|
1114
1242
|
async list(ticketId) {
|
|
1115
1243
|
const assertCurrent = this.chat.captureSession();
|
|
1116
|
-
const teamId = this.chat.
|
|
1244
|
+
const teamId = await this.chat.ensureTeamId();
|
|
1245
|
+
assertCurrent();
|
|
1117
1246
|
const response = await this.chat.fetch(
|
|
1118
1247
|
"/api/widget/messages",
|
|
1119
1248
|
{
|
|
1120
1249
|
method: "GET",
|
|
1121
1250
|
query: {
|
|
1122
|
-
teamId
|
|
1251
|
+
teamId,
|
|
1123
1252
|
ticketId
|
|
1124
1253
|
}
|
|
1125
1254
|
}
|
|
@@ -1130,7 +1259,8 @@ var MessagesApi = class {
|
|
|
1130
1259
|
/** Send a message to a ticket. */
|
|
1131
1260
|
async send(ticketId, content) {
|
|
1132
1261
|
const assertCurrent = this.chat.captureSession();
|
|
1133
|
-
const teamId = this.chat.
|
|
1262
|
+
const teamId = await this.chat.ensureTeamId();
|
|
1263
|
+
assertCurrent();
|
|
1134
1264
|
const response = await this.chat.fetch(
|
|
1135
1265
|
"/api/widget/messages",
|
|
1136
1266
|
{
|