@bettoredge/calcutta 0.4.2 → 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
CHANGED
|
@@ -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 ? (
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState, useMemo, useCallback, useEffect, useRef } from 'react';
|
|
2
|
-
import { StyleSheet, TouchableOpacity, ActivityIndicator, ScrollView, Image, FlatList, TextInput, Platform, useWindowDimensions } from 'react-native';
|
|
2
|
+
import { StyleSheet, TouchableOpacity, ActivityIndicator, ScrollView, Image, FlatList, 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 { CalcuttaParticipantProps } from '@bettoredge/types';
|
|
@@ -34,6 +34,8 @@ export interface CalcuttaDetailProps {
|
|
|
34
34
|
onDepositFunds?: (amount: number) => void;
|
|
35
35
|
initialShowWalkthrough?: boolean;
|
|
36
36
|
onWalkthroughDismiss?: () => void;
|
|
37
|
+
onRefresh?: () => void | Promise<void>;
|
|
38
|
+
onAuctionStarted?: () => void;
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
const getStatusConfig = (status: string) => {
|
|
@@ -63,12 +65,15 @@ export const CalcuttaDetail: React.FC<CalcuttaDetailProps> = ({
|
|
|
63
65
|
device_id,
|
|
64
66
|
player_username,
|
|
65
67
|
player_profile_pic,
|
|
68
|
+
onRefresh,
|
|
69
|
+
onAuctionStarted,
|
|
66
70
|
}) => {
|
|
67
71
|
const { theme } = useTheme();
|
|
68
72
|
const { width: windowWidth } = useWindowDimensions();
|
|
69
73
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
70
74
|
const measuredWidth = containerWidth || windowWidth;
|
|
71
75
|
const isDesktop = Platform.OS === 'web' && measuredWidth >= 700;
|
|
76
|
+
const [refreshing, setRefreshing] = useState(false);
|
|
72
77
|
|
|
73
78
|
// Walkthrough state
|
|
74
79
|
const [showWalkthrough, setShowWalkthrough] = useState(false);
|
|
@@ -123,6 +128,13 @@ export const CalcuttaDetail: React.FC<CalcuttaDetailProps> = ({
|
|
|
123
128
|
const [itemSearch, setItemSearch] = useState('');
|
|
124
129
|
const [mobileListTab, setMobileListTab] = useState<'items' | 'players'>('items');
|
|
125
130
|
|
|
131
|
+
const handleRefresh = async () => {
|
|
132
|
+
setRefreshing(true);
|
|
133
|
+
try {
|
|
134
|
+
await Promise.all([refresh(), fetchEscrow(), onRefresh?.()]);
|
|
135
|
+
} catch {} finally { setRefreshing(false); }
|
|
136
|
+
};
|
|
137
|
+
|
|
126
138
|
const filteredItems = useMemo(() => {
|
|
127
139
|
if (competition?.auction_type !== 'sweepstakes' || !itemSearch.trim()) return items;
|
|
128
140
|
const q = itemSearch.toLowerCase().trim();
|
|
@@ -594,7 +606,7 @@ export const CalcuttaDetail: React.FC<CalcuttaDetailProps> = ({
|
|
|
594
606
|
return (
|
|
595
607
|
<View variant="transparent" style={[styles.container, { backgroundColor: theme.colors.surface.base }]} onLayout={e => setContainerWidth(e.nativeEvent.layout.width)}>
|
|
596
608
|
{isDesktop ? (
|
|
597
|
-
<ScrollView style={{ flex: 1, backgroundColor: theme.colors.surface.base }} contentContainerStyle={{ padding: 12, paddingBottom: 40, backgroundColor: theme.colors.surface.base }} keyboardShouldPersistTaps="handled">
|
|
609
|
+
<ScrollView style={{ flex: 1, backgroundColor: theme.colors.surface.base }} contentContainerStyle={{ padding: 12, paddingBottom: 40, backgroundColor: theme.colors.surface.base }} keyboardShouldPersistTaps="handled" refreshControl={<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />}>
|
|
598
610
|
{/* Row 1: Hero + Info/Action left, Items right */}
|
|
599
611
|
<View variant="transparent" style={{ flexDirection: 'row', gap: 12 }}>
|
|
600
612
|
<View variant="transparent" style={{ flex: 3 }}>
|
|
@@ -647,6 +659,7 @@ export const CalcuttaDetail: React.FC<CalcuttaDetailProps> = ({
|
|
|
647
659
|
style={{ backgroundColor: theme.colors.surface.base }}
|
|
648
660
|
contentContainerStyle={{ paddingBottom: Platform.OS === 'web' ? 300 : 40 }}
|
|
649
661
|
keyboardShouldPersistTaps="handled"
|
|
662
|
+
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />}
|
|
650
663
|
/>
|
|
651
664
|
)}
|
|
652
665
|
|
|
@@ -659,6 +672,7 @@ export const CalcuttaDetail: React.FC<CalcuttaDetailProps> = ({
|
|
|
659
672
|
onComplete={() => {
|
|
660
673
|
setShowCountdown(false);
|
|
661
674
|
refresh();
|
|
675
|
+
onAuctionStarted?.();
|
|
662
676
|
}}
|
|
663
677
|
/>
|
|
664
678
|
|
|
@@ -226,7 +226,7 @@ export const CalcuttaEscrow: React.FC<CalcuttaEscrowComponentProps> = ({
|
|
|
226
226
|
activeOpacity={0.7}
|
|
227
227
|
>
|
|
228
228
|
<Ionicons
|
|
229
|
-
name="arrow-
|
|
229
|
+
name="arrow-up-outline"
|
|
230
230
|
size={18}
|
|
231
231
|
color={action === 'transfer_in' ? '#FFFFFF' : theme.colors.text.secondary}
|
|
232
232
|
/>
|
|
@@ -250,7 +250,7 @@ export const CalcuttaEscrow: React.FC<CalcuttaEscrowComponentProps> = ({
|
|
|
250
250
|
activeOpacity={0.7}
|
|
251
251
|
>
|
|
252
252
|
<Ionicons
|
|
253
|
-
name="arrow-
|
|
253
|
+
name="arrow-down-outline"
|
|
254
254
|
size={18}
|
|
255
255
|
color={action === 'transfer_out' ? '#FFFFFF' : theme.colors.text.secondary}
|
|
256
256
|
/>
|
|
@@ -307,7 +307,7 @@ export const CalcuttaEscrow: React.FC<CalcuttaEscrowComponentProps> = ({
|
|
|
307
307
|
? theme.colors.status.error
|
|
308
308
|
: (loading || isInvalidAmount)
|
|
309
309
|
? theme.colors.surface.elevated
|
|
310
|
-
: theme.colors.
|
|
310
|
+
: theme.colors.status.success,
|
|
311
311
|
}]}
|
|
312
312
|
onPress={handleAction}
|
|
313
313
|
disabled={loading || (isInvalidAmount && !(insufficientBalance && onDepositFunds))}
|
|
@@ -318,7 +318,7 @@ export const CalcuttaEscrow: React.FC<CalcuttaEscrowComponentProps> = ({
|
|
|
318
318
|
) : (
|
|
319
319
|
<>
|
|
320
320
|
<Ionicons
|
|
321
|
-
name={action === 'transfer_in' ? 'arrow-
|
|
321
|
+
name={action === 'transfer_in' ? 'arrow-up-circle' : 'arrow-down-circle'}
|
|
322
322
|
size={20}
|
|
323
323
|
color={isInvalidAmount ? theme.colors.text.tertiary : '#FFFFFF'}
|
|
324
324
|
/>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState, useMemo, useEffect } from 'react';
|
|
2
|
-
import { StyleSheet, ScrollView, TouchableOpacity, Image, FlatList, Platform, useWindowDimensions } from 'react-native';
|
|
2
|
+
import { StyleSheet, ScrollView, TouchableOpacity, Image, FlatList, 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 { CalcuttaParticipantProps } from '@bettoredge/types';
|
|
@@ -19,6 +19,7 @@ export interface CalcuttaResultsProps {
|
|
|
19
19
|
player_profile_pic?: string;
|
|
20
20
|
onManage?: () => void;
|
|
21
21
|
onShare?: () => void;
|
|
22
|
+
onRefresh?: () => void | Promise<void>;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export const CalcuttaResults: React.FC<CalcuttaResultsProps> = ({
|
|
@@ -30,12 +31,14 @@ export const CalcuttaResults: React.FC<CalcuttaResultsProps> = ({
|
|
|
30
31
|
player_profile_pic,
|
|
31
32
|
onManage,
|
|
32
33
|
onShare,
|
|
34
|
+
onRefresh,
|
|
33
35
|
}) => {
|
|
34
36
|
const { theme } = useTheme();
|
|
35
37
|
const { width: windowWidth } = useWindowDimensions();
|
|
36
38
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
37
39
|
const measuredWidth = containerWidth || windowWidth;
|
|
38
40
|
const isDesktop = Platform.OS === 'web' && measuredWidth >= 700;
|
|
41
|
+
const [refreshing, setRefreshing] = useState(false);
|
|
39
42
|
|
|
40
43
|
const {
|
|
41
44
|
loading,
|
|
@@ -60,6 +63,12 @@ export const CalcuttaResults: React.FC<CalcuttaResultsProps> = ({
|
|
|
60
63
|
{ username: player_username, profile_pic: player_profile_pic },
|
|
61
64
|
);
|
|
62
65
|
|
|
66
|
+
const handleRefresh = async () => {
|
|
67
|
+
setRefreshing(true);
|
|
68
|
+
try { await Promise.all([refresh(), onRefresh?.()]); }
|
|
69
|
+
catch {} finally { setRefreshing(false); }
|
|
70
|
+
};
|
|
71
|
+
|
|
63
72
|
// Derived data
|
|
64
73
|
const isAdmin = player_id != null && competition?.admin_id == player_id;
|
|
65
74
|
const myItems = useMemo(() =>
|
|
@@ -381,7 +390,7 @@ export const CalcuttaResults: React.FC<CalcuttaResultsProps> = ({
|
|
|
381
390
|
return (
|
|
382
391
|
<View variant="transparent" style={[styles.container, { backgroundColor: theme.colors.surface.base }]} onLayout={e => setContainerWidth(e.nativeEvent.layout.width)}>
|
|
383
392
|
{isDesktop ? (
|
|
384
|
-
<ScrollView style={{ flex: 1, backgroundColor: theme.colors.surface.base }} contentContainerStyle={{ padding: 12, paddingBottom: 40, backgroundColor: theme.colors.surface.base }} keyboardShouldPersistTaps="handled">
|
|
393
|
+
<ScrollView style={{ flex: 1, backgroundColor: theme.colors.surface.base }} contentContainerStyle={{ padding: 12, paddingBottom: 40, backgroundColor: theme.colors.surface.base }} keyboardShouldPersistTaps="handled" refreshControl={<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />}>
|
|
385
394
|
{renderHeader()}
|
|
386
395
|
{renderMyItems()}
|
|
387
396
|
{renderPotKPIs()}
|
|
@@ -409,7 +418,7 @@ export const CalcuttaResults: React.FC<CalcuttaResultsProps> = ({
|
|
|
409
418
|
</View>
|
|
410
419
|
</ScrollView>
|
|
411
420
|
) : (
|
|
412
|
-
<ScrollView style={{ flex: 1, backgroundColor: theme.colors.surface.base }} contentContainerStyle={{ paddingBottom: Platform.OS === 'web' ? 300 : 40, backgroundColor: theme.colors.surface.base }} keyboardShouldPersistTaps="handled">
|
|
421
|
+
<ScrollView style={{ flex: 1, backgroundColor: theme.colors.surface.base }} contentContainerStyle={{ paddingBottom: Platform.OS === 'web' ? 300 : 40, backgroundColor: theme.colors.surface.base }} keyboardShouldPersistTaps="handled" refreshControl={<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />}>
|
|
413
422
|
{renderHeader()}
|
|
414
423
|
{renderMyItems()}
|
|
415
424
|
{renderPotKPIs()}
|