tf-checkout-react 1.3.46 → 1.3.47-beta.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 (38) hide show
  1. package/dist/.DS_Store +0 -0
  2. package/dist/api/index.d.ts +10 -0
  3. package/dist/components/addonsContainer/utils/index.d.ts +1 -4
  4. package/dist/components/billing-info-container/utils.d.ts +20 -1
  5. package/dist/components/myTicketsContainer/tableConfig.d.ts +1 -4
  6. package/dist/components/ticketsContainer/TicketRow.d.ts +6 -1
  7. package/dist/components/ticketsContainer/TicketsSection.d.ts +6 -1
  8. package/dist/components/ticketsContainer/crypto.d.ts +24 -0
  9. package/dist/tf-checkout-react.cjs.development.js +4701 -1324
  10. package/dist/tf-checkout-react.cjs.development.js.map +1 -1
  11. package/dist/tf-checkout-react.cjs.production.min.js +1 -1
  12. package/dist/tf-checkout-react.cjs.production.min.js.map +1 -1
  13. package/dist/tf-checkout-react.esm.js +4702 -1324
  14. package/dist/tf-checkout-react.esm.js.map +1 -1
  15. package/dist/tf-checkout-styles.css +1 -1
  16. package/dist/types/billing-info-data.d.ts +5 -0
  17. package/dist/utils/getWalletName.d.ts +1 -0
  18. package/dist/utils/loadProfile.d.ts +32 -0
  19. package/package.json +3 -1
  20. package/src/.DS_Store +0 -0
  21. package/src/api/index.ts +42 -1
  22. package/src/assets/.DS_Store +0 -0
  23. package/src/components/addonsContainer/utils/index.tsx +1 -1
  24. package/src/components/billing-info-container/index.tsx +271 -11
  25. package/src/components/billing-info-container/style.css +1 -1
  26. package/src/components/billing-info-container/utils.ts +98 -5
  27. package/src/components/loginModal/index.tsx +70 -5
  28. package/src/components/loginModal/style.css +2 -2
  29. package/src/components/ticketsContainer/TicketRow.tsx +55 -5
  30. package/src/components/ticketsContainer/TicketsSection.tsx +15 -1
  31. package/src/components/ticketsContainer/crypto.js +528 -0
  32. package/src/components/ticketsContainer/index.tsx +216 -2
  33. package/src/components/waitingList/index.tsx +1 -1
  34. package/src/env.ts +2 -2
  35. package/src/types/billing-info-data.ts +6 -0
  36. package/src/utils/getWalletName.tsx +10 -0
  37. package/src/utils/loadProfile.tsx +47 -0
  38. package/src/components/common/dist/PhoneNumberField.js +0 -96
@@ -4,10 +4,12 @@ import _forEach from 'lodash/forEach'
4
4
  import _get from 'lodash/get'
5
5
  import _isArray from 'lodash/isArray'
6
6
  import _map from 'lodash/map'
7
+ import _size from 'lodash/size'
7
8
  import { nanoid } from 'nanoid'
8
9
  import React from 'react'
9
10
 
10
11
  import { IGroupItem } from '../../types'
12
+ import { IBillingCryptoData } from "../../types/billing-info-data"
11
13
  import { CONFIGS } from '../../utils'
12
14
  import { getQueryVariable } from '../../utils/getQueryVariable'
13
15
  import { combineValidators, requiredValidator } from '../../validators'
@@ -63,14 +65,23 @@ export const getInitialValues = (
63
65
  export const createRegisterFormData = (
64
66
  values: IValues = {},
65
67
  checkoutBody: { attributes: { [key: string]: any } },
66
- flagFreeTicket = false
68
+ flagFreeTicket = false,
69
+ cryptoData: IBillingCryptoData
67
70
  ): FormData => {
68
71
  const bodyFormData = new FormData()
69
72
  bodyFormData.append('first_name', values.firstName)
70
73
  bodyFormData.append('last_name', values.lastName)
71
74
  bodyFormData.append('email', values.email)
72
- bodyFormData.append('password', values.password)
73
- bodyFormData.append('password_confirmation', values.confirmPassword)
75
+
76
+ if (cryptoData.cryptoAddress && cryptoData.blockchain && cryptoData.cryptoSignature) {
77
+ bodyFormData.append('crypto_address', cryptoData.cryptoAddress)
78
+ bodyFormData.append('crypto_signature', cryptoData.cryptoSignature)
79
+ bodyFormData.append('blockchain', cryptoData.blockchain)
80
+ } else {
81
+ bodyFormData.append('password', values.password)
82
+ bodyFormData.append('password_confirmation', values.confirmPassword)
83
+ }
84
+
74
85
  bodyFormData.append(
75
86
  'client_id',
76
87
  CONFIGS.CLIENT_ID || 'e9d8f8922797b4621e562255afe90dbf'
@@ -115,6 +126,7 @@ interface IUserData {
115
126
  zip?: string;
116
127
  zipCode?: string;
117
128
  stateId?: string;
129
+ linkedCryptoWallets?: string[];
118
130
  }
119
131
 
120
132
  interface IticketHolder {
@@ -136,6 +148,7 @@ export const setLoggedUserData = (data: IUserData) => ({
136
148
  street_address: data?.streetAddress || '',
137
149
  state: data?.stateId || '',
138
150
  zip: data?.zip || data?.zipCode || '',
151
+ linkedCryptoWallets: data?.linkedCryptoWallets || []
139
152
  })
140
153
 
141
154
  export const createCheckoutDataBody = (
@@ -153,7 +166,7 @@ export const createCheckoutDataBody = (
153
166
  ...restValues
154
167
  } = values
155
168
 
156
- const holders = []
169
+ const holders: any[] = []
157
170
  let ticket_holders: IticketHolder[] = []
158
171
 
159
172
  for (let i = 0; i <= ticketsQuantity; i++) {
@@ -271,6 +284,86 @@ export const assingUniqueIds = (data: any): any => {
271
284
  })
272
285
  }
273
286
 
287
+ export const signSubmit = async ({
288
+ hasAccount,
289
+ ethNonce,
290
+ permittedBlockchains,
291
+ cryptoWrapper,
292
+ selectedBlockchain
293
+ }: {
294
+ hasAccount: string;
295
+ ethNonce: string;
296
+ permittedBlockchains: any;
297
+ cryptoWrapper: any;
298
+ selectedBlockchain: string;
299
+ }): Promise<{ usedAddress?: string, signature?: string, override_crypto_address?: boolean }> => {
300
+ try {
301
+ let cryptoAddress = permittedBlockchains[selectedBlockchain].session_address
302
+ const isFromSession = cryptoAddress !== null && cryptoAddress !== undefined
303
+ cryptoAddress = (isFromSession) ? cryptoAddress : permittedBlockchains[selectedBlockchain].linked_address
304
+ if(cryptoAddress === '' || (hasAccount && isFromSession)) {
305
+ window.alert('You need to link a wallet first.')
306
+ return {}
307
+ }
308
+ let overrideCryptoAddress = false
309
+ let usedAddress = undefined
310
+ let signature = ''
311
+ await cryptoWrapper.enable()
312
+ const address = await cryptoWrapper.getAddress()
313
+ if (isFromSession && address !== cryptoAddress) {
314
+ const message = 'Welcome to The Ticket Fairy! Nonce: '
315
+ try {
316
+ usedAddress = address
317
+ overrideCryptoAddress = true
318
+ await cryptoWrapper.getNonceFromAddress(address)
319
+ window.alert(
320
+ 'This wallet is already connected to an existing account.' +
321
+ ' Please log out of this account and log back in using this wallet.'
322
+ )
323
+ // eslint-disable-next-line no-throw-literal
324
+ throw { errorCode: cryptoWrapper.ERRORS.CONNECTION_REJECTED }
325
+ } catch(e) {
326
+ const error: any = e
327
+ if(error.errorCode === cryptoWrapper.ERRORS.ACCOUNT_NOT_FOUND){
328
+ signature = await cryptoWrapper.getSignature(address, ethNonce, message)
329
+ }
330
+ // eslint-disable-next-line no-throw-literal
331
+ throw error
332
+ }
333
+ } else {
334
+ const message = 'Confirm you are the owner of '+ cryptoAddress + '. Nonce: '
335
+ signature = await cryptoWrapper.getSignature(address, ethNonce, message)
336
+ }
337
+ if (overrideCryptoAddress) {
338
+ return { usedAddress, signature, override_crypto_address: true }
339
+ }
340
+ return { signature }
341
+ } catch(e) {
342
+ const error: any = e
343
+ if (
344
+ error.errorCode !== cryptoWrapper.ERRORS.CONNECTION_REJECTED &&
345
+ error.errorCode !== cryptoWrapper.ERRORS.SIGNATURE_REJECTED
346
+ ) {
347
+ window.alert('Something went wrong, Try again later or contact our support team.')
348
+ }
349
+ return {}
350
+ }
351
+ }
352
+
353
+ export const returnSelectedBlockchain = (permittedBlockchains: any, detectedWallets: string[]) => {
354
+ if (_size(permittedBlockchains || {}) === 1) {
355
+ for (const detectedWallet of detectedWallets) {
356
+ if (permittedBlockchains[detectedWallet]) {
357
+ return {
358
+ selectedBlockchain: detectedWallet,
359
+ linkedAddress: permittedBlockchains[detectedWallet].linked_address as string,
360
+ sessionAddress: permittedBlockchains[detectedWallet].session_address as string
361
+ }
362
+ }
363
+ }
364
+ }
365
+ return { selectedBlockchain: '', linkedAddress: '', sessionAddress: '' }
366
+ }
274
367
  export const isRequiredField = (element: IGroupItem) => {
275
368
  const flagRequirePhone = getQueryVariable('phone_required') === 'true'
276
369
  const collectMandatoryWalletAddress =
@@ -311,4 +404,4 @@ export const getFieldComponent = (element: IGroupItem) => {
311
404
 
312
405
  const fieldComponent = _get(fieldComponentConfigs, type, CustomField)
313
406
  return fieldComponent
314
- }
407
+ }
@@ -7,14 +7,18 @@ import axios, { AxiosError } from 'axios'
7
7
  import { Field, Form, Formik } from 'formik'
8
8
  import _get from 'lodash/get'
9
9
  import _identity from 'lodash/identity'
10
- import React, { FC, useState } from 'react'
10
+ import _map from "lodash/map"
11
+ import React, { FC, useEffect, useState } from 'react'
11
12
 
12
13
  import {
13
14
  authorize,
14
15
  getProfileData,
15
16
  } from '../../api'
17
+ import { getWalletName } from "../../utils/getWalletName"
18
+ import { loadProfile } from "../../utils/loadProfile"
16
19
  import { requiredValidator } from '../../validators'
17
20
  import { PoweredBy } from '../common/PoweredBy'
21
+ import { CryptoIntegration } from "../ticketsContainer/crypto"
18
22
 
19
23
  interface Props {
20
24
  onClose: () => void;
@@ -90,6 +94,62 @@ export const LoginModal: FC<Props> = ({
90
94
  showPoweredByImage = false,
91
95
  }) => {
92
96
  const [error, setError] = useState('')
97
+ const [cryptoWrapper, setCryptoWrapper] = useState<any>(undefined)
98
+ const [detectedWallets, setDetectedWallets] = useState<string[]>([])
99
+ useEffect(() => {
100
+ setCryptoWrapper(CryptoIntegration())
101
+ const wallets: string[] = []
102
+ const registerDetectedWallet = (event: any) => {
103
+ const identifier = event?.detail?.identifier
104
+ const registered = wallets.find(wallet => wallet === identifier)
105
+ if (identifier && !registered) {
106
+ wallets.push(identifier)
107
+ setDetectedWallets(detectedWallets => [...detectedWallets, identifier])
108
+ }
109
+ }
110
+ window.addEventListener('ttf.crypto.new-wallet', registerDetectedWallet)
111
+ return () => window.removeEventListener('ttf.crypto.new-wallet', registerDetectedWallet)
112
+ }, [])
113
+
114
+ const walletLogin = async (blockchain: string) => {
115
+ cryptoWrapper.setBlockchain(blockchain)
116
+ setError('')
117
+
118
+ try {
119
+ await cryptoWrapper.enable()
120
+ const cryptoAddress = await cryptoWrapper.getAddress()
121
+ const nonce = await cryptoWrapper.getNonceFromAddress(cryptoAddress)
122
+ const signature = await cryptoWrapper.getSignature(cryptoAddress, nonce, 'Welcome back, we missed you! Nonce: ')
123
+ await cryptoWrapper.authAddress(cryptoAddress, signature)
124
+
125
+ } catch (e) {
126
+ const error: any = e
127
+ if (error.source === 'backend' && error.errorCode === cryptoWrapper.ERRORS.ACCOUNT_NOT_FOUND) {
128
+ setError('We could not find an account with your wallet address')
129
+ } else if (error.errorCode === cryptoWrapper.ERRORS.SIGNATURE_MISMATCHED) {
130
+ setError(error.message)
131
+ } else if (error.errorCode !== cryptoWrapper.ERRORS.SIGNATURE_REJECTED &&
132
+ error.errorCode !== cryptoWrapper.ERRORS.CONNECTION_REJECTED) {
133
+ setError("Something went wrong, try again or contact out support team.")
134
+ }
135
+ return
136
+ }
137
+
138
+ try {
139
+ const profileData = await loadProfile()
140
+ onGetProfileDataSuccess(profileData)
141
+ if (typeof window !== 'undefined') {
142
+ const event = new window.CustomEvent('tf-login')
143
+ window.document.dispatchEvent(event)
144
+ }
145
+ onLogin()
146
+ } catch (e) {
147
+ if (axios.isAxiosError(e)) {
148
+ onGetProfileDataError(e)
149
+ }
150
+ }
151
+ }
152
+
93
153
  return (
94
154
  <Modal
95
155
  open={true}
@@ -106,10 +166,10 @@ export const LoginModal: FC<Props> = ({
106
166
  try {
107
167
  const body = { email, password }
108
168
  await authorize(body)
109
- let profileResponse = null
169
+ let profileResponse: any = null
110
170
  try {
111
171
  profileResponse = await getProfileData()
112
- onGetProfileDataSuccess(profileResponse.data)
172
+ onGetProfileDataSuccess(profileResponse?.data)
113
173
  } catch (e) {
114
174
  if (axios.isAxiosError(e)) {
115
175
  onGetProfileDataError(e)
@@ -124,8 +184,6 @@ export const LoginModal: FC<Props> = ({
124
184
  'user_data',
125
185
  JSON.stringify(profileDataObj)
126
186
  )
127
- const event = new window.CustomEvent('tf-login')
128
- window.document.dispatchEvent(event)
129
187
  }
130
188
  onLogin()
131
189
  } catch (e) {
@@ -192,6 +250,13 @@ export const LoginModal: FC<Props> = ({
192
250
  <div className="login-action-button">
193
251
  <button type="submit">Login</button>
194
252
  </div>
253
+ {_map(detectedWallets, wallet => (
254
+ <div className="login-wallet-button" key={wallet}>
255
+ <button type="button" onClick={() => {
256
+ walletLogin(wallet)
257
+ }}>{'Login with ' + getWalletName(wallet)}</button>
258
+ </div>
259
+ ))}
195
260
  {showForgotPasswordButton && (
196
261
  <div className="forgot-password">
197
262
  <span aria-hidden="true" onClick={onForgotPassword}>Forgot password?</span>
@@ -27,7 +27,7 @@
27
27
  margin-bottom: 20px;
28
28
  }
29
29
 
30
- .login-action-button button {
30
+ .login-action-button button, .login-wallet-button button {
31
31
  padding: 10px;
32
32
  width: 100%;
33
33
  margin: 10px 0;
@@ -42,7 +42,7 @@
42
42
  outline: none;
43
43
  background-color: #212529;
44
44
  }
45
- .login-action-button button:hover {
45
+ .login-action-button button:hover, .login-wallet-button button:hover {
46
46
  background-color: #505050;
47
47
  border-color: #505050;
48
48
  }
@@ -1,12 +1,13 @@
1
- import React from 'react'
2
1
  import './style.css'
3
- import _has from 'lodash/has'
4
- import _get from 'lodash/get'
2
+
5
3
  import Box from '@mui/material/Box'
6
4
  import FormControl from '@mui/material/FormControl'
7
5
  import MenuItem from '@mui/material/MenuItem'
8
6
  import Select from '@mui/material/Select'
7
+ import _get from 'lodash/get'
8
+ import React from 'react'
9
9
 
10
+ import { temporalNonce } from '../../api'
10
11
  import { getTicketSelectOptions } from './utils'
11
12
 
12
13
  interface ITicketRowProps {
@@ -14,6 +15,11 @@ interface ITicketRowProps {
14
15
  prevTicketTier: any;
15
16
  selectedTickets: any;
16
17
  handleTicketSelect: any;
18
+ walletNotVerified: boolean;
19
+ noWalletConnected: boolean;
20
+ cryptoIdentifier: any;
21
+ cryptoButtonDisabled: boolean;
22
+ walletConnect: (cryptoEventIdentifier: any, ethNonce: any) => void;
17
23
  }
18
24
 
19
25
  export const TicketRow = ({
@@ -21,6 +27,11 @@ export const TicketRow = ({
21
27
  prevTicketTier,
22
28
  selectedTickets,
23
29
  handleTicketSelect,
30
+ noWalletConnected,
31
+ walletNotVerified,
32
+ cryptoIdentifier,
33
+ cryptoButtonDisabled,
34
+ walletConnect
24
35
  }: ITicketRowProps) => {
25
36
  const soldOutMessage = ticketTier.soldOutMessage
26
37
  ? `${ticketTier.soldOutMessage}`.toUpperCase()
@@ -31,7 +42,16 @@ export const TicketRow = ({
31
42
  ticketTier.minQuantity,
32
43
  ticketTier.multiplier
33
44
  )
34
- const ticketsClosedMessage = !ticketTier.salesStarted ? 'Sales not started' : 'Sales Ended'
45
+ const ticketsClosedMessage = !ticketTier.salesStarted ? 'Sales not started' : 'Sales Ended'
46
+
47
+ const isCryptoTicket = ticketTier?.isCryptoTicket
48
+ const cryptoEventIdentifier = (
49
+ ticketTier?.cryptoEventIdentifiers &&
50
+ ticketTier.cryptoEventIdentifiers?.length &&
51
+ ticketTier.cryptoEventIdentifiers[0]
52
+ )
53
+ const isSolanaEvent = cryptoEventIdentifier === 'solana'
54
+ const isEthereumEvent = cryptoEventIdentifier === 'ethereum'
35
55
 
36
56
  const onSaleContent = (
37
57
  <div className="get-tickets">
@@ -67,6 +87,34 @@ export const TicketRow = ({
67
87
  </div>
68
88
  )
69
89
 
90
+ const cryptoLogIn = cryptoIdentifier.length > 0
91
+ ? (
92
+ <div className='nft-container-connect'>
93
+ {isSolanaEvent && !cryptoIdentifier.includes('solana') ? (
94
+ <strong className='phatom enable-wallet'>Phantom-enabled browser required.</strong>
95
+ ) : isEthereumEvent && !cryptoIdentifier.includes('ethereum') ? (
96
+ <strong className='metamask enable-wallet'>Metamask-enabled browser required.</strong>
97
+ ) : (
98
+ (isSolanaEvent && cryptoIdentifier.includes('solana')) ||
99
+ (isEthereumEvent && cryptoIdentifier.includes('ethereum'))
100
+ ) ? (
101
+ <button
102
+ disabled={cryptoButtonDisabled}
103
+ className='ntf-button-connect'
104
+ type='button'
105
+ onClick={async () => {
106
+ const response = await temporalNonce()
107
+ const ethNonce = _get(response, 'data.data.attributes.ethNonce')
108
+ walletConnect(cryptoEventIdentifier, ethNonce)
109
+ }}
110
+ >
111
+ Connect to {isSolanaEvent ? 'Phantom' : 'MetaMask'}
112
+ </button>
113
+ ) : null}
114
+ </div>
115
+ )
116
+ : <strong>Crypto-enabled browser required.</strong>
117
+
70
118
  let returnValue: any = ''
71
119
  // ticketTier.soldOut === false --> means that ticket is in the stock
72
120
  const isSoldOut =
@@ -75,7 +123,9 @@ export const TicketRow = ({
75
123
  ticketTier.soldOut ||
76
124
  ticketTier.soldOut === false
77
125
 
78
- if (isSoldOut) {
126
+ if (isCryptoTicket && (noWalletConnected || walletNotVerified)) {
127
+ returnValue = cryptoLogIn
128
+ } else if (isSoldOut) {
79
129
  returnValue = soldOutMessage
80
130
  } else if (isSalesClosed) {
81
131
  returnValue = ticketsClosedMessage
@@ -12,10 +12,14 @@ interface ITicketsSectionProps {
12
12
  handleTicketSelect: any;
13
13
  sortBySoldOut: boolean;
14
14
  hideTicketsHeader: boolean;
15
-
16
15
  ticketsHeaderComponent?: ReactNode;
17
16
  showGroupNameBlock?: boolean;
18
17
  currencySybmol?: string;
18
+ cryptoIdentifier: any;
19
+ cryptoButtonDisabled: boolean;
20
+ walletConnect: (cryptoEventIdentifier: any, ethNonce: any) => void;
21
+ walletNotVerified: boolean;
22
+ noWalletConnected: boolean;
19
23
  }
20
24
 
21
25
  export const TicketsSection = ({
@@ -28,6 +32,11 @@ export const TicketsSection = ({
28
32
  hideTicketsHeader,
29
33
  showGroupNameBlock,
30
34
  currencySybmol,
35
+ cryptoIdentifier,
36
+ cryptoButtonDisabled,
37
+ walletConnect,
38
+ noWalletConnected,
39
+ walletNotVerified,
31
40
  }: ITicketsSectionProps) => {
32
41
  const {
33
42
  currency: { symbol },
@@ -101,6 +110,11 @@ export const TicketsSection = ({
101
110
  prevTicketTier={arr[i - 1]}
102
111
  selectedTickets={selectedTickets}
103
112
  handleTicketSelect={ticketSelect}
113
+ walletNotVerified={walletNotVerified}
114
+ noWalletConnected={noWalletConnected}
115
+ cryptoIdentifier={cryptoIdentifier}
116
+ cryptoButtonDisabled={cryptoButtonDisabled}
117
+ walletConnect={walletConnect}
104
118
  />
105
119
  </div>
106
120
  </div>