be-components 6.0.8 → 6.1.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/lib/commonjs/BetMatch/api/index.js +183 -0
- package/lib/commonjs/BetMatch/api/index.js.map +1 -0
- package/lib/commonjs/BetMatch/components/BetMatchCard.js +469 -0
- package/lib/commonjs/BetMatch/components/BetMatchCard.js.map +1 -0
- package/lib/commonjs/BetMatch/components/BetMatchCardSmall.js +132 -0
- package/lib/commonjs/BetMatch/components/BetMatchCardSmall.js.map +1 -0
- package/lib/commonjs/BetMatch/index.js +280 -0
- package/lib/commonjs/BetMatch/index.js.map +1 -0
- package/lib/commonjs/Components/AutoScrollFlatList.js +135 -0
- package/lib/commonjs/Components/AutoScrollFlatList.js.map +1 -0
- package/lib/commonjs/Components/Icons.js +26 -0
- package/lib/commonjs/Components/Icons.js.map +1 -1
- package/lib/commonjs/Components/index.js +7 -0
- package/lib/commonjs/Components/index.js.map +1 -1
- package/lib/commonjs/FlashMarket/FlashHolder.js +15 -10
- package/lib/commonjs/FlashMarket/FlashHolder.js.map +1 -1
- package/lib/commonjs/MarketComponents/components/MyOrderList/api/index.js +7 -1
- package/lib/commonjs/MarketComponents/components/MyOrderList/api/index.js.map +1 -1
- package/lib/commonjs/MarketComponents/components/OrderGradeBar.js +5 -1
- package/lib/commonjs/MarketComponents/components/OrderGradeBar.js.map +1 -1
- package/lib/commonjs/index.js +7 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/BetMatch/api/index.js +176 -0
- package/lib/module/BetMatch/api/index.js.map +1 -0
- package/lib/module/BetMatch/components/BetMatchCard.js +461 -0
- package/lib/module/BetMatch/components/BetMatchCard.js.map +1 -0
- package/lib/module/BetMatch/components/BetMatchCardSmall.js +125 -0
- package/lib/module/BetMatch/components/BetMatchCardSmall.js.map +1 -0
- package/lib/module/BetMatch/index.js +271 -0
- package/lib/module/BetMatch/index.js.map +1 -0
- package/lib/module/Components/AutoScrollFlatList.js +127 -0
- package/lib/module/Components/AutoScrollFlatList.js.map +1 -0
- package/lib/module/Components/Icons.js +26 -0
- package/lib/module/Components/Icons.js.map +1 -1
- package/lib/module/Components/index.js +2 -1
- package/lib/module/Components/index.js.map +1 -1
- package/lib/module/FlashMarket/FlashHolder.js +15 -10
- package/lib/module/FlashMarket/FlashHolder.js.map +1 -1
- package/lib/module/MarketComponents/components/MyOrderList/api/index.js +7 -1
- package/lib/module/MarketComponents/components/MyOrderList/api/index.js.map +1 -1
- package/lib/module/MarketComponents/components/OrderGradeBar.js +5 -1
- package/lib/module/MarketComponents/components/OrderGradeBar.js.map +1 -1
- package/lib/module/index.js +2 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/lib/commonjs/BetMatch/api/index.d.ts +19 -0
- package/lib/typescript/lib/commonjs/BetMatch/api/index.d.ts.map +1 -0
- package/lib/typescript/lib/commonjs/BetMatch/components/BetMatchCard.d.ts +12 -0
- package/lib/typescript/lib/commonjs/BetMatch/components/BetMatchCard.d.ts.map +1 -0
- package/lib/typescript/lib/commonjs/BetMatch/components/BetMatchCardSmall.d.ts +12 -0
- package/lib/typescript/lib/commonjs/BetMatch/components/BetMatchCardSmall.d.ts.map +1 -0
- package/lib/typescript/lib/commonjs/BetMatch/index.d.ts +6 -0
- package/lib/typescript/lib/commonjs/BetMatch/index.d.ts.map +1 -0
- package/lib/typescript/lib/commonjs/Components/AutoScrollFlatList.d.ts +13 -0
- package/lib/typescript/lib/commonjs/Components/AutoScrollFlatList.d.ts.map +1 -0
- package/lib/typescript/lib/commonjs/Components/Icons.d.ts +5 -0
- package/lib/typescript/lib/commonjs/Components/Icons.d.ts.map +1 -1
- package/lib/typescript/lib/commonjs/Components/index.d.ts +1 -0
- package/lib/typescript/lib/commonjs/FlashMarket/FlashHolder.d.ts +2 -1
- package/lib/typescript/lib/commonjs/FlashMarket/FlashHolder.d.ts.map +1 -1
- package/lib/typescript/lib/commonjs/MarketComponents/components/MyOrderList/api/index.d.ts +3 -0
- package/lib/typescript/lib/commonjs/MarketComponents/components/MyOrderList/api/index.d.ts.map +1 -1
- package/lib/typescript/lib/commonjs/MarketComponents/components/OrderGradeBar.d.ts +2 -1
- package/lib/typescript/lib/commonjs/MarketComponents/components/OrderGradeBar.d.ts.map +1 -1
- package/lib/typescript/lib/commonjs/index.d.ts +1 -0
- package/lib/typescript/lib/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/lib/module/BetMatch/api/index.d.ts +18 -0
- package/lib/typescript/lib/module/BetMatch/api/index.d.ts.map +1 -0
- package/lib/typescript/lib/module/BetMatch/components/BetMatchCard.d.ts +12 -0
- package/lib/typescript/lib/module/BetMatch/components/BetMatchCard.d.ts.map +1 -0
- package/lib/typescript/lib/module/BetMatch/components/BetMatchCardSmall.d.ts +12 -0
- package/lib/typescript/lib/module/BetMatch/components/BetMatchCardSmall.d.ts.map +1 -0
- package/lib/typescript/lib/module/BetMatch/index.d.ts +6 -0
- package/lib/typescript/lib/module/BetMatch/index.d.ts.map +1 -0
- package/lib/typescript/lib/module/Components/AutoScrollFlatList.d.ts +14 -0
- package/lib/typescript/lib/module/Components/AutoScrollFlatList.d.ts.map +1 -0
- package/lib/typescript/lib/module/Components/Icons.d.ts +5 -0
- package/lib/typescript/lib/module/Components/Icons.d.ts.map +1 -1
- package/lib/typescript/lib/module/Components/index.d.ts +2 -1
- package/lib/typescript/lib/module/Components/index.d.ts.map +1 -1
- package/lib/typescript/lib/module/FlashMarket/FlashHolder.d.ts +2 -1
- package/lib/typescript/lib/module/FlashMarket/FlashHolder.d.ts.map +1 -1
- package/lib/typescript/lib/module/MarketComponents/components/MyOrderList/api/index.d.ts +3 -0
- package/lib/typescript/lib/module/MarketComponents/components/MyOrderList/api/index.d.ts.map +1 -1
- package/lib/typescript/lib/module/MarketComponents/components/OrderGradeBar.d.ts +2 -1
- package/lib/typescript/lib/module/MarketComponents/components/OrderGradeBar.d.ts.map +1 -1
- package/lib/typescript/lib/module/index.d.ts +2 -1
- package/lib/typescript/lib/module/index.d.ts.map +1 -1
- package/lib/typescript/src/BetMatch/api/index.d.ts +19 -0
- package/lib/typescript/src/BetMatch/api/index.d.ts.map +1 -0
- package/lib/typescript/src/BetMatch/components/BetMatchCard.d.ts +16 -0
- package/lib/typescript/src/BetMatch/components/BetMatchCard.d.ts.map +1 -0
- package/lib/typescript/src/BetMatch/components/BetMatchCardSmall.d.ts +15 -0
- package/lib/typescript/src/BetMatch/components/BetMatchCardSmall.d.ts.map +1 -0
- package/lib/typescript/src/BetMatch/index.d.ts +9 -0
- package/lib/typescript/src/BetMatch/index.d.ts.map +1 -0
- package/lib/typescript/src/Components/AutoScrollFlatList.d.ts +18 -0
- package/lib/typescript/src/Components/AutoScrollFlatList.d.ts.map +1 -0
- package/lib/typescript/src/Components/Icons.d.ts +1 -0
- package/lib/typescript/src/Components/Icons.d.ts.map +1 -1
- package/lib/typescript/src/Components/index.d.ts +2 -1
- package/lib/typescript/src/Components/index.d.ts.map +1 -1
- package/lib/typescript/src/FlashMarket/FlashHolder.d.ts +2 -1
- package/lib/typescript/src/FlashMarket/FlashHolder.d.ts.map +1 -1
- package/lib/typescript/src/MarketComponents/components/MyOrderList/api/index.d.ts +3 -0
- package/lib/typescript/src/MarketComponents/components/MyOrderList/api/index.d.ts.map +1 -1
- package/lib/typescript/src/MarketComponents/components/OrderGradeBar.d.ts +2 -1
- package/lib/typescript/src/MarketComponents/components/OrderGradeBar.d.ts.map +1 -1
- package/lib/typescript/src/MarketComponents/index.d.ts +2 -1
- package/lib/typescript/src/MarketComponents/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/BetMatch/api/index.ts +145 -0
- package/src/BetMatch/components/BetMatchCard.tsx +227 -0
- package/src/BetMatch/components/BetMatchCardSmall.tsx +70 -0
- package/src/BetMatch/index.tsx +192 -0
- package/src/Components/AutoScrollFlatList.tsx +169 -0
- package/src/Components/Icons.tsx +17 -0
- package/src/Components/index.tsx +3 -0
- package/src/FlashMarket/FlashHolder.tsx +8 -5
- package/src/MarketComponents/components/MyOrderList/api/index.ts +4 -1
- package/src/MarketComponents/components/OrderGradeBar.tsx +3 -2
- package/src/index.tsx +3 -0
- package/src/types.d.ts +36 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { Button, Text, View } from "../../Components/Themed"
|
|
3
|
+
import { FlatList, Image } from 'react-native';
|
|
4
|
+
import type { AthleteProps, ExternalPriceProps, LeagueProps, OrderProps, PublicPlayerProps, TeamProps } from '../../types';
|
|
5
|
+
import moment from 'moment-mini';
|
|
6
|
+
import { AthleteImage, TeamImage } from '../../Components/Jerseys';
|
|
7
|
+
import OrderGradeBar from '../../MarketComponents/components/OrderGradeBar';
|
|
8
|
+
import { MarketComponentApi, MarketComponentHelpers } from '../../MarketComponents/api';
|
|
9
|
+
import { Icons } from '../../Components';
|
|
10
|
+
import { useColors } from '../../constants/useColors';
|
|
11
|
+
import Pagination from '../../Components/Pagination';
|
|
12
|
+
|
|
13
|
+
type BetMatchCardProps = {
|
|
14
|
+
height:number,
|
|
15
|
+
player:PublicPlayerProps,
|
|
16
|
+
contest_title:string,
|
|
17
|
+
time_detail:string,
|
|
18
|
+
league?:LeagueProps,
|
|
19
|
+
team?:TeamProps,
|
|
20
|
+
athlete?:AthleteProps,
|
|
21
|
+
order:OrderProps,
|
|
22
|
+
onOrder:(order:OrderProps) => void
|
|
23
|
+
}
|
|
24
|
+
const sections = ['header', 'page', 'offer', 'details','available', 'actions']
|
|
25
|
+
const BetMatchCard = ({ order, height, player, contest_title, league, team, athlete }:BetMatchCardProps) => {
|
|
26
|
+
const Colors = useColors();
|
|
27
|
+
const [ betmatch, setBetMatch ] = useState<{
|
|
28
|
+
loading:boolean,
|
|
29
|
+
external_price?:ExternalPriceProps
|
|
30
|
+
}>({
|
|
31
|
+
loading:false
|
|
32
|
+
})
|
|
33
|
+
const { external_price } = betmatch;
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
MarketComponentApi.setEnvironment();
|
|
37
|
+
getExternalPrice()
|
|
38
|
+
},[order.order_id]);
|
|
39
|
+
|
|
40
|
+
const getExternalPrice = async() => {
|
|
41
|
+
setBetMatch({ loading:true });
|
|
42
|
+
let external_prices = await MarketComponentApi.getExternalPrices(order.event_type, order.event_id, order.market_id, order.side);
|
|
43
|
+
if(order.side_type == 'athlete'){
|
|
44
|
+
external_prices = external_prices.filter(p => p.participant_id == order.side_id);
|
|
45
|
+
}
|
|
46
|
+
if(order.side_type == 'team'){
|
|
47
|
+
external_prices = external_prices.filter(p => p.participant_id == order.side_id);
|
|
48
|
+
}
|
|
49
|
+
//Find consensus!
|
|
50
|
+
let consensus_price = external_prices.find(p => p.external_name.toLowerCase() == 'consensus');
|
|
51
|
+
if(!consensus_price){ consensus_price = external_prices[0] }
|
|
52
|
+
setBetMatch({
|
|
53
|
+
loading:false,
|
|
54
|
+
external_price: consensus_price
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const renderSections = (data:{ item:string, index:number }) => {
|
|
59
|
+
switch(data.item){
|
|
60
|
+
case 'header':
|
|
61
|
+
return (
|
|
62
|
+
<View type='header' style={{ flexDirection:'row', alignItems:'center', padding:10, borderTopRightRadius:8, borderTopLeftRadius:8 }}>
|
|
63
|
+
<View transparent style={{ flex:1, marginRight:10 }}>
|
|
64
|
+
<Text theme='h1'>{contest_title}</Text>
|
|
65
|
+
<Text theme='description' style={{ marginTop:4 }}>Offer expires {moment(order.expire_datetime).fromNow()}</Text>
|
|
66
|
+
</View>
|
|
67
|
+
<View transparent style={{ alignItems:'flex-end' }}>
|
|
68
|
+
<Text theme='h1' color={Colors.text.success}>${order.open_amt.toFixed(2)}</Text>
|
|
69
|
+
<Text theme='description' style={{ marginTop:3 }}>Available</Text>
|
|
70
|
+
</View>
|
|
71
|
+
</View>
|
|
72
|
+
)
|
|
73
|
+
case 'offer':
|
|
74
|
+
return (
|
|
75
|
+
<View transparent type='row' style={{ margin:15 }}>
|
|
76
|
+
<View float style={{ borderRadius:100, padding:2 }}>
|
|
77
|
+
{athlete ?
|
|
78
|
+
<AthleteImage
|
|
79
|
+
athlete={athlete}
|
|
80
|
+
size={36}
|
|
81
|
+
/>
|
|
82
|
+
:team ?
|
|
83
|
+
<TeamImage
|
|
84
|
+
team={team}
|
|
85
|
+
size={14}
|
|
86
|
+
/>
|
|
87
|
+
:league ?
|
|
88
|
+
<Image
|
|
89
|
+
source={{ uri: league.league_image }}
|
|
90
|
+
style={{ height:36, width:36, borderRadius:100 }}
|
|
91
|
+
resizeMode='cover'
|
|
92
|
+
/>
|
|
93
|
+
:<></>
|
|
94
|
+
}
|
|
95
|
+
</View>
|
|
96
|
+
<View transparent style={{ flex:1, marginLeft:10 }}>
|
|
97
|
+
<Text theme='h1' size={22}>{order.title}</Text>
|
|
98
|
+
<Text theme='description' style={{ marginTop:3 }}>Offer expires {moment(order.expire_datetime).fromNow()}</Text>
|
|
99
|
+
</View>
|
|
100
|
+
</View>
|
|
101
|
+
)
|
|
102
|
+
case 'details':
|
|
103
|
+
return (
|
|
104
|
+
<View transparent style={{ margin:15 }}>
|
|
105
|
+
<View transparent type='row' style={{ padding:15 }}>
|
|
106
|
+
<View transparent style={{ flex:1 }}>
|
|
107
|
+
<Text theme='description'>Offer Probability</Text>
|
|
108
|
+
<Text theme='h1' size={18} style={{ marginTop:4 }}>{(order.probability * 100).toFixed(2)}%</Text>
|
|
109
|
+
</View>
|
|
110
|
+
<View transparent style={{ flex:1 }}>
|
|
111
|
+
<Text theme='description'>Offer Odds</Text>
|
|
112
|
+
<Text theme='h1' size={18} style={{ marginTop:4 }}>{MarketComponentHelpers.getOddsLabel(order.odds)}</Text>
|
|
113
|
+
</View>
|
|
114
|
+
</View>
|
|
115
|
+
{external_price ?
|
|
116
|
+
<View transparent type='row' style={{ padding:15 }}>
|
|
117
|
+
<View transparent style={{ flex:1 }}>
|
|
118
|
+
<Text theme='description'>External Price</Text>
|
|
119
|
+
<Text theme='h1' size={18} style={{ marginTop:4 }}>{external_price.external_name}</Text>
|
|
120
|
+
</View>
|
|
121
|
+
<View transparent style={{ flex:1 }}>
|
|
122
|
+
<Text theme='description'>Suggested Odds</Text>
|
|
123
|
+
<Text theme='h1' size={18} style={{ marginTop:4 }}>{MarketComponentHelpers.getOddsLabel(external_price.odds)}</Text>
|
|
124
|
+
</View>
|
|
125
|
+
</View>
|
|
126
|
+
:<></>}
|
|
127
|
+
<View transparent type='row' style={{ padding:15 }}>
|
|
128
|
+
<View transparent style={{ flex:1 }}>
|
|
129
|
+
<Text theme='description'>Offer Grade</Text>
|
|
130
|
+
{!order?.grade ?
|
|
131
|
+
<Text style={{ marginTop:3 }} theme="h1">N/A</Text>
|
|
132
|
+
:order.grade > 105 ?
|
|
133
|
+
<Text style={{ marginTop:3 }} theme="h1" color={Colors.text.success}>Amazing Price!</Text>
|
|
134
|
+
: order.grade > 98 ?
|
|
135
|
+
<Text style={{ marginTop:3 }} theme="h1" color={Colors.text.success}>Very Competitive!</Text>
|
|
136
|
+
: order.grade > 95 ?
|
|
137
|
+
<Text style={{ marginTop:3 }} theme="h1" color={Colors.text.warning}>Somewhat Competitive.</Text>
|
|
138
|
+
:
|
|
139
|
+
<Text style={{ marginTop:3 }} theme="h1" color={Colors.text.error}>Not Competitive.</Text>
|
|
140
|
+
}
|
|
141
|
+
<Text theme='h1' size={18} style={{ marginTop:4 }}></Text>
|
|
142
|
+
</View>
|
|
143
|
+
<OrderGradeBar grade={order.grade} view_type='card' />
|
|
144
|
+
</View>
|
|
145
|
+
</View>
|
|
146
|
+
)
|
|
147
|
+
case 'available':
|
|
148
|
+
return (
|
|
149
|
+
<View transparent style={{ margin:15 }}>
|
|
150
|
+
<View transparent type='row' style={{ padding:15 }}>
|
|
151
|
+
<View transparent style={{ flex:1 }}>
|
|
152
|
+
<Text theme='description'>Available</Text>
|
|
153
|
+
<Text theme='h1' size={18} style={{ marginTop:4 }}>${order.open_amt.toFixed(2)}</Text>
|
|
154
|
+
</View>
|
|
155
|
+
<View transparent style={{ flex:1 }}>
|
|
156
|
+
<Text theme='description'>Potential Winnings</Text>
|
|
157
|
+
<Text theme='h1' size={18} style={{ marginTop:4 }}>${order.potential_winnings.toFixed(2)}</Text>
|
|
158
|
+
</View>
|
|
159
|
+
</View>
|
|
160
|
+
</View>
|
|
161
|
+
)
|
|
162
|
+
default: return <></>
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<View style={{ flex:1, height }}>
|
|
168
|
+
<View float transparent style={{ flex:1, marginBottom:30 }}>
|
|
169
|
+
<View float type='footer' style={{ position:'absolute', left:0, right:0, bottom: -30, height:50, margin:0, borderRadius:8 }} />
|
|
170
|
+
<View float type='footer' style={{ position:'absolute', left:0, right:0, bottom: -15, height:50, margin:-5, borderRadius:8 }} />
|
|
171
|
+
<View transparent style={{ flex:1 }}>
|
|
172
|
+
<FlatList
|
|
173
|
+
data={sections}
|
|
174
|
+
key={'bet_match_card_sections'}
|
|
175
|
+
keyExtractor={(item) => item}
|
|
176
|
+
renderItem={renderSections}
|
|
177
|
+
/>
|
|
178
|
+
</View>
|
|
179
|
+
<View type='footer' style={{ flexDirection:'row', padding:5 }}>
|
|
180
|
+
<Button float style={{ flex:1, marginRight:4, flexDirection:'row', justifyContent:'center', alignItems:'center' }}>
|
|
181
|
+
<Icons.CloseIcon size={14} color={Colors.text.error}/>
|
|
182
|
+
<Text theme='h2' style={{ marginLeft:10 }} color={Colors.text.error}>Decline</Text>
|
|
183
|
+
</Button>
|
|
184
|
+
<Button float style={{ flex:1, marginLeft:4, flexDirection:'row', justifyContent:'center', alignItems:'center' }}>
|
|
185
|
+
<Icons.CheckIcon size={14} color={Colors.text.success}/>
|
|
186
|
+
<Text theme='h2' style={{ marginLeft:10 }} color={Colors.text.success}>Accept</Text>
|
|
187
|
+
</Button>
|
|
188
|
+
</View>
|
|
189
|
+
<View float type='footer' style={{ flexDirection:'row', alignItems:'center', borderRadius:8, margin:-10, marginTop:0, padding:10 }}>
|
|
190
|
+
<View float style={{ padding:2, borderRadius:100 }}>
|
|
191
|
+
<Image
|
|
192
|
+
source={{ uri: player.profile_pic && player.profile_pic != '' ? player.profile_pic : 'https://res.cloudinary.com/hoabts6mc/image/upload/v1722453927/default_man_n96ofq.webp' }}
|
|
193
|
+
style={{ height:40, width:40, borderRadius:100}}
|
|
194
|
+
resizeMode='cover'
|
|
195
|
+
/>
|
|
196
|
+
</View>
|
|
197
|
+
<View transparent style={{ flex:1, marginLeft:15 }}>
|
|
198
|
+
<Text theme='h1'>@{player.username}</Text>
|
|
199
|
+
<Text theme='description' style={{ marginTop:3 }}>Joined {moment(player.create_datetime).fromNow()}</Text>
|
|
200
|
+
</View>
|
|
201
|
+
<View transparent style={{ alignItems:'center' }}>
|
|
202
|
+
<Icons.LocationIcon size={18} color={Colors.text.h1}/>
|
|
203
|
+
<Text theme='h2'>{order.region}</Text>
|
|
204
|
+
</View>
|
|
205
|
+
</View>
|
|
206
|
+
</View>
|
|
207
|
+
<View transparent style={{ padding:5 }}>
|
|
208
|
+
<Pagination
|
|
209
|
+
pages={10}
|
|
210
|
+
onNext={() => console.log('')}
|
|
211
|
+
onPrevious={() => console.log('')}
|
|
212
|
+
offset={0}
|
|
213
|
+
/>
|
|
214
|
+
</View>
|
|
215
|
+
</View>
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/*
|
|
220
|
+
<Image
|
|
221
|
+
source={{ uri: 'https://res.cloudinary.com/hoabts6mc/image/upload/v1647289869/ellipse_background_fhh1ut.webp' }}
|
|
222
|
+
style={{ height:400, width:400 }}
|
|
223
|
+
resizeMode='cover'
|
|
224
|
+
/>
|
|
225
|
+
*/
|
|
226
|
+
|
|
227
|
+
export default BetMatchCard
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Button, Text, View } from "../../Components/Themed"
|
|
3
|
+
import type { AthleteProps, LeagueProps, OrderProps, PublicPlayerProps, TeamProps } from "../../types"
|
|
4
|
+
import { useColors } from '../../constants/useColors';
|
|
5
|
+
import { MarketComponentHelpers } from '../../MarketComponents/api';
|
|
6
|
+
import { Image } from 'react-native';
|
|
7
|
+
import { AthleteImage, TeamImage } from '../../Components/Jerseys';
|
|
8
|
+
import OrderGradeBar from '../../MarketComponents/components/OrderGradeBar';
|
|
9
|
+
|
|
10
|
+
type BetMatchCardSmallProps = {
|
|
11
|
+
player:PublicPlayerProps,
|
|
12
|
+
contest_title:string,
|
|
13
|
+
time_detail:string,
|
|
14
|
+
league?:LeagueProps,
|
|
15
|
+
team?:TeamProps,
|
|
16
|
+
athlete?:AthleteProps,
|
|
17
|
+
order:OrderProps,
|
|
18
|
+
onOrder:(order:OrderProps) => void
|
|
19
|
+
}
|
|
20
|
+
const BetMatchCardSmall = ({ contest_title, time_detail, team, athlete, league, order, onOrder }:BetMatchCardSmallProps) => {
|
|
21
|
+
const Colors = useColors();
|
|
22
|
+
return (
|
|
23
|
+
<Button float style={{ flex:1, padding:0 }} onPress={() => onOrder(order)}>
|
|
24
|
+
<View type='header' style={{ flexDirection:'row', alignItems:'center', padding:5, borderTopRightRadius:8, borderTopLeftRadius:8 }}>
|
|
25
|
+
<View float style={{ borderRadius:100, padding:2 }}>
|
|
26
|
+
{athlete ?
|
|
27
|
+
<AthleteImage
|
|
28
|
+
athlete={athlete}
|
|
29
|
+
size={36}
|
|
30
|
+
/>
|
|
31
|
+
:team ?
|
|
32
|
+
<TeamImage
|
|
33
|
+
team={team}
|
|
34
|
+
size={14}
|
|
35
|
+
/>
|
|
36
|
+
:league ?
|
|
37
|
+
<Image
|
|
38
|
+
source={{ uri: league.league_image }}
|
|
39
|
+
style={{ height:36, width:36, borderRadius:100 }}
|
|
40
|
+
resizeMode='cover'
|
|
41
|
+
/>
|
|
42
|
+
:<></>
|
|
43
|
+
}
|
|
44
|
+
</View>
|
|
45
|
+
<View transparent style={{ flex:1, marginLeft:10 }}>
|
|
46
|
+
<Text theme='description' size={12}>{order.title}</Text>
|
|
47
|
+
<Text theme='h1' size={12} color={Colors.text.success} style={{ marginTop:3 }}>${order.open_amt.toFixed(2)} Available</Text>
|
|
48
|
+
</View>
|
|
49
|
+
</View>
|
|
50
|
+
<View type='body' style={{ flex:1 }}>
|
|
51
|
+
|
|
52
|
+
<View transparent type='row' style={{ padding:5, borderBottomWidth:1, borderColor:Colors.borders.light }}>
|
|
53
|
+
<View style={{ flex:1 }}>
|
|
54
|
+
<Text theme='h2' size={12}>Offer Odds:</Text>
|
|
55
|
+
<OrderGradeBar style={{ alignItems:'flex-start', marginTop:5 }} grade={order.grade} view_type='card' />
|
|
56
|
+
</View>
|
|
57
|
+
<Text theme='h1'>{MarketComponentHelpers.getOddsLabel(order.odds)}</Text>
|
|
58
|
+
</View>
|
|
59
|
+
</View>
|
|
60
|
+
<View type='footer' style={{ flexDirection:'row', alignItems:'center', borderBottomLeftRadius:8, borderBottomRightRadius:8, padding:5 }}>
|
|
61
|
+
<View transparent style={{ flex:1 }}>
|
|
62
|
+
<Text theme='h2'>{contest_title}</Text>
|
|
63
|
+
<Text theme='light' style={{ marginTop:3 }}>{time_detail}</Text>
|
|
64
|
+
</View>
|
|
65
|
+
</View>
|
|
66
|
+
</Button>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export default BetMatchCardSmall
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { Text, View } from "../Components/Themed";
|
|
3
|
+
import { MyOrdersHelpers } from '../MarketComponents/components/MyOrderList/api';
|
|
4
|
+
import type { AthleteProps, EventProps, LeagueProps, MarketProps, MatchProps, OrderProps, PlayerFilterProps, PublicPlayerProps, TeamProps, TournamentProps } from '../types';
|
|
5
|
+
import { BetMatchApi } from './api';
|
|
6
|
+
import { ActivityIndicator, FlatList } from 'react-native';
|
|
7
|
+
import BetMatchCardSmall from './components/BetMatchCardSmall';
|
|
8
|
+
import { SocialOrderHelpers } from '../SocialComponents/api';
|
|
9
|
+
import moment from 'moment-mini';
|
|
10
|
+
import { useColors } from '../constants/useColors';
|
|
11
|
+
import BetMatchCard from './components/BetMatchCard';
|
|
12
|
+
|
|
13
|
+
type BetMatchProps = {
|
|
14
|
+
player_filter_id:string,
|
|
15
|
+
style?:any,
|
|
16
|
+
player_id:string
|
|
17
|
+
}
|
|
18
|
+
const sections = ['filter','orders', 'bet']
|
|
19
|
+
const BetMatch = ({ player_filter_id }:BetMatchProps) => {
|
|
20
|
+
const Colors = useColors();
|
|
21
|
+
const [ module_size, setModuleSize ] = useState({ width:0, height:0 });
|
|
22
|
+
const [ viewing_order_id, setViewingOrderId ] = useState<string|undefined>(undefined);
|
|
23
|
+
const [ static_data, setStaticData ] = useState<{
|
|
24
|
+
static_loading:boolean,
|
|
25
|
+
leagues:LeagueProps[],
|
|
26
|
+
markets:MarketProps[],
|
|
27
|
+
}>({
|
|
28
|
+
static_loading: false, leagues:[], markets:[]
|
|
29
|
+
});
|
|
30
|
+
const { leagues } = static_data;
|
|
31
|
+
const [ filter_data, setFilterData ] = useState<{
|
|
32
|
+
loading:boolean,
|
|
33
|
+
player_filter?:PlayerFilterProps,
|
|
34
|
+
orders:OrderProps[],
|
|
35
|
+
}>({
|
|
36
|
+
loading:false, orders:[]
|
|
37
|
+
});
|
|
38
|
+
const { loading, player_filter, orders } = filter_data;
|
|
39
|
+
const viewing_order = orders.find(o => o.order_id == viewing_order_id);
|
|
40
|
+
const [ contest_data, setContestData ] = useState<{
|
|
41
|
+
contest_loading:boolean,
|
|
42
|
+
players:PublicPlayerProps[],
|
|
43
|
+
events:EventProps[],
|
|
44
|
+
tournaments:TournamentProps[],
|
|
45
|
+
matches:MatchProps[],
|
|
46
|
+
teams:TeamProps[],
|
|
47
|
+
athletes:AthleteProps[]
|
|
48
|
+
}>({
|
|
49
|
+
contest_loading: false, players: [], events: [], tournaments: [], matches: [], teams:[], athletes: []
|
|
50
|
+
});
|
|
51
|
+
const { players, events, tournaments, matches, teams, athletes } = contest_data;
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
BetMatchApi.setEnvironment()
|
|
55
|
+
runFilter(player_filter_id);
|
|
56
|
+
getStaticData();
|
|
57
|
+
},[player_filter_id]);
|
|
58
|
+
|
|
59
|
+
const runFilter = async(id:string) => {
|
|
60
|
+
setFilterData({ ...filter_data, loading: true });
|
|
61
|
+
const pf = await BetMatchApi.getPlayerFilterById(id);
|
|
62
|
+
if(!pf){ return }
|
|
63
|
+
const filter_orders = await BetMatchApi.runPlayerFilter(id);
|
|
64
|
+
setFilterData({
|
|
65
|
+
...filter_data,
|
|
66
|
+
orders: filter_orders,
|
|
67
|
+
player_filter: pf,
|
|
68
|
+
loading: false
|
|
69
|
+
});
|
|
70
|
+
getContestData(filter_orders);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const getStaticData = async() => {
|
|
74
|
+
setStaticData({ ...static_data, static_loading:true });
|
|
75
|
+
const lgs = await BetMatchApi.getLeagues();
|
|
76
|
+
const makets = await BetMatchApi.getMarkets();
|
|
77
|
+
setStaticData({
|
|
78
|
+
...static_data,
|
|
79
|
+
static_loading:true,
|
|
80
|
+
leagues: lgs,
|
|
81
|
+
markets: makets
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const getContestData = async(orders:OrderProps[]) => {
|
|
86
|
+
setContestData({ ...contest_data, contest_loading:true });
|
|
87
|
+
const { event_ids, tournament_ids, match_ids, athlete_ids, team_ids, player_ids } = MyOrdersHelpers.getContestsFromOrders(orders);
|
|
88
|
+
const plyers = await BetMatchApi.getPlayersByPlayerIds(player_ids);
|
|
89
|
+
const evs = await BetMatchApi.getEventsByEventIds(event_ids);
|
|
90
|
+
const tourns = await BetMatchApi.getTournamentsByTournamentIds(tournament_ids);
|
|
91
|
+
const matchs = await BetMatchApi.getMatchesByMatchIds(match_ids);
|
|
92
|
+
const aths = await BetMatchApi.getAthletesByIds(athlete_ids);
|
|
93
|
+
const tms = await BetMatchApi.getTeamsByIds(team_ids);
|
|
94
|
+
setContestData({
|
|
95
|
+
...contest_data,
|
|
96
|
+
players: plyers,
|
|
97
|
+
events: evs,
|
|
98
|
+
tournaments: tourns,
|
|
99
|
+
matches: matchs,
|
|
100
|
+
teams: tms,
|
|
101
|
+
athletes: aths
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const renderOrders = (data:{ item:OrderProps, index:number }) => {
|
|
106
|
+
const player = players.find(p => p.player_id == data.item.player_id);
|
|
107
|
+
if(!player){ return <></> }
|
|
108
|
+
const { team, athlete, league, title, time } = SocialOrderHelpers.getContestDetails(data.item, events, tournaments, matches, athletes, teams, leagues)
|
|
109
|
+
return (
|
|
110
|
+
<View transparent key={data.item.order_id.toString()} style={{ width:150, maxWidth:250, flexGrow:1, margin:7 }}>
|
|
111
|
+
<BetMatchCardSmall
|
|
112
|
+
player={player}
|
|
113
|
+
team={team}
|
|
114
|
+
athlete={athlete}
|
|
115
|
+
contest_title={title}
|
|
116
|
+
time_detail={time}
|
|
117
|
+
league={league}
|
|
118
|
+
order={data.item}
|
|
119
|
+
onOrder={(order) => setViewingOrderId(order.order_id)}
|
|
120
|
+
/>
|
|
121
|
+
</View>
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const renderSections = (data:{item:string, index:number}) => {
|
|
126
|
+
switch(data.item){
|
|
127
|
+
case 'filter':
|
|
128
|
+
if(!player_filter){ return <></> }
|
|
129
|
+
if(viewing_order){ return <></> }
|
|
130
|
+
return (
|
|
131
|
+
<View type='header' style={{ flexDirection:'row', alignItems:'center', padding:10 }}>
|
|
132
|
+
<View transparent style={{ flex:1, marginRight:10 }}>
|
|
133
|
+
<Text theme='h1'>{player_filter.name}</Text>
|
|
134
|
+
<Text theme='description' style={{ marginTop:3 }}>Created {moment(player_filter.create_datetime).fromNow()}</Text>
|
|
135
|
+
</View>
|
|
136
|
+
<View transparent style={{ alignItems:'flex-end' }}>
|
|
137
|
+
<Text theme='h1'>{orders.length}</Text>
|
|
138
|
+
<Text theme='description' style={{ marginTop:3 }}>Offers</Text>
|
|
139
|
+
</View>
|
|
140
|
+
</View>
|
|
141
|
+
)
|
|
142
|
+
case 'orders':
|
|
143
|
+
if(viewing_order){ return <></> }
|
|
144
|
+
return (
|
|
145
|
+
<View style={{ flexDirection:'row', flexWrap:'wrap' }}>
|
|
146
|
+
{orders.map((item, index) => {
|
|
147
|
+
return renderOrders({ item, index })
|
|
148
|
+
})}
|
|
149
|
+
</View>
|
|
150
|
+
)
|
|
151
|
+
case 'bet':
|
|
152
|
+
if(!viewing_order){ return <></> }
|
|
153
|
+
const player = players.find(p => p.player_id == viewing_order.player_id);
|
|
154
|
+
if(!player){ return <></> }
|
|
155
|
+
const { team, athlete, league, title, time } = SocialOrderHelpers.getContestDetails(viewing_order, events, tournaments, matches, athletes, teams, leagues)
|
|
156
|
+
return (
|
|
157
|
+
<View style={{ flex:1, padding:20 }}>
|
|
158
|
+
<BetMatchCard
|
|
159
|
+
player={player}
|
|
160
|
+
height={module_size.height - 60}
|
|
161
|
+
order={viewing_order}
|
|
162
|
+
team={team}
|
|
163
|
+
athlete={athlete}
|
|
164
|
+
league={league}
|
|
165
|
+
time_detail={time}
|
|
166
|
+
contest_title={title}
|
|
167
|
+
onOrder={(order) => console.log(order)}
|
|
168
|
+
/>
|
|
169
|
+
</View>
|
|
170
|
+
)
|
|
171
|
+
default: return <></>
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return (
|
|
175
|
+
<View style={{ flex:1 }} onLayout={(ev) => {
|
|
176
|
+
const { width, height } = ev.nativeEvent.layout;
|
|
177
|
+
setModuleSize({ width, height })
|
|
178
|
+
}}>
|
|
179
|
+
{loading ?
|
|
180
|
+
<ActivityIndicator size='large' color={Colors.text.h1} />
|
|
181
|
+
:<></>}
|
|
182
|
+
<FlatList
|
|
183
|
+
data={sections}
|
|
184
|
+
key={'filter_sections'}
|
|
185
|
+
keyExtractor={item => item}
|
|
186
|
+
renderItem={renderSections}
|
|
187
|
+
/>
|
|
188
|
+
</View>
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export default BetMatch
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
FlatList,
|
|
4
|
+
View,
|
|
5
|
+
type LayoutChangeEvent,
|
|
6
|
+
type ViewStyle,
|
|
7
|
+
type NativeScrollEvent,
|
|
8
|
+
type NativeSyntheticEvent,
|
|
9
|
+
Platform,
|
|
10
|
+
} from 'react-native';
|
|
11
|
+
|
|
12
|
+
type AutoScrollingFlatListProps<T> = {
|
|
13
|
+
data: T[];
|
|
14
|
+
renderItem: ({ item, index }: { item: T; index: number }) => React.ReactElement;
|
|
15
|
+
horizontal?: boolean;
|
|
16
|
+
style?: ViewStyle;
|
|
17
|
+
speed: number; // pixels per second
|
|
18
|
+
minDuration?: number; // milliseconds (optional)
|
|
19
|
+
onCompleteScroll?: (completed: boolean) => void;
|
|
20
|
+
resetKey?: string | number; // used to reset internal state
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const AutoScrollingFlatList = <T,>({
|
|
24
|
+
data,
|
|
25
|
+
renderItem,
|
|
26
|
+
horizontal = false,
|
|
27
|
+
style,
|
|
28
|
+
speed,
|
|
29
|
+
minDuration = 0,
|
|
30
|
+
onCompleteScroll,
|
|
31
|
+
resetKey,
|
|
32
|
+
}: AutoScrollingFlatListProps<T>) => {
|
|
33
|
+
const listRef = useRef<FlatList<T>>(null);
|
|
34
|
+
const itemSizes = useRef<(number | undefined)[]>([]);
|
|
35
|
+
const totalSize = useRef(0);
|
|
36
|
+
const offset = useRef(0);
|
|
37
|
+
const scrollTimer = useRef<NodeJS.Timeout | null>(null);
|
|
38
|
+
const scrollStartTime = useRef<number | null>(null);
|
|
39
|
+
|
|
40
|
+
const [ready, setReady] = useState(false);
|
|
41
|
+
const [isPaused, setIsPaused] = useState(false); // replaces isHovered
|
|
42
|
+
const [containerSize, setContainerSize] = useState(0);
|
|
43
|
+
|
|
44
|
+
const handleLayout = (e: LayoutChangeEvent) => {
|
|
45
|
+
const size = horizontal
|
|
46
|
+
? e.nativeEvent.layout.width
|
|
47
|
+
: e.nativeEvent.layout.height;
|
|
48
|
+
setContainerSize(size);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const updateItemLayout = (index: number, e: LayoutChangeEvent) => {
|
|
52
|
+
const size = horizontal
|
|
53
|
+
? e.nativeEvent.layout.width
|
|
54
|
+
: e.nativeEvent.layout.height;
|
|
55
|
+
|
|
56
|
+
itemSizes.current[index] = size;
|
|
57
|
+
|
|
58
|
+
const allMeasured =
|
|
59
|
+
itemSizes.current.length === data.length &&
|
|
60
|
+
itemSizes.current.every((v) => typeof v === 'number');
|
|
61
|
+
|
|
62
|
+
if (allMeasured && !ready) {
|
|
63
|
+
totalSize.current = itemSizes.current
|
|
64
|
+
.filter((v): v is number => typeof v === 'number')
|
|
65
|
+
.reduce((acc, val) => acc + val, 0);
|
|
66
|
+
setReady(true);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const handleScroll = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
71
|
+
const currentOffset = horizontal
|
|
72
|
+
? e.nativeEvent.contentOffset.x
|
|
73
|
+
: e.nativeEvent.contentOffset.y;
|
|
74
|
+
|
|
75
|
+
offset.current = currentOffset;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
const isWeb = Platform.OS === 'web';
|
|
80
|
+
|
|
81
|
+
if (!ready || (isWeb && isPaused) || speed <= 0 || containerSize === 0) return;
|
|
82
|
+
|
|
83
|
+
const frameInterval = 16;
|
|
84
|
+
const pixelsPerFrame = (speed * frameInterval) / 1000;
|
|
85
|
+
scrollStartTime.current = Date.now();
|
|
86
|
+
|
|
87
|
+
scrollTimer.current = setInterval(() => {
|
|
88
|
+
offset.current += pixelsPerFrame;
|
|
89
|
+
|
|
90
|
+
const maxOffset = totalSize.current - containerSize;
|
|
91
|
+
const now = Date.now();
|
|
92
|
+
const reachedEnd = offset.current >= maxOffset;
|
|
93
|
+
const minTimeElapsed = now - (scrollStartTime.current ?? 0) >= minDuration;
|
|
94
|
+
|
|
95
|
+
if (reachedEnd) {
|
|
96
|
+
if (minTimeElapsed) {
|
|
97
|
+
offset.current = 0;
|
|
98
|
+
scrollStartTime.current = Date.now();
|
|
99
|
+
listRef.current?.scrollToOffset({ offset: 0, animated: false });
|
|
100
|
+
if (onCompleteScroll) onCompleteScroll(true);
|
|
101
|
+
} else {
|
|
102
|
+
offset.current = maxOffset;
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
listRef.current?.scrollToOffset({
|
|
106
|
+
offset: offset.current,
|
|
107
|
+
animated: false,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}, frameInterval);
|
|
111
|
+
|
|
112
|
+
return () => {
|
|
113
|
+
if (scrollTimer.current) clearInterval(scrollTimer.current);
|
|
114
|
+
};
|
|
115
|
+
}, [ready, speed, isPaused, containerSize, onCompleteScroll, minDuration]);
|
|
116
|
+
|
|
117
|
+
// Reset state/refs on resetKey change
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
if (scrollTimer.current) clearInterval(scrollTimer.current);
|
|
120
|
+
offset.current = 0;
|
|
121
|
+
scrollStartTime.current = null;
|
|
122
|
+
itemSizes.current = [];
|
|
123
|
+
totalSize.current = 0;
|
|
124
|
+
setReady(false);
|
|
125
|
+
setIsPaused(false);
|
|
126
|
+
}, [resetKey]);
|
|
127
|
+
|
|
128
|
+
const wrappedRenderItem = ({ item, index }: { item: T; index: number }) => (
|
|
129
|
+
<View onLayout={(e) => updateItemLayout(index, e)} style={{ flexShrink: 0 }}>
|
|
130
|
+
{renderItem({ item, index })}
|
|
131
|
+
</View>
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const wrapperProps =
|
|
135
|
+
Platform.OS === 'web'
|
|
136
|
+
? {
|
|
137
|
+
onMouseEnter: () => setIsPaused(true),
|
|
138
|
+
onMouseLeave: () => setIsPaused(false),
|
|
139
|
+
onTouchStart: () => setIsPaused(true), // pause on mobile tap
|
|
140
|
+
onTouchEnd: () => setIsPaused(false),
|
|
141
|
+
}
|
|
142
|
+
: {
|
|
143
|
+
onTouchStart: () => setIsPaused(true),
|
|
144
|
+
onTouchEnd: () => setIsPaused(false),
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<View
|
|
149
|
+
{...(wrapperProps as any)}
|
|
150
|
+
onLayout={handleLayout}
|
|
151
|
+
style={[{ overflow: 'hidden' }, style]}
|
|
152
|
+
>
|
|
153
|
+
<FlatList
|
|
154
|
+
ref={listRef}
|
|
155
|
+
data={data}
|
|
156
|
+
renderItem={wrappedRenderItem}
|
|
157
|
+
keyExtractor={(_, i) => i.toString()}
|
|
158
|
+
horizontal={horizontal}
|
|
159
|
+
scrollEnabled={true}
|
|
160
|
+
onScroll={handleScroll}
|
|
161
|
+
scrollEventThrottle={16}
|
|
162
|
+
showsHorizontalScrollIndicator={false}
|
|
163
|
+
showsVerticalScrollIndicator={false}
|
|
164
|
+
/>
|
|
165
|
+
</View>
|
|
166
|
+
);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
export default AutoScrollingFlatList;
|