be-components 6.6.1 → 6.6.2

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 (78) hide show
  1. package/lib/commonjs/Engage/components/CompetitionCard.js +54 -1
  2. package/lib/commonjs/Engage/components/CompetitionCard.js.map +1 -1
  3. package/lib/commonjs/Group/api/index.js +203 -0
  4. package/lib/commonjs/Group/api/index.js.map +1 -0
  5. package/lib/commonjs/Group/components/GroupCTAButton.js +85 -0
  6. package/lib/commonjs/Group/components/GroupCTAButton.js.map +1 -0
  7. package/lib/commonjs/Group/components/GroupPlayerActionForm.js +166 -0
  8. package/lib/commonjs/Group/components/GroupPlayerActionForm.js.map +1 -0
  9. package/lib/commonjs/Group/components/ManageGroupForm.js +476 -0
  10. package/lib/commonjs/Group/components/ManageGroupForm.js.map +1 -0
  11. package/lib/commonjs/Group/index.js +671 -0
  12. package/lib/commonjs/Group/index.js.map +1 -0
  13. package/lib/commonjs/index.js +7 -0
  14. package/lib/commonjs/index.js.map +1 -1
  15. package/lib/module/Engage/components/CompetitionCard.js +54 -1
  16. package/lib/module/Engage/components/CompetitionCard.js.map +1 -1
  17. package/lib/module/Group/api/index.js +197 -0
  18. package/lib/module/Group/api/index.js.map +1 -0
  19. package/lib/module/Group/components/GroupCTAButton.js +78 -0
  20. package/lib/module/Group/components/GroupCTAButton.js.map +1 -0
  21. package/lib/module/Group/components/GroupPlayerActionForm.js +159 -0
  22. package/lib/module/Group/components/GroupPlayerActionForm.js.map +1 -0
  23. package/lib/module/Group/components/ManageGroupForm.js +469 -0
  24. package/lib/module/Group/components/ManageGroupForm.js.map +1 -0
  25. package/lib/module/Group/index.js +663 -0
  26. package/lib/module/Group/index.js.map +1 -0
  27. package/lib/module/index.js +2 -1
  28. package/lib/module/index.js.map +1 -1
  29. package/lib/typescript/lib/commonjs/Engage/components/CompetitionCard.d.ts +4 -1
  30. package/lib/typescript/lib/commonjs/Engage/components/CompetitionCard.d.ts.map +1 -1
  31. package/lib/typescript/lib/commonjs/Group/api/index.d.ts +38 -0
  32. package/lib/typescript/lib/commonjs/Group/api/index.d.ts.map +1 -0
  33. package/lib/typescript/lib/commonjs/Group/components/GroupCTAButton.d.ts +10 -0
  34. package/lib/typescript/lib/commonjs/Group/components/GroupCTAButton.d.ts.map +1 -0
  35. package/lib/typescript/lib/commonjs/Group/components/GroupPlayerActionForm.d.ts +9 -0
  36. package/lib/typescript/lib/commonjs/Group/components/GroupPlayerActionForm.d.ts.map +1 -0
  37. package/lib/typescript/lib/commonjs/Group/components/ManageGroupForm.d.ts +10 -0
  38. package/lib/typescript/lib/commonjs/Group/components/ManageGroupForm.d.ts.map +1 -0
  39. package/lib/typescript/lib/commonjs/Group/index.d.ts +12 -0
  40. package/lib/typescript/lib/commonjs/Group/index.d.ts.map +1 -0
  41. package/lib/typescript/lib/commonjs/index.d.ts +1 -0
  42. package/lib/typescript/lib/commonjs/index.d.ts.map +1 -1
  43. package/lib/typescript/lib/module/Engage/components/CompetitionCard.d.ts +4 -1
  44. package/lib/typescript/lib/module/Engage/components/CompetitionCard.d.ts.map +1 -1
  45. package/lib/typescript/lib/module/Group/api/index.d.ts +37 -0
  46. package/lib/typescript/lib/module/Group/api/index.d.ts.map +1 -0
  47. package/lib/typescript/lib/module/Group/components/GroupCTAButton.d.ts +10 -0
  48. package/lib/typescript/lib/module/Group/components/GroupCTAButton.d.ts.map +1 -0
  49. package/lib/typescript/lib/module/Group/components/GroupPlayerActionForm.d.ts +9 -0
  50. package/lib/typescript/lib/module/Group/components/GroupPlayerActionForm.d.ts.map +1 -0
  51. package/lib/typescript/lib/module/Group/components/ManageGroupForm.d.ts +10 -0
  52. package/lib/typescript/lib/module/Group/components/ManageGroupForm.d.ts.map +1 -0
  53. package/lib/typescript/lib/module/Group/index.d.ts +12 -0
  54. package/lib/typescript/lib/module/Group/index.d.ts.map +1 -0
  55. package/lib/typescript/lib/module/index.d.ts +2 -1
  56. package/lib/typescript/lib/module/index.d.ts.map +1 -1
  57. package/lib/typescript/src/Engage/components/CompetitionCard.d.ts +5 -2
  58. package/lib/typescript/src/Engage/components/CompetitionCard.d.ts.map +1 -1
  59. package/lib/typescript/src/Group/api/index.d.ts +47 -0
  60. package/lib/typescript/src/Group/api/index.d.ts.map +1 -0
  61. package/lib/typescript/src/Group/components/GroupCTAButton.d.ts +13 -0
  62. package/lib/typescript/src/Group/components/GroupCTAButton.d.ts.map +1 -0
  63. package/lib/typescript/src/Group/components/GroupPlayerActionForm.d.ts +12 -0
  64. package/lib/typescript/src/Group/components/GroupPlayerActionForm.d.ts.map +1 -0
  65. package/lib/typescript/src/Group/components/ManageGroupForm.d.ts +12 -0
  66. package/lib/typescript/src/Group/components/ManageGroupForm.d.ts.map +1 -0
  67. package/lib/typescript/src/Group/index.d.ts +14 -0
  68. package/lib/typescript/src/Group/index.d.ts.map +1 -0
  69. package/lib/typescript/src/index.d.ts +2 -1
  70. package/lib/typescript/src/index.d.ts.map +1 -1
  71. package/package.json +1 -1
  72. package/src/Engage/components/CompetitionCard.tsx +29 -2
  73. package/src/Group/api/index.ts +176 -0
  74. package/src/Group/components/GroupCTAButton.tsx +57 -0
  75. package/src/Group/components/GroupPlayerActionForm.tsx +137 -0
  76. package/src/Group/components/ManageGroupForm.tsx +239 -0
  77. package/src/Group/index.tsx +497 -0
  78. package/src/index.tsx +2 -0
@@ -1,21 +1,25 @@
1
1
  import React from 'react';
2
2
  import { Image } from 'react-native';
3
- import type { CompanyProps, CompetitionProps, CompetitionResultTypeProps, CompetitionTypeProps, PublicPlayerProps } from '../../types';
3
+ import type { CompanyProps, CompetitionProps, CompetitionRecordProps, CompetitionResultProps, CompetitionResultTypeProps, CompetitionTypeProps, MyPlayerProps, PublicPlayerProps } from '../../types';
4
4
  import { Icons } from '../../Components';
5
5
  import moment from 'moment-mini';
6
6
  import { useColors } from '../../constants/useColors';
7
7
  import { Button, Text, View } from '../../Components/Themed';
8
+ import { CompetitionHelpers } from '../../Competition/api';
8
9
 
9
10
  type CompetitionCardProps = {
10
11
  competition:CompetitionProps,
12
+ player?:MyPlayerProps,
11
13
  competition_type:CompetitionTypeProps,
14
+ competition_result?:CompetitionResultProps,
15
+ competition_record?:CompetitionRecordProps,
12
16
  competition_result_type:CompetitionResultTypeProps,
13
17
  company?:CompanyProps,
14
18
  admin?:PublicPlayerProps,
15
19
  pacer?:PublicPlayerProps,
16
20
  onCompetitionSelect:(c:CompetitionProps) => void
17
21
  }
18
- const CompetitionCard = ({ competition, competition_type, company, admin, onCompetitionSelect }:CompetitionCardProps) => {
22
+ const CompetitionCard = ({ competition, competition_record, player, competition_result, competition_type, company, admin, onCompetitionSelect }:CompetitionCardProps) => {
19
23
  const Colors = useColors();
20
24
  const cl = competition.market_type == 'FOR_MONEY' ? '$' : 'E'
21
25
  let current_payout = competition.ticket_revenue
@@ -79,6 +83,29 @@ const CompetitionCard = ({ competition, competition_type, company, admin, onComp
79
83
  </View>
80
84
  </View>
81
85
  </View>
86
+ {player && competition_result && competition_record ?
87
+ <View type='footer' style={{ padding:10, flexDirection:'row', alignItems:'center', borderBottomWidth:1, borderColor:Colors.borders.light }}>
88
+ <Image
89
+ source={{ uri: player.profile_pic }}
90
+ style={{ height:30, width:30, borderRadius:100 }}
91
+ resizeMode='contain'
92
+ />
93
+ <View transparent style={{ flex:1, marginLeft:10 }}>
94
+ <Text theme='h1'>@{player.username}</Text>
95
+ <View transparent style={{ marginTop:3 }}>
96
+ {competition_type.type == 'pick' ?
97
+ <Text theme="description">{competition_record.wins} - {competition_record.losses} - {competition_record.draws} ({competition_record.remaining_picks} Remaining)</Text>
98
+ :competition_type.type == 'wager' ?
99
+ <Text theme="description">{competition_record.earnings.toFixed(2)} Earned ({competition_record.remaining_stakes.toFixed(2)} at risk)</Text>
100
+ :<></>}
101
+ </View>
102
+ </View>
103
+ <View transparent style={{ alignItems:'flex-end' }}>
104
+ <Text theme='h2' color={competition_result.winnings > 0 ? Colors.text.success : Colors.text.warning}>{cl}{competition_result.winnings.toFixed(2)}</Text>
105
+ <Text theme='description' style={{ marginTop:3 }}>{CompetitionHelpers.formatPlace(competition_result.place)} / {competition.tickets_sold}</Text>
106
+ </View>
107
+ </View>
108
+ :<></>}
82
109
  <View type='footer' style={{ flexDirection:'row', alignItems:'center', padding:10, borderBottomRightRadius:8, borderBottomLeftRadius:8 }}>
83
110
  {company ?
84
111
  <Image
@@ -0,0 +1,176 @@
1
+ import axios from "axios";
2
+ import { APIOverrides } from "../../ApiOverrides"
3
+ import type { CompetitionPayoutTypeProps, CompetitionPlayerProps, CompetitionProps, CompetitionRecordProps, CompetitionResultProps, CompetitionResultTypeProps, CompetitionTypeProps, GroupMessageProps, GroupPlayerProps, GroupProps, NewOverallAnalytics, OrderAnalyticsProps, OrderProps, PostProps, PublicPlayerProps } from "../../types";
4
+
5
+
6
+ //let EVENT_SVC_API = ''
7
+ let AUTH_SVC_API = ''
8
+ let MK_SVC_API = ''
9
+ let SOCIAL_SVC_API = ''
10
+ let TP_SVC_API = ''
11
+
12
+ export { GroupApi, GroupHelpers }
13
+
14
+ const GroupApi = {
15
+ setEnvironment: () => {
16
+ const endpoints = APIOverrides.getEndpoints();
17
+ //EVENT_SVC_API = endpoints['EVENT_SVC_API'] as string;
18
+ MK_SVC_API = endpoints['MK_SVC_API'] as string;
19
+ TP_SVC_API = endpoints['TP_SVC_API'] as string;
20
+ AUTH_SVC_API = endpoints['AUTH_SVC_API'] as string;
21
+ SOCIAL_SVC_API = endpoints['SOCIAL_SVC_API'] as string;
22
+ },
23
+ getGroupById: async(group_id:string):Promise<undefined|{ group:GroupProps, group_messages:GroupMessageProps[], group_players:GroupPlayerProps[], my_group_player?:GroupPlayerProps }> => {
24
+ try {
25
+ const resp = await axios.get(`${SOCIAL_SVC_API}/v2/groups/group/${group_id}`);
26
+ return resp.data
27
+ } catch (e) {
28
+ console.log(e);
29
+ return undefined
30
+ }
31
+ },
32
+ acceptInvite:async(group_player_id:string):Promise<GroupPlayerProps | undefined> => {
33
+ try {
34
+ const resp = await axios.post(`${SOCIAL_SVC_API}/v2/groups/player/accept`, { group_player_id });
35
+ return resp.data.group_player
36
+ } catch (e) {
37
+ return undefined
38
+ }
39
+ },
40
+ declineInvite:async(group_player_id:string):Promise<GroupPlayerProps | undefined> => {
41
+ try {
42
+ const resp = await axios.post(`${SOCIAL_SVC_API}/v2/groups/player/decline`, { group_player_id });
43
+ return resp.data.group_player
44
+ } catch (e) {
45
+ return undefined
46
+ }
47
+ },
48
+ join:async(group_id:string):Promise<GroupPlayerProps | undefined> => {
49
+ try {
50
+ const resp = await axios.post(`${SOCIAL_SVC_API}/v2/groups/player/join`, { group_id });
51
+ return resp.data.group_player
52
+ } catch (e) {
53
+ return undefined
54
+ }
55
+ },
56
+ leave:async(group_player_id:string):Promise<GroupPlayerProps | undefined> => {
57
+ try {
58
+ const resp = await axios.post(`${SOCIAL_SVC_API}/v2/groups/player/leave`, { group_player_id });
59
+ return resp.data.group_player
60
+ } catch (e) {
61
+ return undefined
62
+ }
63
+ },
64
+ updateGroup: async(group:GroupProps):Promise<GroupProps|undefined> => {
65
+ try {
66
+ const resp = await axios.post(`${SOCIAL_SVC_API}/v2/groups/group/update`, {group});
67
+ return resp.data.group
68
+ } catch (e) {
69
+ console.log(e);
70
+ return undefined
71
+ }
72
+ },
73
+ getPlayersByIds: async(player_ids:string[]):Promise<PublicPlayerProps[]> => {
74
+ try {
75
+ if(player_ids.length == 0){ return [] }
76
+ const resp = await axios.post(`${AUTH_SVC_API}/v1/players/bulk/get`, { player_ids });
77
+ return resp.data.players
78
+ } catch (e) {
79
+ return []
80
+ }
81
+ },
82
+ getGroupAnalytics: async(group_id:string, to:number, from:number):Promise<OrderAnalyticsProps[]> => {
83
+ try {
84
+ const resp = await axios.get(`${MK_SVC_API}/v1/analytics/group/details/${group_id}?to=${to}&from=${from}`);
85
+ console.log(resp.data)
86
+ return resp.data.analytics
87
+ } catch (e) {
88
+ return []
89
+ }
90
+ },
91
+ getCompetitionOptions: async():Promise<{competition_types:CompetitionTypeProps[], competition_result_types:CompetitionResultTypeProps[], competition_payout_types:CompetitionPayoutTypeProps[]}> => {
92
+ const resp = await axios.get(`${TP_SVC_API}/v1/competitions/options`)
93
+ return resp.data
94
+ },
95
+ getActiveCompetitions: async(group_id:string):Promise<{ competitions:CompetitionProps[], competition_players:CompetitionPlayerProps[], competition_records:CompetitionRecordProps[], competition_results:CompetitionResultProps[] }> => {
96
+ try {
97
+ const resp = await axios.get(`${TP_SVC_API}/v2/competitions/group/active/${group_id}`);
98
+ return resp.data
99
+ } catch (e) {
100
+ console.log(e);
101
+ return { competition_players:[], competition_records:[], competition_results:[], competitions:[] }
102
+ }
103
+ },
104
+ getHistoryCompetitions: async(group_id:string, offset:number):Promise<{ competitions:CompetitionProps[], competition_players:CompetitionPlayerProps[], competition_records:CompetitionRecordProps[], competition_results:CompetitionResultProps[] }> => {
105
+ try {
106
+ const resp = await axios.get(`${TP_SVC_API}/v2/competitions/group/history/${group_id}?offset=${offset}`);
107
+ return resp.data
108
+ } catch (e) {
109
+ console.log(e);
110
+ return { competition_players:[], competition_records:[], competition_results:[], competitions:[] }
111
+ }
112
+ },
113
+ getPostsByGroupId: async(group_id:string, offset:number):Promise<PostProps[]> => {
114
+ try {
115
+ const resp = await axios.get(`${SOCIAL_SVC_API}/v1/posts/group/${group_id}?offset=${offset}`);
116
+ return resp.data.posts
117
+ } catch (e) {
118
+ console.log(e)
119
+ return []
120
+ }
121
+ },
122
+ getBulkOrders: async(order_ids:string[]):Promise<OrderProps[]> => {
123
+ try {
124
+ const resp = await axios.post(`${MK_SVC_API}/v1/orders/bulk/get`, { order_ids })
125
+ return resp.data.orders
126
+ } catch (e) {
127
+ console.log(e);
128
+ return []
129
+ }
130
+ },
131
+ }
132
+
133
+
134
+ const GroupHelpers = {
135
+ sortMembers: (group_players:GroupPlayerProps[], order_analytics:OrderAnalyticsProps[]) => {
136
+ let player_analytics: { [key:string]: NewOverallAnalytics } = {}
137
+ group_players.map(p => {
138
+ let my_analytics = order_analytics.filter(oa => oa.player_id == p.player_id);
139
+ player_analytics[p.player_id] = GroupHelpers.aggregateAnalytics(my_analytics);
140
+ });
141
+
142
+ let sorted_players = group_players.filter(gp => gp.status == 'active').sort((a,b) => {
143
+ let a_analytics = player_analytics[a.player_id]?.roi_pct ?? 0
144
+ let b_analytcs = player_analytics[b.player_id]?.roi_pct ?? 0
145
+ return b_analytcs - a_analytics
146
+ });
147
+ return { sorted_players, player_analytics }
148
+ },
149
+ aggregateAnalytics: (analytics:OrderAnalyticsProps[]):NewOverallAnalytics => {
150
+ const wins = analytics.reduce((a,b) => a + b.wins, 0);
151
+ const losses = analytics.reduce((a,b) => a + b.losses, 0);
152
+ const draws = analytics.reduce((a,b) => a + b.draws, 0);
153
+ const fulfilled_positions = analytics.reduce((a,b) => a + parseFloat(b.fulfilled_positions as any), 0);
154
+ const winnings = analytics.reduce((a,b) => a + b.winnings, 0);
155
+ const original_stake = analytics.reduce((a,b) => a + b.original_stake, 0);
156
+ const cash_rcvd = analytics.reduce((a,b) => a + b.cash_rcvd, 0);
157
+
158
+ const win_pct = (wins + (draws / 2)) / fulfilled_positions
159
+ const earnings = (winnings + cash_rcvd) - original_stake
160
+ const roi_pct = original_stake > 0 ? earnings / original_stake : 0
161
+ return {
162
+ winnings,
163
+ original_stake,
164
+ wins,
165
+ losses,
166
+ draws,
167
+ net_winnings: 0,
168
+ player_id: '',
169
+ fulfilled_positions,
170
+ cash_rcvd,
171
+ earnings,
172
+ win_pct,
173
+ roi_pct
174
+ }
175
+ }
176
+ }
@@ -0,0 +1,57 @@
1
+ import React from 'react';
2
+ import moment from "moment-mini"
3
+ import { useEffect } from "react"
4
+ import type { GroupMessageProps, GroupPlayerProps, GroupProps, PublicPlayerProps } from "../../types"
5
+ import { useColors } from "../../constants/useColors"
6
+ import { Button, Text, View } from "../../Components/Themed"
7
+ import { Icons } from '../../Components';
8
+ import { Image } from 'react-native';
9
+
10
+ type GroupCTAButtonProps = {
11
+ group:GroupProps,
12
+ message_length?:number,
13
+ group_messages:GroupMessageProps[],
14
+ players:PublicPlayerProps[],
15
+ group_players:GroupPlayerProps[],
16
+ onSelectChat:(group:GroupProps) => void
17
+ }
18
+
19
+ const GroupCTAButton = ({ group, players, message_length, group_messages, onSelectChat }:GroupCTAButtonProps) => {
20
+ const Colors = useColors();
21
+
22
+ useEffect(( ) => {
23
+ if(!group){ return }
24
+ //setUpCta();
25
+ },[group.group_id]);
26
+
27
+
28
+ let last_message = group_messages.sort((a,b) => moment(b.create_datetime).unix() - moment(a.create_datetime).unix())[0]
29
+ let cta_message = group.name
30
+ if(last_message?.message){ cta_message = `${last_message.message.slice(0, message_length ?? 15)}...` }
31
+ const last_player = players.find(p => p.player_id == last_message?.player_id)
32
+ return (
33
+ <View transparent>
34
+ <Button transparent style={{ flexDirection:'row', alignItems:'center', padding:2 }} onPress={() => onSelectChat(group)}>
35
+ {last_player ?
36
+ <Image
37
+ source={{ uri: last_player.profile_pic }}
38
+ style={{ height:30, width:30, borderRadius:100 }}
39
+ resizeMode='contain'
40
+ />
41
+ :<></>}
42
+ <View transparent style={{ flex:1, marginLeft:10, marginRight:10 }}>
43
+ <Text theme='h2'>{cta_message}</Text>
44
+ {last_player ?
45
+ <Text theme='description' style={{ marginTop:3 }}>@{last_player.username}</Text>
46
+ :<></>}
47
+ </View>
48
+ <View float style={{ padding:10, flexDirection:'row', alignItems:'center' }}>
49
+ <Icons.ChatIcon size={16} color={Colors.text.h1}/>
50
+ <Text theme='h2' size={12}> Chat</Text>
51
+ </View>
52
+ </Button>
53
+ </View>
54
+
55
+ )
56
+ }
57
+ export default GroupCTAButton
@@ -0,0 +1,137 @@
1
+ import React, { useState } from 'react';
2
+ import { Button, Text, View } from "../../Components/Themed"
3
+ import type { GroupPlayerProps, GroupProps, MyPlayerProps } from '../../types';
4
+ import { GroupApi } from '../api';
5
+
6
+ type GroupPlayerActionFormProps = {
7
+ me:MyPlayerProps,
8
+ group:GroupProps,
9
+ group_player?:GroupPlayerProps,
10
+ onResponse:(gp:GroupPlayerProps) => void,
11
+ onClose:() => void
12
+ }
13
+ const GroupPlayerActionForm = ({ group, group_player, onResponse, onClose }:GroupPlayerActionFormProps) => {
14
+ const [ action_loading, setActionLoading ] = useState(false);
15
+
16
+ const handleAccept = async() => {
17
+ if(!group_player){ return }
18
+ setActionLoading(true)
19
+ const new_gp = await GroupApi.acceptInvite(group_player.group_player_id);
20
+ if(!new_gp){ alert('Unable to process') }
21
+ else {
22
+ onResponse(new_gp);
23
+ }
24
+ setActionLoading(false)
25
+ }
26
+
27
+ const handleDecline = async() => {
28
+ if(!group_player){ return }
29
+ setActionLoading(true)
30
+ const new_gp = await GroupApi.declineInvite(group_player.group_player_id);
31
+ if(!new_gp){ alert('Unable to process') }
32
+ else {
33
+ onResponse(new_gp);
34
+ }
35
+ setActionLoading(false)
36
+ }
37
+
38
+ const handleJoin= async() => {
39
+ setActionLoading(true)
40
+ const new_gp = await GroupApi.join(group.group_id);
41
+ if(!new_gp){ alert('Unable to process') }
42
+ else {
43
+ onResponse(new_gp);
44
+ }
45
+ setActionLoading(false)
46
+ }
47
+
48
+ const handleLeave = async() => {
49
+ if(!group_player){ return }
50
+ setActionLoading(true)
51
+ const new_gp = await GroupApi.leave(group_player.group_player_id);
52
+ if(!new_gp){ alert('Unable to process') }
53
+ else {
54
+ onResponse(new_gp);
55
+ }
56
+ setActionLoading(false)
57
+ }
58
+ return (
59
+ <View float>
60
+ <View type='header' style={{ flexDirection:'row', alignItems:'center', padding:10, borderTopRightRadius:8, borderTopLeftRadius:8 }}>
61
+ <View transparent style={{ flex:1 }}>
62
+ <Text theme='h1'>{group.name}</Text>
63
+ <Text theme='description' style={{ marginTop:3 }}>{group.description}</Text>
64
+ </View>
65
+ <Button
66
+ title='X'
67
+ float
68
+ style={{ padding:10 }}
69
+ onPress={() => onClose()}
70
+ />
71
+ </View>
72
+ <View type='body' style={{ padding:10 }}>
73
+ {group_player ?
74
+ <View>
75
+ {group_player.status == 'invited' ?
76
+ <Text theme='h1'>YOU HAVE BEEN INVITED</Text>
77
+ :<></>}
78
+ {group_player.status == 'active' ?
79
+ <Text theme='h1'>YOU ARE A MEMBER OF THIS GROUP</Text>
80
+ :<></>}
81
+ {group_player.status == 'inactive' ?
82
+ <Text theme='h1'>JOIN THIS GROUP NOW</Text>
83
+ :<></>}
84
+ </View>
85
+ :
86
+ <View>
87
+ <Text theme='h1'>JOIN THIS GROUP NOW</Text>
88
+ </View>
89
+ }
90
+ </View>
91
+ <View type='footer' style={{ flexDirection:'row', alignItems:'center', padding:5, borderBottomRightRadius:8, borderBottomLeftRadius:8 }}>
92
+ {group_player?.status == 'invited' ?
93
+ <Button
94
+ title='DECLINE'
95
+ type='error'
96
+ loading={action_loading}
97
+ style={{ flex:1, opacity: action_loading ? 0.5:1, margin:5 }}
98
+ disabled={action_loading}
99
+ onPress={() => handleDecline()}
100
+ />
101
+ :<></>}
102
+ {group_player?.status == 'invited' ?
103
+ <Button
104
+ title='ACCEPT'
105
+ type='success'
106
+ loading={action_loading}
107
+ style={{ flex:1, opacity: action_loading ? 0.5:1, margin:5 }}
108
+ disabled={action_loading}
109
+ onPress={() => handleAccept()}
110
+ />
111
+ :<></>}
112
+ {group_player?.status == 'active' ?
113
+ <Button
114
+ title='LEAVE GROUP'
115
+ type='error'
116
+ loading={action_loading}
117
+ style={{ flex:1, opacity: action_loading ? 0.5:1, margin:5 }}
118
+ disabled={action_loading}
119
+ onPress={() => handleLeave()}
120
+ />
121
+ :<></>}
122
+ {!group_player || group_player.status == 'inactive' ?
123
+ <Button
124
+ title='JOIN GROUP'
125
+ type='success'
126
+ loading={action_loading}
127
+ style={{ flex:1, opacity: action_loading ? 0.5:1, margin:5 }}
128
+ disabled={action_loading}
129
+ onPress={() => handleJoin()}
130
+ />
131
+ :<></>}
132
+ </View>
133
+ </View>
134
+ )
135
+ }
136
+
137
+ export default GroupPlayerActionForm
@@ -0,0 +1,239 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { Button, Text, TextInput, View } from "../../Components/Themed";
3
+ import { FlatList } from 'react-native';
4
+ import type { FocusPositionProps, GroupProps } from '../../types';
5
+ import { Switch, Toggle } from '../../Components';
6
+ import { GroupApi } from '../api';
7
+
8
+ const sections = ['header','toggle','information', 'settings', 'invite', 'options']
9
+ type ManageGroupFormProps = {
10
+ group:GroupProps,
11
+ onUpdateGroup:(group:GroupProps) => void,
12
+ onInvitePlayer: () => void,
13
+ onClose:() => void,
14
+ onFocusPosition?:(position:FocusPositionProps) => void,
15
+ }
16
+ const ManageGroupForm = ({ group, onFocusPosition, onUpdateGroup, onClose, onInvitePlayer }: ManageGroupFormProps ) => {
17
+ const [ manage_state, setState ] = useState<{
18
+ loading:boolean,
19
+ draft_group:GroupProps,
20
+ active_toggle:string
21
+ }>({
22
+ draft_group: group,
23
+ loading:false,
24
+ active_toggle: 'information'
25
+ });
26
+ const { loading, draft_group, active_toggle } = manage_state;
27
+
28
+ useEffect(() => {
29
+ if(!group){ return }
30
+ setState({ ...manage_state,draft_group: group })
31
+ },[group]);
32
+
33
+ const handleSave = async(dg:GroupProps) => {
34
+ if(!dg){ return }
35
+ if(loading){ return }
36
+ console.log(dg)
37
+ setState({ ...manage_state, loading: true });
38
+ const new_group = await GroupApi.updateGroup(dg);
39
+ if(!new_group){
40
+ setState({ ...manage_state, loading:false })
41
+ return alert('Unable to update this group')
42
+ }
43
+ onUpdateGroup(new_group);
44
+ setState({ ...manage_state, loading:false })
45
+ }
46
+
47
+ const renderSections = (data:{ item:string, index:number }) => {
48
+ switch(data.item){
49
+ case 'header':
50
+ return (
51
+ <View type='header' style={{ flexDirection:'row', alignItems:'center', padding:10, borderTopRightRadius:8, borderTopLeftRadius:8 }}>
52
+ <View transparent style={{ flex:1 }}>
53
+ <Text theme='h1'>EDIT YOUR GROUP</Text>
54
+ <Text theme='description' style={{ marginTop:3 }}>Use the form below to make updates to this group</Text>
55
+ </View>
56
+ <Button
57
+ title='X'
58
+ float
59
+ style={{ padding:10 }}
60
+ onPress={() => onClose()}
61
+ />
62
+ </View>
63
+ )
64
+ case 'information':
65
+ if(!draft_group){ return <></> }
66
+ if(active_toggle != 'information'){ return <></> }
67
+ const is_changed = JSON.stringify({ name: draft_group.name, description: draft_group.description }) != JSON.stringify({ name: group.name, description:group.description }) ? true : false
68
+ return (
69
+ <View float style={{ margin:10 }}>
70
+ <View type='header' style={{ flexDirection:'row', alignItems:'center', padding:10 }}>
71
+ <View transparent style={{ flex:1 }}>
72
+ <Text theme='h1'>Edit Group Information</Text>
73
+ </View>
74
+ <Button
75
+ title='UPDATE'
76
+ type='success'
77
+ disabled={!is_changed}
78
+ loading={loading}
79
+ style={{ opacity: is_changed && !loading ? 1 : 0.5, padding:10 }}
80
+ onPress={() => handleSave(draft_group)}
81
+ />
82
+ </View>
83
+ <View transparent nativeID='title' type='row' style={{ flexWrap:"wrap", padding:10 }}>
84
+ <View transparent style={{ flexGrow:1, minWidth:150 }}>
85
+ <Text theme='h1'>Group Name</Text>
86
+ <Text theme='description' style={{ marginTop:3 }}>Give this group a unique name</Text>
87
+ </View>
88
+ <TextInput
89
+ value={draft_group.name}
90
+ style={{ minWidth:150, flexGrow:1, margin:5 }}
91
+ onFocusPosition={onFocusPosition}
92
+ onChangeText={(text) => setState({ ...manage_state, draft_group: { ...draft_group, name: text } })}
93
+ />
94
+ </View>
95
+ <View transparent nativeID='description' type='row' style={{ flexWrap:"wrap", padding:10 }}>
96
+ <View transparent style={{ flexGrow:1, minWidth:150 }}>
97
+ <Text theme='h1'>Group Description</Text>
98
+ <Text theme='description' style={{ marginTop:3 }}>Give this group a fun description</Text>
99
+ </View>
100
+ <TextInput
101
+ value={draft_group.description}
102
+ style={{ minWidth:300, flexGrow:1, margin:5 }}
103
+ onFocusPosition={onFocusPosition}
104
+ onChangeText={(text) => setState({ ...manage_state, draft_group: { ...draft_group, description: text } })}
105
+ />
106
+ </View>
107
+ </View>
108
+ )
109
+ case 'invite':
110
+ if(!draft_group){ return <></> }
111
+ if(active_toggle != 'information'){ return <></> }
112
+ const code_changed = draft_group.invite_code != group.invite_code ? true : false
113
+ return (
114
+ <View float style={{ margin:10 }}>
115
+ <View type='header' style={{ flexDirection:'row', alignItems:'center', padding:10 }}>
116
+ <View transparent style={{ flex:1 }}>
117
+ <Text theme='h1'>Group Players</Text>
118
+ </View>
119
+ <Button
120
+ title='INVITE'
121
+ type='success'
122
+ loading={loading}
123
+ style={{ padding:10 }}
124
+ onPress={() => onInvitePlayer()}
125
+ />
126
+ </View>
127
+ <View transparent type='body'>
128
+ <View transparent type='row' style={{ padding:10 }}>
129
+ <View transparent style={{flex:1 }}>
130
+ <Text theme='h1'>INVITE ONLY</Text>
131
+ <Text theme='description' style={{ marginTop:3 }}>Make this group invite only so players can only join if invited by you</Text>
132
+ </View>
133
+ <Switch
134
+ value={draft_group.invite_only}
135
+ onChange={(value) => handleSave({ ...draft_group, invite_only: value, invite_code: value ? `IC${group.group_id}`: '' }) }
136
+ switch_type='on_off'
137
+ />
138
+ </View>
139
+ {draft_group.invite_only ?
140
+ <View transparent nativeID='title' type='row' style={{ flexWrap:"wrap" }}>
141
+ <View transparent style={{ flexGrow:1, minWidth:150, padding:10 }}>
142
+ <Text theme='h1'>INVITE CODE</Text>
143
+ <Text theme='description' style={{ marginTop:3 }}>You can set a custom code for users to find your group</Text>
144
+ </View>
145
+ <View transparent style={{ flexDirection:'row', alignItems:'center', minWidth:200, flexGrow:1, padding:10 }}>
146
+ <TextInput
147
+ value={draft_group.invite_code}
148
+ style={{ flexGrow:1, marginRight:5 }}
149
+ onFocusPosition={onFocusPosition}
150
+ onChangeText={(text) => setState({ ...manage_state, draft_group: { ...draft_group, invite_code: text } })}
151
+ />
152
+ <Button
153
+ title='SAVE'
154
+ type='success'
155
+ disabled={!code_changed}
156
+ loading={loading}
157
+ style={{ opacity: code_changed && !loading ? 1 : 0.5, padding:10 }}
158
+ onPress={() => handleSave(draft_group)}
159
+ />
160
+ </View>
161
+ </View>
162
+ :<></>}
163
+ </View>
164
+ </View>
165
+ )
166
+ case 'toggle':
167
+ return (
168
+ <View style={{ padding:10 }}>
169
+ <Toggle
170
+ options={[
171
+ {key: 'information', label: 'Information'},
172
+ { key: 'settings', label: 'settings'}
173
+ ]}
174
+ onSelectOption={(option) => setState({ ...manage_state, active_toggle: option })}
175
+ selected_option={active_toggle}
176
+ />
177
+ </View>
178
+ )
179
+ case 'settings':
180
+ if(active_toggle != 'settings'){ return <></> }
181
+ return (
182
+ <View float style={{ margin:10 }}>
183
+ <View type='header' style={{ flexDirection:'row', alignItems:'center', padding:10, borderTopRightRadius:8, borderTopLeftRadius:8 }}>
184
+ <View transparent style={{ flex:1 }}>
185
+ <Text theme='h1'>Edit Group Settings</Text>
186
+ </View>
187
+ </View>
188
+ <View transparent nativeID='title' type='row' style={{ padding:10 }}>
189
+ <View transparent style={{ flexGrow:1, minWidth:150 }}>
190
+ <Text theme='h1'>Show Leaderboard</Text>
191
+ <Text theme='description' style={{ marginTop:3 }}>Include a leaderboard for all players in this group</Text>
192
+ </View>
193
+ <Switch
194
+ value={draft_group.leaderboard_ind}
195
+ onChange={(value) => handleSave({ ...draft_group, leaderboard_ind: value }) }
196
+ switch_type='on_off'
197
+ />
198
+ </View>
199
+ <View transparent nativeID='title' type='row' style={{ padding:10 }}>
200
+ <View transparent style={{ flexGrow:1, minWidth:150 }}>
201
+ <Text theme='h1'>Order Alerts</Text>
202
+ <Text theme='description' style={{ marginTop:3 }}>Log each order a player in this group makes.</Text>
203
+ </View>
204
+ <Switch
205
+ value={draft_group.order_alerts}
206
+ onChange={(value) => handleSave({ ...draft_group, order_alerts: value }) }
207
+ switch_type='on_off'
208
+ />
209
+ </View>
210
+ <View transparent nativeID='title' type='row' style={{ padding:10 }}>
211
+ <View transparent style={{ flexGrow:1, minWidth:150 }}>
212
+ <Text theme='h1'>Competition Alerts</Text>
213
+ <Text theme='description' style={{ marginTop:3 }}>Log each competition a player in this group joins.</Text>
214
+ </View>
215
+ <Switch
216
+ value={draft_group.competition_alerts}
217
+ onChange={(value) => handleSave({ ...draft_group, competition_alerts: value }) }
218
+ switch_type='on_off'
219
+ />
220
+ </View>
221
+ </View>
222
+ )
223
+ default: return <></>
224
+ }
225
+ }
226
+
227
+ return (
228
+ <View float style={{ flex:1 }}>
229
+ <FlatList
230
+ data={sections}
231
+ key={'group_sections_manage'}
232
+ keyExtractor={item => item}
233
+ renderItem={renderSections}
234
+ />
235
+ </View>
236
+ )
237
+ }
238
+
239
+ export default ManageGroupForm