@chem-po/firebase-native 0.0.39 → 0.0.41

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 (27) hide show
  1. package/lib/commonjs/components/FirebaseSignIn.js +20 -14
  2. package/lib/commonjs/components/FirebaseSignIn.js.map +1 -1
  3. package/lib/commonjs/components/PhoneVerify.js +45 -33
  4. package/lib/commonjs/components/PhoneVerify.js.map +1 -1
  5. package/lib/commonjs/components/TwoFactorAuthModal.js +71 -15
  6. package/lib/commonjs/components/TwoFactorAuthModal.js.map +1 -1
  7. package/lib/commonjs/hooks/usePhoneVerify.js +13 -5
  8. package/lib/commonjs/hooks/usePhoneVerify.js.map +1 -1
  9. package/lib/module/components/FirebaseSignIn.js +21 -15
  10. package/lib/module/components/FirebaseSignIn.js.map +1 -1
  11. package/lib/module/components/PhoneVerify.js +47 -35
  12. package/lib/module/components/PhoneVerify.js.map +1 -1
  13. package/lib/module/components/TwoFactorAuthModal.js +71 -16
  14. package/lib/module/components/TwoFactorAuthModal.js.map +1 -1
  15. package/lib/module/hooks/usePhoneVerify.js +13 -5
  16. package/lib/module/hooks/usePhoneVerify.js.map +1 -1
  17. package/lib/typescript/components/FirebaseSignIn.d.ts.map +1 -1
  18. package/lib/typescript/components/PhoneVerify.d.ts.map +1 -1
  19. package/lib/typescript/components/TwoFactorAuthModal.d.ts +1 -0
  20. package/lib/typescript/components/TwoFactorAuthModal.d.ts.map +1 -1
  21. package/lib/typescript/hooks/usePhoneVerify.d.ts +3 -1
  22. package/lib/typescript/hooks/usePhoneVerify.d.ts.map +1 -1
  23. package/package.json +4 -4
  24. package/src/components/FirebaseSignIn.tsx +21 -14
  25. package/src/components/PhoneVerify.tsx +46 -33
  26. package/src/components/TwoFactorAuthModal.tsx +87 -24
  27. package/src/hooks/usePhoneVerify.ts +15 -4
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@chem-po/firebase-native",
3
3
  "author": "Elan Canfield",
4
4
  "license": "MIT",
5
- "version": "0.0.39",
5
+ "version": "0.0.41",
6
6
  "main": "lib/commonjs/index.js",
7
7
  "types": "lib/typescript/index.d.ts",
8
8
  "source": "src/index.ts",
@@ -44,9 +44,9 @@
44
44
  "react-native-paper": "^5.14.3",
45
45
  "react-native-svg": "15.11.2",
46
46
  "zustand": "^4.3.3",
47
- "@chem-po/react": "0.0.39",
48
- "@chem-po/react-native": "0.0.39",
49
- "@chem-po/core": "0.0.39"
47
+ "@chem-po/core": "0.0.41",
48
+ "@chem-po/react-native": "0.0.41",
49
+ "@chem-po/react": "0.0.41"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@babel/core": "^7.26.0",
@@ -1,8 +1,9 @@
1
1
  import { useBackend } from '../hooks'
2
2
 
3
3
  import { AppleAuthProvider, GoogleAuthProvider, ThirdPartyAuthProvider } from '@chem-po/core'
4
- import { TextField, useBorderColor, useToast } from '@chem-po/react'
4
+ import { TextField, useBorderColor, useTextColor, useToast } from '@chem-po/react'
5
5
  import { LoadingButton, StandaloneInput, Txt } from '@chem-po/react-native'
6
+ import { ButtonText } from '@chem-po/react-native/src/components/button/ButtonText'
6
7
  import appleAuth from '@invertase/react-native-apple-authentication'
7
8
  import React, { useCallback, useState } from 'react'
8
9
  import { StyleSheet, View } from 'react-native'
@@ -40,14 +41,19 @@ const ThirdPartyLogin = ({ provider }: { provider: GoogleAuthProvider | AppleAut
40
41
  })
41
42
  }, [auth, provider, showError])
42
43
 
43
- const borderColor = useBorderColor()
44
-
44
+ const textColor = useTextColor(400)
45
45
  return (
46
46
  <View style={styles.row}>
47
- <LoadingButton style={[styles.googleLoginButton, { borderColor }]} onPress={handleSignIn}>
47
+ <LoadingButton
48
+ color={textColor}
49
+ variant="outline"
50
+ style={[styles.googleLoginButton]}
51
+ onPress={handleSignIn}>
48
52
  <View style={styles.googleLogin}>
49
53
  {thirdPartyInfo[provider.name].icon}
50
- <Txt>Log In with {thirdPartyInfo[provider.name].name}</Txt>
54
+ <ButtonText
55
+ variant="outline"
56
+ color={textColor}>{`Log In with ${thirdPartyInfo[provider.name].name}`}</ButtonText>
51
57
  </View>
52
58
  </LoadingButton>
53
59
  </View>
@@ -57,20 +63,18 @@ const ThirdPartyLogin = ({ provider }: { provider: GoogleAuthProvider | AppleAut
57
63
  const EmailPasswordLogin = () => {
58
64
  const [email, setEmail] = useState('')
59
65
  const [password, setPassword] = useState('')
60
- const [isLoading, setIsLoading] = useState(false)
61
66
  const [error, setError] = useState<string | null>(null)
67
+ const forgotPasswordTextColor = useTextColor(400)
62
68
 
63
69
  const { auth } = useBackend()
64
70
  const { showInfo } = useToast()
65
71
 
66
72
  const handleLogin = async () => {
67
- setIsLoading(true)
68
73
  try {
69
74
  await auth.loginWithPassword({ name: 'email' }, { email, password })
70
75
  } catch (error) {
71
76
  setError(error instanceof Error ? error.message : 'An unknown error occurred')
72
77
  }
73
- setIsLoading(false)
74
78
  }
75
79
 
76
80
  return (
@@ -85,18 +89,19 @@ const EmailPasswordLogin = () => {
85
89
  <View style={styles.row}>
86
90
  <View style={styles.forgotPassword}>
87
91
  <LoadingButton
92
+ color={forgotPasswordTextColor}
88
93
  textStyle={styles.forgotPasswordButtonText}
89
- contentStyle={styles.forgotPasswordButton}
90
- onPress={() => {
94
+ style={styles.forgotPasswordButton}
95
+ size="sm"
96
+ variant="outline"
97
+ onPress={async () => {
91
98
  // console.log('press')
92
99
  showInfo('Visit our website to reset your password')
93
100
  }}>
94
101
  Forgot Password?
95
102
  </LoadingButton>
96
103
  </View>
97
- <LoadingButton onPress={() => handleLogin()} disabled={isLoading}>
98
- Sign In
99
- </LoadingButton>
104
+ <LoadingButton onPress={() => handleLogin()}>Sign In</LoadingButton>
100
105
  </View>
101
106
  </View>
102
107
  )
@@ -149,7 +154,7 @@ const styles = StyleSheet.create({
149
154
  },
150
155
  column: {
151
156
  flexDirection: 'column',
152
- gap: 20,
157
+ gap: 10,
153
158
  flex: 1,
154
159
  width: '100%',
155
160
  alignItems: 'center',
@@ -176,6 +181,7 @@ const styles = StyleSheet.create({
176
181
  },
177
182
  forgotPasswordButton: {
178
183
  borderWidth: 0,
184
+ backgroundColor: 'transparent',
179
185
  },
180
186
  forgotPasswordButtonText: {
181
187
  fontSize: 14,
@@ -183,6 +189,7 @@ const styles = StyleSheet.create({
183
189
  row: {
184
190
  flexDirection: 'row',
185
191
  justifyContent: 'space-between',
192
+ alignItems: 'center',
186
193
  width: '100%',
187
194
  },
188
195
  forgotPassword: {
@@ -1,53 +1,66 @@
1
- import { PhoneEnrollmentFactor } from '@chem-po/core'
2
- import { useAuth } from '@chem-po/react'
3
- import { Txt } from '@chem-po/react-native'
4
- import React from 'react'
5
- import { ActivityIndicator, StyleSheet, TextInput, TouchableOpacity, View } from 'react-native'
1
+ import { formatPhoneNumber, PhoneEnrollmentFactor } from '@chem-po/core'
2
+ import { useAuth, useBorderColor, useButtonColor, usePlaceholderColor, useTextColor } from '@chem-po/react'
3
+ import { CircularProgress, LoadingButton, Txt } from '@chem-po/react-native'
4
+ import React, { useMemo } from 'react'
5
+ import { StyleSheet, TextInput, View } from 'react-native'
6
6
  import { usePhoneVerify } from '../hooks/usePhoneVerify'
7
7
 
8
8
  // UI to send code and verify code
9
9
  export const PhoneVerify = ({ factor }: { factor: PhoneEnrollmentFactor }) => {
10
- const { handleVerify, verifying, error, code, setCode } =
11
- usePhoneVerify(factor, true)
12
-
13
- const {multiFactorVerification: twoFactorVerification, multiFactorLoading: loading} = useAuth()
10
+ const { handleVerify, verifying, error, code, setCode, sendCode, sendingCode } = usePhoneVerify(factor, true)
11
+ const { multiFactorVerification: twoFactorVerification, multiFactorLoading: loading } = useAuth()
12
+ const buttonBackgroundColor = useButtonColor()
13
+ const borderColor = useBorderColor()
14
+ const textColor = useTextColor()
15
+ const placeholderColor = usePlaceholderColor()
14
16
 
15
17
  const verificationId = twoFactorVerification?.verificationId
18
+ const formattedPhoneNumber = useMemo(
19
+ () =>
20
+ Number.isNaN(Number(factor.phoneNumber))
21
+ ? factor.phoneNumber
22
+ : formatPhoneNumber(factor.phoneNumber),
23
+ [factor.phoneNumber]
24
+ )
16
25
 
17
26
  let body: React.ReactNode = null
18
27
 
19
- if (loading) {
28
+ if (sendingCode) {
20
29
  body = (
21
30
  <View style={styles.container}>
22
- <ActivityIndicator size="large" color="#0000ff" />
31
+ <CircularProgress size='large' />
23
32
  <Txt style={styles.text}>Sending verification code...</Txt>
24
33
  </View>
25
34
  )
26
- } else if (verificationId) {
35
+ } else if (verificationId || verifying) {
27
36
  body = (
28
37
  <View style={styles.container}>
29
38
  <Txt style={styles.text}>Enter the code sent to your phone:</Txt>
30
39
  <TextInput
31
- style={styles.input}
40
+ style={[styles.input, { borderColor, color: textColor }]}
32
41
  value={code}
42
+ placeholderTextColor={placeholderColor}
33
43
  onChangeText={setCode}
34
- placeholder="Verification Code"
35
- keyboardType="number-pad"
44
+ placeholder='Verification Code'
45
+ keyboardType='number-pad'
36
46
  maxLength={6}
37
47
  />
38
48
  {error ? <Txt style={styles.errorText}>{error}</Txt> : null}
39
- <TouchableOpacity style={styles.button} onPress={handleVerify} disabled={verifying}>
40
- <Txt style={styles.buttonText}>{verifying ? 'Verifying...' : 'Verify'}</Txt>
41
- </TouchableOpacity>
49
+ <LoadingButton variant='solid' onPress={handleVerify} color={buttonBackgroundColor}>
50
+ Verify
51
+ </LoadingButton>
42
52
  </View>
43
53
  )
44
54
  } else {
45
55
  body = (
46
56
  <View style={styles.container}>
47
- <Txt style={styles.text}>We'll send a verification code to {factor.phoneNumber}</Txt>
48
- <TouchableOpacity style={styles.button} onPress={() => {}} disabled={loading}>
49
- <Txt style={styles.buttonText}>Send Verification Code</Txt>
50
- </TouchableOpacity>
57
+ <View style={styles.textContainer}>
58
+ <Txt style={styles.infoText}>We'll send a verification code to:</Txt>
59
+ <Txt style={styles.text}>{formattedPhoneNumber}</Txt>
60
+ </View>
61
+ <LoadingButton variant='solid' onPress={sendCode} disabled={loading} color={buttonBackgroundColor}>
62
+ Send Verification Code
63
+ </LoadingButton>
51
64
  </View>
52
65
  )
53
66
  }
@@ -64,7 +77,17 @@ const styles = StyleSheet.create({
64
77
  flex: 1,
65
78
  alignItems: 'center',
66
79
  justifyContent: 'center',
67
- gap: 16,
80
+ gap: 8,
81
+ },
82
+ textContainer: {
83
+ alignItems: 'center',
84
+ justifyContent: 'center',
85
+ gap: 4,
86
+ },
87
+ infoText: {
88
+ fontSize: 15,
89
+ opacity: 0.8,
90
+ textAlign: 'center',
68
91
  },
69
92
  text: {
70
93
  fontSize: 16,
@@ -75,22 +98,12 @@ const styles = StyleSheet.create({
75
98
  width: '100%',
76
99
  height: 48,
77
100
  borderWidth: 1,
78
- borderColor: '#ccc',
79
101
  borderRadius: 8,
80
102
  paddingHorizontal: 16,
81
103
  fontSize: 16,
82
104
  marginBottom: 8,
83
105
  },
84
- button: {
85
- backgroundColor: '#007AFF',
86
- paddingHorizontal: 24,
87
- paddingVertical: 12,
88
- borderRadius: 8,
89
- minWidth: 200,
90
- alignItems: 'center',
91
- },
92
106
  buttonText: {
93
- color: 'white',
94
107
  fontSize: 16,
95
108
  fontWeight: '600',
96
109
  },
@@ -1,6 +1,7 @@
1
1
  import { EnrollmentFactor, PhoneEnrollmentFactor } from '@chem-po/core'
2
- import { useAuth, useBackgroundColor } from '@chem-po/react'
3
- import { Txt } from '@chem-po/react-native'
2
+ import { useAuth, useBackgroundColor, useBorderColor, useIconColor } from '@chem-po/react'
3
+ import { Txt, useFont } from '@chem-po/react-native'
4
+ import { Ionicons } from '@expo/vector-icons'
4
5
  import React, { useState } from 'react'
5
6
  import { ScrollView, StyleSheet, TouchableOpacity, View } from 'react-native'
6
7
  import { Modal, Portal } from 'react-native-paper'
@@ -8,6 +9,46 @@ import { useBackend } from '../hooks/backend'
8
9
  import { AuthenticatorVerify } from './AuthenticatorVerify'
9
10
  import { PhoneVerify } from './PhoneVerify'
10
11
 
12
+ const FactorView = ({
13
+ factor,
14
+ buttonBackgroundColor,
15
+ iconColor,
16
+ borderColor,
17
+ onPress,
18
+ }: {
19
+ factor: EnrollmentFactor
20
+ buttonBackgroundColor: string
21
+ iconColor: string
22
+ borderColor: string
23
+ onPress: () => void
24
+ }) => {
25
+ return (
26
+ <TouchableOpacity
27
+ style={[
28
+ styles.factorButton,
29
+ {
30
+ backgroundColor: buttonBackgroundColor,
31
+ borderColor,
32
+ },
33
+ ]}
34
+ onPress={onPress}
35
+ >
36
+ <View style={styles.factorContent}>
37
+ <Ionicons
38
+ name={factor.type === 'phone' ? 'phone-portrait' : 'qr-code'}
39
+ size={20}
40
+ color={iconColor}
41
+ />
42
+ <Txt style={styles.factorHeaderText}>
43
+ {factor.type === 'phone' ? `Phone: ${factor.phoneNumber}` : 'Authenticator App'}
44
+ </Txt>
45
+ {factor.displayName ? (
46
+ <Txt style={styles.factorDetailsText}>{factor.displayName}</Txt>
47
+ ) : null}
48
+ </View>
49
+ </TouchableOpacity>
50
+ )
51
+ }
11
52
 
12
53
  const TwoFactorSelect = ({
13
54
  factors,
@@ -17,30 +58,22 @@ const TwoFactorSelect = ({
17
58
  onSelect: (factor: EnrollmentFactor) => void
18
59
  }) => {
19
60
  const buttonBackgroundColor = useBackgroundColor(100)
61
+ const borderColor = useBorderColor()
62
+ const iconColor = useIconColor()
63
+ const textFont = useFont('body', 'md')
20
64
  return (
21
65
  <View style={styles.selectContainer}>
22
- <Txt style={styles.selectTitle}>Select an option to verify with:</Txt>
66
+ <Txt style={[styles.selectTitle, textFont]}>Select an option to verify with:</Txt>
23
67
  <View style={styles.factorsList}>
24
68
  {factors.map((factor) => (
25
- <TouchableOpacity
26
- key={factor.uid}
27
- style={[
28
- styles.factorButton,
29
- {
30
- backgroundColor: buttonBackgroundColor,
31
- },
32
- ]}
69
+ <FactorView
70
+ key={`${factor.type}-${factor.enrollmentTime}`}
71
+ factor={factor}
72
+ buttonBackgroundColor={buttonBackgroundColor}
33
73
  onPress={() => onSelect(factor)}
34
- >
35
- <Txt style={styles.factorHeaderText}>
36
- {factor.type === 'phone' ? `Phone: ${factor.phoneNumber}` : 'Authenticator App'}
37
- </Txt>
38
- {
39
- factor.displayName ? (
40
- <Txt style={styles.factorDetailsText}>{factor.displayName}</Txt>
41
- ) : null
42
- }
43
- </TouchableOpacity>
74
+ iconColor={iconColor}
75
+ borderColor={borderColor}
76
+ />
44
77
  ))}
45
78
  </View>
46
79
  </View>
@@ -83,7 +116,9 @@ export const TwoFactorAuthModal = () => {
83
116
  <Portal>
84
117
  <Modal
85
118
  visible={true}
86
- onDismiss={() => { auth.logout() }}
119
+ onDismiss={() => {
120
+ auth.logout()
121
+ }}
87
122
  style={styles.modal}
88
123
  >
89
124
  <View style={[styles.modalContent, { backgroundColor }]}>
@@ -96,6 +131,28 @@ export const TwoFactorAuthModal = () => {
96
131
  )
97
132
  }
98
133
 
134
+ const testFactor: EnrollmentFactor = {
135
+ uid: 'test',
136
+ type: 'phone',
137
+ phoneNumber: '1234567890',
138
+ displayName: 'Test Phone',
139
+ enrollmentTime: '2021-01-01',
140
+ }
141
+ export const TestTwoFactorVerifyModal = () => {
142
+ const backgroundColor = useBackgroundColor(150)
143
+ return (
144
+ <Portal>
145
+ <Modal visible={true} onDismiss={() => {}} style={styles.modal}>
146
+ <View style={[styles.modalContent, { backgroundColor }]}>
147
+ <ScrollView contentContainerStyle={styles.scrollContent}>
148
+ <TwoFactorVerify factorInfo={testFactor} />
149
+ </ScrollView>
150
+ </View>
151
+ </Modal>
152
+ </Portal>
153
+ )
154
+ }
155
+
99
156
  const styles = StyleSheet.create({
100
157
  modal: {
101
158
  margin: 0,
@@ -113,18 +170,24 @@ const styles = StyleSheet.create({
113
170
  },
114
171
  selectTitle: {
115
172
  fontSize: 16,
116
- marginBottom: 16,
173
+ marginBottom: 8,
174
+ paddingHorizontal: 4,
117
175
  },
118
176
  factorsList: {
119
177
  gap: 8,
120
178
  },
179
+ factorContent: {
180
+ flexDirection: 'row',
181
+ alignItems: 'center',
182
+ gap: 8,
183
+ },
121
184
  factorButton: {
122
185
  padding: 16,
123
186
  borderRadius: 8,
124
187
  borderWidth: 1,
125
188
  },
126
189
  factorHeaderText: {
127
- fontSize: 14,
190
+ fontSize: 16,
128
191
  fontWeight: 'bold',
129
192
  opacity: 0.8,
130
193
  },
@@ -5,6 +5,7 @@ import { useBackend } from './backend'
5
5
 
6
6
  export const usePhoneVerify = (factor: PhoneEnrollmentFactor, automaticallySendSmsCode: boolean) => {
7
7
  const [code, setCode] = useState('')
8
+ const [sendingCode, setSendingCode] = useState(false)
8
9
  const [verifying, setVerifying] = useState(false)
9
10
  const [error, setError] = useState('')
10
11
 
@@ -15,14 +16,22 @@ export const usePhoneVerify = (factor: PhoneEnrollmentFactor, automaticallySendS
15
16
 
16
17
  const initSendCode = useRef(!!automaticallySendSmsCode)
17
18
 
18
- const { showSuccess, showError, showInfo } = useToast()
19
+ const { showSuccess, showError } = useToast()
19
20
 
20
21
  const sendCode = useCallback(async () => {
21
22
  if (!enrollmentFactors) {
22
23
  showError('Error - No session found')
23
24
  return
24
25
  }
25
- await auth.sendMultiFactorCode(factor, enrollmentFactors.multiFactorResolver)
26
+ setSendingCode(true)
27
+ return auth.sendMultiFactorCode(factor, enrollmentFactors.multiFactorResolver)
28
+ .then(() => {
29
+ setSendingCode(false)
30
+ })
31
+ .catch((e) => {
32
+ setSendingCode(false)
33
+ setError(e.message || 'An error occurred')
34
+ })
26
35
  }, [auth, factor, twoFactorVerification, showError])
27
36
 
28
37
  const initSendCodeFunc = useRef(sendCode)
@@ -32,7 +41,7 @@ export const usePhoneVerify = (factor: PhoneEnrollmentFactor, automaticallySendS
32
41
  }
33
42
  }, [])
34
43
 
35
- const handleVerify = useCallback(() => {
44
+ const handleVerify = useCallback(async () => {
36
45
  setVerifying(true)
37
46
  setError('')
38
47
  if (!twoFactorVerification) {
@@ -54,7 +63,7 @@ export const usePhoneVerify = (factor: PhoneEnrollmentFactor, automaticallySendS
54
63
  showError('Error - Two factor verification is not supported')
55
64
  return
56
65
  }
57
- verify(twoFactorVerification, code)
66
+ return verify(twoFactorVerification, code)
58
67
  .then(() => {
59
68
  setVerifying(false)
60
69
  setCode('')
@@ -69,7 +78,9 @@ export const usePhoneVerify = (factor: PhoneEnrollmentFactor, automaticallySendS
69
78
  return {
70
79
  code,
71
80
  setCode,
81
+ sendCode,
72
82
  verifying,
83
+ sendingCode,
73
84
  error,
74
85
  handleVerify,
75
86
  }