@appgram/react 0.1.0
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/LICENSE +21 -0
- package/dist/chunk-3UBJGXCO.js +6368 -0
- package/dist/chunk-3UBJGXCO.js.map +1 -0
- package/dist/chunk-75P634IK.js +758 -0
- package/dist/chunk-75P634IK.js.map +1 -0
- package/dist/chunk-AIDLOCVJ.mjs +6341 -0
- package/dist/chunk-AIDLOCVJ.mjs.map +1 -0
- package/dist/chunk-KPIKYXAN.mjs +47 -0
- package/dist/chunk-KPIKYXAN.mjs.map +1 -0
- package/dist/chunk-N6PJDQCU.mjs +741 -0
- package/dist/chunk-N6PJDQCU.mjs.map +1 -0
- package/dist/chunk-ZJZ3A2S3.js +49 -0
- package/dist/chunk-ZJZ3A2S3.js.map +1 -0
- package/dist/components/index.d.mts +1283 -0
- package/dist/components/index.d.ts +1283 -0
- package/dist/components/index.js +85 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/index.mjs +4 -0
- package/dist/components/index.mjs.map +1 -0
- package/dist/hooks/index.d.mts +572 -0
- package/dist/hooks/index.d.ts +572 -0
- package/dist/hooks/index.js +53 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/index.mjs +4 -0
- package/dist/hooks/index.mjs.map +1 -0
- package/dist/index.d.mts +399 -0
- package/dist/index.d.ts +399 -0
- package/dist/index.js +783 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +645 -0
- package/dist/index.mjs.map +1 -0
- package/dist/useVote-CLhkwtLT.d.mts +420 -0
- package/dist/useVote-CLhkwtLT.d.ts +420 -0
- package/package.json +82 -0
- package/tailwind-preset.js +48 -0
|
@@ -0,0 +1,741 @@
|
|
|
1
|
+
import { createContext, useContext, useState, useRef, useCallback, useEffect } from 'react';
|
|
2
|
+
import { clsx } from 'clsx';
|
|
3
|
+
import { twMerge } from 'tailwind-merge';
|
|
4
|
+
|
|
5
|
+
// src/provider/context.ts
|
|
6
|
+
var AppgramContext = createContext(null);
|
|
7
|
+
function useAppgramContext() {
|
|
8
|
+
const context = useContext(AppgramContext);
|
|
9
|
+
if (!context) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
"useAppgramContext must be used within an AppgramProvider. Make sure you have wrapped your app with <AppgramProvider>."
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
return context;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/utils/fingerprint.ts
|
|
18
|
+
var STORAGE_KEY = "appgram_fingerprint";
|
|
19
|
+
function simpleHash(str) {
|
|
20
|
+
let hash = 0;
|
|
21
|
+
for (let i = 0; i < str.length; i++) {
|
|
22
|
+
const char = str.charCodeAt(i);
|
|
23
|
+
hash = (hash << 5) - hash + char;
|
|
24
|
+
hash = hash & hash;
|
|
25
|
+
}
|
|
26
|
+
return Math.abs(hash).toString(36);
|
|
27
|
+
}
|
|
28
|
+
function getBrowserCharacteristics() {
|
|
29
|
+
if (typeof window === "undefined") {
|
|
30
|
+
return "server-side";
|
|
31
|
+
}
|
|
32
|
+
const characteristics = [
|
|
33
|
+
navigator.userAgent,
|
|
34
|
+
navigator.language,
|
|
35
|
+
screen.colorDepth,
|
|
36
|
+
screen.width,
|
|
37
|
+
screen.height,
|
|
38
|
+
(/* @__PURE__ */ new Date()).getTimezoneOffset(),
|
|
39
|
+
navigator.hardwareConcurrency || 0,
|
|
40
|
+
"deviceMemory" in navigator ? navigator.deviceMemory : 0
|
|
41
|
+
];
|
|
42
|
+
return characteristics.join("|");
|
|
43
|
+
}
|
|
44
|
+
function generateFingerprint() {
|
|
45
|
+
const characteristics = getBrowserCharacteristics();
|
|
46
|
+
const timestamp = Date.now().toString(36);
|
|
47
|
+
const randomBytes = new Uint8Array(6);
|
|
48
|
+
crypto.getRandomValues(randomBytes);
|
|
49
|
+
const random = Array.from(randomBytes, (b) => b.toString(36)).join("").substring(0, 10);
|
|
50
|
+
return `${simpleHash(characteristics)}-${timestamp}-${random}`;
|
|
51
|
+
}
|
|
52
|
+
function getFingerprint() {
|
|
53
|
+
if (typeof window === "undefined") {
|
|
54
|
+
return "server-side-fingerprint";
|
|
55
|
+
}
|
|
56
|
+
let fingerprint = localStorage.getItem(STORAGE_KEY);
|
|
57
|
+
if (!fingerprint) {
|
|
58
|
+
fingerprint = generateFingerprint();
|
|
59
|
+
try {
|
|
60
|
+
localStorage.setItem(STORAGE_KEY, fingerprint);
|
|
61
|
+
} catch {
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return fingerprint;
|
|
65
|
+
}
|
|
66
|
+
function resetFingerprint() {
|
|
67
|
+
if (typeof window === "undefined") return;
|
|
68
|
+
try {
|
|
69
|
+
localStorage.removeItem(STORAGE_KEY);
|
|
70
|
+
} catch {
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function cn(...inputs) {
|
|
74
|
+
return twMerge(clsx(inputs));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// src/utils/index.ts
|
|
78
|
+
function getErrorMessage(error, fallback = "An error occurred") {
|
|
79
|
+
if (!error) return fallback;
|
|
80
|
+
if (typeof error === "string") return error;
|
|
81
|
+
if (typeof error === "object" && error !== null) {
|
|
82
|
+
if ("message" in error && typeof error.message === "string") {
|
|
83
|
+
return error.message;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return fallback;
|
|
87
|
+
}
|
|
88
|
+
function useWishes(options = {}) {
|
|
89
|
+
const { client, fingerprint } = useAppgramContext();
|
|
90
|
+
const [wishes, setWishes] = useState([]);
|
|
91
|
+
const [isLoading, setIsLoading] = useState(!options.skip);
|
|
92
|
+
const [error, setError] = useState(null);
|
|
93
|
+
const [total, setTotal] = useState(0);
|
|
94
|
+
const [page, setPage] = useState(options.filters?.page || 1);
|
|
95
|
+
const [totalPages, setTotalPages] = useState(0);
|
|
96
|
+
const [filters, setFilters] = useState(options.filters || {});
|
|
97
|
+
const intervalRef = useRef(null);
|
|
98
|
+
const fetchWishes = useCallback(async () => {
|
|
99
|
+
if (options.skip) return;
|
|
100
|
+
setIsLoading(true);
|
|
101
|
+
setError(null);
|
|
102
|
+
try {
|
|
103
|
+
const response = await client.getPublicWishes({
|
|
104
|
+
...filters,
|
|
105
|
+
page,
|
|
106
|
+
fingerprint: fingerprint ?? void 0
|
|
107
|
+
});
|
|
108
|
+
if (response.success && response.data) {
|
|
109
|
+
const wishData = response.data.data || [];
|
|
110
|
+
setWishes(wishData);
|
|
111
|
+
setTotal(response.data.total);
|
|
112
|
+
setTotalPages(response.data.total_pages);
|
|
113
|
+
} else {
|
|
114
|
+
setError(getErrorMessage(response.error, "Failed to fetch wishes"));
|
|
115
|
+
}
|
|
116
|
+
} catch (err) {
|
|
117
|
+
setError(getErrorMessage(err, "An error occurred"));
|
|
118
|
+
} finally {
|
|
119
|
+
setIsLoading(false);
|
|
120
|
+
}
|
|
121
|
+
}, [client, fingerprint, filters, page, options.skip]);
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
fetchWishes();
|
|
124
|
+
}, [fetchWishes]);
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
if (options.refreshInterval && options.refreshInterval > 0) {
|
|
127
|
+
intervalRef.current = setInterval(fetchWishes, options.refreshInterval);
|
|
128
|
+
}
|
|
129
|
+
return () => {
|
|
130
|
+
if (intervalRef.current) {
|
|
131
|
+
clearInterval(intervalRef.current);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}, [options.refreshInterval, fetchWishes]);
|
|
135
|
+
const handleSetFilters = useCallback((newFilters) => {
|
|
136
|
+
setFilters(newFilters);
|
|
137
|
+
setPage(1);
|
|
138
|
+
}, []);
|
|
139
|
+
return {
|
|
140
|
+
wishes,
|
|
141
|
+
isLoading,
|
|
142
|
+
error,
|
|
143
|
+
total,
|
|
144
|
+
page,
|
|
145
|
+
totalPages,
|
|
146
|
+
setFilters: handleSetFilters,
|
|
147
|
+
setPage,
|
|
148
|
+
refetch: fetchWishes
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function useVote(options) {
|
|
152
|
+
const { client, fingerprint } = useAppgramContext();
|
|
153
|
+
const [hasVoted, setHasVoted] = useState(options.initialHasVoted || false);
|
|
154
|
+
const [voteCount, setVoteCount] = useState(options.initialVoteCount || 0);
|
|
155
|
+
const [voteId, setVoteId] = useState(null);
|
|
156
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
157
|
+
const [isChecking, setIsChecking] = useState(false);
|
|
158
|
+
const [hasChecked, setHasChecked] = useState(false);
|
|
159
|
+
const [error, setError] = useState(null);
|
|
160
|
+
const skipAutoCheck = options.skipAutoCheck !== false;
|
|
161
|
+
const checkVoteStatus = useCallback(async () => {
|
|
162
|
+
if (!fingerprint || !options.wishId || hasChecked) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
setIsChecking(true);
|
|
166
|
+
try {
|
|
167
|
+
const response = await client.checkVote(options.wishId, fingerprint);
|
|
168
|
+
if (response.success && response.data) {
|
|
169
|
+
setHasVoted(response.data.has_voted);
|
|
170
|
+
if (response.data.vote_id) {
|
|
171
|
+
setVoteId(response.data.vote_id);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
setHasChecked(true);
|
|
175
|
+
} catch {
|
|
176
|
+
setHasChecked(true);
|
|
177
|
+
} finally {
|
|
178
|
+
setIsChecking(false);
|
|
179
|
+
}
|
|
180
|
+
}, [client, fingerprint, options.wishId, hasChecked]);
|
|
181
|
+
useEffect(() => {
|
|
182
|
+
if (!skipAutoCheck) {
|
|
183
|
+
checkVoteStatus();
|
|
184
|
+
}
|
|
185
|
+
}, [skipAutoCheck, checkVoteStatus]);
|
|
186
|
+
useEffect(() => {
|
|
187
|
+
if (options.initialVoteCount !== void 0) {
|
|
188
|
+
setVoteCount(options.initialVoteCount);
|
|
189
|
+
}
|
|
190
|
+
}, [options.initialVoteCount]);
|
|
191
|
+
useEffect(() => {
|
|
192
|
+
if (options.initialHasVoted !== void 0) {
|
|
193
|
+
setHasVoted(options.initialHasVoted);
|
|
194
|
+
}
|
|
195
|
+
}, [options.initialHasVoted]);
|
|
196
|
+
const vote = useCallback(async () => {
|
|
197
|
+
if (!fingerprint || isLoading) return;
|
|
198
|
+
if (!hasChecked) {
|
|
199
|
+
setIsLoading(true);
|
|
200
|
+
try {
|
|
201
|
+
const checkResponse = await client.checkVote(options.wishId, fingerprint);
|
|
202
|
+
if (checkResponse.success && checkResponse.data) {
|
|
203
|
+
setHasVoted(checkResponse.data.has_voted);
|
|
204
|
+
if (checkResponse.data.vote_id) {
|
|
205
|
+
setVoteId(checkResponse.data.vote_id);
|
|
206
|
+
}
|
|
207
|
+
setHasChecked(true);
|
|
208
|
+
if (checkResponse.data.has_voted) {
|
|
209
|
+
setIsLoading(false);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
} catch {
|
|
214
|
+
setHasChecked(true);
|
|
215
|
+
}
|
|
216
|
+
} else if (hasVoted) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
setIsLoading(true);
|
|
220
|
+
setError(null);
|
|
221
|
+
try {
|
|
222
|
+
const response = await client.createVote(
|
|
223
|
+
options.wishId,
|
|
224
|
+
fingerprint,
|
|
225
|
+
options.voterEmail
|
|
226
|
+
);
|
|
227
|
+
if (response.success && response.data) {
|
|
228
|
+
setHasVoted(true);
|
|
229
|
+
setVoteId(response.data.id);
|
|
230
|
+
setVoteCount((prev) => prev + 1);
|
|
231
|
+
options.onVoteChange?.(true, voteCount + 1);
|
|
232
|
+
} else {
|
|
233
|
+
setError(getErrorMessage(response.error, "Failed to vote"));
|
|
234
|
+
}
|
|
235
|
+
} catch (err) {
|
|
236
|
+
setError(getErrorMessage(err, "Failed to vote"));
|
|
237
|
+
} finally {
|
|
238
|
+
setIsLoading(false);
|
|
239
|
+
}
|
|
240
|
+
}, [client, fingerprint, hasVoted, hasChecked, isLoading, options, voteCount]);
|
|
241
|
+
const unvote = useCallback(async () => {
|
|
242
|
+
if (!voteId || !hasVoted || isLoading) return;
|
|
243
|
+
setIsLoading(true);
|
|
244
|
+
setError(null);
|
|
245
|
+
try {
|
|
246
|
+
const response = await client.deleteVote(voteId);
|
|
247
|
+
if (response.success) {
|
|
248
|
+
setHasVoted(false);
|
|
249
|
+
setVoteId(null);
|
|
250
|
+
setVoteCount((prev) => Math.max(0, prev - 1));
|
|
251
|
+
options.onVoteChange?.(false, Math.max(0, voteCount - 1));
|
|
252
|
+
} else {
|
|
253
|
+
setError(getErrorMessage(response.error, "Failed to remove vote"));
|
|
254
|
+
}
|
|
255
|
+
} catch (err) {
|
|
256
|
+
setError(getErrorMessage(err, "Failed to remove vote"));
|
|
257
|
+
} finally {
|
|
258
|
+
setIsLoading(false);
|
|
259
|
+
}
|
|
260
|
+
}, [client, hasVoted, isLoading, options, voteCount, voteId]);
|
|
261
|
+
const toggle = useCallback(async () => {
|
|
262
|
+
if (!fingerprint || isLoading) return;
|
|
263
|
+
if (!hasChecked) {
|
|
264
|
+
setIsLoading(true);
|
|
265
|
+
try {
|
|
266
|
+
const checkResponse = await client.checkVote(options.wishId, fingerprint);
|
|
267
|
+
if (checkResponse.success && checkResponse.data) {
|
|
268
|
+
const alreadyVoted = checkResponse.data.has_voted;
|
|
269
|
+
setHasVoted(alreadyVoted);
|
|
270
|
+
if (checkResponse.data.vote_id) {
|
|
271
|
+
setVoteId(checkResponse.data.vote_id);
|
|
272
|
+
}
|
|
273
|
+
setHasChecked(true);
|
|
274
|
+
setIsLoading(false);
|
|
275
|
+
if (alreadyVoted) {
|
|
276
|
+
if (checkResponse.data.vote_id) {
|
|
277
|
+
await unvote();
|
|
278
|
+
}
|
|
279
|
+
} else {
|
|
280
|
+
await vote();
|
|
281
|
+
}
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
} catch {
|
|
285
|
+
setHasChecked(true);
|
|
286
|
+
setIsLoading(false);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (hasVoted) {
|
|
290
|
+
await unvote();
|
|
291
|
+
} else {
|
|
292
|
+
await vote();
|
|
293
|
+
}
|
|
294
|
+
}, [client, fingerprint, hasVoted, hasChecked, isLoading, options.wishId, unvote, vote]);
|
|
295
|
+
return {
|
|
296
|
+
hasVoted,
|
|
297
|
+
voteCount,
|
|
298
|
+
isLoading,
|
|
299
|
+
isChecking,
|
|
300
|
+
error,
|
|
301
|
+
toggle,
|
|
302
|
+
vote,
|
|
303
|
+
unvote
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
function useComments(options) {
|
|
307
|
+
const { client } = useAppgramContext();
|
|
308
|
+
const [comments, setComments] = useState([]);
|
|
309
|
+
const [isLoading, setIsLoading] = useState(!options.skip);
|
|
310
|
+
const [isCreating, setIsCreating] = useState(false);
|
|
311
|
+
const [error, setError] = useState(null);
|
|
312
|
+
const [total, setTotal] = useState(0);
|
|
313
|
+
const [page, setPage] = useState(1);
|
|
314
|
+
const [totalPages, setTotalPages] = useState(0);
|
|
315
|
+
const perPage = options.perPage || 20;
|
|
316
|
+
const fetchComments = useCallback(async () => {
|
|
317
|
+
if (options.skip || !options.wishId) return;
|
|
318
|
+
setIsLoading(true);
|
|
319
|
+
setError(null);
|
|
320
|
+
try {
|
|
321
|
+
const response = await client.getComments(options.wishId, {
|
|
322
|
+
page,
|
|
323
|
+
per_page: perPage
|
|
324
|
+
});
|
|
325
|
+
if (response.success && response.data) {
|
|
326
|
+
setComments(response.data.data || []);
|
|
327
|
+
setTotal(response.data.total);
|
|
328
|
+
setTotalPages(response.data.total_pages);
|
|
329
|
+
} else {
|
|
330
|
+
setError(getErrorMessage(response.error, "Failed to fetch comments"));
|
|
331
|
+
}
|
|
332
|
+
} catch (err) {
|
|
333
|
+
setError(getErrorMessage(err, "An error occurred"));
|
|
334
|
+
} finally {
|
|
335
|
+
setIsLoading(false);
|
|
336
|
+
}
|
|
337
|
+
}, [client, options.wishId, options.skip, page, perPage]);
|
|
338
|
+
useEffect(() => {
|
|
339
|
+
fetchComments();
|
|
340
|
+
}, [fetchComments]);
|
|
341
|
+
const createComment = useCallback(
|
|
342
|
+
async (data) => {
|
|
343
|
+
setIsCreating(true);
|
|
344
|
+
setError(null);
|
|
345
|
+
try {
|
|
346
|
+
const response = await client.createComment({
|
|
347
|
+
...data,
|
|
348
|
+
wish_id: options.wishId
|
|
349
|
+
});
|
|
350
|
+
if (response.success && response.data) {
|
|
351
|
+
setComments((prev) => [response.data, ...prev]);
|
|
352
|
+
setTotal((prev) => prev + 1);
|
|
353
|
+
return response.data;
|
|
354
|
+
} else {
|
|
355
|
+
setError(getErrorMessage(response.error, "Failed to create comment"));
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
} catch (err) {
|
|
359
|
+
setError(getErrorMessage(err, "An error occurred"));
|
|
360
|
+
return null;
|
|
361
|
+
} finally {
|
|
362
|
+
setIsCreating(false);
|
|
363
|
+
}
|
|
364
|
+
},
|
|
365
|
+
[client, options.wishId]
|
|
366
|
+
);
|
|
367
|
+
const nextPage = useCallback(() => {
|
|
368
|
+
if (page < totalPages) {
|
|
369
|
+
setPage((prev) => prev + 1);
|
|
370
|
+
}
|
|
371
|
+
}, [page, totalPages]);
|
|
372
|
+
const prevPage = useCallback(() => {
|
|
373
|
+
if (page > 1) {
|
|
374
|
+
setPage((prev) => prev - 1);
|
|
375
|
+
}
|
|
376
|
+
}, [page]);
|
|
377
|
+
return {
|
|
378
|
+
comments,
|
|
379
|
+
isLoading,
|
|
380
|
+
isCreating,
|
|
381
|
+
error,
|
|
382
|
+
total,
|
|
383
|
+
page,
|
|
384
|
+
totalPages,
|
|
385
|
+
nextPage,
|
|
386
|
+
prevPage,
|
|
387
|
+
setPage,
|
|
388
|
+
createComment,
|
|
389
|
+
refetch: fetchComments
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
function useRoadmap(options = {}) {
|
|
393
|
+
const { client } = useAppgramContext();
|
|
394
|
+
const [roadmap, setRoadmap] = useState(null);
|
|
395
|
+
const [columns, setColumns] = useState([]);
|
|
396
|
+
const [totalItems, setTotalItems] = useState(0);
|
|
397
|
+
const [isLoading, setIsLoading] = useState(!options.skip);
|
|
398
|
+
const [error, setError] = useState(null);
|
|
399
|
+
const fetchRoadmap = useCallback(async () => {
|
|
400
|
+
if (options.skip) return;
|
|
401
|
+
setIsLoading(true);
|
|
402
|
+
setError(null);
|
|
403
|
+
try {
|
|
404
|
+
const response = await client.getRoadmapData();
|
|
405
|
+
if (response.success && response.data) {
|
|
406
|
+
setRoadmap(response.data.roadmap);
|
|
407
|
+
const cols = response.data.columns || response.data.roadmap?.columns || [];
|
|
408
|
+
setColumns(cols);
|
|
409
|
+
const itemCount = response.data.total_items || cols.reduce((sum, col) => sum + (col.items?.length || 0), 0);
|
|
410
|
+
setTotalItems(itemCount);
|
|
411
|
+
} else {
|
|
412
|
+
setError(getErrorMessage(response.error, "Failed to fetch roadmap"));
|
|
413
|
+
}
|
|
414
|
+
} catch (err) {
|
|
415
|
+
setError(getErrorMessage(err, "An error occurred"));
|
|
416
|
+
} finally {
|
|
417
|
+
setIsLoading(false);
|
|
418
|
+
}
|
|
419
|
+
}, [client, options.skip]);
|
|
420
|
+
useEffect(() => {
|
|
421
|
+
fetchRoadmap();
|
|
422
|
+
}, [fetchRoadmap]);
|
|
423
|
+
useEffect(() => {
|
|
424
|
+
if (!options.refreshInterval || options.refreshInterval <= 0) return;
|
|
425
|
+
const interval = setInterval(fetchRoadmap, options.refreshInterval);
|
|
426
|
+
return () => clearInterval(interval);
|
|
427
|
+
}, [options.refreshInterval, fetchRoadmap]);
|
|
428
|
+
return {
|
|
429
|
+
roadmap,
|
|
430
|
+
columns,
|
|
431
|
+
totalItems,
|
|
432
|
+
isLoading,
|
|
433
|
+
error,
|
|
434
|
+
refetch: fetchRoadmap
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
function useReleases(options = {}) {
|
|
438
|
+
const { client } = useAppgramContext();
|
|
439
|
+
const [releases, setReleases] = useState([]);
|
|
440
|
+
const [isLoading, setIsLoading] = useState(!options.skip);
|
|
441
|
+
const [error, setError] = useState(null);
|
|
442
|
+
const fetchReleases = useCallback(async () => {
|
|
443
|
+
if (options.skip) return;
|
|
444
|
+
setIsLoading(true);
|
|
445
|
+
setError(null);
|
|
446
|
+
try {
|
|
447
|
+
const response = await client.getReleases({
|
|
448
|
+
limit: options.limit || 50
|
|
449
|
+
});
|
|
450
|
+
if (response.success && response.data) {
|
|
451
|
+
setReleases(response.data);
|
|
452
|
+
} else {
|
|
453
|
+
setError(getErrorMessage(response.error, "Failed to fetch releases"));
|
|
454
|
+
}
|
|
455
|
+
} catch (err) {
|
|
456
|
+
setError(getErrorMessage(err, "An error occurred"));
|
|
457
|
+
} finally {
|
|
458
|
+
setIsLoading(false);
|
|
459
|
+
}
|
|
460
|
+
}, [client, options.skip, options.limit]);
|
|
461
|
+
useEffect(() => {
|
|
462
|
+
fetchReleases();
|
|
463
|
+
}, [fetchReleases]);
|
|
464
|
+
return {
|
|
465
|
+
releases,
|
|
466
|
+
isLoading,
|
|
467
|
+
error,
|
|
468
|
+
refetch: fetchReleases
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
function useRelease(options) {
|
|
472
|
+
const { client } = useAppgramContext();
|
|
473
|
+
const [release, setRelease] = useState(null);
|
|
474
|
+
const [isLoading, setIsLoading] = useState(!options.skip);
|
|
475
|
+
const [error, setError] = useState(null);
|
|
476
|
+
const fetchRelease = useCallback(async () => {
|
|
477
|
+
if (options.skip || !options.releaseSlug) return;
|
|
478
|
+
setIsLoading(true);
|
|
479
|
+
setError(null);
|
|
480
|
+
try {
|
|
481
|
+
const response = await client.getRelease(options.releaseSlug);
|
|
482
|
+
if (response.success && response.data) {
|
|
483
|
+
setRelease(response.data);
|
|
484
|
+
} else {
|
|
485
|
+
setError(getErrorMessage(response.error, "Failed to fetch release"));
|
|
486
|
+
}
|
|
487
|
+
} catch (err) {
|
|
488
|
+
setError(getErrorMessage(err, "An error occurred"));
|
|
489
|
+
} finally {
|
|
490
|
+
setIsLoading(false);
|
|
491
|
+
}
|
|
492
|
+
}, [client, options.skip, options.releaseSlug]);
|
|
493
|
+
useEffect(() => {
|
|
494
|
+
fetchRelease();
|
|
495
|
+
}, [fetchRelease]);
|
|
496
|
+
return {
|
|
497
|
+
release,
|
|
498
|
+
isLoading,
|
|
499
|
+
error,
|
|
500
|
+
refetch: fetchRelease
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
function useHelpCenter(options = {}) {
|
|
504
|
+
const { client } = useAppgramContext();
|
|
505
|
+
const [collection, setCollection] = useState(null);
|
|
506
|
+
const [flows, setFlows] = useState([]);
|
|
507
|
+
const [isLoading, setIsLoading] = useState(!options.skip);
|
|
508
|
+
const [error, setError] = useState(null);
|
|
509
|
+
const fetchHelpCenter = useCallback(async () => {
|
|
510
|
+
if (options.skip) return;
|
|
511
|
+
setIsLoading(true);
|
|
512
|
+
setError(null);
|
|
513
|
+
try {
|
|
514
|
+
const response = await client.getHelpCollection();
|
|
515
|
+
if (response.success && response.data) {
|
|
516
|
+
setCollection(response.data.collection);
|
|
517
|
+
setFlows(response.data.flows || []);
|
|
518
|
+
} else {
|
|
519
|
+
setError(getErrorMessage(response.error, "Failed to fetch help center"));
|
|
520
|
+
}
|
|
521
|
+
} catch (err) {
|
|
522
|
+
setError(getErrorMessage(err, "An error occurred"));
|
|
523
|
+
} finally {
|
|
524
|
+
setIsLoading(false);
|
|
525
|
+
}
|
|
526
|
+
}, [client, options.skip]);
|
|
527
|
+
useEffect(() => {
|
|
528
|
+
fetchHelpCenter();
|
|
529
|
+
}, [fetchHelpCenter]);
|
|
530
|
+
return {
|
|
531
|
+
collection,
|
|
532
|
+
flows,
|
|
533
|
+
isLoading,
|
|
534
|
+
error,
|
|
535
|
+
refetch: fetchHelpCenter
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
function useHelpFlow(options) {
|
|
539
|
+
const { client } = useAppgramContext();
|
|
540
|
+
const [flow, setFlow] = useState(null);
|
|
541
|
+
const [isLoading, setIsLoading] = useState(!options.skip);
|
|
542
|
+
const [error, setError] = useState(null);
|
|
543
|
+
const fetchFlow = useCallback(async () => {
|
|
544
|
+
if (options.skip || !options.flowSlug) return;
|
|
545
|
+
setIsLoading(true);
|
|
546
|
+
setError(null);
|
|
547
|
+
try {
|
|
548
|
+
const response = await client.getHelpFlow(options.flowSlug);
|
|
549
|
+
if (response.success && response.data) {
|
|
550
|
+
setFlow(response.data);
|
|
551
|
+
} else {
|
|
552
|
+
setError(getErrorMessage(response.error, "Failed to fetch flow"));
|
|
553
|
+
}
|
|
554
|
+
} catch (err) {
|
|
555
|
+
setError(getErrorMessage(err, "An error occurred"));
|
|
556
|
+
} finally {
|
|
557
|
+
setIsLoading(false);
|
|
558
|
+
}
|
|
559
|
+
}, [client, options.skip, options.flowSlug]);
|
|
560
|
+
useEffect(() => {
|
|
561
|
+
fetchFlow();
|
|
562
|
+
}, [fetchFlow]);
|
|
563
|
+
return {
|
|
564
|
+
flow,
|
|
565
|
+
isLoading,
|
|
566
|
+
error,
|
|
567
|
+
refetch: fetchFlow
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
function useHelpArticle(options) {
|
|
571
|
+
const { client } = useAppgramContext();
|
|
572
|
+
const [article, setArticle] = useState(null);
|
|
573
|
+
const [isLoading, setIsLoading] = useState(!options.skip);
|
|
574
|
+
const [error, setError] = useState(null);
|
|
575
|
+
const fetchArticle = useCallback(async () => {
|
|
576
|
+
if (options.skip || !options.articleSlug || !options.flowId) return;
|
|
577
|
+
setIsLoading(true);
|
|
578
|
+
setError(null);
|
|
579
|
+
try {
|
|
580
|
+
const response = await client.getHelpArticle(options.articleSlug, options.flowId);
|
|
581
|
+
if (response.success && response.data) {
|
|
582
|
+
setArticle(response.data);
|
|
583
|
+
} else {
|
|
584
|
+
setError(getErrorMessage(response.error, "Failed to fetch article"));
|
|
585
|
+
}
|
|
586
|
+
} catch (err) {
|
|
587
|
+
setError(getErrorMessage(err, "An error occurred"));
|
|
588
|
+
} finally {
|
|
589
|
+
setIsLoading(false);
|
|
590
|
+
}
|
|
591
|
+
}, [client, options.skip, options.articleSlug, options.flowId]);
|
|
592
|
+
useEffect(() => {
|
|
593
|
+
fetchArticle();
|
|
594
|
+
}, [fetchArticle]);
|
|
595
|
+
return {
|
|
596
|
+
article,
|
|
597
|
+
isLoading,
|
|
598
|
+
error,
|
|
599
|
+
refetch: fetchArticle
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
function useSupport(options = {}) {
|
|
603
|
+
const { client } = useAppgramContext();
|
|
604
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
605
|
+
const [isSendingMagicLink, setIsSendingMagicLink] = useState(false);
|
|
606
|
+
const [isVerifying, setIsVerifying] = useState(false);
|
|
607
|
+
const [error, setError] = useState(null);
|
|
608
|
+
const [successMessage, setSuccessMessage] = useState(null);
|
|
609
|
+
const clearMessages = useCallback(() => {
|
|
610
|
+
setError(null);
|
|
611
|
+
setSuccessMessage(null);
|
|
612
|
+
}, []);
|
|
613
|
+
const submitTicket = useCallback(
|
|
614
|
+
async (data) => {
|
|
615
|
+
setIsSubmitting(true);
|
|
616
|
+
setError(null);
|
|
617
|
+
setSuccessMessage(null);
|
|
618
|
+
try {
|
|
619
|
+
const response = await client.submitSupportRequest(data);
|
|
620
|
+
if (response.success && response.data) {
|
|
621
|
+
setSuccessMessage("Your support request has been submitted successfully.");
|
|
622
|
+
options.onSubmitSuccess?.(response.data);
|
|
623
|
+
return response.data;
|
|
624
|
+
} else {
|
|
625
|
+
const errorMsg = getErrorMessage(response.error, "Failed to submit support request");
|
|
626
|
+
setError(errorMsg);
|
|
627
|
+
options.onSubmitError?.(errorMsg);
|
|
628
|
+
return null;
|
|
629
|
+
}
|
|
630
|
+
} catch (err) {
|
|
631
|
+
const errorMsg = getErrorMessage(err, "An error occurred");
|
|
632
|
+
setError(errorMsg);
|
|
633
|
+
options.onSubmitError?.(errorMsg);
|
|
634
|
+
return null;
|
|
635
|
+
} finally {
|
|
636
|
+
setIsSubmitting(false);
|
|
637
|
+
}
|
|
638
|
+
},
|
|
639
|
+
[client, options]
|
|
640
|
+
);
|
|
641
|
+
const requestMagicLink = useCallback(
|
|
642
|
+
async (email) => {
|
|
643
|
+
setIsSendingMagicLink(true);
|
|
644
|
+
setError(null);
|
|
645
|
+
setSuccessMessage(null);
|
|
646
|
+
try {
|
|
647
|
+
const response = await client.sendSupportMagicLink(email);
|
|
648
|
+
if (response.success) {
|
|
649
|
+
setSuccessMessage("A magic link has been sent to your email.");
|
|
650
|
+
return true;
|
|
651
|
+
} else {
|
|
652
|
+
setError(getErrorMessage(response.error, "Failed to send magic link"));
|
|
653
|
+
return false;
|
|
654
|
+
}
|
|
655
|
+
} catch (err) {
|
|
656
|
+
setError(getErrorMessage(err, "An error occurred"));
|
|
657
|
+
return false;
|
|
658
|
+
} finally {
|
|
659
|
+
setIsSendingMagicLink(false);
|
|
660
|
+
}
|
|
661
|
+
},
|
|
662
|
+
[client]
|
|
663
|
+
);
|
|
664
|
+
const verifyToken = useCallback(
|
|
665
|
+
async (token) => {
|
|
666
|
+
setIsVerifying(true);
|
|
667
|
+
setError(null);
|
|
668
|
+
try {
|
|
669
|
+
const response = await client.verifySupportToken(token);
|
|
670
|
+
if (response.success && response.data) {
|
|
671
|
+
return {
|
|
672
|
+
tickets: response.data.tickets,
|
|
673
|
+
userEmail: response.data.user_email
|
|
674
|
+
};
|
|
675
|
+
} else {
|
|
676
|
+
setError(getErrorMessage(response.error, "Invalid or expired token"));
|
|
677
|
+
return null;
|
|
678
|
+
}
|
|
679
|
+
} catch (err) {
|
|
680
|
+
setError(getErrorMessage(err, "An error occurred"));
|
|
681
|
+
return null;
|
|
682
|
+
} finally {
|
|
683
|
+
setIsVerifying(false);
|
|
684
|
+
}
|
|
685
|
+
},
|
|
686
|
+
[client]
|
|
687
|
+
);
|
|
688
|
+
const getTicket = useCallback(
|
|
689
|
+
async (ticketId, token) => {
|
|
690
|
+
setError(null);
|
|
691
|
+
try {
|
|
692
|
+
const response = await client.getSupportTicket(ticketId, token);
|
|
693
|
+
if (response.success && response.data) {
|
|
694
|
+
return response.data;
|
|
695
|
+
} else {
|
|
696
|
+
setError(getErrorMessage(response.error, "Failed to fetch ticket"));
|
|
697
|
+
return null;
|
|
698
|
+
}
|
|
699
|
+
} catch (err) {
|
|
700
|
+
setError(getErrorMessage(err, "An error occurred"));
|
|
701
|
+
return null;
|
|
702
|
+
}
|
|
703
|
+
},
|
|
704
|
+
[client]
|
|
705
|
+
);
|
|
706
|
+
const addMessage = useCallback(
|
|
707
|
+
async (ticketId, token, content) => {
|
|
708
|
+
setError(null);
|
|
709
|
+
try {
|
|
710
|
+
const response = await client.addSupportMessage(ticketId, token, content);
|
|
711
|
+
if (response.success && response.data) {
|
|
712
|
+
return response.data;
|
|
713
|
+
} else {
|
|
714
|
+
setError(getErrorMessage(response.error, "Failed to add message"));
|
|
715
|
+
return null;
|
|
716
|
+
}
|
|
717
|
+
} catch (err) {
|
|
718
|
+
setError(getErrorMessage(err, "An error occurred"));
|
|
719
|
+
return null;
|
|
720
|
+
}
|
|
721
|
+
},
|
|
722
|
+
[client]
|
|
723
|
+
);
|
|
724
|
+
return {
|
|
725
|
+
isSubmitting,
|
|
726
|
+
isSendingMagicLink,
|
|
727
|
+
isVerifying,
|
|
728
|
+
error,
|
|
729
|
+
successMessage,
|
|
730
|
+
submitTicket,
|
|
731
|
+
requestMagicLink,
|
|
732
|
+
verifyToken,
|
|
733
|
+
getTicket,
|
|
734
|
+
addMessage,
|
|
735
|
+
clearMessages
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
export { AppgramContext, cn, getErrorMessage, getFingerprint, resetFingerprint, useAppgramContext, useComments, useHelpArticle, useHelpCenter, useHelpFlow, useRelease, useReleases, useRoadmap, useSupport, useVote, useWishes };
|
|
740
|
+
//# sourceMappingURL=chunk-N6PJDQCU.mjs.map
|
|
741
|
+
//# sourceMappingURL=chunk-N6PJDQCU.mjs.map
|