@cstar.help/react 0.3.0 → 0.4.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/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  React hooks for the [cStar](https://cstar.help) customer support platform. Build custom chat widgets and knowledge base UIs with type-safe, SSR-compatible hooks.
4
4
 
5
5
  - **React 18+** — hooks with automatic loading states and error handling
6
- - **Two modules** — Chat (real-time messaging) and Library (knowledge base)
6
+ - **Three modules** — Chat (real-time messaging), Library (knowledge base), and Community (forum)
7
7
  - **TypeScript-first** — full type definitions included
8
8
  - **Tiny** — ~18 KB, tree-shakeable, only peer deps
9
9
 
@@ -182,6 +182,87 @@ function SearchBar() {
182
182
  }
183
183
  ```
184
184
 
185
+ ## Community
186
+
187
+ Wrap in `<CStarCommunityProvider>` for public community forum access — no auth required.
188
+
189
+ ### Setup
190
+
191
+ ```tsx
192
+ import { CStarCommunityProvider } from '@cstar.help/react';
193
+
194
+ function App() {
195
+ return (
196
+ <CStarCommunityProvider teamSlug="acme">
197
+ <CommunityForum />
198
+ </CStarCommunityProvider>
199
+ );
200
+ }
201
+ ```
202
+
203
+ ### Topics + Posts
204
+
205
+ ```tsx
206
+ import { useTopics, usePosts, usePost } from '@cstar.help/react';
207
+
208
+ function CommunityForum() {
209
+ const { topics, isLoading: topicsLoading } = useTopics();
210
+ const { posts, count, isLoading: postsLoading } = usePosts({ sort: 'votes' });
211
+
212
+ return (
213
+ <>
214
+ <nav>
215
+ {topics.map((topic) => (
216
+ <a key={topic.id}>{topic.name}</a>
217
+ ))}
218
+ </nav>
219
+ <p>{count} posts</p>
220
+ <ul>
221
+ {posts.map((post) => (
222
+ <li key={post.id}>
223
+ {post.title} — {post.voteCount} votes
224
+ </li>
225
+ ))}
226
+ </ul>
227
+ </>
228
+ );
229
+ }
230
+
231
+ // Single post with comments
232
+ function PostPage({ slug }) {
233
+ const { data, isLoading, error } = usePost(slug);
234
+ if (isLoading) return <Spinner />;
235
+ if (!data) return <NotFound />;
236
+ return (
237
+ <>
238
+ <h1>{data.post.title}</h1>
239
+ {data.comments.map((c) => (
240
+ <p key={c.id}>{c.body}</p>
241
+ ))}
242
+ </>
243
+ );
244
+ }
245
+ ```
246
+
247
+ ### Search
248
+
249
+ ```tsx
250
+ import { useCommunitySearch } from '@cstar.help/react';
251
+
252
+ function CommunitySearch() {
253
+ const { results, isLoading, search } = useCommunitySearch();
254
+
255
+ return (
256
+ <>
257
+ <input onChange={(e) => search(e.target.value)} placeholder="Search posts..." />
258
+ {results?.data.map((post) => (
259
+ <a key={post.id}>{post.title}</a>
260
+ ))}
261
+ </>
262
+ );
263
+ }
264
+ ```
265
+
185
266
  ## API Reference
186
267
 
187
268
  ### Chat Hooks
@@ -202,12 +283,22 @@ function SearchBar() {
202
283
  | `useArticle(slug)` | `{ article, isLoading, error, refresh }` | Single article by slug |
203
284
  | `useArticleSearch(query)` | `{ results, totalCount, isLoading, error }` | Search with 300ms debounce |
204
285
 
286
+ ### Community Hooks
287
+
288
+ | Hook | Returns | Description |
289
+ | ---------------------- | --------------------------------------------- | ----------------------------------------------------- |
290
+ | `useTopics()` | `{ topics, isLoading, error, refresh }` | Community discussion topics |
291
+ | `usePosts(params?)` | `{ posts, count, isLoading, error, refresh }` | Posts with optional filters (topicSlug, status, sort) |
292
+ | `usePost(slug)` | `{ data, isLoading, error, refresh }` | Single post with comments |
293
+ | `useCommunitySearch()` | `{ results, isLoading, error, search }` | On-demand search across posts |
294
+
205
295
  ### Context Accessors
206
296
 
207
- | Function | Returns | Description |
208
- | -------------------- | --------------- | ---------------------------------------------- |
209
- | `useChatClient()` | `ChatClient` | Raw client from nearest `CStarChatProvider` |
210
- | `useLibraryClient()` | `LibraryClient` | Raw client from nearest `CStarLibraryProvider` |
297
+ | Function | Returns | Description |
298
+ | ---------------------- | ----------------- | ------------------------------------------------ |
299
+ | `useChatClient()` | `ChatClient` | Raw client from nearest `CStarChatProvider` |
300
+ | `useLibraryClient()` | `LibraryClient` | Raw client from nearest `CStarLibraryProvider` |
301
+ | `useCommunityClient()` | `CommunityClient` | Raw client from nearest `CStarCommunityProvider` |
211
302
 
212
303
  ## Requirements
213
304
 
package/dist/index.cjs CHANGED
@@ -22,6 +22,8 @@ var src_exports = {};
22
22
  __export(src_exports, {
23
23
  CStarChatContext: () => CStarChatContext,
24
24
  CStarChatProvider: () => CStarChatProvider,
25
+ CStarCommunityContext: () => CStarCommunityContext,
26
+ CStarCommunityProvider: () => CStarCommunityProvider,
25
27
  CStarLibraryContext: () => CStarLibraryContext,
26
28
  CStarLibraryProvider: () => CStarLibraryProvider,
27
29
  useArticle: () => useArticle,
@@ -30,9 +32,14 @@ __export(src_exports, {
30
32
  useCategories: () => useCategories,
31
33
  useChat: () => useChat,
32
34
  useChatClient: () => useChatClient,
35
+ useCommunityClient: () => useCommunityClient,
36
+ useCommunitySearch: () => useCommunitySearch,
33
37
  useConversations: () => useConversations,
34
38
  useLibraryClient: () => useLibraryClient,
35
39
  useMessages: () => useMessages,
40
+ usePost: () => usePost,
41
+ usePosts: () => usePosts,
42
+ useTopics: () => useTopics,
36
43
  useTyping: () => useTyping
37
44
  });
38
45
  module.exports = __toCommonJS(src_exports);
@@ -41,6 +48,7 @@ module.exports = __toCommonJS(src_exports);
41
48
  var import_react = require("react");
42
49
  var CStarChatContext = (0, import_react.createContext)(null);
43
50
  var CStarLibraryContext = (0, import_react.createContext)(null);
51
+ var CStarCommunityContext = (0, import_react.createContext)(null);
44
52
  function useChatClient() {
45
53
  const client = (0, import_react.useContext)(CStarChatContext);
46
54
  if (!client) {
@@ -55,6 +63,13 @@ function useLibraryClient() {
55
63
  }
56
64
  return client;
57
65
  }
66
+ function useCommunityClient() {
67
+ const client = (0, import_react.useContext)(CStarCommunityContext);
68
+ if (!client) {
69
+ throw new Error("useCommunityClient must be used within a <CStarCommunityProvider>");
70
+ }
71
+ return client;
72
+ }
58
73
 
59
74
  // src/chat/CStarChatProvider.tsx
60
75
  var import_react2 = require("react");
@@ -480,10 +495,181 @@ function useArticleSearch(query) {
480
495
  }, [client, query]);
481
496
  return { results, totalCount, isLoading, error };
482
497
  }
498
+
499
+ // src/community/CStarCommunityProvider.tsx
500
+ var import_react12 = require("react");
501
+ var import_community = require("@cstar.help/js/community");
502
+ var import_jsx_runtime3 = require("react/jsx-runtime");
503
+ function CStarCommunityProvider({ children, ...config }) {
504
+ const clientRef = (0, import_react12.useRef)(null);
505
+ if (!clientRef.current) {
506
+ clientRef.current = new import_community.CommunityClient(config);
507
+ }
508
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CStarCommunityContext.Provider, { value: clientRef.current, children });
509
+ }
510
+
511
+ // src/community/useTopics.ts
512
+ var import_react13 = require("react");
513
+ function useTopics() {
514
+ const client = useCommunityClient();
515
+ const [topics, setTopics] = (0, import_react13.useState)([]);
516
+ const [isLoading, setIsLoading] = (0, import_react13.useState)(true);
517
+ const [error, setError] = (0, import_react13.useState)(null);
518
+ const mountedRef = (0, import_react13.useRef)(true);
519
+ const refresh = (0, import_react13.useCallback)(async () => {
520
+ setIsLoading(true);
521
+ setError(null);
522
+ try {
523
+ const data = await client.topics();
524
+ if (mountedRef.current) {
525
+ setTopics(data);
526
+ }
527
+ } catch (err) {
528
+ if (mountedRef.current) {
529
+ setError(err instanceof Error ? err : new Error(String(err)));
530
+ }
531
+ } finally {
532
+ if (mountedRef.current) {
533
+ setIsLoading(false);
534
+ }
535
+ }
536
+ }, [client]);
537
+ (0, import_react13.useEffect)(() => {
538
+ mountedRef.current = true;
539
+ refresh();
540
+ return () => {
541
+ mountedRef.current = false;
542
+ };
543
+ }, [refresh]);
544
+ return { topics, isLoading, error, refresh };
545
+ }
546
+
547
+ // src/community/usePosts.ts
548
+ var import_react14 = require("react");
549
+ function usePosts(params) {
550
+ const client = useCommunityClient();
551
+ const [posts, setPosts] = (0, import_react14.useState)([]);
552
+ const [count, setCount] = (0, import_react14.useState)(0);
553
+ const [isLoading, setIsLoading] = (0, import_react14.useState)(true);
554
+ const [error, setError] = (0, import_react14.useState)(null);
555
+ const mountedRef = (0, import_react14.useRef)(true);
556
+ const topicSlug = params?.topicSlug;
557
+ const status = params?.status;
558
+ const sort = params?.sort;
559
+ const limit = params?.limit;
560
+ const offset = params?.offset;
561
+ const refresh = (0, import_react14.useCallback)(async () => {
562
+ setIsLoading(true);
563
+ setError(null);
564
+ try {
565
+ const result = await client.posts({ topicSlug, status, sort, limit, offset });
566
+ if (mountedRef.current) {
567
+ setPosts(result.posts);
568
+ setCount(result.count);
569
+ }
570
+ } catch (err) {
571
+ if (mountedRef.current) {
572
+ setError(err instanceof Error ? err : new Error(String(err)));
573
+ }
574
+ } finally {
575
+ if (mountedRef.current) {
576
+ setIsLoading(false);
577
+ }
578
+ }
579
+ }, [client, topicSlug, status, sort, limit, offset]);
580
+ (0, import_react14.useEffect)(() => {
581
+ mountedRef.current = true;
582
+ refresh();
583
+ return () => {
584
+ mountedRef.current = false;
585
+ };
586
+ }, [refresh]);
587
+ return { posts, count, isLoading, error, refresh };
588
+ }
589
+
590
+ // src/community/usePost.ts
591
+ var import_react15 = require("react");
592
+ function usePost(slug) {
593
+ const client = useCommunityClient();
594
+ const [data, setData] = (0, import_react15.useState)(null);
595
+ const [isLoading, setIsLoading] = (0, import_react15.useState)(true);
596
+ const [error, setError] = (0, import_react15.useState)(null);
597
+ const mountedRef = (0, import_react15.useRef)(true);
598
+ const refresh = (0, import_react15.useCallback)(async () => {
599
+ setIsLoading(true);
600
+ setError(null);
601
+ try {
602
+ const result = await client.post(slug);
603
+ if (mountedRef.current) {
604
+ setData(result);
605
+ }
606
+ } catch (err) {
607
+ if (mountedRef.current) {
608
+ setError(err instanceof Error ? err : new Error(String(err)));
609
+ }
610
+ } finally {
611
+ if (mountedRef.current) {
612
+ setIsLoading(false);
613
+ }
614
+ }
615
+ }, [client, slug]);
616
+ (0, import_react15.useEffect)(() => {
617
+ mountedRef.current = true;
618
+ refresh();
619
+ return () => {
620
+ mountedRef.current = false;
621
+ };
622
+ }, [refresh]);
623
+ return { data, isLoading, error, refresh };
624
+ }
625
+
626
+ // src/community/useCommunitySearch.ts
627
+ var import_react16 = require("react");
628
+ function useCommunitySearch() {
629
+ const client = useCommunityClient();
630
+ const [results, setResults] = (0, import_react16.useState)(null);
631
+ const [isLoading, setIsLoading] = (0, import_react16.useState)(false);
632
+ const [error, setError] = (0, import_react16.useState)(null);
633
+ const mountedRef = (0, import_react16.useRef)(true);
634
+ (0, import_react16.useEffect)(() => {
635
+ mountedRef.current = true;
636
+ return () => {
637
+ mountedRef.current = false;
638
+ };
639
+ }, []);
640
+ const search = (0, import_react16.useCallback)(
641
+ async (query) => {
642
+ if (!query.trim()) {
643
+ setResults(null);
644
+ return;
645
+ }
646
+ setIsLoading(true);
647
+ setError(null);
648
+ try {
649
+ const data = await client.search(query);
650
+ if (mountedRef.current) {
651
+ setResults(data);
652
+ }
653
+ } catch (err) {
654
+ if (mountedRef.current) {
655
+ setError(err instanceof Error ? err : new Error(String(err)));
656
+ }
657
+ } finally {
658
+ if (mountedRef.current) {
659
+ setIsLoading(false);
660
+ }
661
+ }
662
+ },
663
+ [client]
664
+ );
665
+ return { results, isLoading, error, search };
666
+ }
483
667
  // Annotate the CommonJS export names for ESM import in node:
484
668
  0 && (module.exports = {
485
669
  CStarChatContext,
486
670
  CStarChatProvider,
671
+ CStarCommunityContext,
672
+ CStarCommunityProvider,
487
673
  CStarLibraryContext,
488
674
  CStarLibraryProvider,
489
675
  useArticle,
@@ -492,8 +678,13 @@ function useArticleSearch(query) {
492
678
  useCategories,
493
679
  useChat,
494
680
  useChatClient,
681
+ useCommunityClient,
682
+ useCommunitySearch,
495
683
  useConversations,
496
684
  useLibraryClient,
497
685
  useMessages,
686
+ usePost,
687
+ usePosts,
688
+ useTopics,
498
689
  useTyping
499
690
  });
package/dist/index.d.cts CHANGED
@@ -4,10 +4,13 @@ import { ChatClient, ChatConfig, IdentifyParams, IdentifyResult, Conversation, C
4
4
  export { ChatConfig, Conversation, ConversationCreateParams, ConversationListResponse, IdentifyParams, IdentifyResult, TypingPayload, WidgetMessage } from '@cstar.help/js/chat';
5
5
  import { LibraryClient, LibraryConfig, Category, LibraryArticle, LibraryArticleListParams } from '@cstar.help/js/library';
6
6
  export { Category, LibraryArticle, LibraryArticleListParams, LibraryConfig, LibrarySearchParams, LibrarySearchResult } from '@cstar.help/js/library';
7
+ import { CommunityClient, CommunityConfig, CommunityTopic, CommunityPost, CommunityPostListParams, CommunityPostDetail, CommunitySearchResult } from '@cstar.help/js/community';
8
+ export { CommunityComment, CommunityConfig, CommunityPost, CommunityPostDetail, CommunityPostListParams, CommunitySearchResult, CommunityTopic } from '@cstar.help/js/community';
7
9
  import * as react_jsx_runtime from 'react/jsx-runtime';
8
10
 
9
11
  declare const CStarChatContext: react.Context<ChatClient | null>;
10
12
  declare const CStarLibraryContext: react.Context<LibraryClient | null>;
13
+ declare const CStarCommunityContext: react.Context<CommunityClient | null>;
11
14
  /**
12
15
  * Access the ChatClient from the nearest CStarChatProvider.
13
16
  * Throws if used outside a provider.
@@ -18,6 +21,11 @@ declare function useChatClient(): ChatClient;
18
21
  * Throws if used outside a provider.
19
22
  */
20
23
  declare function useLibraryClient(): LibraryClient;
24
+ /**
25
+ * Access the CommunityClient from the nearest CStarCommunityProvider.
26
+ * Throws if used outside a provider.
27
+ */
28
+ declare function useCommunityClient(): CommunityClient;
21
29
 
22
30
  interface CStarChatProviderProps extends ChatConfig {
23
31
  children: ReactNode;
@@ -217,4 +225,99 @@ interface UseArticleSearchReturn {
217
225
  */
218
226
  declare function useArticleSearch(query: string): UseArticleSearchReturn;
219
227
 
220
- export { CStarChatContext, CStarChatProvider, type CStarChatProviderProps, CStarLibraryContext, CStarLibraryProvider, type CStarLibraryProviderProps, type TypingAgent, type UseArticleReturn, type UseArticleSearchReturn, type UseArticlesReturn, type UseCategoriesReturn, type UseChatReturn, type UseConversationsReturn, type UseMessagesReturn, type UseTypingReturn, useArticle, useArticleSearch, useArticles, useCategories, useChat, useChatClient, useConversations, useLibraryClient, useMessages, useTyping };
228
+ interface CStarCommunityProviderProps extends CommunityConfig {
229
+ children: ReactNode;
230
+ }
231
+ /**
232
+ * Provides a CommunityClient instance to all child components.
233
+ *
234
+ * ```tsx
235
+ * <CStarCommunityProvider teamSlug="acme">
236
+ * <CommunityForum />
237
+ * </CStarCommunityProvider>
238
+ * ```
239
+ */
240
+ declare function CStarCommunityProvider({ children, ...config }: CStarCommunityProviderProps): react_jsx_runtime.JSX.Element;
241
+
242
+ interface UseTopicsReturn {
243
+ /** Community discussion topics. */
244
+ topics: CommunityTopic[];
245
+ /** Whether the fetch is in progress. */
246
+ isLoading: boolean;
247
+ /** The last error, or null. */
248
+ error: Error | null;
249
+ /** Re-fetch topics. */
250
+ refresh: () => Promise<void>;
251
+ }
252
+ /**
253
+ * Fetch community discussion topics.
254
+ *
255
+ * ```tsx
256
+ * const { topics, isLoading } = useTopics();
257
+ * ```
258
+ */
259
+ declare function useTopics(): UseTopicsReturn;
260
+
261
+ interface UsePostsReturn {
262
+ /** Community posts. */
263
+ posts: CommunityPost[];
264
+ /** Total count of matching posts (for pagination). */
265
+ count: number;
266
+ /** Whether the fetch is in progress. */
267
+ isLoading: boolean;
268
+ /** The last error, or null. */
269
+ error: Error | null;
270
+ /** Re-fetch posts with the current params. */
271
+ refresh: () => Promise<void>;
272
+ }
273
+ /**
274
+ * Fetch community posts with optional filters.
275
+ *
276
+ * ```tsx
277
+ * const { posts, isLoading } = usePosts({ topicSlug: 'feedback', sort: 'votes' });
278
+ * ```
279
+ */
280
+ declare function usePosts(params?: CommunityPostListParams): UsePostsReturn;
281
+
282
+ interface UsePostReturn {
283
+ /** The post with comments, or null if not loaded. */
284
+ data: CommunityPostDetail | null;
285
+ /** Whether the fetch is in progress. */
286
+ isLoading: boolean;
287
+ /** The last error, or null. */
288
+ error: Error | null;
289
+ /** Re-fetch the post. */
290
+ refresh: () => Promise<void>;
291
+ }
292
+ /**
293
+ * Fetch a single community post by slug, including all comments.
294
+ *
295
+ * ```tsx
296
+ * const { data, isLoading } = usePost('dark-mode-support');
297
+ * ```
298
+ */
299
+ declare function usePost(slug: string): UsePostReturn;
300
+
301
+ interface UseCommunitySearchReturn {
302
+ /** Search results (null until first search). */
303
+ results: CommunitySearchResult | null;
304
+ /** Whether a search is in progress. */
305
+ isLoading: boolean;
306
+ /** The last error, or null. */
307
+ error: Error | null;
308
+ /** Execute a search query. */
309
+ search: (query: string) => Promise<void>;
310
+ }
311
+ /**
312
+ * Search community posts by title and body content.
313
+ *
314
+ * ```tsx
315
+ * const { results, isLoading, search } = useCommunitySearch();
316
+ *
317
+ * <input onChange={(e) => search(e.target.value)} />
318
+ * {results?.data.map(post => <PostCard key={post.id} post={post} />)}
319
+ * ```
320
+ */
321
+ declare function useCommunitySearch(): UseCommunitySearchReturn;
322
+
323
+ export { CStarChatContext, CStarChatProvider, type CStarChatProviderProps, CStarCommunityContext, CStarCommunityProvider, type CStarCommunityProviderProps, CStarLibraryContext, CStarLibraryProvider, type CStarLibraryProviderProps, type TypingAgent, type UseArticleReturn, type UseArticleSearchReturn, type UseArticlesReturn, type UseCategoriesReturn, type UseChatReturn, type UseCommunitySearchReturn, type UseConversationsReturn, type UseMessagesReturn, type UsePostReturn, type UsePostsReturn, type UseTopicsReturn, type UseTypingReturn, useArticle, useArticleSearch, useArticles, useCategories, useChat, useChatClient, useCommunityClient, useCommunitySearch, useConversations, useLibraryClient, useMessages, usePost, usePosts, useTopics, useTyping };
package/dist/index.d.ts CHANGED
@@ -4,10 +4,13 @@ import { ChatClient, ChatConfig, IdentifyParams, IdentifyResult, Conversation, C
4
4
  export { ChatConfig, Conversation, ConversationCreateParams, ConversationListResponse, IdentifyParams, IdentifyResult, TypingPayload, WidgetMessage } from '@cstar.help/js/chat';
5
5
  import { LibraryClient, LibraryConfig, Category, LibraryArticle, LibraryArticleListParams } from '@cstar.help/js/library';
6
6
  export { Category, LibraryArticle, LibraryArticleListParams, LibraryConfig, LibrarySearchParams, LibrarySearchResult } from '@cstar.help/js/library';
7
+ import { CommunityClient, CommunityConfig, CommunityTopic, CommunityPost, CommunityPostListParams, CommunityPostDetail, CommunitySearchResult } from '@cstar.help/js/community';
8
+ export { CommunityComment, CommunityConfig, CommunityPost, CommunityPostDetail, CommunityPostListParams, CommunitySearchResult, CommunityTopic } from '@cstar.help/js/community';
7
9
  import * as react_jsx_runtime from 'react/jsx-runtime';
8
10
 
9
11
  declare const CStarChatContext: react.Context<ChatClient | null>;
10
12
  declare const CStarLibraryContext: react.Context<LibraryClient | null>;
13
+ declare const CStarCommunityContext: react.Context<CommunityClient | null>;
11
14
  /**
12
15
  * Access the ChatClient from the nearest CStarChatProvider.
13
16
  * Throws if used outside a provider.
@@ -18,6 +21,11 @@ declare function useChatClient(): ChatClient;
18
21
  * Throws if used outside a provider.
19
22
  */
20
23
  declare function useLibraryClient(): LibraryClient;
24
+ /**
25
+ * Access the CommunityClient from the nearest CStarCommunityProvider.
26
+ * Throws if used outside a provider.
27
+ */
28
+ declare function useCommunityClient(): CommunityClient;
21
29
 
22
30
  interface CStarChatProviderProps extends ChatConfig {
23
31
  children: ReactNode;
@@ -217,4 +225,99 @@ interface UseArticleSearchReturn {
217
225
  */
218
226
  declare function useArticleSearch(query: string): UseArticleSearchReturn;
219
227
 
220
- export { CStarChatContext, CStarChatProvider, type CStarChatProviderProps, CStarLibraryContext, CStarLibraryProvider, type CStarLibraryProviderProps, type TypingAgent, type UseArticleReturn, type UseArticleSearchReturn, type UseArticlesReturn, type UseCategoriesReturn, type UseChatReturn, type UseConversationsReturn, type UseMessagesReturn, type UseTypingReturn, useArticle, useArticleSearch, useArticles, useCategories, useChat, useChatClient, useConversations, useLibraryClient, useMessages, useTyping };
228
+ interface CStarCommunityProviderProps extends CommunityConfig {
229
+ children: ReactNode;
230
+ }
231
+ /**
232
+ * Provides a CommunityClient instance to all child components.
233
+ *
234
+ * ```tsx
235
+ * <CStarCommunityProvider teamSlug="acme">
236
+ * <CommunityForum />
237
+ * </CStarCommunityProvider>
238
+ * ```
239
+ */
240
+ declare function CStarCommunityProvider({ children, ...config }: CStarCommunityProviderProps): react_jsx_runtime.JSX.Element;
241
+
242
+ interface UseTopicsReturn {
243
+ /** Community discussion topics. */
244
+ topics: CommunityTopic[];
245
+ /** Whether the fetch is in progress. */
246
+ isLoading: boolean;
247
+ /** The last error, or null. */
248
+ error: Error | null;
249
+ /** Re-fetch topics. */
250
+ refresh: () => Promise<void>;
251
+ }
252
+ /**
253
+ * Fetch community discussion topics.
254
+ *
255
+ * ```tsx
256
+ * const { topics, isLoading } = useTopics();
257
+ * ```
258
+ */
259
+ declare function useTopics(): UseTopicsReturn;
260
+
261
+ interface UsePostsReturn {
262
+ /** Community posts. */
263
+ posts: CommunityPost[];
264
+ /** Total count of matching posts (for pagination). */
265
+ count: number;
266
+ /** Whether the fetch is in progress. */
267
+ isLoading: boolean;
268
+ /** The last error, or null. */
269
+ error: Error | null;
270
+ /** Re-fetch posts with the current params. */
271
+ refresh: () => Promise<void>;
272
+ }
273
+ /**
274
+ * Fetch community posts with optional filters.
275
+ *
276
+ * ```tsx
277
+ * const { posts, isLoading } = usePosts({ topicSlug: 'feedback', sort: 'votes' });
278
+ * ```
279
+ */
280
+ declare function usePosts(params?: CommunityPostListParams): UsePostsReturn;
281
+
282
+ interface UsePostReturn {
283
+ /** The post with comments, or null if not loaded. */
284
+ data: CommunityPostDetail | null;
285
+ /** Whether the fetch is in progress. */
286
+ isLoading: boolean;
287
+ /** The last error, or null. */
288
+ error: Error | null;
289
+ /** Re-fetch the post. */
290
+ refresh: () => Promise<void>;
291
+ }
292
+ /**
293
+ * Fetch a single community post by slug, including all comments.
294
+ *
295
+ * ```tsx
296
+ * const { data, isLoading } = usePost('dark-mode-support');
297
+ * ```
298
+ */
299
+ declare function usePost(slug: string): UsePostReturn;
300
+
301
+ interface UseCommunitySearchReturn {
302
+ /** Search results (null until first search). */
303
+ results: CommunitySearchResult | null;
304
+ /** Whether a search is in progress. */
305
+ isLoading: boolean;
306
+ /** The last error, or null. */
307
+ error: Error | null;
308
+ /** Execute a search query. */
309
+ search: (query: string) => Promise<void>;
310
+ }
311
+ /**
312
+ * Search community posts by title and body content.
313
+ *
314
+ * ```tsx
315
+ * const { results, isLoading, search } = useCommunitySearch();
316
+ *
317
+ * <input onChange={(e) => search(e.target.value)} />
318
+ * {results?.data.map(post => <PostCard key={post.id} post={post} />)}
319
+ * ```
320
+ */
321
+ declare function useCommunitySearch(): UseCommunitySearchReturn;
322
+
323
+ export { CStarChatContext, CStarChatProvider, type CStarChatProviderProps, CStarCommunityContext, CStarCommunityProvider, type CStarCommunityProviderProps, CStarLibraryContext, CStarLibraryProvider, type CStarLibraryProviderProps, type TypingAgent, type UseArticleReturn, type UseArticleSearchReturn, type UseArticlesReturn, type UseCategoriesReturn, type UseChatReturn, type UseCommunitySearchReturn, type UseConversationsReturn, type UseMessagesReturn, type UsePostReturn, type UsePostsReturn, type UseTopicsReturn, type UseTypingReturn, useArticle, useArticleSearch, useArticles, useCategories, useChat, useChatClient, useCommunityClient, useCommunitySearch, useConversations, useLibraryClient, useMessages, usePost, usePosts, useTopics, useTyping };
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import { createContext, useContext } from "react";
3
3
  var CStarChatContext = createContext(null);
4
4
  var CStarLibraryContext = createContext(null);
5
+ var CStarCommunityContext = createContext(null);
5
6
  function useChatClient() {
6
7
  const client = useContext(CStarChatContext);
7
8
  if (!client) {
@@ -16,6 +17,13 @@ function useLibraryClient() {
16
17
  }
17
18
  return client;
18
19
  }
20
+ function useCommunityClient() {
21
+ const client = useContext(CStarCommunityContext);
22
+ if (!client) {
23
+ throw new Error("useCommunityClient must be used within a <CStarCommunityProvider>");
24
+ }
25
+ return client;
26
+ }
19
27
 
20
28
  // src/chat/CStarChatProvider.tsx
21
29
  import { useEffect, useRef } from "react";
@@ -441,9 +449,180 @@ function useArticleSearch(query) {
441
449
  }, [client, query]);
442
450
  return { results, totalCount, isLoading, error };
443
451
  }
452
+
453
+ // src/community/CStarCommunityProvider.tsx
454
+ import { useRef as useRef10 } from "react";
455
+ import { CommunityClient } from "@cstar.help/js/community";
456
+ import { jsx as jsx3 } from "react/jsx-runtime";
457
+ function CStarCommunityProvider({ children, ...config }) {
458
+ const clientRef = useRef10(null);
459
+ if (!clientRef.current) {
460
+ clientRef.current = new CommunityClient(config);
461
+ }
462
+ return /* @__PURE__ */ jsx3(CStarCommunityContext.Provider, { value: clientRef.current, children });
463
+ }
464
+
465
+ // src/community/useTopics.ts
466
+ import { useState as useState9, useEffect as useEffect9, useCallback as useCallback8, useRef as useRef11 } from "react";
467
+ function useTopics() {
468
+ const client = useCommunityClient();
469
+ const [topics, setTopics] = useState9([]);
470
+ const [isLoading, setIsLoading] = useState9(true);
471
+ const [error, setError] = useState9(null);
472
+ const mountedRef = useRef11(true);
473
+ const refresh = useCallback8(async () => {
474
+ setIsLoading(true);
475
+ setError(null);
476
+ try {
477
+ const data = await client.topics();
478
+ if (mountedRef.current) {
479
+ setTopics(data);
480
+ }
481
+ } catch (err) {
482
+ if (mountedRef.current) {
483
+ setError(err instanceof Error ? err : new Error(String(err)));
484
+ }
485
+ } finally {
486
+ if (mountedRef.current) {
487
+ setIsLoading(false);
488
+ }
489
+ }
490
+ }, [client]);
491
+ useEffect9(() => {
492
+ mountedRef.current = true;
493
+ refresh();
494
+ return () => {
495
+ mountedRef.current = false;
496
+ };
497
+ }, [refresh]);
498
+ return { topics, isLoading, error, refresh };
499
+ }
500
+
501
+ // src/community/usePosts.ts
502
+ import { useState as useState10, useEffect as useEffect10, useCallback as useCallback9, useRef as useRef12 } from "react";
503
+ function usePosts(params) {
504
+ const client = useCommunityClient();
505
+ const [posts, setPosts] = useState10([]);
506
+ const [count, setCount] = useState10(0);
507
+ const [isLoading, setIsLoading] = useState10(true);
508
+ const [error, setError] = useState10(null);
509
+ const mountedRef = useRef12(true);
510
+ const topicSlug = params?.topicSlug;
511
+ const status = params?.status;
512
+ const sort = params?.sort;
513
+ const limit = params?.limit;
514
+ const offset = params?.offset;
515
+ const refresh = useCallback9(async () => {
516
+ setIsLoading(true);
517
+ setError(null);
518
+ try {
519
+ const result = await client.posts({ topicSlug, status, sort, limit, offset });
520
+ if (mountedRef.current) {
521
+ setPosts(result.posts);
522
+ setCount(result.count);
523
+ }
524
+ } catch (err) {
525
+ if (mountedRef.current) {
526
+ setError(err instanceof Error ? err : new Error(String(err)));
527
+ }
528
+ } finally {
529
+ if (mountedRef.current) {
530
+ setIsLoading(false);
531
+ }
532
+ }
533
+ }, [client, topicSlug, status, sort, limit, offset]);
534
+ useEffect10(() => {
535
+ mountedRef.current = true;
536
+ refresh();
537
+ return () => {
538
+ mountedRef.current = false;
539
+ };
540
+ }, [refresh]);
541
+ return { posts, count, isLoading, error, refresh };
542
+ }
543
+
544
+ // src/community/usePost.ts
545
+ import { useState as useState11, useEffect as useEffect11, useCallback as useCallback10, useRef as useRef13 } from "react";
546
+ function usePost(slug) {
547
+ const client = useCommunityClient();
548
+ const [data, setData] = useState11(null);
549
+ const [isLoading, setIsLoading] = useState11(true);
550
+ const [error, setError] = useState11(null);
551
+ const mountedRef = useRef13(true);
552
+ const refresh = useCallback10(async () => {
553
+ setIsLoading(true);
554
+ setError(null);
555
+ try {
556
+ const result = await client.post(slug);
557
+ if (mountedRef.current) {
558
+ setData(result);
559
+ }
560
+ } catch (err) {
561
+ if (mountedRef.current) {
562
+ setError(err instanceof Error ? err : new Error(String(err)));
563
+ }
564
+ } finally {
565
+ if (mountedRef.current) {
566
+ setIsLoading(false);
567
+ }
568
+ }
569
+ }, [client, slug]);
570
+ useEffect11(() => {
571
+ mountedRef.current = true;
572
+ refresh();
573
+ return () => {
574
+ mountedRef.current = false;
575
+ };
576
+ }, [refresh]);
577
+ return { data, isLoading, error, refresh };
578
+ }
579
+
580
+ // src/community/useCommunitySearch.ts
581
+ import { useState as useState12, useEffect as useEffect12, useCallback as useCallback11, useRef as useRef14 } from "react";
582
+ function useCommunitySearch() {
583
+ const client = useCommunityClient();
584
+ const [results, setResults] = useState12(null);
585
+ const [isLoading, setIsLoading] = useState12(false);
586
+ const [error, setError] = useState12(null);
587
+ const mountedRef = useRef14(true);
588
+ useEffect12(() => {
589
+ mountedRef.current = true;
590
+ return () => {
591
+ mountedRef.current = false;
592
+ };
593
+ }, []);
594
+ const search = useCallback11(
595
+ async (query) => {
596
+ if (!query.trim()) {
597
+ setResults(null);
598
+ return;
599
+ }
600
+ setIsLoading(true);
601
+ setError(null);
602
+ try {
603
+ const data = await client.search(query);
604
+ if (mountedRef.current) {
605
+ setResults(data);
606
+ }
607
+ } catch (err) {
608
+ if (mountedRef.current) {
609
+ setError(err instanceof Error ? err : new Error(String(err)));
610
+ }
611
+ } finally {
612
+ if (mountedRef.current) {
613
+ setIsLoading(false);
614
+ }
615
+ }
616
+ },
617
+ [client]
618
+ );
619
+ return { results, isLoading, error, search };
620
+ }
444
621
  export {
445
622
  CStarChatContext,
446
623
  CStarChatProvider,
624
+ CStarCommunityContext,
625
+ CStarCommunityProvider,
447
626
  CStarLibraryContext,
448
627
  CStarLibraryProvider,
449
628
  useArticle,
@@ -452,8 +631,13 @@ export {
452
631
  useCategories,
453
632
  useChat,
454
633
  useChatClient,
634
+ useCommunityClient,
635
+ useCommunitySearch,
455
636
  useConversations,
456
637
  useLibraryClient,
457
638
  useMessages,
639
+ usePost,
640
+ usePosts,
641
+ useTopics,
458
642
  useTyping
459
643
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cstar.help/react",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "React hooks for the cStar customer support platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",