@huma-finance/widgets 0.0.62-beta.612 → 0.0.62-beta.616

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 (40) hide show
  1. package/API.md +0 -185
  2. package/dist/cjs/index.cjs +955 -2880
  3. package/dist/cjs/index.cjs.map +1 -1
  4. package/dist/index.d.ts +8 -116
  5. package/dist/index.js +658 -2601
  6. package/dist/index.js.map +1 -1
  7. package/package.json +9 -12
  8. package/src/components/InputAmountModal.tsx +2 -12
  9. package/src/components/Lend/addRedemptionV2/2-ChooseAmount.tsx +6 -1
  10. package/src/components/Lend/supplyV2/6-Success.tsx +10 -8
  11. package/src/components/Lend/supplyV2/7-Notifications.tsx +6 -1
  12. package/src/components/Notifi/NotifiContextWrapper.tsx +57 -0
  13. package/src/components/Notifi/NotifiSubscriptionModal.tsx +118 -113
  14. package/src/components/WidgetWrapper.tsx +4 -6
  15. package/src/hooks/useNotifi.ts +28 -17
  16. package/src/index.tsx +7 -124
  17. package/src/store/widgets.reducers.ts +0 -5
  18. package/src/store/widgets.store.ts +0 -1
  19. package/src/theme/coreThemeConstants.ts +1 -1
  20. package/src/components/CreditLine/solanaBorrow/1-ChooseAmount.tsx +0 -86
  21. package/src/components/CreditLine/solanaBorrow/2-ApproveAllowance.tsx +0 -87
  22. package/src/components/CreditLine/solanaBorrow/index.tsx +0 -96
  23. package/src/components/Lend/solanaAddRedemption/1-ChooseTranche.tsx +0 -114
  24. package/src/components/Lend/solanaAddRedemption/2-ChooseAmount.tsx +0 -183
  25. package/src/components/Lend/solanaAddRedemption/3-Transfer.tsx +0 -102
  26. package/src/components/Lend/solanaAddRedemption/4-Done.tsx +0 -29
  27. package/src/components/Lend/solanaAddRedemption/index.tsx +0 -140
  28. package/src/components/Lend/solanaCancelRedemption/1-ChooseTranche.tsx +0 -114
  29. package/src/components/Lend/solanaCancelRedemption/2-Transfer.tsx +0 -99
  30. package/src/components/Lend/solanaCancelRedemption/3-Done.tsx +0 -55
  31. package/src/components/Lend/solanaCancelRedemption/index.tsx +0 -152
  32. package/src/components/Lend/solanaSupply/1-ChooseTranche.tsx +0 -116
  33. package/src/components/Lend/solanaSupply/2-Evaluation.tsx +0 -0
  34. package/src/components/Lend/solanaSupply/3-ChooseAmount.tsx +0 -130
  35. package/src/components/Lend/solanaSupply/4-Transfer.tsx +0 -89
  36. package/src/components/Lend/solanaSupply/5-Success.tsx +0 -50
  37. package/src/components/Lend/solanaSupply/index.tsx +0 -143
  38. package/src/components/SolanaTxDoneModal.tsx +0 -115
  39. package/src/components/SolanaTxSendModal.tsx +0 -61
  40. package/src/components/SolanaViewOnExplorer.tsx +0 -29
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huma-finance/widgets",
3
- "version": "0.0.62-beta.612+99e0987",
3
+ "version": "0.0.62-beta.616+c3a83d3",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -25,20 +25,22 @@
25
25
  "@emotion/styled": "^11.3.0",
26
26
  "@ethersproject/address": "^5.7.0",
27
27
  "@ethersproject/bignumber": "^5.6.0",
28
+ "@ethersproject/bytes": "^5.7.0",
28
29
  "@ethersproject/constants": "^5.7.0",
29
30
  "@ethersproject/contracts": "^5.7.0",
30
31
  "@ethersproject/experimental": "^5.7.0",
31
32
  "@ethersproject/providers": "^5.6.0",
32
33
  "@ethersproject/units": "^5.6.0",
33
- "@huma-finance/sdk": "^0.0.62-beta.612+99e0987",
34
- "@huma-finance/shared": "^0.0.62-beta.612+99e0987",
35
- "@huma-finance/web-shared": "^0.0.62-beta.612+99e0987",
34
+ "@huma-finance/sdk": "^0.0.62-beta.616+c3a83d3",
35
+ "@huma-finance/shared": "^0.0.62-beta.616+c3a83d3",
36
+ "@huma-finance/web-shared": "^0.0.61",
36
37
  "@mui/icons-material": "^5.3.0",
37
38
  "@mui/material": "^5.0.6",
38
39
  "@mui/styles": "^5.0.2",
39
40
  "@mui/system": "^5.0.6",
40
41
  "@mui/x-date-pickers": "^5.0.7",
41
- "@notifi-network/notifi-frontend-client": "^0.91.1",
42
+ "@notifi-network/notifi-frontend-client": "^1.1.2",
43
+ "@notifi-network/notifi-react": "^1.1.2",
42
44
  "@reduxjs/toolkit": "^1.8.6",
43
45
  "@types/utf8": "^3.0.1",
44
46
  "@walletconnect/ethereum-provider": "1.8.0",
@@ -76,7 +78,7 @@
76
78
  "react-router-dom": "^5.3.0",
77
79
  "react-scripts": "5.0.1",
78
80
  "tslib": "^2.5.0",
79
- "typescript": "^5.0.0",
81
+ "typescript": "^4.8.4",
80
82
  "utf8": "^3.0.0",
81
83
  "web-vitals": "^2.1.4",
82
84
  "yup": "^0.32.11"
@@ -186,11 +188,6 @@
186
188
  "wagmi": "^0.12.10"
187
189
  },
188
190
  "peerDependencies": {
189
- "@coral-xyz/anchor": "^0.30.1",
190
- "@solana/spl-token": "^0.4.8",
191
- "@solana/wallet-adapter-base": "^0.9.23",
192
- "@solana/wallet-adapter-react": "^0.15.35",
193
- "@solana/web3.js": "^1.95.3",
194
191
  "ethers": "^5.6.1",
195
192
  "react": ">=16.8.0",
196
193
  "react-dom": ">=16.8.0",
@@ -200,5 +197,5 @@
200
197
  "optionalDependencies": {
201
198
  "encoding": "^0.1.13"
202
199
  },
203
- "gitHead": "99e0987b7a460f47f40d91836af7d44c9744142b"
200
+ "gitHead": "c3a83d314e09557e5d192ea8c2b4dd6c90c20609"
204
201
  }
@@ -20,7 +20,6 @@ type Props = {
20
20
  tokenSymbol: string
21
21
  suffix?: string
22
22
  currentAmount: number | string
23
- minimumAmount?: number
24
23
  maxAmount: number | string
25
24
  maxAmountText?: string
26
25
  maxAmountTitle?: string
@@ -36,7 +35,6 @@ export function InputAmountModal({
36
35
  tokenSymbol,
37
36
  suffix: defaultSuffix,
38
37
  currentAmount,
39
- minimumAmount,
40
38
  maxAmount,
41
39
  maxAmountText = 'MAX',
42
40
  maxAmountTitle,
@@ -127,9 +125,6 @@ export function InputAmountModal({
127
125
  if (!isEmpty(maxAmount) && Number(currentAmount) > Number(maxAmount)) {
128
126
  return true
129
127
  }
130
- if (!isEmpty(minimumAmount) && Number(currentAmount) < minimumAmount!) {
131
- return true
132
- }
133
128
  return false
134
129
  }
135
130
 
@@ -162,6 +157,7 @@ export function InputAmountModal({
162
157
  />
163
158
  </Box>
164
159
  </Box>
160
+
165
161
  {infos && (
166
162
  <Box>
167
163
  {infos.map((info) => (
@@ -171,13 +167,7 @@ export function InputAmountModal({
171
167
  ))}
172
168
  </Box>
173
169
  )}
174
- {!!minimumAmount && minimumAmount > 0 && (
175
- <Box>
176
- <Typography css={styles.info} key='minDeposit'>
177
- Minimum deposit: {minimumAmount} {tokenSymbol}
178
- </Typography>
179
- </Box>
180
- )}
170
+
181
171
  <BottomButton
182
172
  variant='contained'
183
173
  onClick={handleAction}
@@ -68,13 +68,18 @@ export function ChooseAmount({
68
68
  lpConfig.withdrawalLockoutPeriodInDays * SECONDS_IN_A_DAY
69
69
  if (nextEpochStartTime < lockupEndTime) {
70
70
  const lockupEndTimeDayjs = dayjs.unix(lockupEndTime).date(1)
71
+ const withdrawTime = lockupEndTimeDayjs.add(1, 'month')
71
72
  dispatch(setStep(WIDGET_STEP.Error))
72
73
  dispatch(
73
74
  setError({
74
75
  errorReason: 'Redemption too soon',
75
76
  errorMessage: `Your last deposit was on ${timestampToLL(
76
77
  depositRecord!.lastDepositTime.toNumber(),
77
- )}. You can redeem on ${timestampToLL(lockupEndTimeDayjs.unix())}.`,
78
+ )}. You can begin submitting redemption requests on ${timestampToLL(
79
+ lockupEndTimeDayjs.unix(),
80
+ )}, which can be redeemed starting ${timestampToLL(
81
+ withdrawTime.unix(),
82
+ )}.`,
78
83
  }),
79
84
  )
80
85
  }
@@ -9,10 +9,10 @@ import {
9
9
  import { sendTxAtom } from '@huma-finance/web-shared'
10
10
  import { useWeb3React } from '@web3-react/core'
11
11
  import { useAtom } from 'jotai'
12
- import moment from 'moment'
13
12
  import React, { useCallback, useEffect, useState } from 'react'
14
13
  import { useDispatch } from 'react-redux'
15
14
 
15
+ import dayjs from 'dayjs'
16
16
  import { Campaign } from '.'
17
17
  import {
18
18
  useDoesChainSupportNotifi,
@@ -90,14 +90,16 @@ export function Success({
90
90
  ]
91
91
 
92
92
  const getSubContent = () => {
93
- const currentTime = moment().add(
94
- lpConfig.withdrawalLockoutPeriodInDays,
95
- 'days',
96
- )
93
+ const redemptionTime = dayjs()
94
+ .add(lpConfig.withdrawalLockoutPeriodInDays, 'day')
95
+ .date(1)
96
+ const withdrawTime = redemptionTime.add(1, 'month')
97
97
  return [
98
- `First redemption date: ${timeUtil.timestampToLL(
99
- currentTime.unix(),
100
- )}. You can redeem end of each month after.`,
98
+ `You can begin submitting redemption requests on ${timeUtil.timestampToLL(
99
+ redemptionTime.unix(),
100
+ )}, which can be redeemed starting ${timeUtil.timestampToLL(
101
+ withdrawTime.unix(),
102
+ )}.`,
101
103
  ]
102
104
  }
103
105
 
@@ -25,5 +25,10 @@ export function Notifications({
25
25
  }
26
26
  }, [campaign, dispatch, handleAction])
27
27
 
28
- return <NotifiSubscriptionModal handleSuccess={handleUserAction} />
28
+ return (
29
+ <NotifiSubscriptionModal
30
+ successText={campaign ? 'VIEW POINTS' : undefined}
31
+ handleSuccess={handleUserAction}
32
+ />
33
+ )
29
34
  }
@@ -0,0 +1,57 @@
1
+ import { arrayify } from '@ethersproject/bytes'
2
+ import { ChainEnum, getBlockchainConfigFromChain } from '@huma-finance/shared'
3
+ import { NotifiContextProvider } from '@notifi-network/notifi-react'
4
+ import { useWeb3React } from '@web3-react/core'
5
+ import { providers } from 'ethers'
6
+ import React, { useMemo } from 'react'
7
+
8
+ type Props = {
9
+ chainId: number | undefined
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
+ children?: any
12
+ }
13
+
14
+ export function NotifiContextWrapper({
15
+ chainId,
16
+ children,
17
+ }: Props): React.ReactElement | null {
18
+ const { account, provider } = useWeb3React()
19
+ const signer = useMemo(() => {
20
+ if (provider instanceof providers.JsonRpcProvider) {
21
+ return provider.getSigner()
22
+ }
23
+ return undefined
24
+ }, [provider])
25
+
26
+ if (account == null || signer == null || chainId == null) {
27
+ return null
28
+ }
29
+
30
+ let cardId = ''
31
+ if (chainId === ChainEnum.Celo || chainId === ChainEnum.Alfajores) {
32
+ cardId = process.env.REACT_APP_NOTIFI_CONFIG_ID_CELO ?? ''
33
+ } else if (chainId === ChainEnum.Polygon || chainId === ChainEnum.Amoy) {
34
+ cardId = process.env.REACT_APP_NOTIFI_CONFIG_ID_POLYGON ?? ''
35
+ } else if (
36
+ chainId === ChainEnum.Scroll ||
37
+ chainId === ChainEnum.ScrollSepolia
38
+ ) {
39
+ cardId = process.env.REACT_APP_NOTIFI_CONFIG_ID_SCROLL ?? ''
40
+ }
41
+
42
+ return (
43
+ <NotifiContextProvider
44
+ tenantId='humafinanceprod'
45
+ env='Production'
46
+ signMessage={async (message: Uint8Array) => {
47
+ const result = await signer.signMessage(message)
48
+ return arrayify(result)
49
+ }}
50
+ walletPublicKey={account}
51
+ walletBlockchain={getBlockchainConfigFromChain(chainId)}
52
+ cardId={cardId}
53
+ >
54
+ {children}
55
+ </NotifiContextProvider>
56
+ )
57
+ }
@@ -1,21 +1,17 @@
1
- import { getBlockchainConfigFromChain } from '@huma-finance/shared'
2
1
  import { txAtom } from '@huma-finance/web-shared'
3
2
  import { Box, css, TextField, Typography, useTheme } from '@mui/material'
4
- import {
5
- BroadcastEventTypeItem,
6
- CardConfigItemV1,
7
- DirectPushEventTypeItem,
8
- EventTypeItem,
9
- NotifiFrontendClient,
10
- SignMessageParams,
11
- Uint8SignMessageFunction,
12
- } from '@notifi-network/notifi-frontend-client'
13
- import { useWeb3React } from '@web3-react/core'
14
- import { ethers } from 'ethers'
15
3
  import { useResetAtom } from 'jotai/utils'
16
- import React, { useCallback, useState } from 'react'
4
+ import React, { useCallback, useEffect, useState } from 'react'
17
5
 
18
- import { useNotifiClient } from '../../hooks/useNotifi'
6
+ import {
7
+ FtuStage,
8
+ useNotifiFrontendClientContext,
9
+ useNotifiTargetContext,
10
+ useNotifiTenantConfigContext,
11
+ useNotifiTopicContext,
12
+ useNotifiUserSettingContext,
13
+ } from '@notifi-network/notifi-react'
14
+ import { NotifiFrontendClient } from '@notifi-network/notifi-frontend-client'
19
15
  import { useAppDispatch } from '../../hooks/useRedux'
20
16
  import { resetState } from '../../store/widgets.reducers'
21
17
  import { BottomButton } from '../BottomButton'
@@ -24,83 +20,48 @@ import { LoadingModal } from '../LoadingModal'
24
20
  import { WrapperModal } from '../WrapperModal'
25
21
 
26
22
  type Props = {
23
+ successText?: string
27
24
  handleSuccess: () => void
28
25
  }
29
26
 
30
- function getIdFromEventType(eventType: EventTypeItem): string {
31
- if (eventType.type === 'directPush') {
32
- const directPushEventType = eventType as DirectPushEventTypeItem
33
- return (
34
- directPushEventType.directPushId as {
35
- value: string
36
- }
37
- ).value
38
- }
39
- if (eventType.type === 'broadcast') {
40
- const broadcastEventType = eventType as BroadcastEventTypeItem
41
- return (
42
- broadcastEventType.broadcastId as {
43
- value: string
44
- }
45
- ).value
46
- }
47
-
48
- return ''
49
- }
50
-
51
- async function fetchEventTypes(
52
- notifiClient: NotifiFrontendClient,
53
- ): Promise<(EventTypeItem & { enabled: boolean })[]> {
54
- const subscribedAlerts = await notifiClient.getAlerts()
55
-
56
- const subscriptionConfig = (await notifiClient.fetchSubscriptionCard({
57
- id: process.env.REACT_APP_NOTIFI_CONFIG_ID ?? '',
58
- type: 'SUBSCRIPTION_CARD',
59
- })) as CardConfigItemV1
60
-
61
- const eventTypes = subscriptionConfig?.eventTypes?.map(
62
- (eventType): EventTypeItem & { enabled: boolean } => {
63
- const eventTypeId = getIdFromEventType(eventType)
64
- let enabled = false
65
- // Check whether the user is subscribed to the event
66
- const subscribedAlert = subscribedAlerts?.find(
67
- (alert) => alert.filterOptions.indexOf(eventTypeId) >= 0,
68
- )
69
-
70
- if (subscribedAlert) {
71
- enabled = true
72
- }
73
-
74
- return {
75
- ...eventType,
76
- enabled,
77
- }
78
- },
79
- )
80
-
81
- return eventTypes
27
+ const validateDefaultTargetGroup = async (
28
+ frontendClient: NotifiFrontendClient,
29
+ ) => {
30
+ // NOTE: this extra request is necessary as the targetGroupId state in NotifiTargetContext will not be updated constantly right after login
31
+ const targetGroup = (
32
+ await frontendClient?.fetchFusionData()
33
+ )?.targetGroup?.find((group) => group?.name === 'Default')
34
+ return !!targetGroup
82
35
  }
83
36
 
84
37
  export function NotifiSubscriptionModal({
38
+ successText,
85
39
  handleSuccess,
86
40
  }: Props): React.ReactElement {
87
41
  const theme = useTheme()
88
42
  const reset = useResetAtom(txAtom)
89
43
  const dispatch = useAppDispatch()
90
- const { account, chainId, provider } = useWeb3React()
91
44
  const [showSuccessScreen, setShowSuccessScreen] = useState<boolean>(false)
92
45
  const [isLoading, setIsLoading] = useState<boolean>(false)
93
46
  const [emailAddress, setEmailAddress] = useState<string>('')
94
47
  const [emailValid, setEmailValid] = useState<boolean>()
95
- const { notifiClient } = useNotifiClient(account, chainId)
96
-
97
- const signMessage: Uint8SignMessageFunction = async (
98
- message: Uint8Array,
99
- ): Promise<Uint8Array> => {
100
- const signer = provider!.getSigner()
101
- const signature = await signer.signMessage(Buffer.from(message))
102
- return ethers.utils.arrayify(signature)
103
- }
48
+ const {
49
+ frontendClientStatus: { isInitialized, isAuthenticated, isExpired },
50
+ frontendClient,
51
+ login,
52
+ } = useNotifiFrontendClientContext()
53
+ const { fusionEventTopics } = useNotifiTenantConfigContext()
54
+ const { subscribeAlertsDefault } = useNotifiTopicContext()
55
+ const {
56
+ targetDocument: { targetGroupId },
57
+ updateTargetInputs,
58
+ renewTargetGroup,
59
+ } = useNotifiTargetContext()
60
+ const {
61
+ ftuStage,
62
+ isLoading: isLoadingFtuStage,
63
+ updateFtuStage,
64
+ } = useNotifiUserSettingContext()
104
65
 
105
66
  const handleEmailAddressChange = (
106
67
  event: React.ChangeEvent<HTMLInputElement>,
@@ -110,43 +71,89 @@ export function NotifiSubscriptionModal({
110
71
  setEmailAddress(event.target.value)
111
72
  }
112
73
 
74
+ useEffect(() => {
75
+ async function subscribe() {
76
+ if (
77
+ !isInitialized ||
78
+ !isAuthenticated ||
79
+ !targetGroupId ||
80
+ isLoadingFtuStage
81
+ ) {
82
+ return
83
+ }
84
+
85
+ // If a user is authenticated but already finished with the FTU (First Time User) setup
86
+ // (AKA their login expired and they logged back in), then we can skip the subscription step.
87
+ if (ftuStage !== FtuStage.Done) {
88
+ await updateTargetInputs('email', { value: emailAddress! })
89
+ // Authorize this email address for this wallet
90
+
91
+ if (!targetGroupId) {
92
+ setIsLoading(false)
93
+ return
94
+ }
95
+
96
+ await subscribeAlertsDefault(fusionEventTopics, targetGroupId)
97
+ await updateFtuStage(FtuStage.Done)
98
+
99
+ const targetGroup = (
100
+ await frontendClient?.fetchFusionData()
101
+ )?.targetGroup?.find((group) => group?.name === 'Default')
102
+ const emailTarget = targetGroup?.emailTargets?.[0]
103
+ if (emailTarget && !emailTarget.isConfirmed) {
104
+ await frontendClient.sendEmailTargetVerification({
105
+ targetId: emailTarget.id,
106
+ })
107
+ }
108
+ }
109
+
110
+ setShowSuccessScreen(true)
111
+ setIsLoading(false)
112
+ }
113
+
114
+ subscribe()
115
+ }, [
116
+ isInitialized,
117
+ isAuthenticated,
118
+ targetGroupId,
119
+ updateTargetInputs,
120
+ emailAddress,
121
+ subscribeAlertsDefault,
122
+ fusionEventTopics,
123
+ updateFtuStage,
124
+ isLoadingFtuStage,
125
+ ftuStage,
126
+ frontendClient,
127
+ ])
128
+
129
+ useEffect(() => {
130
+ const loginIfExpired = async () => {
131
+ if (isExpired) {
132
+ await login()
133
+ }
134
+ }
135
+
136
+ loginIfExpired()
137
+ }, [isExpired, login])
138
+
113
139
  const logInAndSubscribe = async (): Promise<void> => {
114
- if (notifiClient != null && chainId != null) {
140
+ if (!isAuthenticated) {
115
141
  setIsLoading(true)
116
142
 
117
143
  // Ask user to sign a message to authenticate
118
144
  try {
119
- await notifiClient.logIn({
120
- walletBlockchain: getBlockchainConfigFromChain(chainId),
121
- signMessage,
122
- } as SignMessageParams)
145
+ await login()
146
+
147
+ const isDefaultTargetExist = await validateDefaultTargetGroup(
148
+ frontendClient,
149
+ )
150
+
151
+ if (!isDefaultTargetExist) {
152
+ await renewTargetGroup()
153
+ }
123
154
  } catch (e) {
124
155
  setIsLoading(false)
125
- return
126
- }
127
-
128
- // Fetch all event types
129
- const fetchedEventTypes = await fetchEventTypes(notifiClient)
130
-
131
- // Authorize this email address for this wallet
132
- await notifiClient.ensureTargetGroup({
133
- name: 'Default',
134
- emailAddress: emailAddress!,
135
- })
136
-
137
- // Intentionally running this in serial because the Notifi ensureAlert can't handle
138
- // multiple calls. Notified their team to fix this.
139
- // eslint-disable-next-line no-restricted-syntax
140
- for (const eventType of fetchedEventTypes) {
141
- // eslint-disable-next-line no-await-in-loop
142
- await notifiClient.ensureAlert({
143
- eventType,
144
- inputs: {},
145
- })
146
156
  }
147
-
148
- setShowSuccessScreen(true)
149
- setIsLoading(false)
150
157
  }
151
158
  }
152
159
 
@@ -196,7 +203,7 @@ export function NotifiSubscriptionModal({
196
203
  <CheckIcon />
197
204
  </Box>
198
205
  <BottomButton variant='contained' onClick={handleCloseModal}>
199
- DONE
206
+ {successText ?? 'DONE'}
200
207
  </BottomButton>
201
208
  </WrapperModal>
202
209
  )
@@ -204,11 +211,7 @@ export function NotifiSubscriptionModal({
204
211
 
205
212
  return (
206
213
  <WrapperModal title='Connect Your Email'>
207
- <Typography
208
- variant='body2'
209
- textAlign='justify'
210
- margin={theme.spacing(6, 0, 4, 0)}
211
- >
214
+ <Typography variant='body2' margin={theme.spacing(6, 0, 4, 0)}>
212
215
  Connect your emails to receive important update about your account.
213
216
  </Typography>
214
217
  <Box css={styles.inputWrapper}>
@@ -218,7 +221,9 @@ export function NotifiSubscriptionModal({
218
221
  type='email'
219
222
  value={emailAddress}
220
223
  onChange={handleEmailAddressChange}
221
- variant='standard'
224
+ variant='outlined'
225
+ color='primary'
226
+ focused
222
227
  />
223
228
  </Box>
224
229
  <Box css={styles.disclaimer}>
@@ -228,7 +233,7 @@ export function NotifiSubscriptionModal({
228
233
  <BottomButton
229
234
  variant='contained'
230
235
  onClick={logInAndSubscribe}
231
- disabled={!emailValid || isLoading}
236
+ disabled={!emailValid || isLoading || !isInitialized}
232
237
  >
233
238
  CONNECT EMAIL
234
239
  </BottomButton>
@@ -31,7 +31,7 @@ export function WidgetWrapper({
31
31
  }: WCProps<Props>): React.ReactElement | null {
32
32
  const theme = useTheme()
33
33
  const dispatch = useAppDispatch()
34
- const { step, solanaSignature } = useAppSelector(selectWidgetState)
34
+ const { step } = useAppSelector(selectWidgetState)
35
35
  const [{ state, failReason, txReceipt }] = useAtom(sendTxAtom)
36
36
  const reset = useResetAtom(txAtom)
37
37
 
@@ -42,14 +42,12 @@ export function WidgetWrapper({
42
42
  }, [dispatch, failReason, state])
43
43
 
44
44
  useEffect(() => {
45
- if (step === WIDGET_STEP.Done && (txReceipt || solanaSignature)) {
46
- if (handleSuccess && txReceipt) {
45
+ if (step === WIDGET_STEP.Done && txReceipt) {
46
+ if (handleSuccess) {
47
47
  handleSuccess(txReceipt.blockNumber)
48
- } else if (handleSuccess && solanaSignature) {
49
- handleSuccess(0) // solana tx doesn't have block number
50
48
  }
51
49
  }
52
- }, [handleSuccess, solanaSignature, step, txReceipt])
50
+ }, [handleSuccess, step, txReceipt])
53
51
 
54
52
  const handleCloseModal = useCallback(() => {
55
53
  reset()
@@ -8,6 +8,11 @@ import {
8
8
  NotifiFrontendClient,
9
9
  newFrontendClient,
10
10
  } from '@notifi-network/notifi-frontend-client'
11
+ import {
12
+ FtuStage,
13
+ useNotifiFrontendClientContext,
14
+ useNotifiUserSettingContext,
15
+ } from '@notifi-network/notifi-react'
11
16
 
12
17
  export const useDoesChainSupportNotifi = (chainId: number | undefined) => {
13
18
  const [notifiChainSupported, setNotifiChainSupported] = useState(false)
@@ -54,32 +59,31 @@ export const useNotifiClient = (
54
59
  return { notifiClient }
55
60
  }
56
61
 
62
+ // Check if user is authenticated
63
+ // If not authenticated or expired, have user login
64
+ // After login, check FTU stage
65
+ // If FTU stage is not Done, go to signup flow. Otherwise, skip to next step
66
+
57
67
  export const useIsFirstTimeNotifiUser = (
58
68
  account: string | undefined,
59
69
  chainId: number | undefined,
60
70
  ) => {
61
71
  const [isFirstTimeNotifiUser, setIsFirstTimeNotifiUser] = useState(false)
62
- const { notifiClient } = useNotifiClient(account, chainId)
72
+ const {
73
+ frontendClientStatus: { isInitialized, isAuthenticated, isExpired },
74
+ } = useNotifiFrontendClientContext()
75
+ const { ftuStage, isLoading: isLoadingFtu } = useNotifiUserSettingContext()
63
76
  const { notifiChainSupported } = useDoesChainSupportNotifi(chainId)
64
77
 
65
78
  useEffect(() => {
66
79
  const checkIsFirstTimeNotifiUser = async () => {
67
- if (notifiClient != null && notifiChainSupported) {
68
- const { userState } = notifiClient
69
- // If the user is not authenticated or expired, they are a first time user
70
- if (
71
- userState == null ||
72
- (userState.status !== 'authenticated' &&
73
- userState.status !== 'expired')
74
- ) {
80
+ if (isInitialized && notifiChainSupported) {
81
+ // If the user is not authenticated or expired, they are possibly a first time user.
82
+ if (!isAuthenticated || isExpired) {
75
83
  setIsFirstTimeNotifiUser(true)
76
- } else if (userState.status === 'authenticated') {
77
- // If a user is authenticated, check if they have a target group (AKA email)
78
- // associated with their wallet. If they do not, show them the notifi onboarding flow.
79
- const targetGroups = await notifiClient.getTargetGroups()
80
- const emailTargets = targetGroups[0]?.emailTargets?.[0]
81
-
82
- if (emailTargets == null || emailTargets.emailAddress == null) {
84
+ } else if (isAuthenticated) {
85
+ // If a user is authenticated, check if they've completed all the FTU (First Time User) stages.
86
+ if (ftuStage !== FtuStage.Done) {
83
87
  setIsFirstTimeNotifiUser(true)
84
88
  }
85
89
  }
@@ -87,7 +91,14 @@ export const useIsFirstTimeNotifiUser = (
87
91
  }
88
92
 
89
93
  checkIsFirstTimeNotifiUser()
90
- }, [notifiChainSupported, notifiClient])
94
+ }, [
95
+ ftuStage,
96
+ isAuthenticated,
97
+ isExpired,
98
+ isInitialized,
99
+ isLoadingFtu,
100
+ notifiChainSupported,
101
+ ])
91
102
 
92
103
  return { isFirstTimeNotifiUser }
93
104
  }