@osise/api-client 0.0.9 → 0.0.11
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/dist/cjs/hooks/index.js +285 -0
- package/dist/cjs/hooks/index.js.map +1 -1
- package/dist/cjs/index.js +11 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/realtime/client.js +238 -0
- package/dist/cjs/realtime/client.js.map +1 -0
- package/dist/cjs/types/index.js +2 -0
- package/dist/cjs/types/index.js.map +1 -1
- package/dist/cjs/types/realtime.js +7 -0
- package/dist/cjs/types/realtime.js.map +1 -0
- package/dist/esm/hooks/index.js +278 -0
- package/dist/esm/hooks/index.js.map +1 -1
- package/dist/esm/index.js +9 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/realtime/client.js +234 -0
- package/dist/esm/realtime/client.js.map +1 -0
- package/dist/esm/types/index.js +2 -0
- package/dist/esm/types/index.js.map +1 -1
- package/dist/esm/types/realtime.js +6 -0
- package/dist/esm/types/realtime.js.map +1 -0
- package/dist/types/hooks/index.d.ts +104 -0
- package/dist/types/hooks/index.d.ts.map +1 -1
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/realtime/client.d.ts +101 -0
- package/dist/types/realtime/client.d.ts.map +1 -0
- package/dist/types/types/artisan.d.ts +70 -45
- package/dist/types/types/artisan.d.ts.map +1 -1
- package/dist/types/types/index.d.ts +1 -0
- package/dist/types/types/index.d.ts.map +1 -1
- package/dist/types/types/realtime.d.ts +134 -0
- package/dist/types/types/realtime.d.ts.map +1 -0
- package/package.json +9 -6
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Real-time client for Osise SignalR hubs
|
|
4
|
+
* Provides typed connections to ChatHub and NotificationHub
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.OsiseRealTimeClient = void 0;
|
|
8
|
+
const signalr_1 = require("@microsoft/signalr");
|
|
9
|
+
/**
|
|
10
|
+
* Manages SignalR connections to the Osise real-time hubs.
|
|
11
|
+
*
|
|
12
|
+
* Messages flow through REST API (not through SignalR). The hubs push
|
|
13
|
+
* server-side events to connected clients in real-time.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* const realtime = new OsiseRealTimeClient({
|
|
18
|
+
* baseUrl: 'https://api.osise.com',
|
|
19
|
+
* accessTokenFactory: () => client.getAccessToken(),
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* realtime.chat.onMessage = (msg) => console.log('New message:', msg);
|
|
23
|
+
* realtime.notifications.onNotification = (n) => console.log('Notification:', n);
|
|
24
|
+
*
|
|
25
|
+
* await realtime.connect();
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
class OsiseRealTimeClient {
|
|
29
|
+
constructor(config) {
|
|
30
|
+
this.chatConnection = null;
|
|
31
|
+
this.notificationConnection = null;
|
|
32
|
+
/** Chat hub event handlers */
|
|
33
|
+
this.chat = {};
|
|
34
|
+
/** Notification hub event handlers */
|
|
35
|
+
this.notifications = {};
|
|
36
|
+
/** Connection lifecycle event handlers */
|
|
37
|
+
this.connection = {};
|
|
38
|
+
/** Current chat hub connection state */
|
|
39
|
+
this.chatState = 'disconnected';
|
|
40
|
+
/** Current notification hub connection state */
|
|
41
|
+
this.notificationState = 'disconnected';
|
|
42
|
+
this.config = {
|
|
43
|
+
autoReconnect: true,
|
|
44
|
+
reconnectDelays: [0, 2000, 5000, 10000, 30000],
|
|
45
|
+
debug: false,
|
|
46
|
+
...config,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
// ==================== Connection Management ====================
|
|
50
|
+
/**
|
|
51
|
+
* Connect to both chat and notification hubs.
|
|
52
|
+
* Call this after the user is authenticated.
|
|
53
|
+
*/
|
|
54
|
+
async connect() {
|
|
55
|
+
await Promise.all([
|
|
56
|
+
this.connectChat(),
|
|
57
|
+
this.connectNotifications(),
|
|
58
|
+
]);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Disconnect from both hubs.
|
|
62
|
+
* Call this on logout or when real-time features are no longer needed.
|
|
63
|
+
*/
|
|
64
|
+
async disconnect() {
|
|
65
|
+
await Promise.all([
|
|
66
|
+
this.disconnectChat(),
|
|
67
|
+
this.disconnectNotifications(),
|
|
68
|
+
]);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Connect to the chat hub only.
|
|
72
|
+
*/
|
|
73
|
+
async connectChat() {
|
|
74
|
+
if (this.chatConnection?.state === signalr_1.HubConnectionState.Connected)
|
|
75
|
+
return;
|
|
76
|
+
this.chatConnection = this.buildConnection('/hubs/chat');
|
|
77
|
+
this.registerChatHandlers(this.chatConnection);
|
|
78
|
+
this.registerConnectionHandlers(this.chatConnection, 'chat');
|
|
79
|
+
this.chatState = 'connecting';
|
|
80
|
+
await this.chatConnection.start();
|
|
81
|
+
this.chatState = 'connected';
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Connect to the notification hub only.
|
|
85
|
+
*/
|
|
86
|
+
async connectNotifications() {
|
|
87
|
+
if (this.notificationConnection?.state === signalr_1.HubConnectionState.Connected)
|
|
88
|
+
return;
|
|
89
|
+
this.notificationConnection = this.buildConnection('/hubs/notifications');
|
|
90
|
+
this.registerNotificationHandlers(this.notificationConnection);
|
|
91
|
+
this.registerConnectionHandlers(this.notificationConnection, 'notifications');
|
|
92
|
+
this.notificationState = 'connecting';
|
|
93
|
+
await this.notificationConnection.start();
|
|
94
|
+
this.notificationState = 'connected';
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Disconnect from the chat hub.
|
|
98
|
+
*/
|
|
99
|
+
async disconnectChat() {
|
|
100
|
+
if (this.chatConnection) {
|
|
101
|
+
await this.chatConnection.stop();
|
|
102
|
+
this.chatConnection = null;
|
|
103
|
+
this.chatState = 'disconnected';
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Disconnect from the notification hub.
|
|
108
|
+
*/
|
|
109
|
+
async disconnectNotifications() {
|
|
110
|
+
if (this.notificationConnection) {
|
|
111
|
+
await this.notificationConnection.stop();
|
|
112
|
+
this.notificationConnection = null;
|
|
113
|
+
this.notificationState = 'disconnected';
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// ==================== Chat Hub Client → Server Methods ====================
|
|
117
|
+
/**
|
|
118
|
+
* Send a typing indicator to another user.
|
|
119
|
+
*
|
|
120
|
+
* @param conversationId - The conversation where typing is happening
|
|
121
|
+
* @param recipientUserId - The other participant's user ID
|
|
122
|
+
* @param isTyping - Whether the user is currently typing
|
|
123
|
+
*/
|
|
124
|
+
async sendTypingIndicator(conversationId, recipientUserId, isTyping) {
|
|
125
|
+
this.ensureChatConnected();
|
|
126
|
+
await this.chatConnection.invoke('SendTypingIndicator', conversationId, recipientUserId, isTyping);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Join a conversation group to receive targeted events.
|
|
130
|
+
* Call this when the user opens a conversation.
|
|
131
|
+
*
|
|
132
|
+
* @param conversationId - The conversation to join
|
|
133
|
+
*/
|
|
134
|
+
async joinConversation(conversationId) {
|
|
135
|
+
this.ensureChatConnected();
|
|
136
|
+
await this.chatConnection.invoke('JoinConversation', conversationId);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Leave a conversation group.
|
|
140
|
+
* Call this when the user navigates away from a conversation.
|
|
141
|
+
*
|
|
142
|
+
* @param conversationId - The conversation to leave
|
|
143
|
+
*/
|
|
144
|
+
async leaveConversation(conversationId) {
|
|
145
|
+
this.ensureChatConnected();
|
|
146
|
+
await this.chatConnection.invoke('LeaveConversation', conversationId);
|
|
147
|
+
}
|
|
148
|
+
// ==================== Notification Hub Client → Server Methods ====================
|
|
149
|
+
/**
|
|
150
|
+
* Acknowledge receipt of a notification.
|
|
151
|
+
*
|
|
152
|
+
* @param notificationId - The notification to acknowledge
|
|
153
|
+
*/
|
|
154
|
+
async acknowledgeNotification(notificationId) {
|
|
155
|
+
this.ensureNotificationConnected();
|
|
156
|
+
await this.notificationConnection.invoke('AcknowledgeNotification', notificationId);
|
|
157
|
+
}
|
|
158
|
+
// ==================== Private Helpers ====================
|
|
159
|
+
buildConnection(hubPath) {
|
|
160
|
+
const url = `${this.config.baseUrl}${hubPath}`;
|
|
161
|
+
const logLevel = this.config.debug ? signalr_1.LogLevel.Debug : signalr_1.LogLevel.Warning;
|
|
162
|
+
let builder = new signalr_1.HubConnectionBuilder()
|
|
163
|
+
.withUrl(url, {
|
|
164
|
+
accessTokenFactory: () => this.config.accessTokenFactory() ?? '',
|
|
165
|
+
})
|
|
166
|
+
.configureLogging(logLevel);
|
|
167
|
+
if (this.config.autoReconnect) {
|
|
168
|
+
builder = builder.withAutomaticReconnect(this.config.reconnectDelays);
|
|
169
|
+
}
|
|
170
|
+
return builder.build();
|
|
171
|
+
}
|
|
172
|
+
registerChatHandlers(connection) {
|
|
173
|
+
connection.on('ReceiveMessage', (message) => {
|
|
174
|
+
this.chat.onMessage?.(message);
|
|
175
|
+
});
|
|
176
|
+
connection.on('TypingIndicator', (event) => {
|
|
177
|
+
this.chat.onTypingIndicator?.(event);
|
|
178
|
+
});
|
|
179
|
+
connection.on('MessageDelivered', (event) => {
|
|
180
|
+
this.chat.onMessageDelivered?.(event);
|
|
181
|
+
});
|
|
182
|
+
connection.on('MessagesRead', (event) => {
|
|
183
|
+
this.chat.onMessagesRead?.(event);
|
|
184
|
+
});
|
|
185
|
+
connection.on('MessageEdited', (event) => {
|
|
186
|
+
this.chat.onMessageEdited?.(event);
|
|
187
|
+
});
|
|
188
|
+
connection.on('MessageDeleted', (event) => {
|
|
189
|
+
this.chat.onMessageDeleted?.(event);
|
|
190
|
+
});
|
|
191
|
+
connection.on('UserPresenceChanged', (event) => {
|
|
192
|
+
this.chat.onUserPresenceChanged?.(event);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
registerNotificationHandlers(connection) {
|
|
196
|
+
connection.on('ReceiveNotification', (notification) => {
|
|
197
|
+
this.notifications.onNotification?.(notification);
|
|
198
|
+
});
|
|
199
|
+
connection.on('NotificationCountUpdated', (event) => {
|
|
200
|
+
this.notifications.onNotificationCountUpdated?.(event);
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
registerConnectionHandlers(connection, hub) {
|
|
204
|
+
connection.onclose((error) => {
|
|
205
|
+
if (hub === 'chat')
|
|
206
|
+
this.chatState = 'disconnected';
|
|
207
|
+
else
|
|
208
|
+
this.notificationState = 'disconnected';
|
|
209
|
+
this.connection.onDisconnected?.(error);
|
|
210
|
+
});
|
|
211
|
+
connection.onreconnecting((error) => {
|
|
212
|
+
if (hub === 'chat')
|
|
213
|
+
this.chatState = 'reconnecting';
|
|
214
|
+
else
|
|
215
|
+
this.notificationState = 'reconnecting';
|
|
216
|
+
this.connection.onReconnecting?.(error);
|
|
217
|
+
});
|
|
218
|
+
connection.onreconnected((connectionId) => {
|
|
219
|
+
if (hub === 'chat')
|
|
220
|
+
this.chatState = 'connected';
|
|
221
|
+
else
|
|
222
|
+
this.notificationState = 'connected';
|
|
223
|
+
this.connection.onReconnected?.(connectionId);
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
ensureChatConnected() {
|
|
227
|
+
if (this.chatConnection?.state !== signalr_1.HubConnectionState.Connected) {
|
|
228
|
+
throw new Error('Chat hub is not connected. Call connectChat() first.');
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
ensureNotificationConnected() {
|
|
232
|
+
if (this.notificationConnection?.state !== signalr_1.HubConnectionState.Connected) {
|
|
233
|
+
throw new Error('Notification hub is not connected. Call connectNotifications() first.');
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
exports.OsiseRealTimeClient = OsiseRealTimeClient;
|
|
238
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/realtime/client.ts"],"names":[],"mappings":"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"}
|
package/dist/cjs/types/index.js
CHANGED
|
@@ -50,4 +50,6 @@ __exportStar(require("./communication"), exports);
|
|
|
50
50
|
__exportStar(require("./location"), exports);
|
|
51
51
|
// Paystack integration types
|
|
52
52
|
__exportStar(require("./paystack"), exports);
|
|
53
|
+
// Real-time types (SignalR)
|
|
54
|
+
__exportStar(require("./realtime"), exports);
|
|
53
55
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;AAEH,QAAQ;AACR,0CAAwB;AAExB,eAAe;AACf,2CAAyB;AAEzB,aAAa;AACb,yCAAuB;AAEvB,iBAAiB;AACjB,6CAA2B;AAE3B,gBAAgB;AAChB,4CAA0B;AAE1B,cAAc;AACd,0CAAwB;AAkFxB,gBAAgB;AAChB,4CAA0B;AAE1B,0BAA0B;AAC1B,kDAAgC;AAEhC,qBAAqB;AACrB,iDAA+B;AAE/B,sBAAsB;AACtB,kDAAgC;AAEhC,iCAAiC;AACjC,wCAAsB;AAEtB,wBAAwB;AACxB,uCAAqB;AAErB,gBAAgB;AAChB,4CAA0B;AAE1B,iEAAiE;AACjE,kDAAgC;AAEhC,iBAAiB;AACjB,6CAA2B;AAE3B,6BAA6B;AAC7B,6CAA2B"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;AAEH,QAAQ;AACR,0CAAwB;AAExB,eAAe;AACf,2CAAyB;AAEzB,aAAa;AACb,yCAAuB;AAEvB,iBAAiB;AACjB,6CAA2B;AAE3B,gBAAgB;AAChB,4CAA0B;AAE1B,cAAc;AACd,0CAAwB;AAkFxB,gBAAgB;AAChB,4CAA0B;AAE1B,0BAA0B;AAC1B,kDAAgC;AAEhC,qBAAqB;AACrB,iDAA+B;AAE/B,sBAAsB;AACtB,kDAAgC;AAEhC,iCAAiC;AACjC,wCAAsB;AAEtB,wBAAwB;AACxB,uCAAqB;AAErB,gBAAgB;AAChB,4CAA0B;AAE1B,iEAAiE;AACjE,kDAAgC;AAEhC,iBAAiB;AACjB,6CAA2B;AAE3B,6BAA6B;AAC7B,6CAA2B;AAE3B,4BAA4B;AAC5B,6CAA2B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime.js","sourceRoot":"","sources":["../../../src/types/realtime.ts"],"names":[],"mappings":";AAAA;;;GAGG"}
|
package/dist/esm/hooks/index.js
CHANGED
|
@@ -2034,4 +2034,282 @@ export function useRateArtisanTicket() {
|
|
|
2034
2034
|
const client = useOsiseClient();
|
|
2035
2035
|
return useMutation(({ ticketId, data }) => client.artisanSupport.rateTicket(ticketId, data));
|
|
2036
2036
|
}
|
|
2037
|
+
/**
|
|
2038
|
+
* Hook to access the real-time client from the Osise provider.
|
|
2039
|
+
*
|
|
2040
|
+
* @example
|
|
2041
|
+
* ```tsx
|
|
2042
|
+
* const realtime = useRealTime();
|
|
2043
|
+
* await realtime.connect();
|
|
2044
|
+
* ```
|
|
2045
|
+
*/
|
|
2046
|
+
export function useRealTime() {
|
|
2047
|
+
const client = useOsiseClient();
|
|
2048
|
+
return client.realtime;
|
|
2049
|
+
}
|
|
2050
|
+
/**
|
|
2051
|
+
* Hook that manages the SignalR connection lifecycle.
|
|
2052
|
+
* Connects when the user is authenticated and disconnects on logout or unmount.
|
|
2053
|
+
*
|
|
2054
|
+
* @example
|
|
2055
|
+
* ```tsx
|
|
2056
|
+
* function App() {
|
|
2057
|
+
* const { chatState, notificationState } = useRealTimeConnection();
|
|
2058
|
+
* return <div>Chat: {chatState}, Notifications: {notificationState}</div>;
|
|
2059
|
+
* }
|
|
2060
|
+
* ```
|
|
2061
|
+
*/
|
|
2062
|
+
export function useRealTimeConnection() {
|
|
2063
|
+
const client = useOsiseClient();
|
|
2064
|
+
const isAuthenticated = useIsAuthenticated();
|
|
2065
|
+
const realtime = client.realtime;
|
|
2066
|
+
const [chatState, setChatState] = useState('disconnected');
|
|
2067
|
+
const [notificationState, setNotificationState] = useState('disconnected');
|
|
2068
|
+
useEffect(() => {
|
|
2069
|
+
if (!isAuthenticated)
|
|
2070
|
+
return;
|
|
2071
|
+
realtime.connection.onDisconnected = () => {
|
|
2072
|
+
setChatState(realtime.chatState);
|
|
2073
|
+
setNotificationState(realtime.notificationState);
|
|
2074
|
+
};
|
|
2075
|
+
realtime.connection.onReconnecting = () => {
|
|
2076
|
+
setChatState(realtime.chatState);
|
|
2077
|
+
setNotificationState(realtime.notificationState);
|
|
2078
|
+
};
|
|
2079
|
+
realtime.connection.onReconnected = () => {
|
|
2080
|
+
setChatState(realtime.chatState);
|
|
2081
|
+
setNotificationState(realtime.notificationState);
|
|
2082
|
+
};
|
|
2083
|
+
realtime.connect().then(() => {
|
|
2084
|
+
setChatState(realtime.chatState);
|
|
2085
|
+
setNotificationState(realtime.notificationState);
|
|
2086
|
+
}).catch(() => {
|
|
2087
|
+
setChatState('disconnected');
|
|
2088
|
+
setNotificationState('disconnected');
|
|
2089
|
+
});
|
|
2090
|
+
return () => {
|
|
2091
|
+
realtime.disconnect();
|
|
2092
|
+
setChatState('disconnected');
|
|
2093
|
+
setNotificationState('disconnected');
|
|
2094
|
+
};
|
|
2095
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2096
|
+
}, [isAuthenticated]);
|
|
2097
|
+
return { chatState, notificationState, realtime };
|
|
2098
|
+
}
|
|
2099
|
+
/**
|
|
2100
|
+
* Hook that subscribes to incoming chat messages.
|
|
2101
|
+
*
|
|
2102
|
+
* @example
|
|
2103
|
+
* ```tsx
|
|
2104
|
+
* const messages = useChatMessages();
|
|
2105
|
+
* // messages is an array of received messages since mount
|
|
2106
|
+
* ```
|
|
2107
|
+
*/
|
|
2108
|
+
export function useChatMessages() {
|
|
2109
|
+
const realtime = useRealTime();
|
|
2110
|
+
const [messages, setMessages] = useState([]);
|
|
2111
|
+
useEffect(() => {
|
|
2112
|
+
const prev = realtime.chat.onMessage;
|
|
2113
|
+
realtime.chat.onMessage = (msg) => {
|
|
2114
|
+
setMessages((m) => [...m, msg]);
|
|
2115
|
+
prev?.(msg);
|
|
2116
|
+
};
|
|
2117
|
+
return () => {
|
|
2118
|
+
realtime.chat.onMessage = prev;
|
|
2119
|
+
};
|
|
2120
|
+
}, [realtime]);
|
|
2121
|
+
const clear = useCallback(() => setMessages([]), []);
|
|
2122
|
+
return { messages, clear };
|
|
2123
|
+
}
|
|
2124
|
+
/**
|
|
2125
|
+
* Hook that tracks typing indicators for a specific conversation.
|
|
2126
|
+
*
|
|
2127
|
+
* @param conversationId - The conversation to monitor
|
|
2128
|
+
*
|
|
2129
|
+
* @example
|
|
2130
|
+
* ```tsx
|
|
2131
|
+
* const { typingUsers, sendTyping } = useTypingIndicator(conversationId, recipientId);
|
|
2132
|
+
* // typingUsers is a Set<string> of user IDs currently typing
|
|
2133
|
+
* ```
|
|
2134
|
+
*/
|
|
2135
|
+
export function useTypingIndicator(conversationId, recipientUserId) {
|
|
2136
|
+
const realtime = useRealTime();
|
|
2137
|
+
const [typingUsers, setTypingUsers] = useState(new Set());
|
|
2138
|
+
const timersRef = useRef(new Map());
|
|
2139
|
+
useEffect(() => {
|
|
2140
|
+
const prev = realtime.chat.onTypingIndicator;
|
|
2141
|
+
realtime.chat.onTypingIndicator = (event) => {
|
|
2142
|
+
if (event.conversationId === conversationId) {
|
|
2143
|
+
setTypingUsers((current) => {
|
|
2144
|
+
const next = new Set(current);
|
|
2145
|
+
if (event.isTyping) {
|
|
2146
|
+
next.add(event.userId);
|
|
2147
|
+
// Auto-clear after 5s if no stop event
|
|
2148
|
+
const existing = timersRef.current.get(event.userId);
|
|
2149
|
+
if (existing)
|
|
2150
|
+
clearTimeout(existing);
|
|
2151
|
+
timersRef.current.set(event.userId, setTimeout(() => {
|
|
2152
|
+
setTypingUsers((c) => {
|
|
2153
|
+
const n = new Set(c);
|
|
2154
|
+
n.delete(event.userId);
|
|
2155
|
+
return n;
|
|
2156
|
+
});
|
|
2157
|
+
timersRef.current.delete(event.userId);
|
|
2158
|
+
}, 5000));
|
|
2159
|
+
}
|
|
2160
|
+
else {
|
|
2161
|
+
next.delete(event.userId);
|
|
2162
|
+
const existing = timersRef.current.get(event.userId);
|
|
2163
|
+
if (existing) {
|
|
2164
|
+
clearTimeout(existing);
|
|
2165
|
+
timersRef.current.delete(event.userId);
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
return next;
|
|
2169
|
+
});
|
|
2170
|
+
}
|
|
2171
|
+
prev?.(event);
|
|
2172
|
+
};
|
|
2173
|
+
return () => {
|
|
2174
|
+
realtime.chat.onTypingIndicator = prev;
|
|
2175
|
+
timersRef.current.forEach((timer) => clearTimeout(timer));
|
|
2176
|
+
timersRef.current.clear();
|
|
2177
|
+
};
|
|
2178
|
+
}, [realtime, conversationId]);
|
|
2179
|
+
const sendTyping = useCallback(async (isTyping) => {
|
|
2180
|
+
if (recipientUserId) {
|
|
2181
|
+
await realtime.sendTypingIndicator(conversationId, recipientUserId, isTyping);
|
|
2182
|
+
}
|
|
2183
|
+
}, [realtime, conversationId, recipientUserId]);
|
|
2184
|
+
return { typingUsers, sendTyping };
|
|
2185
|
+
}
|
|
2186
|
+
/**
|
|
2187
|
+
* Hook that tracks online/offline presence for specific user IDs.
|
|
2188
|
+
*
|
|
2189
|
+
* @param userIds - User IDs to track presence for
|
|
2190
|
+
*
|
|
2191
|
+
* @example
|
|
2192
|
+
* ```tsx
|
|
2193
|
+
* const presence = usePresence([artisanId]);
|
|
2194
|
+
* // presence.get(artisanId) → { isOnline: true, lastSeenAt: '...' }
|
|
2195
|
+
* ```
|
|
2196
|
+
*/
|
|
2197
|
+
export function usePresence(userIds) {
|
|
2198
|
+
const realtime = useRealTime();
|
|
2199
|
+
const [presence, setPresence] = useState(new Map());
|
|
2200
|
+
useEffect(() => {
|
|
2201
|
+
const prev = realtime.chat.onUserPresenceChanged;
|
|
2202
|
+
realtime.chat.onUserPresenceChanged = (event) => {
|
|
2203
|
+
if (userIds.includes(event.userId)) {
|
|
2204
|
+
setPresence((current) => {
|
|
2205
|
+
const next = new Map(current);
|
|
2206
|
+
next.set(event.userId, { isOnline: event.isOnline, lastSeenAt: event.lastSeenAt });
|
|
2207
|
+
return next;
|
|
2208
|
+
});
|
|
2209
|
+
}
|
|
2210
|
+
prev?.(event);
|
|
2211
|
+
};
|
|
2212
|
+
return () => {
|
|
2213
|
+
realtime.chat.onUserPresenceChanged = prev;
|
|
2214
|
+
};
|
|
2215
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2216
|
+
}, [realtime, ...userIds]);
|
|
2217
|
+
return presence;
|
|
2218
|
+
}
|
|
2219
|
+
/**
|
|
2220
|
+
* Hook that subscribes to incoming notifications in real-time.
|
|
2221
|
+
*
|
|
2222
|
+
* @example
|
|
2223
|
+
* ```tsx
|
|
2224
|
+
* const { notifications, unreadCount } = useRealtimeNotifications();
|
|
2225
|
+
* ```
|
|
2226
|
+
*/
|
|
2227
|
+
export function useRealtimeNotifications() {
|
|
2228
|
+
const realtime = useRealTime();
|
|
2229
|
+
const [notifications, setNotifications] = useState([]);
|
|
2230
|
+
const [unreadCount, setUnreadCount] = useState(null);
|
|
2231
|
+
useEffect(() => {
|
|
2232
|
+
const prevNotif = realtime.notifications.onNotification;
|
|
2233
|
+
const prevCount = realtime.notifications.onNotificationCountUpdated;
|
|
2234
|
+
realtime.notifications.onNotification = (notification) => {
|
|
2235
|
+
setNotifications((n) => [notification, ...n]);
|
|
2236
|
+
prevNotif?.(notification);
|
|
2237
|
+
};
|
|
2238
|
+
realtime.notifications.onNotificationCountUpdated = (event) => {
|
|
2239
|
+
setUnreadCount(event.unreadCount);
|
|
2240
|
+
prevCount?.(event);
|
|
2241
|
+
};
|
|
2242
|
+
return () => {
|
|
2243
|
+
realtime.notifications.onNotification = prevNotif;
|
|
2244
|
+
realtime.notifications.onNotificationCountUpdated = prevCount;
|
|
2245
|
+
};
|
|
2246
|
+
}, [realtime]);
|
|
2247
|
+
const clear = useCallback(() => setNotifications([]), []);
|
|
2248
|
+
return { notifications, unreadCount, clear };
|
|
2249
|
+
}
|
|
2250
|
+
/**
|
|
2251
|
+
* Hook that manages joining/leaving a conversation and provides
|
|
2252
|
+
* delivery and read receipt events for that conversation.
|
|
2253
|
+
*
|
|
2254
|
+
* @param conversationId - The conversation to join
|
|
2255
|
+
*
|
|
2256
|
+
* @example
|
|
2257
|
+
* ```tsx
|
|
2258
|
+
* const { deliveredIds, readMessageIds } = useConversation(conversationId);
|
|
2259
|
+
* ```
|
|
2260
|
+
*/
|
|
2261
|
+
export function useConversation(conversationId) {
|
|
2262
|
+
const realtime = useRealTime();
|
|
2263
|
+
const [deliveredIds, setDeliveredIds] = useState(new Set());
|
|
2264
|
+
const [readMessageIds, setReadMessageIds] = useState(new Set());
|
|
2265
|
+
const [editedMessages, setEditedMessages] = useState(new Map());
|
|
2266
|
+
const [deletedIds, setDeletedIds] = useState(new Set());
|
|
2267
|
+
useEffect(() => {
|
|
2268
|
+
realtime.joinConversation(conversationId).catch(() => { });
|
|
2269
|
+
const prevDelivered = realtime.chat.onMessageDelivered;
|
|
2270
|
+
const prevRead = realtime.chat.onMessagesRead;
|
|
2271
|
+
const prevEdited = realtime.chat.onMessageEdited;
|
|
2272
|
+
const prevDeleted = realtime.chat.onMessageDeleted;
|
|
2273
|
+
realtime.chat.onMessageDelivered = (event) => {
|
|
2274
|
+
if (event.conversationId === conversationId) {
|
|
2275
|
+
setDeliveredIds((s) => new Set(s).add(event.messageId));
|
|
2276
|
+
}
|
|
2277
|
+
prevDelivered?.(event);
|
|
2278
|
+
};
|
|
2279
|
+
realtime.chat.onMessagesRead = (event) => {
|
|
2280
|
+
if (event.conversationId === conversationId) {
|
|
2281
|
+
setReadMessageIds((s) => {
|
|
2282
|
+
const next = new Set(s);
|
|
2283
|
+
event.messageIds.forEach((id) => next.add(id));
|
|
2284
|
+
return next;
|
|
2285
|
+
});
|
|
2286
|
+
}
|
|
2287
|
+
prevRead?.(event);
|
|
2288
|
+
};
|
|
2289
|
+
realtime.chat.onMessageEdited = (event) => {
|
|
2290
|
+
if (event.conversationId === conversationId) {
|
|
2291
|
+
setEditedMessages((m) => {
|
|
2292
|
+
const next = new Map(m);
|
|
2293
|
+
next.set(event.messageId, { content: event.newContent, editedAt: event.editedAt });
|
|
2294
|
+
return next;
|
|
2295
|
+
});
|
|
2296
|
+
}
|
|
2297
|
+
prevEdited?.(event);
|
|
2298
|
+
};
|
|
2299
|
+
realtime.chat.onMessageDeleted = (event) => {
|
|
2300
|
+
if (event.conversationId === conversationId) {
|
|
2301
|
+
setDeletedIds((s) => new Set(s).add(event.messageId));
|
|
2302
|
+
}
|
|
2303
|
+
prevDeleted?.(event);
|
|
2304
|
+
};
|
|
2305
|
+
return () => {
|
|
2306
|
+
realtime.leaveConversation(conversationId).catch(() => { });
|
|
2307
|
+
realtime.chat.onMessageDelivered = prevDelivered;
|
|
2308
|
+
realtime.chat.onMessagesRead = prevRead;
|
|
2309
|
+
realtime.chat.onMessageEdited = prevEdited;
|
|
2310
|
+
realtime.chat.onMessageDeleted = prevDeleted;
|
|
2311
|
+
};
|
|
2312
|
+
}, [realtime, conversationId]);
|
|
2313
|
+
return { deliveredIds, readMessageIds, editedMessages, deletedIds };
|
|
2314
|
+
}
|
|
2037
2315
|
//# sourceMappingURL=index.js.map
|