@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/dist/index.d.mts +59 -6
- package/dist/index.d.ts +59 -6
- package/dist/index.js +68 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +66 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/chat/hooks.ts +44 -0
- package/src/chat/index.ts +3 -0
- package/src/chat/provider.tsx +33 -6
- package/src/chat/socket.ts +3 -0
- package/src/chat/types.ts +10 -0
- package/src/client.ts +10 -0
- package/src/hooks/usePushNotifications.ts +22 -3
- package/src/index.ts +3 -0
package/package.json
CHANGED
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,
|
package/src/chat/provider.tsx
CHANGED
|
@@ -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')
|
|
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
|
-
|
|
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>;
|
package/src/chat/socket.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
-
/**
|
|
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 (
|
|
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,
|