@dubsdotapp/expo 0.5.21 → 0.5.23

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dubsdotapp/expo",
3
- "version": "0.5.21",
3
+ "version": "0.5.23",
4
4
  "description": "React Native SDK for the Dubs betting platform",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
package/src/chat/hooks.ts CHANGED
@@ -9,6 +9,7 @@ import type {
9
9
  DirectMessage,
10
10
  FriendUser,
11
11
  FriendRequest,
12
+ SentFriendRequest,
12
13
  SendMessageParams,
13
14
  } from './types';
14
15
 
@@ -225,6 +226,49 @@ export function useFriendRequests(): {
225
226
  return { requests: pendingRequests, loading, refetch };
226
227
  }
227
228
 
229
+ /** Get pending friend requests this user has SENT (still awaiting response). */
230
+ export function useSentFriendRequests(): {
231
+ requests: SentFriendRequest[];
232
+ loading: boolean;
233
+ refetch: () => Promise<void>;
234
+ } {
235
+ const { sentFriendRequests, refreshSentFriendRequests } = useChatContext();
236
+ const [loading, setLoading] = useState(false);
237
+
238
+ const refetch = useCallback(async () => {
239
+ setLoading(true);
240
+ await refreshSentFriendRequests();
241
+ setLoading(false);
242
+ }, [refreshSentFriendRequests]);
243
+
244
+ return { requests: sentFriendRequests, loading, refetch };
245
+ }
246
+
247
+ /** Cancel a pending friend request the current user previously sent. */
248
+ export function useCancelFriendRequest(): {
249
+ cancel: (requestId: number) => Promise<void>;
250
+ loading: boolean;
251
+ } {
252
+ const { client } = useDubs();
253
+ const { refreshSentFriendRequests } = useChatContext();
254
+ const [loading, setLoading] = useState(false);
255
+
256
+ const cancel = useCallback(
257
+ async (requestId: number) => {
258
+ setLoading(true);
259
+ try {
260
+ await client.cancelFriendRequest(requestId);
261
+ await refreshSentFriendRequests();
262
+ } finally {
263
+ setLoading(false);
264
+ }
265
+ },
266
+ [client, refreshSentFriendRequests],
267
+ );
268
+
269
+ return { cancel, loading };
270
+ }
271
+
228
272
  /** Search for users by username */
229
273
  export function useSearchUsers(): {
230
274
  results: FriendUser[];
package/src/chat/index.ts CHANGED
@@ -17,9 +17,11 @@ export {
17
17
  useDirectMessages,
18
18
  useFriends,
19
19
  useFriendRequests,
20
+ useSentFriendRequests,
20
21
  useSearchUsers,
21
22
  useSendFriendRequest,
22
23
  useRespondToFriendRequest,
24
+ useCancelFriendRequest,
23
25
  } from './hooks';
24
26
 
25
27
  // Types
@@ -31,6 +33,7 @@ export type {
31
33
  Conversation,
32
34
  FriendUser,
33
35
  FriendRequest,
36
+ SentFriendRequest,
34
37
  OnlineUser,
35
38
  TypingEvent,
36
39
  ChatNotification,
@@ -9,6 +9,7 @@ import type {
9
9
  Conversation,
10
10
  FriendUser,
11
11
  FriendRequest,
12
+ SentFriendRequest,
12
13
  DirectMessage,
13
14
  ChatNotification,
14
15
  } from './types';
@@ -30,16 +31,20 @@ export interface ChatContextValue {
30
31
  conversations: Conversation[];
31
32
  /** Friends list */
32
33
  friends: FriendUser[];
33
- /** Pending friend requests */
34
+ /** Pending friend requests (received) */
34
35
  pendingRequests: FriendRequest[];
36
+ /** Pending friend requests this user has sent (still awaiting response) */
37
+ sentFriendRequests: SentFriendRequest[];
35
38
  /** Reload messages from REST */
36
39
  refreshMessages: () => Promise<void>;
37
40
  /** Reload conversations from REST */
38
41
  refreshConversations: () => Promise<void>;
39
42
  /** Reload friends from REST */
40
43
  refreshFriends: () => Promise<void>;
41
- /** Reload pending friend requests from REST */
44
+ /** Reload pending friend requests (received) from REST */
42
45
  refreshPendingRequests: () => Promise<void>;
46
+ /** Reload pending friend requests (sent) from REST */
47
+ refreshSentFriendRequests: () => Promise<void>;
43
48
  }
44
49
 
45
50
  const ChatContext = createContext<ChatContextValue | null>(null);
@@ -66,6 +71,7 @@ export function ChatProvider({ children, autoConnect = true }: ChatProviderProps
66
71
  const [conversations, setConversations] = useState<Conversation[]>([]);
67
72
  const [friends, setFriends] = useState<FriendUser[]>([]);
68
73
  const [pendingRequests, setPendingRequests] = useState<FriendRequest[]>([]);
74
+ const [sentFriendRequests, setSentFriendRequests] = useState<SentFriendRequest[]>([]);
69
75
 
70
76
  // ── REST loaders ──
71
77
 
@@ -106,6 +112,15 @@ export function ChatProvider({ children, autoConnect = true }: ChatProviderProps
106
112
  }
107
113
  }, [client]);
108
114
 
115
+ const refreshSentFriendRequests = useCallback(async () => {
116
+ try {
117
+ const res = await client.getSentFriendRequests();
118
+ setSentFriendRequests(res.requests);
119
+ } catch (_) {
120
+ // Server may not yet expose /social/friend-requests/sent — silent fail
121
+ }
122
+ }, [client]);
123
+
109
124
  // ── Socket setup ──
110
125
 
111
126
  useEffect(() => {
@@ -140,9 +155,18 @@ export function ChatProvider({ children, autoConnect = true }: ChatProviderProps
140
155
  setUnreadCount((prev) => prev + 1);
141
156
  // Refresh relevant lists on social notifications
142
157
  if (n.type === 'friend_request') refreshPendingRequests();
143
- if (n.type === 'friend_request_accepted') refreshFriends();
158
+ if (n.type === 'friend_request_accepted') {
159
+ refreshFriends();
160
+ refreshSentFriendRequests();
161
+ }
162
+ if (n.type === 'friend_request_declined') refreshSentFriendRequests();
163
+ },
164
+ onFriendRequestAccepted: () => {
165
+ refreshFriends();
166
+ refreshSentFriendRequests();
144
167
  },
145
- onFriendRequestAccepted: () => refreshFriends(),
168
+ onFriendRequestDeclined: () => refreshSentFriendRequests(),
169
+ onFriendRequestCancelled: () => refreshPendingRequests(),
146
170
  onFriendRemoved: () => refreshFriends(),
147
171
  });
148
172
 
@@ -152,11 +176,12 @@ export function ChatProvider({ children, autoConnect = true }: ChatProviderProps
152
176
  refreshMessages();
153
177
  refreshFriends().catch(() => {});
154
178
  refreshPendingRequests().catch(() => {});
179
+ refreshSentFriendRequests().catch(() => {});
155
180
 
156
181
  return () => {
157
182
  chatSocket.disconnect();
158
183
  };
159
- }, [client, autoConnect, refreshMessages, refreshConversations, refreshFriends, refreshPendingRequests]);
184
+ }, [client, autoConnect, refreshMessages, refreshConversations, refreshFriends, refreshPendingRequests, refreshSentFriendRequests]);
160
185
 
161
186
  // ── Reconnect on app foreground ──
162
187
 
@@ -192,12 +217,14 @@ export function ChatProvider({ children, autoConnect = true }: ChatProviderProps
192
217
  conversations,
193
218
  friends,
194
219
  pendingRequests,
220
+ sentFriendRequests,
195
221
  refreshMessages,
196
222
  refreshConversations,
197
223
  refreshFriends,
198
224
  refreshPendingRequests,
225
+ refreshSentFriendRequests,
199
226
  }),
200
- [status, messages, onlineUsers, onlineCount, unreadCount, conversations, friends, pendingRequests, refreshMessages, refreshConversations, refreshFriends, refreshPendingRequests],
227
+ [status, messages, onlineUsers, onlineCount, unreadCount, conversations, friends, pendingRequests, sentFriendRequests, refreshMessages, refreshConversations, refreshFriends, refreshPendingRequests, refreshSentFriendRequests],
201
228
  );
202
229
 
203
230
  return <ChatContext.Provider value={value}>{children}</ChatContext.Provider>;
@@ -36,6 +36,8 @@ export interface ChatSocketListeners {
36
36
  // Social
37
37
  onFriendRequestAccepted?: (data: { requestId: number; acceptedBy: number; acceptedByUsername: string }) => void;
38
38
  onFriendRequestDeclined?: (data: { requestId: number; declinedBy: number }) => void;
39
+ /** Recipient-side: the original sender cancelled a pending friend request */
40
+ onFriendRequestCancelled?: (data: { requestId: number; cancelledBy: number }) => void;
39
41
  onFriendRemoved?: (data: { removedBy: number }) => void;
40
42
  // Errors
41
43
  onError?: (error: { message: string }) => void;
@@ -108,6 +110,7 @@ export class ChatSocket {
108
110
  // Social events
109
111
  this.socket.on('friend_request_accepted', (data: any) => this.listeners.onFriendRequestAccepted?.(data));
110
112
  this.socket.on('friend_request_declined', (data: any) => this.listeners.onFriendRequestDeclined?.(data));
113
+ this.socket.on('friend_request_cancelled', (data: any) => this.listeners.onFriendRequestCancelled?.(data));
111
114
  this.socket.on('friend_removed', (data: any) => this.listeners.onFriendRemoved?.(data));
112
115
 
113
116
  // Error
package/src/chat/types.ts CHANGED
@@ -95,6 +95,16 @@ export interface FriendRequest {
95
95
  createdAt: string;
96
96
  }
97
97
 
98
+ /** A friend request the current user has SENT (still pending). */
99
+ export interface SentFriendRequest {
100
+ id: number;
101
+ toUserId: number;
102
+ toUsername: string;
103
+ toAvatar: string | null;
104
+ toWallet: string;
105
+ createdAt: string;
106
+ }
107
+
98
108
  // ── Online / Real-time Types ──
99
109
 
100
110
  export interface OnlineUser {
package/src/client.ts CHANGED
@@ -730,6 +730,16 @@ export class DubsClient {
730
730
  return this.request('GET', '/social/friend-requests');
731
731
  }
732
732
 
733
+ /** Get pending friend requests this user has sent (still awaiting response) */
734
+ async getSentFriendRequests(): Promise<{ requests: any[] }> {
735
+ return this.request('GET', '/social/friend-requests/sent');
736
+ }
737
+
738
+ /** Cancel a pending friend request the user previously sent */
739
+ async cancelFriendRequest(requestId: number): Promise<void> {
740
+ await this.request('DELETE', `/social/friend-request/${requestId}`);
741
+ }
742
+
733
743
  /** Accept a friend request */
734
744
  async acceptFriendRequest(requestId: number): Promise<void> {
735
745
  await this.request('POST', `/social/request/${requestId}/accept`);
@@ -3,11 +3,29 @@ import { Platform } from 'react-native';
3
3
  import { useDubs } from '../provider';
4
4
 
5
5
  export interface PushNotificationStatus {
6
- /** Whether push notifications are enabled in the SDK configuration */
6
+ /**
7
+ * Whether push is currently delivering to this user — the right signal for
8
+ * UI bindings like a "Notifications: Enabled/Disabled" toggle.
9
+ *
10
+ * Equivalent to `enabled && !!pushToken`. Goes false after `unregister()`
11
+ * even though OS permission is still granted. Goes true after `register()`
12
+ * succeeds.
13
+ *
14
+ * Use this — NOT `hasPermission` — when binding switches/toggles.
15
+ */
16
+ isActive: boolean;
17
+ /** Whether push notifications are enabled in the SDK configuration (the `pushEnabled` prop on DubsProvider). */
7
18
  enabled: boolean;
8
- /** Whether notification permission has been granted */
19
+ /**
20
+ * Whether OS-level notification permission has been granted on this device.
21
+ *
22
+ * Note: this stays true after `unregister()` because the OS doesn't revoke
23
+ * permission when a server token is dropped. Useful for permission-flow
24
+ * prompts (e.g. "Re-enable in Settings" hint), but NOT for "is push on" UI —
25
+ * use `isActive` for that.
26
+ */
9
27
  hasPermission: boolean;
10
- /** The push token (native FCM/APNs), if registered */
28
+ /** The native device push token (FCM/APNs), if currently registered with the server. */
11
29
  pushToken: string | null;
12
30
  /**
13
31
  * @deprecated Use pushToken instead. Kept for backwards compatibility.
@@ -188,6 +206,7 @@ export function usePushNotifications(): PushNotificationStatus {
188
206
  }, []); // eslint-disable-line react-hooks/exhaustive-deps
189
207
 
190
208
  return {
209
+ isActive: pushEnabled && !!pushToken,
191
210
  enabled: pushEnabled,
192
211
  hasPermission,
193
212
  pushToken,
package/src/index.ts CHANGED
@@ -186,9 +186,11 @@ export {
186
186
  useDirectMessages,
187
187
  useFriends,
188
188
  useFriendRequests,
189
+ useSentFriendRequests,
189
190
  useSearchUsers,
190
191
  useSendFriendRequest,
191
192
  useRespondToFriendRequest,
193
+ useCancelFriendRequest,
192
194
  } from './chat';
193
195
  export type {
194
196
  ChatProviderProps,
@@ -202,6 +204,7 @@ export type {
202
204
  Conversation,
203
205
  FriendUser,
204
206
  FriendRequest,
207
+ SentFriendRequest,
205
208
  OnlineUser,
206
209
  TypingEvent,
207
210
  ChatNotification,