@dubsdotapp/expo 0.5.29 → 0.5.31

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.29",
3
+ "version": "0.5.31",
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/client.ts CHANGED
@@ -53,6 +53,7 @@ import type {
53
53
  JackpotConfig,
54
54
  BuildJackpotEnterResult,
55
55
  ConfirmJackpotEnterResult,
56
+ PromoCredit,
56
57
  } from './types';
57
58
 
58
59
  export interface DubsClientConfig {
@@ -592,6 +593,22 @@ export class DubsClient {
592
593
  return { round: res.round, lastWinner: res.lastWinner };
593
594
  }
594
595
 
596
+ /**
597
+ * List the authenticated user's active promo credits — codes that
598
+ * have been reserved to them but not yet used in a game. Includes
599
+ * streak-milestone unlocks (auto-minted at each 1000-dub crossing)
600
+ * and any manually-reserved Twitter giveaway codes.
601
+ */
602
+ async getCredits(): Promise<{ credits: PromoCredit[]; totalLamports: number; totalSOL: number }> {
603
+ const res = await this.request<{
604
+ success: true;
605
+ credits: PromoCredit[];
606
+ totalLamports: number;
607
+ totalSOL: number;
608
+ }>('GET', '/me/credits');
609
+ return { credits: res.credits, totalLamports: res.totalLamports, totalSOL: res.totalSOL };
610
+ }
611
+
595
612
  /** Get current round entries with odds */
596
613
  async getJackpotEntries(): Promise<{ roundId: string; entries: JackpotEntry[]; totalEntries: number }> {
597
614
  const res = await this.request<{ success: true; roundId: string; entries: JackpotEntry[]; totalEntries: number }>(
@@ -40,3 +40,5 @@ export { useJackpotHistory } from './useJackpotHistory';
40
40
  export type { UseJackpotHistoryResult } from './useJackpotHistory';
41
41
  export { useEnterJackpot } from './useEnterJackpot';
42
42
  export type { EnterJackpotMutationResult } from './useEnterJackpot';
43
+ export { useCredits } from './useCredits';
44
+ export type { UseCreditsResult } from './useCredits';
@@ -0,0 +1,48 @@
1
+ import { useState, useEffect, useCallback } from 'react';
2
+ import { useDubs } from '../provider';
3
+ import type { PromoCredit } from '../types';
4
+
5
+ export interface UseCreditsResult {
6
+ credits: PromoCredit[];
7
+ totalLamports: number;
8
+ totalSOL: number;
9
+ loading: boolean;
10
+ error: Error | null;
11
+ refetch: () => Promise<void>;
12
+ }
13
+
14
+ /**
15
+ * Returns every active promo credit reserved to the authenticated user
16
+ * (streak-milestone unlocks + any unused Twitter giveaway codes).
17
+ *
18
+ * Credits are kept on the server (`promo_codes` table); this hook just
19
+ * mirrors them. After a streak shake that mints a new unlock, call
20
+ * `refetch()` to pull the new credit into the list immediately.
21
+ */
22
+ export function useCredits(): UseCreditsResult {
23
+ const { client } = useDubs();
24
+ const [credits, setCredits] = useState<PromoCredit[]>([]);
25
+ const [totalLamports, setTotalLamports] = useState(0);
26
+ const [totalSOL, setTotalSOL] = useState(0);
27
+ const [loading, setLoading] = useState(false);
28
+ const [error, setError] = useState<Error | null>(null);
29
+
30
+ const fetch = useCallback(async () => {
31
+ setLoading(true);
32
+ setError(null);
33
+ try {
34
+ const result = await client.getCredits();
35
+ setCredits(result.credits);
36
+ setTotalLamports(result.totalLamports);
37
+ setTotalSOL(result.totalSOL);
38
+ } catch (err) {
39
+ setError(err instanceof Error ? err : new Error(String(err)));
40
+ } finally {
41
+ setLoading(false);
42
+ }
43
+ }, [client]);
44
+
45
+ useEffect(() => { fetch(); }, [fetch]);
46
+
47
+ return { credits, totalLamports, totalSOL, loading, error, refetch: fetch };
48
+ }
package/src/index.ts CHANGED
@@ -86,6 +86,7 @@ export type {
86
86
  JackpotConfig,
87
87
  BuildJackpotEnterResult,
88
88
  ConfirmJackpotEnterResult,
89
+ PromoCredit,
89
90
  } from './types';
90
91
 
91
92
  // Provider
package/src/types.ts CHANGED
@@ -680,3 +680,18 @@ export interface WhatsNewPost {
680
680
  created_at: string;
681
681
  updated_at: string;
682
682
  }
683
+
684
+ /**
685
+ * A single promo credit reserved to the authenticated user. Returned by
686
+ * GET /me/credits and surfaced via useCredits. `source = 'streak_milestone'`
687
+ * for credits auto-minted at each 1000-dub streak threshold; 'manual' for
688
+ * Twitter-giveaway codes typed in during onboarding.
689
+ */
690
+ export interface PromoCredit {
691
+ id: number;
692
+ code: string;
693
+ amountLamports: number;
694
+ amountSOL: number;
695
+ awardedAt: string;
696
+ source: 'streak_milestone' | 'manual' | string;
697
+ }
@@ -167,19 +167,26 @@ export function JoinGameSheet({
167
167
  const homeBetsCount = bettors.filter(b => b.team === 'home').length;
168
168
  const awayBetsCount = bettors.filter(b => b.team === 'away').length;
169
169
  const drawBetsCount = bettors.filter(b => b.team === 'draw').length;
170
+ // Show comparable odds for every side by computing each as "if I bet
171
+ // `wager` on this team". Total pool grows by `wager` regardless of
172
+ // which side you pick, so newPool is constant; only the side pool
173
+ // shifts. Previously this only added `wager` to the *selected* side,
174
+ // which left an unbetted side with sidePool === 0 → odds shown as
175
+ // "—". Empty sides now show their (high) implied multiplier so the
176
+ // user can actually compare risk/return before committing.
170
177
  const newPool = totalPool + wager;
171
- const newHome = homePool + (selectedTeam === 'home' ? wager : 0);
172
- const newAway = awayPool + (selectedTeam === 'away' ? wager : 0);
173
- const newDraw = drawPool + (selectedTeam === 'draw' ? wager : 0);
178
+ const homeWith = homePool + wager;
179
+ const awayWith = awayPool + wager;
180
+ const drawWith = drawPool + wager;
174
181
  return {
175
- homeOdds: newHome > 0 ? (newPool / newHome).toFixed(2) : '—',
176
- awayOdds: newAway > 0 ? (newPool / newAway).toFixed(2) : '—',
177
- drawOdds: newDraw > 0 ? (newPool / newDraw).toFixed(2) : '—',
182
+ homeOdds: homeWith > 0 ? (newPool / homeWith).toFixed(2) : '—',
183
+ awayOdds: awayWith > 0 ? (newPool / awayWith).toFixed(2) : '—',
184
+ drawOdds: drawWith > 0 ? (newPool / drawWith).toFixed(2) : '—',
178
185
  homeBets: homeBetsCount,
179
186
  awayBets: awayBetsCount,
180
187
  drawBets: drawBetsCount,
181
188
  };
182
- }, [totalPool, homePool, awayPool, drawPool, bettors, wager, selectedTeam]);
189
+ }, [totalPool, homePool, awayPool, drawPool, bettors, wager]);
183
190
 
184
191
  const selectedOdds = selectedTeam === 'home' ? homeOdds : selectedTeam === 'away' ? awayOdds : selectedTeam === 'draw' ? drawOdds : '—';
185
192
  const potentialWinnings = selectedOdds !== '—' ? formatSol(parseFloat(selectedOdds) * wager) : '—';