@bettoredge/calcutta 0.4.1 → 0.5.0
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 +1 -1
- package/src/components/CalcuttaActionCard.tsx +31 -2
- package/src/components/CalcuttaAuction.tsx +26 -12
- package/src/components/CalcuttaDetail.tsx +261 -168
- package/src/components/CalcuttaEscrow.tsx +4 -4
- package/src/components/CalcuttaResults.tsx +470 -0
- package/src/components/CalcuttaWalkthrough.tsx +371 -0
- package/src/components/sealed/SealedBidAuction.tsx +140 -158
- package/src/index.ts +4 -0
package/package.json
CHANGED
|
@@ -15,6 +15,7 @@ export interface CalcuttaActionCardProps {
|
|
|
15
15
|
activeBidCount?: number;
|
|
16
16
|
itemsWon?: number;
|
|
17
17
|
isAdmin?: boolean;
|
|
18
|
+
participantCount?: number;
|
|
18
19
|
onJoin?: () => void;
|
|
19
20
|
onDepositEscrow?: () => void;
|
|
20
21
|
onManage?: () => void;
|
|
@@ -30,8 +31,12 @@ interface CardConfig {
|
|
|
30
31
|
ctaLabel?: string;
|
|
31
32
|
ctaColor?: string;
|
|
32
33
|
ctaAction?: () => void;
|
|
34
|
+
secondaryCtaLabel?: string;
|
|
35
|
+
secondaryCtaColor?: string;
|
|
36
|
+
secondaryCtaAction?: () => void;
|
|
33
37
|
showCountdown?: 'start' | 'end';
|
|
34
38
|
loading?: boolean;
|
|
39
|
+
secondaryLoading?: boolean;
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
export const CalcuttaActionCard: React.FC<CalcuttaActionCardProps> = ({
|
|
@@ -42,6 +47,7 @@ export const CalcuttaActionCard: React.FC<CalcuttaActionCardProps> = ({
|
|
|
42
47
|
activeBidCount = 0,
|
|
43
48
|
itemsWon = 0,
|
|
44
49
|
isAdmin,
|
|
50
|
+
participantCount = 0,
|
|
45
51
|
onJoin,
|
|
46
52
|
onDepositEscrow,
|
|
47
53
|
onManage,
|
|
@@ -56,8 +62,7 @@ export const CalcuttaActionCard: React.FC<CalcuttaActionCardProps> = ({
|
|
|
56
62
|
const getConfig = (): CardConfig => {
|
|
57
63
|
// Admin: can start auction
|
|
58
64
|
if (isAdmin && lifecycleState === 'scheduled' && onStartAuction) {
|
|
59
|
-
const
|
|
60
|
-
return {
|
|
65
|
+
const config: CardConfig = {
|
|
61
66
|
icon: 'play-circle-outline',
|
|
62
67
|
title: 'Ready to start?',
|
|
63
68
|
description: `${participantCount} player${participantCount !== 1 ? 's' : ''} have joined. Start the ${isSweepstakes ? 'competition' : 'auction'} when ready.`,
|
|
@@ -67,6 +72,14 @@ export const CalcuttaActionCard: React.FC<CalcuttaActionCardProps> = ({
|
|
|
67
72
|
ctaAction: onStartAuction,
|
|
68
73
|
showCountdown: 'start',
|
|
69
74
|
};
|
|
75
|
+
// Admin hasn't joined yet — show join as secondary action
|
|
76
|
+
if (!hasJoined && onJoin) {
|
|
77
|
+
config.secondaryCtaLabel = isFree ? 'Join Free' : `Join — ${formatCurrency(entryFee, competition.market_type)}`;
|
|
78
|
+
config.secondaryCtaColor = '#10B981';
|
|
79
|
+
config.secondaryCtaAction = onJoin;
|
|
80
|
+
config.secondaryLoading = joining;
|
|
81
|
+
}
|
|
82
|
+
return config;
|
|
70
83
|
}
|
|
71
84
|
|
|
72
85
|
// Not joined — prompt to join
|
|
@@ -228,6 +241,22 @@ export const CalcuttaActionCard: React.FC<CalcuttaActionCardProps> = ({
|
|
|
228
241
|
</View>
|
|
229
242
|
)}
|
|
230
243
|
|
|
244
|
+
{/* Secondary CTA Button */}
|
|
245
|
+
{config.secondaryCtaLabel && config.secondaryCtaAction && (
|
|
246
|
+
<TouchableOpacity
|
|
247
|
+
style={[styles.ctaButton, { backgroundColor: config.secondaryCtaColor || '#10B981' }]}
|
|
248
|
+
onPress={config.secondaryCtaAction}
|
|
249
|
+
activeOpacity={0.7}
|
|
250
|
+
disabled={config.secondaryLoading}
|
|
251
|
+
>
|
|
252
|
+
{config.secondaryLoading ? (
|
|
253
|
+
<ActivityIndicator size="small" color="#FFFFFF" />
|
|
254
|
+
) : (
|
|
255
|
+
<Text variant="body" bold style={{ color: '#FFFFFF' }}>{config.secondaryCtaLabel}</Text>
|
|
256
|
+
)}
|
|
257
|
+
</TouchableOpacity>
|
|
258
|
+
)}
|
|
259
|
+
|
|
231
260
|
{/* CTA Button */}
|
|
232
261
|
{config.ctaLabel && config.ctaAction && (
|
|
233
262
|
<TouchableOpacity
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useState, useRef, useCallback, useMemo } from 'react';
|
|
2
|
-
import { StyleSheet, TouchableOpacity, ActivityIndicator, FlatList, ScrollView, Image, TextInput, Platform, useWindowDimensions } from 'react-native';
|
|
2
|
+
import { StyleSheet, TouchableOpacity, ActivityIndicator, FlatList, ScrollView, Image, TextInput, Platform, useWindowDimensions, RefreshControl } from 'react-native';
|
|
3
3
|
import { View, Text, useTheme } from '@bettoredge/styles';
|
|
4
4
|
import { Ionicons } from '@expo/vector-icons';
|
|
5
5
|
import type { CalcuttaEscrowProps } from '@bettoredge/types';
|
|
@@ -39,6 +39,8 @@ export interface CalcuttaAuctionProps {
|
|
|
39
39
|
onManage?: () => void;
|
|
40
40
|
onJoin?: () => void | Promise<void>;
|
|
41
41
|
onLeave?: () => void | Promise<void>;
|
|
42
|
+
onRefresh?: () => void | Promise<void>;
|
|
43
|
+
onAuctionClosed?: () => void;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
// Inner component for live auction (original layout)
|
|
@@ -54,12 +56,15 @@ const LiveAuction: React.FC<CalcuttaAuctionProps> = ({
|
|
|
54
56
|
onNextItem,
|
|
55
57
|
player_balance,
|
|
56
58
|
onDepositFunds,
|
|
59
|
+
onRefresh,
|
|
60
|
+
onAuctionClosed: onAuctionClosedProp,
|
|
57
61
|
}) => {
|
|
58
62
|
const { theme } = useTheme();
|
|
59
63
|
const { width: windowWidth } = useWindowDimensions();
|
|
60
64
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
61
65
|
const measuredWidth = containerWidth || windowWidth;
|
|
62
66
|
const isDesktop = Platform.OS === 'web' && measuredWidth >= 700;
|
|
67
|
+
const [refreshing, setRefreshing] = useState(false);
|
|
63
68
|
const {
|
|
64
69
|
loading,
|
|
65
70
|
competition,
|
|
@@ -87,6 +92,13 @@ const LiveAuction: React.FC<CalcuttaAuctionProps> = ({
|
|
|
87
92
|
|
|
88
93
|
const isAdmin = player_id != null && competition?.admin_id === player_id;
|
|
89
94
|
|
|
95
|
+
const handleRefresh = async () => {
|
|
96
|
+
setRefreshing(true);
|
|
97
|
+
try {
|
|
98
|
+
await Promise.all([refreshAuction(), fetchEscrow(), onRefresh?.()]);
|
|
99
|
+
} catch {} finally { setRefreshing(false); }
|
|
100
|
+
};
|
|
101
|
+
|
|
90
102
|
// Auction complete overlay
|
|
91
103
|
const [showAuctionComplete, setShowAuctionComplete] = useState(false);
|
|
92
104
|
|
|
@@ -94,7 +106,8 @@ const LiveAuction: React.FC<CalcuttaAuctionProps> = ({
|
|
|
94
106
|
handleAuctionClosed(data);
|
|
95
107
|
// Show complete overlay after a short delay (let last item celebration finish)
|
|
96
108
|
setTimeout(() => setShowAuctionComplete(true), 1000);
|
|
97
|
-
|
|
109
|
+
onAuctionClosedProp?.();
|
|
110
|
+
}, [handleAuctionClosed, onAuctionClosedProp]);
|
|
98
111
|
|
|
99
112
|
// Celebration modal state
|
|
100
113
|
const [celebrationData, setCelebrationData] = useState<{
|
|
@@ -535,7 +548,7 @@ const LiveAuction: React.FC<CalcuttaAuctionProps> = ({
|
|
|
535
548
|
Full width: Active item
|
|
536
549
|
Below: [Items (paginated)] [Players] [Info]
|
|
537
550
|
═══════════════════════════════════════ */
|
|
538
|
-
<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 12, paddingBottom: 40 }} keyboardShouldPersistTaps="handled">
|
|
551
|
+
<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 12, paddingBottom: 40 }} keyboardShouldPersistTaps="handled" refreshControl={<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />}>
|
|
539
552
|
{/* Top bar: escrow + admin */}
|
|
540
553
|
<View variant="transparent" style={{ flexDirection: 'row', alignItems: 'center', marginBottom: 12, zIndex: 200 }}>
|
|
541
554
|
<View variant="transparent" style={{ position: 'relative', zIndex: 200 }}>
|
|
@@ -666,6 +679,7 @@ const LiveAuction: React.FC<CalcuttaAuctionProps> = ({
|
|
|
666
679
|
keyboardShouldPersistTaps="handled"
|
|
667
680
|
contentContainerStyle={{ paddingBottom: 40 }}
|
|
668
681
|
style={{ backgroundColor: theme.colors.surface.base }}
|
|
682
|
+
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />}
|
|
669
683
|
extraData={`${infoTab}-${items.map(i => `${i.calcutta_auction_item_id}:${i.status}:${i.current_bid}`).join(',')}-${my_bids.length}-${paused}-${escrow?.escrow_balance}`}
|
|
670
684
|
ListHeaderComponent={
|
|
671
685
|
<View variant="transparent">
|
|
@@ -745,12 +759,19 @@ const SweepstakesWaiting: React.FC<CalcuttaAuctionProps & { competition: any }>
|
|
|
745
759
|
player_id,
|
|
746
760
|
onClose,
|
|
747
761
|
onManage,
|
|
762
|
+
onRefresh,
|
|
748
763
|
competition,
|
|
749
764
|
}) => {
|
|
750
765
|
const { theme } = useTheme();
|
|
751
|
-
const { items, rounds, participants, item_results } = useCalcuttaCompetition(calcutta_competition_id);
|
|
766
|
+
const { items, rounds, participants, item_results, refresh } = useCalcuttaCompetition(calcutta_competition_id);
|
|
752
767
|
const { images: itemImages } = useCalcuttaItemImages(items);
|
|
753
768
|
const isAdmin = player_id != null && competition?.admin_id == player_id;
|
|
769
|
+
const [refreshing, setRefreshing] = useState(false);
|
|
770
|
+
const handleRefresh = async () => {
|
|
771
|
+
setRefreshing(true);
|
|
772
|
+
try { await Promise.all([refresh(), onRefresh?.()]); }
|
|
773
|
+
catch {} finally { setRefreshing(false); }
|
|
774
|
+
};
|
|
754
775
|
|
|
755
776
|
// Build player lookup for owner usernames
|
|
756
777
|
const ownerIds = items.filter(i => i.winning_player_id).map(i => i.winning_player_id!);
|
|
@@ -787,7 +808,7 @@ const SweepstakesWaiting: React.FC<CalcuttaAuctionProps & { competition: any }>
|
|
|
787
808
|
|
|
788
809
|
return (
|
|
789
810
|
<View variant="transparent" style={[styles.container, { backgroundColor: theme.colors.surface.base }]}>
|
|
790
|
-
<ScrollView style={{ flex: 1, backgroundColor: theme.colors.surface.base }} contentContainerStyle={{ gap: 16, paddingBottom: Platform.OS === 'web' ? 300 : 40, backgroundColor: theme.colors.surface.base }} keyboardShouldPersistTaps="handled">
|
|
811
|
+
<ScrollView style={{ flex: 1, backgroundColor: theme.colors.surface.base }} contentContainerStyle={{ gap: 16, paddingBottom: Platform.OS === 'web' ? 300 : 40, backgroundColor: theme.colors.surface.base }} keyboardShouldPersistTaps="handled" refreshControl={<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />}>
|
|
791
812
|
{/* Hero with embedded title */}
|
|
792
813
|
<View style={{ position: 'relative' }}>
|
|
793
814
|
{competition.image?.url ? (
|
|
@@ -1078,13 +1099,6 @@ export const CalcuttaAuction: React.FC<CalcuttaAuctionProps> = (props) => {
|
|
|
1078
1099
|
);
|
|
1079
1100
|
}
|
|
1080
1101
|
|
|
1081
|
-
// Post-auction: show results view for all types
|
|
1082
|
-
const isPostAuction = ['auction_closed', 'inprogress', 'closed'].includes(competition.status);
|
|
1083
|
-
|
|
1084
|
-
if (competition.auction_type === 'sweepstakes' || isPostAuction) {
|
|
1085
|
-
return <SweepstakesWaiting {...props} competition={competition} />;
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
1102
|
if (competition.auction_type === 'sealed_bid') {
|
|
1089
1103
|
return <SealedBidAuction {...props} />;
|
|
1090
1104
|
}
|