@solarpunkltd/swarm-chat-js 4.2.2 → 5.0.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
@@ -43,7 +43,7 @@ npm/pnpm install @solarpunkltd/swarm-chat-js
43
43
  ### Imports
44
44
 
45
45
  ```typescript
46
- import { EVENTS, MessageData, SwarmChat, ChatSettings } from '@solarpunkltd/swarm-chat-js';
46
+ import { EVENTS, MessageData, MessageType, SwarmChat, ChatSettings } from '@solarpunkltd/swarm-chat-js';
47
47
  ```
48
48
 
49
49
  ### `ChatSettings` Interface
@@ -106,118 +106,69 @@ The `SwarmChat` class instance provides the following core methods:
106
106
  - `start()`: Initializes and starts the chat service, including setting up listeners and beginning to poll for messages.
107
107
  - `stop()`: Stops the chat service, clears intervals, and cleans up resources.
108
108
  - `getEmitter()`: Returns an event emitter instance, allowing your application to subscribe to the `EVENTS` listed above.
109
- - `sendMessage(message: string)`: Initiates the process of sending a new chat message from the current user.
109
+ - `sendMessage(message: string, type: MessageType, targetMessageId?: string, id?: string)`: Initiates the process of sending a new chat message from the current user. Supports different message types including text, threads, and reactions.
110
110
  - `fetchPreviousMessages()`: Manually triggers the fetching of older messages from the aggregated chat feed.
111
+ - `hasPreviousMessages()`: Returns a boolean indicating whether there are previous messages available to fetch (determined by checking if more than one message state reference exists).
111
112
  - `retrySendMessage(message: MessageData)`: Attempts to resend a message that previously encountered an error during the initial request phase (e.g., failed to write to the user's own feed).
112
113
  - `retryBroadcastUserMessage(message: MessageData)`: Attempts to re-broadcast a message update via GSOC if the message was successfully uploaded to the user's feed but the GSOC broadcast might have failed or needs retrying.
113
114
 
115
+ ### Message Types
116
+
117
+ The library supports three types of messages through the `MessageType` enum:
118
+
119
+ - `MessageType.TEXT`: Regular chat messages
120
+ - `MessageType.THREAD`: Reply messages that reference a parent message via `targetMessageId`
121
+ - `MessageType.REACTION`: Emoji reactions to existing messages, also using `targetMessageId` to reference the target message
122
+
123
+ ### Message State Management
124
+
125
+ The library includes robust message state handling with:
126
+
127
+ - **Automatic retry logic**: Failed message state references are automatically retried with exponential backoff
128
+ - **Ref banning**: Persistently failing references are banned after maximum retry attempts to prevent infinite loops
129
+ - **Efficient caching**: Message state data is cached to avoid redundant network requests
130
+
114
131
  ---
115
132
 
116
133
  ## 🚀 Usage Example (React)
117
134
 
118
135
  Here's an example of how `swarm-chat-js` can be integrated into a React application using a custom hook (`useSwarmChat`). This hook encapsulates chat logic, state management, and event handling.
119
136
 
137
+ ### Complete Implementation
138
+
139
+ For a full working example, check out our React integration:
140
+
141
+ **📖 [View Complete useSwarmChat Hook Implementation](https://github.com/Solar-Punk-Ltd/swarm-chat-react-example/blob/master/src/hooks/useSwarmChat.tsx)**
142
+
143
+ ### Basic Usage
144
+
120
145
  ```typescript
121
- import { useEffect, useRef, useState } from 'react';
122
- import { EVENTS, MessageData, SwarmChat, ChatSettings } from '@solarpunkltd/swarm-chat-js';
123
-
124
- export interface VisibleMessage extends MessageData {
125
- requested?: boolean;
126
- uploaded?: boolean;
127
- received?: boolean;
128
- error?: boolean;
129
- }
146
+ import { useSwarmChat } from './hooks/useSwarmChat';
147
+ import { MessageType } from '@solarpunkltd/swarm-chat-js';
148
+
149
+ function ChatComponent() {
150
+ const { messages, isLoading, sendMessage, hasPreviousMessages, loadPreviousMessages } = useSwarmChat(chatSettings);
130
151
 
131
- export const useSwarmChat = ({ user, infra }: ChatSettings) => {
132
- const chat = useRef<SwarmChat | null>(null);
133
- const messageCache = useRef<VisibleMessage[]>([]);
134
- const [allMessages, setAllMessages] = useState<VisibleMessage[]>([]);
135
- const [chatLoading, setChatLoading] = useState<boolean>(true);
136
- const [messagesLoading, setMessagesLoading] = useState<boolean>(false);
137
- const [error, setError] = useState<any | null>(null);
138
-
139
- useEffect(() => {
140
- if (!chat.current) {
141
- const newChat = new SwarmChat({
142
- user,
143
- infra,
144
- });
145
-
146
- chat.current = newChat;
147
-
148
- const { on } = newChat.getEmitter();
149
-
150
- const updateMessage = (id: string, updates: Partial<VisibleMessage>) => {
151
- messageCache.current = messageCache.current.map((msg) => (msg.id === id ? { ...msg, ...updates } : msg));
152
- setAllMessages(chat.current?.orderMessages([...messageCache.current]) || []);
153
- };
154
-
155
- const handleMessageEvent = (event: string, updates: Partial<VisibleMessage>) => {
156
- on(event, (d: MessageData | string) => {
157
- const data = typeof d === 'string' ? JSON.parse(d) : d;
158
-
159
- const existingMessage = messageCache.current.find((msg) => msg.id === data.id);
160
- if (existingMessage) {
161
- updateMessage(data.id, updates);
162
- } else {
163
- messageCache.current.push({ ...data, ...updates });
164
- setAllMessages(chat.current?.orderMessages([...messageCache.current]) || []);
165
- }
166
- });
167
- };
168
-
169
- handleMessageEvent(EVENTS.MESSAGE_REQUEST_INITIATED, {
170
- error: false,
171
- requested: true,
172
- });
173
- handleMessageEvent(EVENTS.MESSAGE_REQUEST_UPLOADED, {
174
- error: false,
175
- uploaded: true,
176
- });
177
- handleMessageEvent(EVENTS.MESSAGE_RECEIVED, {
178
- error: false,
179
- received: true,
180
- });
181
- handleMessageEvent(EVENTS.MESSAGE_REQUEST_ERROR, { error: true });
182
-
183
- on(EVENTS.LOADING_INIT, setChatLoading);
184
- on(EVENTS.LOADING_PREVIOUS_MESSAGES, setMessagesLoading);
185
- on(EVENTS.CRITICAL_ERROR, setError);
186
-
187
- newChat.start();
188
- }
189
-
190
- return () => {
191
- if (chat.current) {
192
- chat.current.stop();
193
- chat.current = null;
194
- }
195
- };
196
- }, [user.privateKey]);
197
-
198
- const sendMessage = (message: string) => chat.current?.sendMessage(message);
199
-
200
- const fetchPreviousMessages = () => chat.current?.fetchPreviousMessages();
201
-
202
- const retrySendMessage = (message: VisibleMessage) => {
203
- if (message.requested && message.error) {
204
- chat.current?.retrySendMessage(message);
205
- }
206
- if (message.uploaded && message.error) {
207
- chat.current?.retryBroadcastUserMessage(message);
208
- }
152
+ const handleSendMessage = (text: string) => {
153
+ sendMessage(text, MessageType.TEXT);
209
154
  };
210
155
 
211
- return {
212
- chatLoading,
213
- messagesLoading,
214
- allMessages,
215
- sendMessage,
216
- fetchPreviousMessages,
217
- retrySendMessage,
218
- error,
156
+ const handleReaction = (targetMessageId: string, emoji: string) => {
157
+ sendMessage(emoji, MessageType.REACTION, targetMessageId);
219
158
  };
220
- };
159
+
160
+ const handleReply = (targetMessageId: string, replyText: string) => {
161
+ sendMessage(replyText, MessageType.THREAD, targetMessageId);
162
+ };
163
+
164
+ return (
165
+ <div className="chat-container">
166
+ {/* Your chat UI implementation */}
167
+ {hasPreviousMessages() && <button onClick={loadPreviousMessages}>Load Previous Messages</button>}
168
+ {/* Message list, input field, etc. */}
169
+ </div>
170
+ );
171
+ }
221
172
  ```
222
173
 
223
174
  ---
@@ -244,7 +195,8 @@ npm run mine -- <bee-address> <topic-name>
244
195
 
245
196
  ## 💡 Future Development
246
197
 
247
- - **Push-Based Event System:** We are actively designing a new architecture to transition from the polling mechanism to a more efficient, push-based event system for message delivery. This will significantly reduce node load and improve real-time message propagation. Stay tuned! ☀️
198
+ - **Push-Based Event System:** We are actively designing a new architecture to transition from the polling mechanism to a more efficient, push-based event system for message delivery. This will significantly reduce node load and improve real-time message propagation.
199
+ - **Performance Optimizations:** Further improvements to message state handling and caching mechanisms.
248
200
 
249
201
  ---
250
202
 
@@ -253,5 +205,6 @@ npm run mine -- <bee-address> <topic-name>
253
205
  - [What are Feeds? (Official Swarm Documentation)](https://docs.ethswarm.org/docs/develop/tools-and-features/feeds#what-are-feeds)
254
206
  - [GSOC Introduction (Official Swarm Documentation)](https://docs.ethswarm.org/docs/develop/tools-and-features/gsoc/#introduction)
255
207
  - [Example Aggregator: Solar-Punk-Ltd/swarm-chat-aggregator-js](https://github.com/Solar-Punk-Ltd/swarm-chat-aggregator-js)
208
+ - [Example React client](https://github.com/Solar-Punk-Ltd/swarm-chat-react-example)
256
209
 
257
210
  ---
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { SwarmChat } from './lib/core';
2
2
  export { EVENTS } from './lib/constants';
3
- export type { MessageData, ChatSettings } from './interfaces';
3
+ export type { MessageData, ChatSettings, StatefulMessage, MessageStateRef } from './interfaces';
4
+ export { MessageType } from './interfaces';
4
5
  export type { EthAddress } from '../node_modules/@ethersphere/bee-js/dist/types/index.d.ts';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE9D,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEhG,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC"}