be-components 6.6.2 → 6.6.4

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.
Files changed (57) hide show
  1. package/lib/commonjs/Components/ConfirmAlert.js +34 -0
  2. package/lib/commonjs/Components/ConfirmAlert.js.map +1 -0
  3. package/lib/commonjs/Group/api/index.js +108 -8
  4. package/lib/commonjs/Group/api/index.js.map +1 -1
  5. package/lib/commonjs/Group/components/GroupCTAButton.js +23 -21
  6. package/lib/commonjs/Group/components/GroupCTAButton.js.map +1 -1
  7. package/lib/commonjs/Group/index.js +365 -89
  8. package/lib/commonjs/Group/index.js.map +1 -1
  9. package/lib/commonjs/SocialComponents/Contacts/useContacts.js +4 -1
  10. package/lib/commonjs/SocialComponents/Contacts/useContacts.js.map +1 -1
  11. package/lib/module/Components/ConfirmAlert.js +29 -0
  12. package/lib/module/Components/ConfirmAlert.js.map +1 -0
  13. package/lib/module/Group/api/index.js +108 -8
  14. package/lib/module/Group/api/index.js.map +1 -1
  15. package/lib/module/Group/components/GroupCTAButton.js +23 -21
  16. package/lib/module/Group/components/GroupCTAButton.js.map +1 -1
  17. package/lib/module/Group/index.js +365 -89
  18. package/lib/module/Group/index.js.map +1 -1
  19. package/lib/module/SocialComponents/Contacts/useContacts.js +4 -1
  20. package/lib/module/SocialComponents/Contacts/useContacts.js.map +1 -1
  21. package/lib/typescript/lib/commonjs/Components/ConfirmAlert.d.ts +6 -0
  22. package/lib/typescript/lib/commonjs/Components/ConfirmAlert.d.ts.map +1 -0
  23. package/lib/typescript/lib/commonjs/Group/api/index.d.ts +9 -1
  24. package/lib/typescript/lib/commonjs/Group/api/index.d.ts.map +1 -1
  25. package/lib/typescript/lib/commonjs/Group/components/GroupCTAButton.d.ts +3 -1
  26. package/lib/typescript/lib/commonjs/Group/components/GroupCTAButton.d.ts.map +1 -1
  27. package/lib/typescript/lib/commonjs/Group/index.d.ts +5 -1
  28. package/lib/typescript/lib/commonjs/Group/index.d.ts.map +1 -1
  29. package/lib/typescript/lib/commonjs/SocialComponents/Contacts/useContacts.d.ts +1 -0
  30. package/lib/typescript/lib/commonjs/SocialComponents/Contacts/useContacts.d.ts.map +1 -1
  31. package/lib/typescript/lib/commonjs/SocialComponents/index.d.ts +1 -0
  32. package/lib/typescript/lib/module/Components/ConfirmAlert.d.ts +5 -0
  33. package/lib/typescript/lib/module/Components/ConfirmAlert.d.ts.map +1 -0
  34. package/lib/typescript/lib/module/Group/api/index.d.ts +9 -1
  35. package/lib/typescript/lib/module/Group/api/index.d.ts.map +1 -1
  36. package/lib/typescript/lib/module/Group/components/GroupCTAButton.d.ts +3 -1
  37. package/lib/typescript/lib/module/Group/components/GroupCTAButton.d.ts.map +1 -1
  38. package/lib/typescript/lib/module/Group/index.d.ts +5 -1
  39. package/lib/typescript/lib/module/Group/index.d.ts.map +1 -1
  40. package/lib/typescript/lib/module/SocialComponents/Contacts/useContacts.d.ts +1 -0
  41. package/lib/typescript/lib/module/SocialComponents/Contacts/useContacts.d.ts.map +1 -1
  42. package/lib/typescript/src/Components/ConfirmAlert.d.ts +5 -0
  43. package/lib/typescript/src/Components/ConfirmAlert.d.ts.map +1 -0
  44. package/lib/typescript/src/Group/api/index.d.ts +15 -2
  45. package/lib/typescript/src/Group/api/index.d.ts.map +1 -1
  46. package/lib/typescript/src/Group/components/GroupCTAButton.d.ts +4 -1
  47. package/lib/typescript/src/Group/components/GroupCTAButton.d.ts.map +1 -1
  48. package/lib/typescript/src/Group/index.d.ts +6 -2
  49. package/lib/typescript/src/Group/index.d.ts.map +1 -1
  50. package/lib/typescript/src/SocialComponents/Contacts/useContacts.d.ts +1 -0
  51. package/lib/typescript/src/SocialComponents/Contacts/useContacts.d.ts.map +1 -1
  52. package/package.json +1 -1
  53. package/src/Components/ConfirmAlert.tsx +39 -0
  54. package/src/Group/api/index.ts +81 -10
  55. package/src/Group/components/GroupCTAButton.tsx +12 -10
  56. package/src/Group/index.tsx +258 -75
  57. package/src/SocialComponents/Contacts/useContacts.tsx +4 -2
@@ -1,7 +1,7 @@
1
1
  import React, { useEffect, useState } from 'react';
2
2
  import { Button, Text, View } from "../Components/Themed"
3
3
  import { GroupApi, GroupHelpers } from './api';
4
- import type { CompetitionPayoutTypeProps, CompetitionPlayerProps, CompetitionProps, CompetitionRecordProps, CompetitionResultProps, CompetitionResultTypeProps, CompetitionTypeProps, GroupMessageProps, GroupPlayerProps, GroupProps, MyPlayerProps, OrderAnalyticsProps, OrderProps, PostProps, PublicPlayerProps } from '../types';
4
+ import type { CompetitionPayoutTypeProps, CompetitionPlayerProps, CompetitionProps, CompetitionRecordProps, CompetitionResultProps, CompetitionResultTypeProps, CompetitionTypeProps, EventProps, GroupMessageProps, GroupPlayerProps, GroupProps, MyPlayerProps, NewOverallAnalytics, OrderAnalyticsProps, OrderProps, PollCampaignProps, PostProps, PublicPlayerProps, SquaresCompetitionProps } from '../types';
5
5
  import { ActivityIndicator, FlatList, Image } from 'react-native';
6
6
  import { Icons, Toggle } from '../Components';
7
7
  import { useColors } from '../constants/useColors';
@@ -12,24 +12,34 @@ import CompetitionCard from '../Engage/components/CompetitionCard';
12
12
  import { PostCard } from '../SocialComponents';
13
13
  import Pagination from '../Components/Pagination';
14
14
  import GroupCTAButton from './components/GroupCTAButton';
15
+ import PollCampaignCard from '../Engage/components/PollCampaignCard';
16
+ import SquaresCompetitionCard from '../Engage/components/SquaresCompetitionCard';
17
+ import { showConfirmAlert } from '../Components/ConfirmAlert';
15
18
 
16
19
  type GroupComponentProps = {
17
20
  group_id:string,
18
21
  mode:'desktop'|'mobile',
22
+ refresh_key:string,
19
23
  onSelectChat?:(group:GroupProps) => void
20
24
  onInvitePlayer:() => void,
25
+ onNudgePlayer:(p:PublicPlayerProps, gp:GroupPlayerProps) => void,
21
26
  onSelectPlayer:(p:PublicPlayerProps) => void,
22
27
  onSelectCompetition:(c:CompetitionProps) => void,
28
+ onSelectPollCampaign:(pc:PollCampaignProps) => void,
29
+ onSelectSquaresCompetition:(sq:SquaresCompetitionProps) => void,
23
30
  player?:MyPlayerProps
24
31
  }
25
32
  type GroupStateProps = {
26
33
  group?:GroupProps,
27
34
  loading:boolean,
28
35
  editing:boolean,
36
+ action_loading?:string,
29
37
  show_player_status:boolean,
30
38
  active_toggle:string,
39
+ stats_expanded:boolean,
31
40
  group_players:GroupPlayerProps[],
32
41
  players:PublicPlayerProps[],
42
+ active_stat?: { stat_key:string, label:string, multiplier:number, prefix?:string, suffix?:string },
33
43
  group_messages: GroupMessageProps[],
34
44
  my_group_player?:GroupPlayerProps
35
45
  }
@@ -39,8 +49,16 @@ type TimeOptionProps = {
39
49
  option:string,
40
50
  label:string
41
51
  }
42
- const sections = ['header','chat','me','toggle', 'competition_toggle', 'members', 'competitions', 'activity']
43
- const GroupComponent = ({ group_id, player, mode, onInvitePlayer, onSelectCompetition, onSelectChat, onSelectPlayer } : GroupComponentProps) => {
52
+ const sections = ['header','chat','toggle','me', 'competition_toggle', 'new_competition', 'members', 'competitions', 'activity']
53
+ const member_stat_options:{ stat_key:string, label:string, multiplier:number, prefix?:string, suffix?:string }[] = [
54
+ { stat_key: 'roi_pct', label: 'ROI Percent', multiplier:100, suffix: '%' },
55
+ { stat_key: 'winnings', label: 'Winnings', multiplier:1 , prefix: '$'},
56
+ { stat_key: 'win_pct', label: 'Win Percent', multiplier: 100, suffix: '%' },
57
+ { stat_key: 'fulfilled_positions', label: 'Matched Bets', multiplier:1, suffix: ' Bets' },
58
+ { stat_key: 'original_stake', label: 'Betting Volume', multiplier: 1, prefix: '$' }
59
+ ]
60
+
61
+ const GroupComponent = ({ refresh_key, group_id, player, mode, onInvitePlayer, onSelectCompetition, onSelectChat, onSelectPlayer, onNudgePlayer, onSelectPollCampaign, onSelectSquaresCompetition } : GroupComponentProps) => {
44
62
  const Colors = useColors();
45
63
  const leader_options:TimeOptionProps[] = [
46
64
  { to: moment().subtract(1, 'days').unix(), from: moment().unix(), option: '24_hours', label: '24 Hours' },
@@ -62,7 +80,10 @@ const GroupComponent = ({ group_id, player, mode, onInvitePlayer, onSelectCompet
62
80
  const [ comp_data, setCompData ] = useState<{
63
81
  loading:boolean,
64
82
  toggle: 'upcoming'|'history',
83
+ poll_campaigns:PollCampaignProps[],
84
+ squares_competitions:SquaresCompetitionProps[],
65
85
  competition_types:CompetitionTypeProps[],
86
+ events:EventProps[],
66
87
  competition_result_types:CompetitionResultTypeProps[],
67
88
  competition_payout_types:CompetitionPayoutTypeProps[],
68
89
  active_competitions:CompetitionProps[],
@@ -75,6 +96,9 @@ const GroupComponent = ({ group_id, player, mode, onInvitePlayer, onSelectCompet
75
96
  toggle: 'upcoming',
76
97
  competition_payout_types:[],
77
98
  competition_result_types:[],
99
+ events:[],
100
+ squares_competitions:[],
101
+ poll_campaigns: [],
78
102
  competition_types:[],
79
103
  active_competitions: [],
80
104
  competition_players:[],
@@ -99,16 +123,20 @@ const GroupComponent = ({ group_id, player, mode, onInvitePlayer, onSelectCompet
99
123
  group_messages: [],
100
124
  editing:false,
101
125
  show_player_status:false,
126
+ stats_expanded: false,
102
127
  active_toggle: 'members',
128
+ active_stat: member_stat_options[0],
103
129
  group_players: [],
104
130
  players: []
105
131
  });
106
- const { loading, group, active_toggle, editing,show_player_status, group_messages, group_players, my_group_player, players } = group_data;
107
- const { sorted_players, player_analytics } = GroupHelpers.sortMembers(group_players, order_analytics);
132
+ const { loading, group, action_loading, stats_expanded, active_toggle, editing,show_player_status, active_stat, group_messages, group_players, my_group_player, players } = group_data;
133
+ const admin = player?.player_id == group?.group_admin ? true : false;
134
+ const { sorted_players, player_analytics } = GroupHelpers.sortMembers(group_players, order_analytics, active_stat?.stat_key);
135
+ const sorted_engagements = GroupHelpers.sortCompetitionAction([], comp_data.active_competitions, [], comp_data.squares_competitions, comp_data.poll_campaigns);
136
+
108
137
  useEffect(() => {
109
138
  getGroupData(group_id);
110
- },[group_id]);
111
-
139
+ },[group_id, refresh_key]);
112
140
 
113
141
  useEffect(() => {
114
142
  getToggleData(active_toggle);
@@ -126,9 +154,8 @@ const GroupComponent = ({ group_id, player, mode, onInvitePlayer, onSelectCompet
126
154
  default: return
127
155
  }
128
156
  }
129
-
130
157
  const getActitiyData = async(page:number) => {
131
- setActivityData({ ...activity_data, loading: false });
158
+ setActivityData({ ...activity_data, loading: true });
132
159
  const activity = await GroupApi.getPostsByGroupId(group_id, page);
133
160
  let order_ids:string[] = []
134
161
  activity.filter(a => a.memo_orders).map(mo => mo.memo_orders.map(o => order_ids.push(o)));
@@ -145,18 +172,31 @@ const GroupComponent = ({ group_id, player, mode, onInvitePlayer, onSelectCompet
145
172
  const getCompetitionData = async(type:'upcoming' | 'history', page:number) => {
146
173
  setCompData({ ...comp_data, loading:true });
147
174
  let resp:undefined | { competitions:CompetitionProps[], competition_players:CompetitionPlayerProps[], competition_records:CompetitionRecordProps[], competition_results:CompetitionResultProps[] } = {
148
- competitions:[], competition_players:[], competition_records:[], competition_results:[]
149
- }
175
+ competitions:[], competition_players:[], competition_records:[], competition_results:[] }
176
+ let server_polls:PollCampaignProps[] = []
177
+ let server_sq: SquaresCompetitionProps[] = []
178
+ let server_events:EventProps[] = []
150
179
  if(type == 'upcoming'){
151
180
  resp = await GroupApi.getActiveCompetitions(group_id);
181
+ server_polls = await GroupApi.getActivePolls(group_id);
182
+ server_sq = await GroupApi.getActiveSquares(group_id);
183
+ let event_ids = server_sq.map(sq => sq.event_id);
184
+ server_events = await GroupApi.getEventsByIds(event_ids);
152
185
  } else {
153
186
  resp = await GroupApi.getHistoryCompetitions(group_id, page);
187
+ server_polls = await GroupApi.getHistoryPolls(group_id, page);
188
+ server_sq = await GroupApi.getHistorySquares(group_id, page);
189
+ let event_ids = server_sq.map(sq => sq.event_id);
190
+ server_events = await GroupApi.getEventsByIds(event_ids);
154
191
  }
155
192
  const opts = await GroupApi.getCompetitionOptions();
156
193
  setCompData({
157
194
  ...comp_data,
158
195
  loading:false,
159
196
  toggle: type,
197
+ poll_campaigns: server_polls,
198
+ squares_competitions: server_sq,
199
+ events: server_events,
160
200
  competition_players: resp.competition_players,
161
201
  competition_records: resp.competition_records,
162
202
  competition_results: resp.competition_results,
@@ -202,46 +242,144 @@ const GroupComponent = ({ group_id, player, mode, onInvitePlayer, onSelectCompet
202
242
  }
203
243
 
204
244
  const renderPlayers = (data:{ item:GroupPlayerProps, index:number }) => {
205
- const player = players.find(p => p.player_id == data.item.player_id);
206
- if(!player){ return <></> }
245
+ const pl = players.find(p => p.player_id == data.item.player_id);
246
+ if(!pl){ return <></> }
207
247
  const player_analytic = player_analytics[data.item.player_id]
248
+ const analytic = player_analytic ? player_analytic[active_stat?.stat_key as keyof NewOverallAnalytics] : 0
249
+ const is_loading = action_loading == data.item.group_player_id ? true : false
208
250
  return (
209
- <Button transparent style={{ flexDirection:'row', alignItems:'center', padding:10, borderBottomWidth:1, borderColor:Colors.borders.light, borderRadius:0 }} onPress={() => onSelectPlayer(player)}>
251
+ <Button transparent style={{ flexDirection:'row', alignItems:'center', padding:10, borderBottomWidth:1, borderColor:Colors.borders.light, borderRadius:0 }} onPress={() => onSelectPlayer(pl)}>
252
+ {admin && data.item.player_id != player?.player_id?
253
+ <Button
254
+ title='X'
255
+ title_color={Colors.text.error}
256
+ loading={is_loading}
257
+ style={{ padding:10, opacity: is_loading ? 0.5 : 1 }}
258
+ onPress={() => {
259
+ showConfirmAlert('Are you sure?', 'This will boot and ban this player from the group. You can unban the player later',
260
+ async() => {
261
+ //Ok we have confirmed it!
262
+ let new_pl = await GroupApi.bootPlayer(data.item.group_player_id);
263
+ if(!new_pl){
264
+ setGroupData({ ...group_data, action_loading: undefined });
265
+ return alert('Unable to boot player at this time. Please try again')
266
+ }
267
+ setGroupData({
268
+ ...group_data,
269
+ action_loading: undefined,
270
+ group_players: group_players.filter(gp => gp.group_player_id != new_pl.group_player_id).concat(new_pl)
271
+ })
272
+ }
273
+ ),
274
+ () => { console.log('cancelled') }
275
+ }}
276
+ />
277
+ :<></>}
210
278
  <View float style={{ borderRadius:100, padding:2 }}>
211
279
  <Image
212
- source={{ uri: player.profile_pic }}
280
+ source={{ uri: pl.profile_pic }}
213
281
  style={{ height:45, width:45, borderRadius:100 }}
214
282
  resizeMode='contain'
215
283
  />
216
284
  </View>
217
285
  <View transparent style={{ flex:1, marginLeft:10 }}>
218
- <Text theme='h1'>@{player.username}</Text>
219
- <Text theme='description' style={{ marginTop:3 }}>Joined {moment(data.item.create_datetime).fromNow()}</Text>
286
+ <Text theme='h1'>@{pl.username}</Text>
287
+ <Text theme='description' style={{ marginTop:3 }}>{data.item.status == 'active' ? 'Joined' : 'Invited'} {moment(data.item.last_update_datetime).fromNow()}</Text>
288
+ </View>
289
+ {active_stat && player_analytic && data.item.status == 'active' ?
290
+ <View style={{ alignItems:'flex-end' }}>
291
+ <Text theme='h1'>{active_stat.prefix}{(parseFloat(analytic as string) * active_stat.multiplier).toFixed(2)}{active_stat.suffix}</Text>
292
+ <Text theme='light' style={{ marginTop:3 }}>{active_stat.label}</Text>
220
293
  </View>
221
- {player_analytic ?
222
- <Text theme='h2'>ROI {(player_analytic.roi_pct*100).toFixed(2)}%</Text>
294
+ :<></>}
295
+ {data.item.status == 'invited' ?
296
+ <Button
297
+ disabled={!admin}
298
+ type='text'
299
+ style={{ padding:10, opacity:is_loading ? 0.5: 1 }}
300
+ loading={is_loading}
301
+ title_color={Colors.text.action}
302
+ title={admin?'Nudge':'Invited'}
303
+ onPress={() => onNudgePlayer(pl, data.item)}
304
+ />
305
+ :<></>}
306
+ {admin && data.item.status == 'invited' ?
307
+ <Button
308
+ transparent
309
+ style={{ padding:10, opacity: is_loading ? 0.5:1 }}
310
+ title_color={Colors.text.error}
311
+ loading={is_loading}
312
+ title='Uninvite'
313
+ onPress={async() => {
314
+ setGroupData({ ...group_data, action_loading: data.item.group_player_id });
315
+ const new_pl = await GroupApi.uninvitePlayer(data.item.group_player_id);
316
+ if(!new_pl){
317
+ setGroupData({ ...group_data, action_loading: undefined });
318
+ return alert('Unable to uninvite player. Please try again')
319
+ }
320
+ setGroupData({
321
+ ...group_data,
322
+ action_loading: undefined,
323
+ group_players: group_players.filter(gp => gp.group_player_id != new_pl.group_player_id).concat(new_pl)
324
+ })
325
+ }}
326
+ />
223
327
  :<></>}
224
328
  </Button>
225
329
  )
226
330
  }
227
331
 
228
- const renderCompetitions = (data:{ item:CompetitionProps, index:number }) => {
229
- const type = comp_data.competition_types.find(t => t.competition_type_id == data.item.competition_type_id);
230
- const result = comp_data.competition_result_types.find(r => r.competition_result_type_id == data.item.competition_result_type_id);
231
- if(!type || !result){ return <></> }
232
- const my_result = comp_data.competition_results.find(cr => cr.player_id == player?.player_id && cr.competition_id == data.item.competition_id)
233
- const my_record = comp_data.competition_records.find(cr => cr.player_id == player?.player_id && cr.competition_id == data.item.competition_id)
234
- return (
235
- <CompetitionCard
236
- competition={data.item}
237
- player={player}
238
- competition_type={type}
239
- competition_record={my_record}
240
- competition_result={my_result}
241
- competition_result_type={result}
242
- onCompetitionSelect={onSelectCompetition}
243
- />
244
- )
332
+ const renderEngagements = (data: { item:{id:string, type:string, scheduled_datetime:any}, index: number }) => {
333
+ switch (data.item.type){
334
+ case 'competition':
335
+ const competition = comp_data.active_competitions.find(c => c.competition_id == data.item.id);
336
+ if(!competition){ return <></> }
337
+ const type = comp_data.competition_types.find(t => t.competition_type_id == competition.competition_type_id);
338
+ const result = comp_data.competition_result_types.find(r => r.competition_result_type_id == competition.competition_result_type_id);
339
+ if(!type || !result){ return <></> }
340
+ const my_result = comp_data.competition_results.find(cr => cr.player_id == player?.player_id && cr.competition_id == competition.competition_id)
341
+ const my_record = comp_data.competition_records.find(cr => cr.player_id == player?.player_id && cr.competition_id == competition.competition_id)
342
+ return (
343
+ <View style={{ padding:5 }}>
344
+ <CompetitionCard
345
+ competition={competition}
346
+ player={player}
347
+ competition_type={type}
348
+ competition_record={my_record}
349
+ competition_result={my_result}
350
+ competition_result_type={result}
351
+ onCompetitionSelect={onSelectCompetition}
352
+ />
353
+ </View>
354
+ )
355
+ case 'bracket':
356
+ return <></>
357
+ case 'squares':
358
+ const squares_comp = comp_data.squares_competitions.find(sc => sc.sq_comp_id == data.item.id);
359
+ if(!squares_comp){ return <></> }
360
+ const event = comp_data.events.find(e => e.event_id == squares_comp.event_id);
361
+ return (
362
+ <View style={{ padding:5 }}>
363
+ <SquaresCompetitionCard
364
+ squares_competition={squares_comp}
365
+ event={event}
366
+ onSelectCompetition={onSelectSquaresCompetition}
367
+ />
368
+ </View>
369
+ )
370
+ case 'poll_campaign':
371
+ const poll_campaign = comp_data.poll_campaigns.find(pc => pc.poll_campaign_id == data.item.id);
372
+ if(!poll_campaign){ return <></> }
373
+ return (
374
+ <View style={{ padding:5 }}>
375
+ <PollCampaignCard
376
+ poll_campaign={poll_campaign}
377
+ onSelectPollCampaign={onSelectPollCampaign}
378
+ />
379
+ </View>
380
+ )
381
+ default:return <></>
382
+ }
245
383
  }
246
384
 
247
385
  const renderPosts = (data:{ item:PostProps, index:number }) => {
@@ -280,6 +418,7 @@ const GroupComponent = ({ group_id, player, mode, onInvitePlayer, onSelectCompet
280
418
  <Text theme='h1'>{group.name}</Text>
281
419
  <Text theme='description' style={{ marginTop:3 }}>{group.description}</Text>
282
420
  </View>
421
+
283
422
  {player?.player_id == group.group_admin ?
284
423
  <Button float style={{ padding:10 }} onPress={() => setGroupData({ ...group_data, editing:true })}>
285
424
  <Icons.EditIcon size={14} color={Colors.text.h1} />
@@ -293,7 +432,8 @@ const GroupComponent = ({ group_id, player, mode, onInvitePlayer, onSelectCompet
293
432
  <View type='header' style={{ padding:10, borderBottomWidth:1, borderColor:Colors.borders.light }}>
294
433
  <GroupCTAButton
295
434
  group={group}
296
- message_length={100}
435
+ hide_from_player
436
+ message_length={20}
297
437
  players={players}
298
438
  group_messages={group_messages}
299
439
  group_players={group_players}
@@ -301,7 +441,24 @@ const GroupComponent = ({ group_id, player, mode, onInvitePlayer, onSelectCompet
301
441
  />
302
442
  </View>
303
443
  )
444
+ case 'new_competition':
445
+ if(active_toggle != 'competitions'){ return <></> }
446
+ return (
447
+ <View type='footer' style={{ flexDirection:'row', alignItems:'center', padding:10 }}>
448
+ <View transparent style={{ flex:1, marginRight:10 }}>
449
+ <Text theme='h2'>CREATE A NEW COMPETITION</Text>
450
+ <Text theme='description' style={{ marginTop:3 }}>Create a new pick-em or other engagement for this group!</Text>
451
+ </View>
452
+ <Button
453
+ type='success'
454
+ style={{ padding:10 }}
455
+ title='CREATE'
456
+ onPress={() => console.log('New competition!')}
457
+ />
458
+ </View>
459
+ )
304
460
  case 'me':
461
+ if(active_toggle != 'members'){ return <></> }
305
462
  return (
306
463
  <View type='row' style={{ padding:10 }}>
307
464
  {player ?
@@ -332,13 +489,15 @@ const GroupComponent = ({ group_id, player, mode, onInvitePlayer, onSelectCompet
332
489
  style={{ padding:10 }}
333
490
  onPress={() => setGroupData({ ...group_data, show_player_status: true })}
334
491
  />
335
- : my_group_player?.status != 'invited' ?
492
+ : my_group_player?.status != 'invited' && !admin ?
336
493
  <Button
337
494
  type='error'
338
495
  title='LEAVE'
339
496
  style={{ padding:10 }}
340
497
  onPress={() => setGroupData({ ...group_data, show_player_status: true })}
341
498
  />
499
+ :my_group_player?.status != 'invited' && admin ?
500
+ <Text theme='h1' color={Colors.text.gold}>ADMIN</Text>
342
501
  :<></>}
343
502
  </View>
344
503
  :<></>}
@@ -351,7 +510,7 @@ const GroupComponent = ({ group_id, player, mode, onInvitePlayer, onSelectCompet
351
510
  options={[
352
511
  { key: 'members', label: 'Members' },
353
512
  { key: 'activity', label: 'Activity' },
354
- { key: 'competitions', label: 'Competitions' }
513
+ { key: 'competitions', label: 'Engagements' }
355
514
  ]}
356
515
  selected_option={active_toggle}
357
516
  onSelectOption={(option) => setGroupData({ ...group_data, active_toggle: option })}
@@ -369,13 +528,11 @@ const GroupComponent = ({ group_id, player, mode, onInvitePlayer, onSelectCompet
369
528
  if(active_toggle != 'activity'){ return <></> }
370
529
  return (
371
530
  <View style={{ padding:10 }}>
372
- <Pagination
373
- offset={activity_data.offset}
374
- onNext={() => getActitiyData(activity_data.offset + 1)}
375
- onPrevious={() => getActitiyData(activity_data.offset - 1)}
376
- />
531
+ {activity_data.loading ?
532
+ <ActivityIndicator style={{ padding:20, alignSelf:'center' }} size={'large'} color={Colors.text.h1} />
533
+ :<></>}
377
534
  <FlatList
378
- data={activity_data.posts}
535
+ data={activity_data.posts.sort((a,b) => moment(b.create_datetime).unix() - moment(a.create_datetime).unix())}
379
536
  key={'post_list'}
380
537
  keyExtractor={item => item.memo_post_id.toString()}
381
538
  renderItem={renderPosts}
@@ -385,13 +542,51 @@ const GroupComponent = ({ group_id, player, mode, onInvitePlayer, onSelectCompet
385
542
  case 'members':
386
543
  if(active_toggle != 'members'){ return <></> }
387
544
  return (
388
- <View style={{ padding:10 }}>
389
- <FlatList
390
- data={sorted_players}
391
- keyExtractor={item => item.group_player_id.toString()}
392
- renderItem={renderPlayers}
393
- key={'group_members'}
545
+ <View>
546
+ <View type='header' style={{ flexDirection:'row', alignItems:'center', padding:10, zIndex: 10 }}>
547
+ <View transparent style={{ flex:1, marginRight:10 }}>
548
+ <Text theme='h1'>Group Members</Text>
549
+ <Text theme='description'>Active and invited members are show below. You can sort the members by different leaderboards using the dropdown on the right.</Text>
550
+ </View>
551
+ <View float>
552
+ <Button transparent style={{ flexDirection:'row', alignItems:'center', padding:10 }} onPress={() => setGroupData({ ...group_data, stats_expanded: !stats_expanded })}>
553
+ <Text style={{ marginRight:5 }} theme='h2'>{active_stat?.label ?? 'Select'}</Text>
554
+ <Icons.ChevronIcon direction={stats_expanded?'up':'down'} color={Colors.text.h1} size={8} />
555
+ </Button>
556
+ {stats_expanded ?
557
+ <View float style={{ position:'absolute', top:0, right:0, width:200 }}>
558
+ {member_stat_options.map(so => {
559
+ return (
560
+ <Button transparent style={{ padding:10, borderRadius:0, borderBottomWidth:1, borderColor:Colors.borders.light }} onPress={() => setGroupData({ ...group_data, active_stat: so, stats_expanded: false })}>
561
+ <Text theme='h1'>{so.label}</Text>
562
+ </Button>
563
+ )
564
+ })}
565
+ </View>
566
+ :<></>}
567
+ </View>
568
+ </View>
569
+ <View style={{ padding:10}}>
570
+ <Toggle
571
+ options={leader_options.map(o => { return { key: o.option, label: o.label } })}
572
+ selected_option={time_option?.option}
573
+ onSelectOption={(option) => {
574
+ const selected = leader_options.find(o => o.option == option)
575
+ if(!selected){ return }
576
+ getGroupAnalytics(group_id, selected);
577
+ }}
394
578
  />
579
+ </View>
580
+ <View style={{ padding:10, zIndex: -10 }}>
581
+ <FlatList
582
+ data={sorted_players}
583
+ keyExtractor={item => item.group_player_id.toString()}
584
+ renderItem={renderPlayers}
585
+ initialNumToRender={10}
586
+ windowSize={4}
587
+ key={'group_members'}
588
+ />
589
+ </View>
395
590
  </View>
396
591
  )
397
592
  case 'competition_toggle':
@@ -412,11 +607,14 @@ const GroupComponent = ({ group_id, player, mode, onInvitePlayer, onSelectCompet
412
607
  if(active_toggle != 'competitions'){ return <></> }
413
608
  return (
414
609
  <View transparent style={{ padding:10 }}>
610
+ {comp_data.loading ?
611
+ <ActivityIndicator style={{ padding:20, alignSelf:'center' }} size={'large'} color={Colors.text.h1} />
612
+ :<></>}
415
613
  <FlatList
416
614
  key={'competition_list'}
417
- keyExtractor={item => item.competition_id.toString()}
418
- data={comp_data.active_competitions.sort((a,b) => moment(a.scheduled_datetime).unix() - moment(b.scheduled_datetime).unix())}
419
- renderItem={renderCompetitions}
615
+ keyExtractor={item => item.id.toString()}
616
+ data={sorted_engagements.sort((a,b) => moment(a.scheduled_datetime).unix() - moment(b.scheduled_datetime).unix())}
617
+ renderItem={renderEngagements}
420
618
  />
421
619
  </View>
422
620
  )
@@ -434,28 +632,13 @@ const GroupComponent = ({ group_id, player, mode, onInvitePlayer, onSelectCompet
434
632
  renderItem={renderSections}
435
633
  />
436
634
  {active_toggle == 'members' ?
437
- <Toggle
438
- options={leader_options.map(o => { return { key: o.option, label: o.label } })}
439
- selected_option={time_option?.option}
440
- onSelectOption={(option) => {
441
- const selected = leader_options.find(o => o.option == option)
442
- if(!selected){ return }
443
- getGroupAnalytics(group_id, selected);
444
- }}
635
+ <></>
636
+ :active_toggle == 'activity' ?
637
+ <Pagination
638
+ offset={activity_data.offset}
639
+ onNext={() => getActitiyData(activity_data.offset + 1)}
640
+ onPrevious={() => getActitiyData(activity_data.offset - 1)}
445
641
  />
446
- :active_toggle == 'competitions' ?
447
- <View type='footer' style={{ flexDirection:'row', alignItems:'center', padding:10 }}>
448
- <View transparent style={{ flex:1, marginRight:10 }}>
449
- <Text theme='h2'>CREATE A NEW COMPETITION</Text>
450
- <Text theme='description' style={{ marginTop:3 }}>Create a new pick-em or other engagement for this group!</Text>
451
- </View>
452
- <Button
453
- type='success'
454
- style={{ padding:10 }}
455
- title='CREATE'
456
- onPress={() => console.log('New competition!')}
457
- />
458
- </View>
459
642
  :<></>}
460
643
  </View>
461
644
  {editing && group ?
@@ -139,8 +139,9 @@ const useContacts = ({ referral_code }:UseContactProps) => {
139
139
  }
140
140
  }
141
141
 
142
- const getContact = async():Promise<{ player:PublicPlayerProps, number:string } | undefined> => {
142
+ const getContact = async():Promise<{ player:PublicPlayerProps, number:string, new_player:boolean } | undefined> => {
143
143
  try {
144
+ let new_player = false
144
145
  if(!permission?.granted){ return undefined } //We do not have permission
145
146
  setContactLoading(true);
146
147
  const contact = await Contacts.presentContactPickerAsync();
@@ -168,9 +169,10 @@ const useContacts = ({ referral_code }:UseContactProps) => {
168
169
  setContactLoading(false);
169
170
  return undefined
170
171
  }
172
+ new_player = true
171
173
  }
172
174
  setContactLoading(false);
173
- return { player, number }
175
+ return { player, number, new_player }
174
176
 
175
177
  } catch (e) {
176
178
  return undefined