@dubsdotapp/expo 0.5.24 → 0.5.26
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 +55 -1
- package/dist/index.d.ts +55 -1
- package/dist/index.js +101 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +99 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/chat/hooks.ts +62 -0
- package/src/chat/index.ts +1 -0
- package/src/chat/provider.tsx +43 -5
- package/src/client.ts +28 -0
- package/src/index.ts +3 -0
- package/src/types.ts +20 -0
package/package.json
CHANGED
package/src/chat/hooks.ts
CHANGED
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
SentFriendRequest,
|
|
13
13
|
SendMessageParams,
|
|
14
14
|
} from './types';
|
|
15
|
+
import type { WhatsNewPost } from '../types';
|
|
15
16
|
|
|
16
17
|
// ── Connection ──
|
|
17
18
|
|
|
@@ -362,3 +363,64 @@ export function useRespondToFriendRequest(): {
|
|
|
362
363
|
|
|
363
364
|
return { accept, reject, loading };
|
|
364
365
|
}
|
|
366
|
+
|
|
367
|
+
// ── What's New ────────────────────────────────────────────────────────────
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Read-only subscription to the calling app's What's New posts.
|
|
371
|
+
*
|
|
372
|
+
* Auto-refreshes via the chat socket when a new `whats_new` notification
|
|
373
|
+
* arrives, so the unread badge / list update without manual refetch.
|
|
374
|
+
*/
|
|
375
|
+
export function useWhatsNew(): {
|
|
376
|
+
posts: WhatsNewPost[];
|
|
377
|
+
unreadCount: number;
|
|
378
|
+
loading: boolean;
|
|
379
|
+
refetch: () => Promise<void>;
|
|
380
|
+
markRead: (postIds: number[]) => Promise<void>;
|
|
381
|
+
markAllRead: () => Promise<void>;
|
|
382
|
+
} {
|
|
383
|
+
const { client } = useDubs();
|
|
384
|
+
const { whatsNewPosts, whatsNewUnreadCount, refreshWhatsNew } = useChatContext();
|
|
385
|
+
const [loading, setLoading] = useState(false);
|
|
386
|
+
|
|
387
|
+
const refetch = useCallback(async () => {
|
|
388
|
+
setLoading(true);
|
|
389
|
+
try {
|
|
390
|
+
await refreshWhatsNew();
|
|
391
|
+
} finally {
|
|
392
|
+
setLoading(false);
|
|
393
|
+
}
|
|
394
|
+
}, [refreshWhatsNew]);
|
|
395
|
+
|
|
396
|
+
const markRead = useCallback(
|
|
397
|
+
async (postIds: number[]) => {
|
|
398
|
+
if (postIds.length === 0) return;
|
|
399
|
+
try {
|
|
400
|
+
await client.markWhatsNewRead(postIds);
|
|
401
|
+
await refreshWhatsNew();
|
|
402
|
+
} catch (err) {
|
|
403
|
+
console.error('[Dubs:useWhatsNew] markRead error:', err);
|
|
404
|
+
}
|
|
405
|
+
},
|
|
406
|
+
[client, refreshWhatsNew],
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
const markAllRead = useCallback(async () => {
|
|
410
|
+
try {
|
|
411
|
+
await client.markAllWhatsNewRead();
|
|
412
|
+
await refreshWhatsNew();
|
|
413
|
+
} catch (err) {
|
|
414
|
+
console.error('[Dubs:useWhatsNew] markAllRead error:', err);
|
|
415
|
+
}
|
|
416
|
+
}, [client, refreshWhatsNew]);
|
|
417
|
+
|
|
418
|
+
return {
|
|
419
|
+
posts: whatsNewPosts,
|
|
420
|
+
unreadCount: whatsNewUnreadCount,
|
|
421
|
+
loading,
|
|
422
|
+
refetch,
|
|
423
|
+
markRead,
|
|
424
|
+
markAllRead,
|
|
425
|
+
};
|
|
426
|
+
}
|
package/src/chat/index.ts
CHANGED
package/src/chat/provider.tsx
CHANGED
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
DirectMessage,
|
|
14
14
|
ChatNotification,
|
|
15
15
|
} from './types';
|
|
16
|
+
import type { WhatsNewPost } from '../types';
|
|
16
17
|
|
|
17
18
|
export interface ChatContextValue {
|
|
18
19
|
/** The underlying socket manager */
|
|
@@ -45,6 +46,12 @@ export interface ChatContextValue {
|
|
|
45
46
|
refreshPendingRequests: () => Promise<void>;
|
|
46
47
|
/** Reload pending friend requests (sent) from REST */
|
|
47
48
|
refreshSentFriendRequests: () => Promise<void>;
|
|
49
|
+
/** What's New posts for this app */
|
|
50
|
+
whatsNewPosts: WhatsNewPost[];
|
|
51
|
+
/** Number of unread What's New posts */
|
|
52
|
+
whatsNewUnreadCount: number;
|
|
53
|
+
/** Reload What's New posts + unread count from REST */
|
|
54
|
+
refreshWhatsNew: () => Promise<void>;
|
|
48
55
|
}
|
|
49
56
|
|
|
50
57
|
const ChatContext = createContext<ChatContextValue | null>(null);
|
|
@@ -72,6 +79,8 @@ export function ChatProvider({ children, autoConnect = true }: ChatProviderProps
|
|
|
72
79
|
const [friends, setFriends] = useState<FriendUser[]>([]);
|
|
73
80
|
const [pendingRequests, setPendingRequests] = useState<FriendRequest[]>([]);
|
|
74
81
|
const [sentFriendRequests, setSentFriendRequests] = useState<SentFriendRequest[]>([]);
|
|
82
|
+
const [whatsNewPosts, setWhatsNewPosts] = useState<WhatsNewPost[]>([]);
|
|
83
|
+
const [whatsNewUnreadCount, setWhatsNewUnreadCount] = useState(0);
|
|
75
84
|
|
|
76
85
|
// ── REST loaders ──
|
|
77
86
|
|
|
@@ -121,6 +130,18 @@ export function ChatProvider({ children, autoConnect = true }: ChatProviderProps
|
|
|
121
130
|
}
|
|
122
131
|
}, [client]);
|
|
123
132
|
|
|
133
|
+
const refreshWhatsNew = useCallback(async () => {
|
|
134
|
+
try {
|
|
135
|
+
const res = await client.getWhatsNewPosts();
|
|
136
|
+
setWhatsNewPosts(res.posts);
|
|
137
|
+
// Count unread from the same payload (avoids a second roundtrip)
|
|
138
|
+
const unread = res.posts.filter((p: WhatsNewPost) => !p.is_read).length;
|
|
139
|
+
setWhatsNewUnreadCount(unread);
|
|
140
|
+
} catch (_) {
|
|
141
|
+
// Server may not yet expose /whats-new endpoints — silent fail
|
|
142
|
+
}
|
|
143
|
+
}, [client]);
|
|
144
|
+
|
|
124
145
|
// ── Socket setup ──
|
|
125
146
|
|
|
126
147
|
useEffect(() => {
|
|
@@ -160,13 +181,25 @@ export function ChatProvider({ children, autoConnect = true }: ChatProviderProps
|
|
|
160
181
|
refreshSentFriendRequests();
|
|
161
182
|
}
|
|
162
183
|
if (n.type === 'friend_request_declined') refreshSentFriendRequests();
|
|
184
|
+
// What's New posts: a developer published a new one for this app
|
|
185
|
+
if (n.type === 'whats_new') refreshWhatsNew();
|
|
163
186
|
},
|
|
187
|
+
// These events now fan out to BOTH parties (server emits to actor's
|
|
188
|
+
// own room as well), so each handler refreshes every list that could
|
|
189
|
+
// be affected — works whether this client is the actor or the target.
|
|
164
190
|
onFriendRequestAccepted: () => {
|
|
165
191
|
refreshFriends();
|
|
192
|
+
refreshPendingRequests();
|
|
193
|
+
refreshSentFriendRequests();
|
|
194
|
+
},
|
|
195
|
+
onFriendRequestDeclined: () => {
|
|
196
|
+
refreshPendingRequests();
|
|
197
|
+
refreshSentFriendRequests();
|
|
198
|
+
},
|
|
199
|
+
onFriendRequestCancelled: () => {
|
|
200
|
+
refreshPendingRequests();
|
|
166
201
|
refreshSentFriendRequests();
|
|
167
202
|
},
|
|
168
|
-
onFriendRequestDeclined: () => refreshSentFriendRequests(),
|
|
169
|
-
onFriendRequestCancelled: () => refreshPendingRequests(),
|
|
170
203
|
onFriendRemoved: () => refreshFriends(),
|
|
171
204
|
});
|
|
172
205
|
|
|
@@ -177,11 +210,12 @@ export function ChatProvider({ children, autoConnect = true }: ChatProviderProps
|
|
|
177
210
|
refreshFriends().catch(() => {});
|
|
178
211
|
refreshPendingRequests().catch(() => {});
|
|
179
212
|
refreshSentFriendRequests().catch(() => {});
|
|
213
|
+
refreshWhatsNew().catch(() => {});
|
|
180
214
|
|
|
181
215
|
return () => {
|
|
182
216
|
chatSocket.disconnect();
|
|
183
217
|
};
|
|
184
|
-
}, [client, autoConnect, refreshMessages, refreshConversations, refreshFriends, refreshPendingRequests, refreshSentFriendRequests]);
|
|
218
|
+
}, [client, autoConnect, refreshMessages, refreshConversations, refreshFriends, refreshPendingRequests, refreshSentFriendRequests, refreshWhatsNew]);
|
|
185
219
|
|
|
186
220
|
// ── Reconnect on app foreground ──
|
|
187
221
|
|
|
@@ -205,12 +239,13 @@ export function ChatProvider({ children, autoConnect = true }: ChatProviderProps
|
|
|
205
239
|
refreshFriends().catch(() => {});
|
|
206
240
|
refreshPendingRequests().catch(() => {});
|
|
207
241
|
refreshSentFriendRequests().catch(() => {});
|
|
242
|
+
refreshWhatsNew().catch(() => {});
|
|
208
243
|
}
|
|
209
244
|
};
|
|
210
245
|
|
|
211
246
|
const sub = AppState.addEventListener('change', handleAppState);
|
|
212
247
|
return () => sub.remove();
|
|
213
|
-
}, [client, refreshMessages, refreshConversations, refreshFriends, refreshPendingRequests, refreshSentFriendRequests]);
|
|
248
|
+
}, [client, refreshMessages, refreshConversations, refreshFriends, refreshPendingRequests, refreshSentFriendRequests, refreshWhatsNew]);
|
|
214
249
|
|
|
215
250
|
const value = useMemo<ChatContextValue>(
|
|
216
251
|
() => ({
|
|
@@ -224,13 +259,16 @@ export function ChatProvider({ children, autoConnect = true }: ChatProviderProps
|
|
|
224
259
|
friends,
|
|
225
260
|
pendingRequests,
|
|
226
261
|
sentFriendRequests,
|
|
262
|
+
whatsNewPosts,
|
|
263
|
+
whatsNewUnreadCount,
|
|
227
264
|
refreshMessages,
|
|
228
265
|
refreshConversations,
|
|
229
266
|
refreshFriends,
|
|
230
267
|
refreshPendingRequests,
|
|
231
268
|
refreshSentFriendRequests,
|
|
269
|
+
refreshWhatsNew,
|
|
232
270
|
}),
|
|
233
|
-
[status, messages, onlineUsers, onlineCount, unreadCount, conversations, friends, pendingRequests, sentFriendRequests, refreshMessages, refreshConversations, refreshFriends, refreshPendingRequests, refreshSentFriendRequests],
|
|
271
|
+
[status, messages, onlineUsers, onlineCount, unreadCount, conversations, friends, pendingRequests, sentFriendRequests, whatsNewPosts, whatsNewUnreadCount, refreshMessages, refreshConversations, refreshFriends, refreshPendingRequests, refreshSentFriendRequests, refreshWhatsNew],
|
|
234
272
|
);
|
|
235
273
|
|
|
236
274
|
return <ChatContext.Provider value={value}>{children}</ChatContext.Provider>;
|
package/src/client.ts
CHANGED
|
@@ -35,6 +35,7 @@ import type {
|
|
|
35
35
|
CheckUsernameResult,
|
|
36
36
|
LiveScore,
|
|
37
37
|
UiConfig,
|
|
38
|
+
WhatsNewPost,
|
|
38
39
|
UFCEvent,
|
|
39
40
|
UFCFighterDetail,
|
|
40
41
|
ArcadePool,
|
|
@@ -765,6 +766,33 @@ export class DubsClient {
|
|
|
765
766
|
return this.request('GET', '/social/blocked');
|
|
766
767
|
}
|
|
767
768
|
|
|
769
|
+
// ── What's New ──
|
|
770
|
+
|
|
771
|
+
/** List published What's New posts for this app, with is_read for the current user. */
|
|
772
|
+
async getWhatsNewPosts(): Promise<{ posts: WhatsNewPost[] }> {
|
|
773
|
+
return this.request('GET', '/whats-new/posts');
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/** Fetch a single What's New post by id. */
|
|
777
|
+
async getWhatsNewPost(postId: number): Promise<{ post: WhatsNewPost }> {
|
|
778
|
+
return this.request('GET', `/whats-new/posts/${postId}`);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/** Number of unread What's New posts for the current user (this app). */
|
|
782
|
+
async getWhatsNewUnreadCount(): Promise<{ count: number }> {
|
|
783
|
+
return this.request('GET', '/whats-new/unread-count');
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/** Mark specific posts as read. */
|
|
787
|
+
async markWhatsNewRead(postIds: number[]): Promise<void> {
|
|
788
|
+
await this.request('POST', '/whats-new/mark-read', { postIds });
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/** Mark every unread post as read. */
|
|
792
|
+
async markAllWhatsNewRead(): Promise<{ updated: number }> {
|
|
793
|
+
return this.request('POST', '/whats-new/mark-all-read');
|
|
794
|
+
}
|
|
795
|
+
|
|
768
796
|
// ── App Config ──
|
|
769
797
|
|
|
770
798
|
/** Fetch the app's UI customization config (accent color, icon, tagline, environment) */
|
package/src/index.ts
CHANGED
|
@@ -62,6 +62,8 @@ export type {
|
|
|
62
62
|
LiveScore,
|
|
63
63
|
LiveScoreCompetitor,
|
|
64
64
|
UiConfig,
|
|
65
|
+
WhatsNewPost,
|
|
66
|
+
WhatsNewCategory,
|
|
65
67
|
UFCFighter,
|
|
66
68
|
UFCFighterDetail,
|
|
67
69
|
UFCData,
|
|
@@ -191,6 +193,7 @@ export {
|
|
|
191
193
|
useSendFriendRequest,
|
|
192
194
|
useRespondToFriendRequest,
|
|
193
195
|
useCancelFriendRequest,
|
|
196
|
+
useWhatsNew,
|
|
194
197
|
} from './chat';
|
|
195
198
|
export type {
|
|
196
199
|
ChatProviderProps,
|
package/src/types.ts
CHANGED
|
@@ -660,3 +660,23 @@ export interface UiConfig {
|
|
|
660
660
|
ios: boolean;
|
|
661
661
|
};
|
|
662
662
|
}
|
|
663
|
+
|
|
664
|
+
// ── What's New (per-app feature announcements) ──
|
|
665
|
+
|
|
666
|
+
export type WhatsNewCategory = 'feature' | 'improvement' | 'bugfix' | 'announcement';
|
|
667
|
+
|
|
668
|
+
export interface WhatsNewPost {
|
|
669
|
+
id: number;
|
|
670
|
+
title: string;
|
|
671
|
+
content: string;
|
|
672
|
+
gif_url: string | null;
|
|
673
|
+
category: WhatsNewCategory;
|
|
674
|
+
version: string | null;
|
|
675
|
+
is_pinned: boolean;
|
|
676
|
+
is_published: boolean;
|
|
677
|
+
/** Present when the post is fetched in an authenticated context. */
|
|
678
|
+
is_read?: boolean;
|
|
679
|
+
created_by: string;
|
|
680
|
+
created_at: string;
|
|
681
|
+
updated_at: string;
|
|
682
|
+
}
|