@anonfly/react 1.0.1 → 1.0.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 CHANGED
@@ -1,6 +1,21 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/codewithasterixh/anonflyclient/main/public/logo.png" width="96" />
3
+ </p>
4
+
1
5
  # @anonfly/react
2
6
 
3
- React UI components and hooks for the Anonfly SDK.
7
+ [![Version](https://img.shields.io/npm/v/@anonfly/react?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/@anonfly/react)
8
+ [![Downloads](https://img.shields.io/npm/dm/@anonfly/react?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/@anonfly/react)
9
+
10
+ React hooks and headless components for the Anonfly SDK. Build premium, secure messaging interfaces with ease.
11
+
12
+ ## Why use @anonfly/react?
13
+
14
+ While `@anonfly/sdk` provides the core logic, `@anonfly/react` bridges the gap between the SDK and your React components. It handles state synchronization, WebSocket event lifecycle, and common UI patterns so you don't have to.
15
+
16
+ - **Headless Hooks:** Pure logic hooks that give you full control over the UI.
17
+ - **Automatic Sync:** State is kept in sync with the server and WebSocket events automatically.
18
+ - **Provider Pattern:** Easily inject configuration across your entire application.
4
19
 
5
20
  ## Installation
6
21
 
@@ -11,24 +26,73 @@ npm install @anonfly/sdk @anonfly/react
11
26
  ## Quick Start
12
27
 
13
28
  ```tsx
14
- import { AnonflyProvider, MessageList, ChatInput } from '@anonfly/react';
29
+ import { AnonflyProvider } from '@anonfly/react';
15
30
 
16
31
  const config = {
17
32
  apiKey: 'YOUR_API_KEY',
18
33
  baseUrl: 'https://api.anonfly.com/v1',
19
- wsUrl: 'wss://api.anonfly.com',
20
34
  };
21
35
 
22
36
  function App() {
23
37
  return (
24
38
  <AnonflyProvider config={config}>
25
- <MessageList roomId="general" />
26
- <ChatInput roomId="general" />
39
+ <MainApp />
27
40
  </AnonflyProvider>
28
41
  );
29
42
  }
30
43
  ```
31
44
 
45
+ ## Core Hooks
46
+
47
+ ### `useAnonflyMessages(roomId)`
48
+ Manages message fetching, sending, and real-time updates for a specific room.
49
+
50
+ ```tsx
51
+ const { messages, loading, sendMessage, error } = useAnonflyMessages('general');
52
+ ```
53
+
54
+ ### `useAnonflyAuth()`
55
+ Handles the authentication state, login, and registration.
56
+
57
+ ```tsx
58
+ const { user, login, logout, isAuthenticated } = useAnonflyAuth();
59
+ ```
60
+
61
+ ### `useAnonflyPresence(roomId)`
62
+ Track which users are currently online in a room.
63
+
64
+ ```tsx
65
+ const { participants } = useAnonflyPresence('general');
66
+ ```
67
+
68
+ ### `useAnonflyConversations()`
69
+ Lists and manages the current user's active rooms and conversations.
70
+
71
+ ```tsx
72
+ const { conversations, refresh } = useAnonflyConversations();
73
+ ```
74
+
75
+ ## Advanced Usage
76
+
77
+ ### Customizing Reconnection Logic
78
+ You can pass SDK-specific configuration directly to the `AnonflyProvider`.
79
+
80
+ ```tsx
81
+ <AnonflyProvider config={{ ...config, retries: 5 }}>
82
+ {/* ... */}
83
+ </AnonflyProvider>
84
+ ```
85
+
86
+ ### Accessing the SDK Instance
87
+ If you need to perform low-level operations, you can access the raw SDK instance:
88
+
89
+ ```tsx
90
+ import { useAnonfly } from '@anonfly/react';
91
+
92
+ const sdk = useAnonfly();
93
+ // sdk.http.get(...)
94
+ ```
95
+
32
96
  ## License
33
97
 
34
- MIT
98
+ [MIT](../../LICENSE)
@@ -0,0 +1,17 @@
1
+ import { AuthSession } from '@anonfly/sdk';
2
+ /**
3
+ * Headless hook for Anonfly Authentication.
4
+ * Handles login, session management, and premium status.
5
+ */
6
+ export declare function useAnonflyAuth(): {
7
+ session: AuthSession | null;
8
+ isAuthenticated: boolean;
9
+ loading: boolean;
10
+ error: Error | null;
11
+ login: (aid: string, username: string, challenge: string, signature: string, publicKey: string, exchangePublicKey: string) => Promise<AuthSession>;
12
+ logout: () => void;
13
+ getChallenge: (aid: string) => Promise<{
14
+ nonce: string;
15
+ }>;
16
+ };
17
+ //# sourceMappingURL=useAnonflyAuth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAnonflyAuth.d.ts","sourceRoot":"","sources":["../../src/hooks/useAnonflyAuth.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C;;;GAGG;AACH,wBAAgB,cAAc;;;;;iBAMY,MAAM,YAAY,MAAM,aAAa,MAAM,aAAa,MAAM,aAAa,MAAM,qBAAqB,MAAM;;wBA4BrG,MAAM;;;EAatD"}
@@ -0,0 +1,17 @@
1
+ import { Room } from '@anonfly/sdk';
2
+ /**
3
+ * Headless hook for browsing and managing Anonfly conversations (rooms).
4
+ */
5
+ export declare function useAnonflyConversations(): {
6
+ rooms: Room[];
7
+ loading: boolean;
8
+ error: Error | null;
9
+ fetchRooms: (region?: string) => Promise<void>;
10
+ createRoom: (data: {
11
+ roomname: string;
12
+ isPrivate?: boolean;
13
+ description?: string;
14
+ password?: string;
15
+ }) => Promise<Room>;
16
+ };
17
+ //# sourceMappingURL=useAnonflyConversations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAnonflyConversations.d.ts","sourceRoot":"","sources":["../../src/hooks/useAnonflyConversations.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEpC;;GAEG;AACH,wBAAgB,uBAAuB;;;;0BAMY,MAAM;uBAYT;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE;EA0BjI"}
@@ -0,0 +1,16 @@
1
+ import { Message } from '@anonfly/sdk';
2
+ /**
3
+ * Headless hook for managing Anonfly messages within a specific room.
4
+ * Supports fetching history and subscribing to real-time updates.
5
+ */
6
+ export declare function useAnonflyMessages(roomId?: string): {
7
+ messages: Message[];
8
+ loading: boolean;
9
+ error: Error | null;
10
+ fetchMessages: (options?: {
11
+ limit?: number;
12
+ before?: string;
13
+ }) => Promise<void>;
14
+ sendMessage: (content: string) => Promise<Message>;
15
+ };
16
+ //# sourceMappingURL=useAnonflyMessages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAnonflyMessages.d.ts","sourceRoot":"","sources":["../../src/hooks/useAnonflyMessages.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,CAAC,EAAE,MAAM;;;;8BAMK;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;2BAatC,MAAM;EA4BzD"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Headless hook for tracking presence and participants in a room.
3
+ */
4
+ export declare function useAnonflyPresence(roomId?: string): {
5
+ participants: any[];
6
+ loading: boolean;
7
+ error: Error | null;
8
+ };
9
+ //# sourceMappingURL=useAnonflyPresence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAnonflyPresence.d.ts","sourceRoot":"","sources":["../../src/hooks/useAnonflyPresence.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,CAAC,EAAE,MAAM;;;;EAgCjD"}
package/dist/index.cjs CHANGED
@@ -21,11 +21,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  AnonflyProvider: () => AnonflyProvider,
24
- ChatInput: () => ChatInput,
25
- MessageList: () => MessageList,
26
24
  useAnonfly: () => useAnonfly,
27
- useMessages: () => useMessages,
28
- useRooms: () => useRooms
25
+ useAnonflyAuth: () => useAnonflyAuth,
26
+ useAnonflyConversations: () => useAnonflyConversations,
27
+ useAnonflyMessages: () => useAnonflyMessages,
28
+ useAnonflyPresence: () => useAnonflyPresence
29
29
  });
30
30
  module.exports = __toCommonJS(index_exports);
31
31
 
@@ -46,158 +46,174 @@ var useAnonfly = () => {
46
46
  return context.client;
47
47
  };
48
48
 
49
- // src/hooks/useRooms.ts
49
+ // src/hooks/useAnonflyAuth.ts
50
50
  var import_react2 = require("react");
51
- var useRooms = () => {
51
+ function useAnonflyAuth() {
52
52
  const client = useAnonfly();
53
- const [rooms, setRooms] = (0, import_react2.useState)([]);
54
- const [loading, setLoading] = (0, import_react2.useState)(true);
53
+ const [session, setSession] = (0, import_react2.useState)(null);
54
+ const [loading, setLoading] = (0, import_react2.useState)(false);
55
55
  const [error, setError] = (0, import_react2.useState)(null);
56
- const fetchRooms = async () => {
56
+ const login = (0, import_react2.useCallback)(async (aid, username, challenge, signature, publicKey, exchangePublicKey) => {
57
+ setLoading(true);
58
+ setError(null);
57
59
  try {
58
- setLoading(true);
59
- const data = await client.rooms.list();
60
- setRooms(data);
60
+ const authSession = await client.auth.verify({
61
+ challenge,
62
+ signature,
63
+ identity: {
64
+ aid,
65
+ username,
66
+ publicKey,
67
+ exchangePublicKey
68
+ }
69
+ });
70
+ setSession(authSession);
71
+ return authSession;
61
72
  } catch (err) {
62
73
  setError(err);
74
+ throw err;
63
75
  } finally {
64
76
  setLoading(false);
65
77
  }
66
- };
67
- (0, import_react2.useEffect)(() => {
68
- fetchRooms();
69
78
  }, [client]);
70
- return { rooms, loading, error, refetch: fetchRooms };
71
- };
79
+ const logout = (0, import_react2.useCallback)(() => {
80
+ setSession(null);
81
+ }, []);
82
+ const getChallenge = (0, import_react2.useCallback)(async (aid) => {
83
+ return client.auth.generateChallenge(aid);
84
+ }, [client]);
85
+ return {
86
+ session,
87
+ isAuthenticated: !!session,
88
+ loading,
89
+ error,
90
+ login,
91
+ logout,
92
+ getChallenge
93
+ };
94
+ }
72
95
 
73
- // src/hooks/useMessages.ts
96
+ // src/hooks/useAnonflyMessages.ts
74
97
  var import_react3 = require("react");
75
- var useMessages = (roomId) => {
98
+ function useAnonflyMessages(roomId) {
76
99
  const client = useAnonfly();
77
100
  const [messages, setMessages] = (0, import_react3.useState)([]);
78
- const [loading, setLoading] = (0, import_react3.useState)(true);
101
+ const [loading, setLoading] = (0, import_react3.useState)(false);
79
102
  const [error, setError] = (0, import_react3.useState)(null);
103
+ const fetchMessages = (0, import_react3.useCallback)(async (options) => {
104
+ if (!roomId) return;
105
+ setLoading(true);
106
+ try {
107
+ const data = await client.messages.list(roomId, options);
108
+ setMessages(data);
109
+ } catch (err) {
110
+ setError(err);
111
+ } finally {
112
+ setLoading(false);
113
+ }
114
+ }, [client, roomId]);
115
+ const sendMessage = (0, import_react3.useCallback)(async (content) => {
116
+ if (!roomId) throw new Error("Room ID is required to send a message");
117
+ return client.messages.send(roomId, content);
118
+ }, [client, roomId]);
80
119
  (0, import_react3.useEffect)(() => {
81
- const fetchMessages = async () => {
82
- try {
83
- setLoading(true);
84
- const data = await client.messages.list(roomId);
85
- setMessages(data);
86
- } catch (err) {
87
- setError(err);
88
- } finally {
89
- setLoading(false);
120
+ if (!roomId || !client.ws) return;
121
+ const handler = (event) => {
122
+ if (event.type === "message" && event.data.roomId === roomId) {
123
+ setMessages((prev) => [...prev, event.data]);
90
124
  }
91
125
  };
92
- fetchMessages();
93
- if (client.ws) {
94
- const handleMessage = (msg) => {
95
- if (msg.roomId === roomId) {
96
- setMessages((prev) => [...prev, msg]);
97
- }
98
- };
99
- client.ws.on("message", handleMessage);
100
- return () => {
101
- client.ws?.off("message", handleMessage);
102
- };
103
- }
104
- }, [client, roomId]);
105
- const sendMessage = async (content) => {
106
- const newMessage = await client.messages.send(roomId, content);
107
- return newMessage;
126
+ client.ws.on("message", handler);
127
+ return () => {
128
+ client.ws?.removeListener("message", handler);
129
+ };
130
+ }, [client.ws, roomId]);
131
+ return {
132
+ messages,
133
+ loading,
134
+ error,
135
+ fetchMessages,
136
+ sendMessage
108
137
  };
109
- return { messages, loading, error, sendMessage };
110
- };
111
-
112
- // src/components/MessageList.tsx
113
- var import_clsx = require("clsx");
114
- var import_tailwind_merge = require("tailwind-merge");
115
- var import_jsx_runtime2 = require("react/jsx-runtime");
116
- function cn(...inputs) {
117
- return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
118
138
  }
119
- var MessageList = ({ roomId, className }) => {
120
- const { messages, loading, error } = useMessages(roomId);
121
- if (loading && messages.length === 0) {
122
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "p-4 text-center opacity-50", children: "Loading messages..." });
123
- }
124
- if (error) {
125
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "p-4 text-center text-red-500", children: [
126
- "Error: ",
127
- error.message
128
- ] });
129
- }
130
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: cn("flex flex-col gap-4 overflow-y-auto p-4", className), children: messages.map((msg) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col gap-1", children: [
131
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", children: [
132
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-xs font-bold opacity-70", children: msg.senderId }),
133
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-[10px] opacity-40", children: new Date(msg.timestamp).toLocaleTimeString() })
134
- ] }),
135
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "rounded-lg bg-gray-100 p-3 text-sm dark:bg-zinc-800", children: msg.content })
136
- ] }, msg.id)) });
137
- };
138
139
 
139
- // src/components/ChatInput.tsx
140
+ // src/hooks/useAnonflyConversations.ts
140
141
  var import_react4 = require("react");
141
- var import_lucide_react = require("lucide-react");
142
- var import_clsx2 = require("clsx");
143
- var import_tailwind_merge2 = require("tailwind-merge");
144
- var import_jsx_runtime3 = require("react/jsx-runtime");
145
- function cn2(...inputs) {
146
- return (0, import_tailwind_merge2.twMerge)((0, import_clsx2.clsx)(inputs));
147
- }
148
- var ChatInput = ({ roomId, className, placeholder = "Type a message..." }) => {
149
- const { sendMessage } = useMessages(roomId);
150
- const [content, setContent] = (0, import_react4.useState)("");
151
- const [sending, setSending] = (0, import_react4.useState)(false);
152
- const handleSubmit = async (e) => {
153
- e.preventDefault();
154
- if (!content.trim() || sending) return;
142
+ function useAnonflyConversations() {
143
+ const client = useAnonfly();
144
+ const [rooms, setRooms] = (0, import_react4.useState)([]);
145
+ const [loading, setLoading] = (0, import_react4.useState)(false);
146
+ const [error, setError] = (0, import_react4.useState)(null);
147
+ const fetchRooms = (0, import_react4.useCallback)(async (region) => {
148
+ setLoading(true);
155
149
  try {
156
- setSending(true);
157
- await sendMessage(content);
158
- setContent("");
150
+ const data = await client.rooms.list(region);
151
+ setRooms(data);
159
152
  } catch (err) {
160
- console.error("Failed to send message:", err);
153
+ setError(err);
161
154
  } finally {
162
- setSending(false);
155
+ setLoading(false);
163
156
  }
157
+ }, [client]);
158
+ const createRoom = (0, import_react4.useCallback)(async (data) => {
159
+ return client.rooms.create(data);
160
+ }, [client]);
161
+ (0, import_react4.useEffect)(() => {
162
+ let unsubscribe;
163
+ client.rooms.subscribeToPublicList((updatedRooms) => {
164
+ setRooms(updatedRooms);
165
+ }).then((unsub) => {
166
+ unsubscribe = unsub;
167
+ });
168
+ return () => {
169
+ if (unsubscribe) unsubscribe();
170
+ };
171
+ }, [client]);
172
+ return {
173
+ rooms,
174
+ loading,
175
+ error,
176
+ fetchRooms,
177
+ createRoom
164
178
  };
165
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
166
- "form",
167
- {
168
- onSubmit: handleSubmit,
169
- className: cn2("flex items-center gap-2 border-t p-4 dark:border-zinc-800", className),
170
- children: [
171
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
172
- "input",
173
- {
174
- type: "text",
175
- value: content,
176
- onChange: (e) => setContent(e.target.value),
177
- placeholder,
178
- disabled: sending,
179
- className: "flex-1 rounded-full bg-gray-100 px-4 py-2 text-sm outline-none focus:ring-2 focus:ring-blue-500 dark:bg-zinc-800"
180
- }
181
- ),
182
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
183
- "button",
184
- {
185
- type: "submit",
186
- disabled: sending || !content.trim(),
187
- className: "flex h-9 w-9 items-center justify-center rounded-full bg-blue-600 font-bold text-white transition-opacity hover:opacity-90 disabled:opacity-50",
188
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react.Send, { className: "h-4 w-4" })
189
- }
190
- )
191
- ]
192
- }
193
- );
194
- };
179
+ }
180
+
181
+ // src/hooks/useAnonflyPresence.ts
182
+ var import_react5 = require("react");
183
+ function useAnonflyPresence(roomId) {
184
+ const client = useAnonfly();
185
+ const [participants, setParticipants] = (0, import_react5.useState)([]);
186
+ const [loading, setLoading] = (0, import_react5.useState)(false);
187
+ const [error, setError] = (0, import_react5.useState)(null);
188
+ (0, import_react5.useEffect)(() => {
189
+ if (!roomId) return;
190
+ setLoading(true);
191
+ let unsubscribe;
192
+ client.rooms.subscribeToRoomDetails(roomId, (details) => {
193
+ setParticipants(details.participants);
194
+ setLoading(false);
195
+ }).then((unsub) => {
196
+ unsubscribe = unsub;
197
+ }).catch((err) => {
198
+ setError(err);
199
+ setLoading(false);
200
+ });
201
+ return () => {
202
+ if (unsubscribe) unsubscribe();
203
+ };
204
+ }, [client, roomId]);
205
+ return {
206
+ participants,
207
+ loading,
208
+ error
209
+ };
210
+ }
195
211
  // Annotate the CommonJS export names for ESM import in node:
196
212
  0 && (module.exports = {
197
213
  AnonflyProvider,
198
- ChatInput,
199
- MessageList,
200
214
  useAnonfly,
201
- useMessages,
202
- useRooms
215
+ useAnonflyAuth,
216
+ useAnonflyConversations,
217
+ useAnonflyMessages,
218
+ useAnonflyPresence
203
219
  });
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- export * from './context/AnonflyContext.js';
2
- export * from './hooks/useRooms.js';
3
- export * from './hooks/useMessages.js';
4
- export * from './components/MessageList.js';
5
- export * from './components/ChatInput.js';
1
+ export * from './context/AnonflyContext';
2
+ export * from './hooks/useAnonflyAuth';
3
+ export * from './hooks/useAnonflyMessages';
4
+ export * from './hooks/useAnonflyConversations';
5
+ export * from './hooks/useAnonflyPresence';
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iCAAiC,CAAC;AAChD,cAAc,4BAA4B,CAAC"}
package/dist/index.js CHANGED
@@ -15,157 +15,173 @@ var useAnonfly = () => {
15
15
  return context.client;
16
16
  };
17
17
 
18
- // src/hooks/useRooms.ts
19
- import { useEffect, useState } from "react";
20
- var useRooms = () => {
18
+ // src/hooks/useAnonflyAuth.ts
19
+ import { useState, useCallback } from "react";
20
+ function useAnonflyAuth() {
21
21
  const client = useAnonfly();
22
- const [rooms, setRooms] = useState([]);
23
- const [loading, setLoading] = useState(true);
22
+ const [session, setSession] = useState(null);
23
+ const [loading, setLoading] = useState(false);
24
24
  const [error, setError] = useState(null);
25
- const fetchRooms = async () => {
25
+ const login = useCallback(async (aid, username, challenge, signature, publicKey, exchangePublicKey) => {
26
+ setLoading(true);
27
+ setError(null);
26
28
  try {
27
- setLoading(true);
28
- const data = await client.rooms.list();
29
- setRooms(data);
29
+ const authSession = await client.auth.verify({
30
+ challenge,
31
+ signature,
32
+ identity: {
33
+ aid,
34
+ username,
35
+ publicKey,
36
+ exchangePublicKey
37
+ }
38
+ });
39
+ setSession(authSession);
40
+ return authSession;
30
41
  } catch (err) {
31
42
  setError(err);
43
+ throw err;
32
44
  } finally {
33
45
  setLoading(false);
34
46
  }
35
- };
36
- useEffect(() => {
37
- fetchRooms();
38
47
  }, [client]);
39
- return { rooms, loading, error, refetch: fetchRooms };
40
- };
48
+ const logout = useCallback(() => {
49
+ setSession(null);
50
+ }, []);
51
+ const getChallenge = useCallback(async (aid) => {
52
+ return client.auth.generateChallenge(aid);
53
+ }, [client]);
54
+ return {
55
+ session,
56
+ isAuthenticated: !!session,
57
+ loading,
58
+ error,
59
+ login,
60
+ logout,
61
+ getChallenge
62
+ };
63
+ }
41
64
 
42
- // src/hooks/useMessages.ts
43
- import { useEffect as useEffect2, useState as useState2 } from "react";
44
- var useMessages = (roomId) => {
65
+ // src/hooks/useAnonflyMessages.ts
66
+ import { useState as useState2, useCallback as useCallback2, useEffect } from "react";
67
+ function useAnonflyMessages(roomId) {
45
68
  const client = useAnonfly();
46
69
  const [messages, setMessages] = useState2([]);
47
- const [loading, setLoading] = useState2(true);
70
+ const [loading, setLoading] = useState2(false);
48
71
  const [error, setError] = useState2(null);
49
- useEffect2(() => {
50
- const fetchMessages = async () => {
51
- try {
52
- setLoading(true);
53
- const data = await client.messages.list(roomId);
54
- setMessages(data);
55
- } catch (err) {
56
- setError(err);
57
- } finally {
58
- setLoading(false);
59
- }
60
- };
61
- fetchMessages();
62
- if (client.ws) {
63
- const handleMessage = (msg) => {
64
- if (msg.roomId === roomId) {
65
- setMessages((prev) => [...prev, msg]);
66
- }
67
- };
68
- client.ws.on("message", handleMessage);
69
- return () => {
70
- client.ws?.off("message", handleMessage);
71
- };
72
+ const fetchMessages = useCallback2(async (options) => {
73
+ if (!roomId) return;
74
+ setLoading(true);
75
+ try {
76
+ const data = await client.messages.list(roomId, options);
77
+ setMessages(data);
78
+ } catch (err) {
79
+ setError(err);
80
+ } finally {
81
+ setLoading(false);
72
82
  }
73
83
  }, [client, roomId]);
74
- const sendMessage = async (content) => {
75
- const newMessage = await client.messages.send(roomId, content);
76
- return newMessage;
84
+ const sendMessage = useCallback2(async (content) => {
85
+ if (!roomId) throw new Error("Room ID is required to send a message");
86
+ return client.messages.send(roomId, content);
87
+ }, [client, roomId]);
88
+ useEffect(() => {
89
+ if (!roomId || !client.ws) return;
90
+ const handler = (event) => {
91
+ if (event.type === "message" && event.data.roomId === roomId) {
92
+ setMessages((prev) => [...prev, event.data]);
93
+ }
94
+ };
95
+ client.ws.on("message", handler);
96
+ return () => {
97
+ client.ws?.removeListener("message", handler);
98
+ };
99
+ }, [client.ws, roomId]);
100
+ return {
101
+ messages,
102
+ loading,
103
+ error,
104
+ fetchMessages,
105
+ sendMessage
77
106
  };
78
- return { messages, loading, error, sendMessage };
79
- };
80
-
81
- // src/components/MessageList.tsx
82
- import { clsx } from "clsx";
83
- import { twMerge } from "tailwind-merge";
84
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
85
- function cn(...inputs) {
86
- return twMerge(clsx(inputs));
87
107
  }
88
- var MessageList = ({ roomId, className }) => {
89
- const { messages, loading, error } = useMessages(roomId);
90
- if (loading && messages.length === 0) {
91
- return /* @__PURE__ */ jsx2("div", { className: "p-4 text-center opacity-50", children: "Loading messages..." });
92
- }
93
- if (error) {
94
- return /* @__PURE__ */ jsxs("div", { className: "p-4 text-center text-red-500", children: [
95
- "Error: ",
96
- error.message
97
- ] });
98
- }
99
- return /* @__PURE__ */ jsx2("div", { className: cn("flex flex-col gap-4 overflow-y-auto p-4", className), children: messages.map((msg) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
100
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
101
- /* @__PURE__ */ jsx2("span", { className: "text-xs font-bold opacity-70", children: msg.senderId }),
102
- /* @__PURE__ */ jsx2("span", { className: "text-[10px] opacity-40", children: new Date(msg.timestamp).toLocaleTimeString() })
103
- ] }),
104
- /* @__PURE__ */ jsx2("div", { className: "rounded-lg bg-gray-100 p-3 text-sm dark:bg-zinc-800", children: msg.content })
105
- ] }, msg.id)) });
106
- };
107
108
 
108
- // src/components/ChatInput.tsx
109
- import { useState as useState3 } from "react";
110
- import { Send } from "lucide-react";
111
- import { clsx as clsx2 } from "clsx";
112
- import { twMerge as twMerge2 } from "tailwind-merge";
113
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
114
- function cn2(...inputs) {
115
- return twMerge2(clsx2(inputs));
116
- }
117
- var ChatInput = ({ roomId, className, placeholder = "Type a message..." }) => {
118
- const { sendMessage } = useMessages(roomId);
119
- const [content, setContent] = useState3("");
120
- const [sending, setSending] = useState3(false);
121
- const handleSubmit = async (e) => {
122
- e.preventDefault();
123
- if (!content.trim() || sending) return;
109
+ // src/hooks/useAnonflyConversations.ts
110
+ import { useState as useState3, useCallback as useCallback3, useEffect as useEffect2 } from "react";
111
+ function useAnonflyConversations() {
112
+ const client = useAnonfly();
113
+ const [rooms, setRooms] = useState3([]);
114
+ const [loading, setLoading] = useState3(false);
115
+ const [error, setError] = useState3(null);
116
+ const fetchRooms = useCallback3(async (region) => {
117
+ setLoading(true);
124
118
  try {
125
- setSending(true);
126
- await sendMessage(content);
127
- setContent("");
119
+ const data = await client.rooms.list(region);
120
+ setRooms(data);
128
121
  } catch (err) {
129
- console.error("Failed to send message:", err);
122
+ setError(err);
130
123
  } finally {
131
- setSending(false);
124
+ setLoading(false);
132
125
  }
126
+ }, [client]);
127
+ const createRoom = useCallback3(async (data) => {
128
+ return client.rooms.create(data);
129
+ }, [client]);
130
+ useEffect2(() => {
131
+ let unsubscribe;
132
+ client.rooms.subscribeToPublicList((updatedRooms) => {
133
+ setRooms(updatedRooms);
134
+ }).then((unsub) => {
135
+ unsubscribe = unsub;
136
+ });
137
+ return () => {
138
+ if (unsubscribe) unsubscribe();
139
+ };
140
+ }, [client]);
141
+ return {
142
+ rooms,
143
+ loading,
144
+ error,
145
+ fetchRooms,
146
+ createRoom
133
147
  };
134
- return /* @__PURE__ */ jsxs2(
135
- "form",
136
- {
137
- onSubmit: handleSubmit,
138
- className: cn2("flex items-center gap-2 border-t p-4 dark:border-zinc-800", className),
139
- children: [
140
- /* @__PURE__ */ jsx3(
141
- "input",
142
- {
143
- type: "text",
144
- value: content,
145
- onChange: (e) => setContent(e.target.value),
146
- placeholder,
147
- disabled: sending,
148
- className: "flex-1 rounded-full bg-gray-100 px-4 py-2 text-sm outline-none focus:ring-2 focus:ring-blue-500 dark:bg-zinc-800"
149
- }
150
- ),
151
- /* @__PURE__ */ jsx3(
152
- "button",
153
- {
154
- type: "submit",
155
- disabled: sending || !content.trim(),
156
- className: "flex h-9 w-9 items-center justify-center rounded-full bg-blue-600 font-bold text-white transition-opacity hover:opacity-90 disabled:opacity-50",
157
- children: /* @__PURE__ */ jsx3(Send, { className: "h-4 w-4" })
158
- }
159
- )
160
- ]
161
- }
162
- );
163
- };
148
+ }
149
+
150
+ // src/hooks/useAnonflyPresence.ts
151
+ import { useState as useState4, useEffect as useEffect3 } from "react";
152
+ function useAnonflyPresence(roomId) {
153
+ const client = useAnonfly();
154
+ const [participants, setParticipants] = useState4([]);
155
+ const [loading, setLoading] = useState4(false);
156
+ const [error, setError] = useState4(null);
157
+ useEffect3(() => {
158
+ if (!roomId) return;
159
+ setLoading(true);
160
+ let unsubscribe;
161
+ client.rooms.subscribeToRoomDetails(roomId, (details) => {
162
+ setParticipants(details.participants);
163
+ setLoading(false);
164
+ }).then((unsub) => {
165
+ unsubscribe = unsub;
166
+ }).catch((err) => {
167
+ setError(err);
168
+ setLoading(false);
169
+ });
170
+ return () => {
171
+ if (unsubscribe) unsubscribe();
172
+ };
173
+ }, [client, roomId]);
174
+ return {
175
+ participants,
176
+ loading,
177
+ error
178
+ };
179
+ }
164
180
  export {
165
181
  AnonflyProvider,
166
- ChatInput,
167
- MessageList,
168
182
  useAnonfly,
169
- useMessages,
170
- useRooms
183
+ useAnonflyAuth,
184
+ useAnonflyConversations,
185
+ useAnonflyMessages,
186
+ useAnonflyPresence
171
187
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anonfly/react",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "type": "module",
5
5
  "description": "React UI components for Anonfly SDK",
6
6
  "main": "./dist/index.cjs",
@@ -17,22 +17,22 @@
17
17
  "dev": "tsup src/index.ts --format cjs,esm --watch --dts --external react"
18
18
  },
19
19
  "dependencies": {
20
- "@anonfly/sdk": "0.0.0",
21
- "lucide-react": "^0.300.0",
22
- "clsx": "^2.1.0",
23
- "tailwind-merge": "^2.2.0"
20
+ "@anonfly/sdk": "^1.0.1",
21
+ "lucide-react": "^0.473.0",
22
+ "clsx": "^2.1.1",
23
+ "tailwind-merge": "^3.0.1"
24
24
  },
25
25
  "peerDependencies": {
26
26
  "react": ">=18",
27
27
  "react-dom": ">=18"
28
28
  },
29
29
  "devDependencies": {
30
- "@types/react": "^18.0.0",
31
- "@types/react-dom": "^18.0.0",
32
- "react": "^18.0.0",
33
- "react-dom": "^18.0.0",
34
- "tsup": "^8.0.0",
35
- "typescript": "^5.0.0"
30
+ "@types/react": "^19.0.0",
31
+ "@types/react-dom": "^19.0.0",
32
+ "react": "^19.0.0",
33
+ "react-dom": "^19.0.0",
34
+ "tsup": "^8.3.5",
35
+ "typescript": "^5.7.2"
36
36
  },
37
37
  "publishConfig": {
38
38
  "access": "public"