fixing sing up flow now using an email code to verify.. on sign up we create user in backend that has reference to clerkID for filtering posts later
This commit is contained in:
@@ -1,44 +1,23 @@
|
||||
|
||||
import mongoose from 'mongoose';
|
||||
import validator from 'validator';
|
||||
import mongoose from 'mongoose'
|
||||
import validator from 'validator'
|
||||
|
||||
const userSchema = new mongoose.Schema({
|
||||
name: {
|
||||
type: String,
|
||||
required: [true, 'Please tell us your name!']
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: [true, 'Please provide your email'],
|
||||
unique: true,
|
||||
lowercase: true,
|
||||
validate: [validator.isEmail, 'Please provide a valid email']
|
||||
},
|
||||
role: {
|
||||
type: String,
|
||||
enum: ['user', 'admin'],
|
||||
default: 'user'
|
||||
}
|
||||
});
|
||||
name: {
|
||||
type: String,
|
||||
required: [true, 'Please tell us your name!']
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: [true, 'Please provide your email'],
|
||||
unique: true,
|
||||
lowercase: true,
|
||||
validate: [validator.isEmail, 'Please provide a valid email']
|
||||
},
|
||||
clerkUserID: {
|
||||
type: String
|
||||
}
|
||||
})
|
||||
|
||||
const User = mongoose.model('User', userSchema);
|
||||
const User = mongoose.model('User', userSchema)
|
||||
|
||||
export default User;
|
||||
|
||||
// password: {
|
||||
// type: String,
|
||||
// required: [true, 'Please provide a password'],
|
||||
// minlength: 8,
|
||||
// select: false
|
||||
// },
|
||||
// passwordConfirm: {
|
||||
// type: String,
|
||||
// required: [true, 'Please confirm your password'],
|
||||
// validate: {
|
||||
// // This only works on CREATE and SAVE!!!
|
||||
// validator: function(el) {
|
||||
// return el === this.password;
|
||||
// },
|
||||
// message: 'Passwords are not the same!'
|
||||
// }
|
||||
// }
|
||||
export default User
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
import { useSignUp } from '@clerk/clerk-expo'
|
||||
import { isClerkAPIResponseError, useSignUp } from '@clerk/clerk-expo'
|
||||
import { ClerkAPIError } from '@clerk/types'
|
||||
import { router } from 'expo-router'
|
||||
import React from 'react'
|
||||
import { useState } from 'react'
|
||||
import { StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native'
|
||||
|
||||
export default function SignUpScreen() {
|
||||
const { signUp, setActive, isLoaded } = useSignUp()
|
||||
const [fname, setFname] = React.useState('')
|
||||
const [lname, setLname] = React.useState('')
|
||||
const [email, setUsername] = React.useState('')
|
||||
const [password, setPassword] = React.useState('')
|
||||
const [pendingVerification, setPendingVerification] = useState(false)
|
||||
const [code, setCode] = useState('')
|
||||
const [errors, setErrors] = useState<ClerkAPIError[]>([])
|
||||
const [fname, setFname] = useState('')
|
||||
const [lname, setLname] = useState('')
|
||||
const [email, setUsername] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
|
||||
async function handleSignUp() {
|
||||
async function onSignUpPress() {
|
||||
if (!isLoaded) return
|
||||
setErrors([])
|
||||
|
||||
try {
|
||||
// Start Auth
|
||||
const signUpAttempt = await signUp.create({
|
||||
await signUp.create({
|
||||
firstName: fname,
|
||||
lastName: lname,
|
||||
emailAddress: email,
|
||||
@@ -23,58 +28,137 @@ export default function SignUpScreen() {
|
||||
})
|
||||
|
||||
// Set confirmation
|
||||
await signUp.prepareVerification({
|
||||
strategy: 'email_link',
|
||||
redirectUrl: '/'
|
||||
})
|
||||
|
||||
if (signUpAttempt.status === 'complete') {
|
||||
setActive({ session: signUpAttempt.createdSessionId })
|
||||
router.replace('/')
|
||||
} else {
|
||||
console.log(signUpAttempt)
|
||||
}
|
||||
await signUp.prepareEmailAddressVerification()
|
||||
setPendingVerification(true)
|
||||
} catch (e) {
|
||||
if (isClerkAPIResponseError(e)) setErrors(e.errors)
|
||||
console.log(JSON.stringify(e))
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.text}>Create an account</Text>
|
||||
<View style={{ width: '80%', marginTop: 20 }}>
|
||||
<Text style={styles.textlabel}>First name</Text>
|
||||
<TextInput style={styles.input} value={fname} placeholder='Enter first name' onChangeText={setFname} />
|
||||
<Text style={styles.textlabel}>Last name</Text>
|
||||
<TextInput style={styles.input} value={lname} placeholder='Enter last name' onChangeText={setLname} />
|
||||
<Text style={styles.textlabel}>Email</Text>
|
||||
<TextInput style={styles.input} value={email} placeholder='Enter email' onChangeText={setUsername} />
|
||||
<Text style={styles.textlabel}>Password</Text>
|
||||
async function onVerifyPress() {
|
||||
if (!isLoaded) return
|
||||
setErrors([])
|
||||
|
||||
try {
|
||||
// Use the code the user provided to attempt verification
|
||||
const signUpAttempt = await signUp.attemptEmailAddressVerification({
|
||||
code
|
||||
})
|
||||
|
||||
// If verification was completed, set the session to active
|
||||
// and redirect the user
|
||||
if (signUpAttempt.status === 'complete') {
|
||||
await createUser(signUpAttempt.createdUserId)
|
||||
await setActive({ session: signUpAttempt.createdSessionId })
|
||||
} else {
|
||||
// If the status is not complete, check why. User may need to
|
||||
// complete further steps.
|
||||
console.error(JSON.stringify(signUpAttempt, null, 2))
|
||||
}
|
||||
} catch (e: any) {
|
||||
// See https://clerk.com/docs/custom-flows/error-handling
|
||||
// for more info on error handling
|
||||
console.error(JSON.stringify(e, null, 2))
|
||||
setErrors(e.errors)
|
||||
}
|
||||
}
|
||||
|
||||
async function createUser(clerkUserID: string | null) {
|
||||
fetch('http://localhost:3000/api/v1/users', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: `${fname} ${lname}`,
|
||||
email,
|
||||
clerkUserID
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (pendingVerification) {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.textCenter}>Enter the verification code we sent to {email}</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
value={password}
|
||||
placeholder='Enter password'
|
||||
secureTextEntry
|
||||
onChangeText={setPassword}
|
||||
value={code}
|
||||
placeholder='Enter your verification code'
|
||||
onChangeText={(code) => setCode(code)}
|
||||
/>
|
||||
<TouchableOpacity
|
||||
style={styles.button}
|
||||
onPress={() => {
|
||||
handleSignUp()
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: '#fff', fontWeight: 'bold' }}>Sign Up</Text>
|
||||
<TouchableOpacity onPress={onVerifyPress} style={styles.button}>
|
||||
<Text>Verify</Text>
|
||||
</TouchableOpacity>
|
||||
<Text style={styles.textCenter}>Already have an account?</Text>
|
||||
<TouchableOpacity
|
||||
style={styles.signIn}
|
||||
onPress={() => {
|
||||
router.navigate('/sign-in')
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: '#fff' }}>Sign In</Text>
|
||||
</TouchableOpacity>{' '}
|
||||
{errors.map((error) => (
|
||||
<Text key={error.longMessage} style={{ color: 'red' }}>
|
||||
{error.longMessage}
|
||||
</Text>
|
||||
))}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<>
|
||||
<Text style={styles.text}>Create an account</Text>
|
||||
<View style={{ width: '80%', marginTop: 20 }}>
|
||||
<Text style={styles.textlabel}>First name</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
value={fname}
|
||||
placeholder='Enter first name'
|
||||
onChangeText={setFname}
|
||||
/>
|
||||
<Text style={styles.textlabel}>Last name</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
value={lname}
|
||||
placeholder='Enter last name'
|
||||
onChangeText={setLname}
|
||||
/>
|
||||
<Text style={styles.textlabel}>Email</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
value={email}
|
||||
placeholder='Enter email'
|
||||
onChangeText={setUsername}
|
||||
/>
|
||||
<Text style={styles.textlabel}>Password</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
value={password}
|
||||
placeholder='Enter password'
|
||||
secureTextEntry
|
||||
onChangeText={setPassword}
|
||||
/>
|
||||
<TouchableOpacity
|
||||
style={styles.button}
|
||||
onPress={() => {
|
||||
onSignUpPress()
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: '#fff', fontWeight: 'bold' }}>Sign Up</Text>
|
||||
</TouchableOpacity>
|
||||
<Text style={styles.textCenter}>Already have an account?</Text>
|
||||
<TouchableOpacity
|
||||
style={styles.signIn}
|
||||
onPress={() => {
|
||||
router.navigate('/sign-in')
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: '#fff' }}>Sign In</Text>
|
||||
</TouchableOpacity>{' '}
|
||||
</View>
|
||||
{errors.map((error) => (
|
||||
<Text key={error.longMessage} style={{ color: 'red' }}>
|
||||
{error.longMessage}
|
||||
</Text>
|
||||
))}
|
||||
</>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -92,7 +176,7 @@ const styles = StyleSheet.create({
|
||||
textCenter: {
|
||||
color: '#ffffff',
|
||||
alignSelf: 'center',
|
||||
marginBottom: 6
|
||||
marginBottom: 12
|
||||
},
|
||||
textlabel: {
|
||||
color: '#fff',
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { useUser } from '@clerk/clerk-react'
|
||||
import Ionicons from '@expo/vector-icons/Ionicons'
|
||||
import { Tabs } from 'expo-router'
|
||||
import React from 'react'
|
||||
|
||||
export default function TabLayout() {
|
||||
const { user } = useUser()
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
screenOptions={{
|
||||
@@ -34,7 +37,8 @@ export default function TabLayout() {
|
||||
headerShown: false,
|
||||
tabBarIcon: ({ color, focused }) => (
|
||||
<Ionicons name={focused ? 'rocket-sharp' : 'rocket-outline'} size={24} color={color} />
|
||||
)
|
||||
),
|
||||
tabBarItemStyle: { display: user?.publicMetadata.role === 'admin' ? 'flex' : 'none' }
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
|
||||
@@ -71,7 +71,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
profileInfoCard: {
|
||||
backgroundColor: '#373d44ff',
|
||||
flex: 0.25,
|
||||
marginTop: 12,
|
||||
borderColor: '#626e7aff',
|
||||
borderStyle: 'solid',
|
||||
@@ -79,25 +78,24 @@ const styles = StyleSheet.create({
|
||||
borderRadius: 12
|
||||
},
|
||||
profilePic: {
|
||||
flex: 0.4,
|
||||
marginTop: 12,
|
||||
display: 'flex',
|
||||
alignItems: 'center'
|
||||
},
|
||||
image: {
|
||||
height: 75,
|
||||
width: 75
|
||||
width: 75,
|
||||
borderRadius: 37.5
|
||||
},
|
||||
nameContainer: { flex: 0.3, display: 'flex', alignItems: 'center' },
|
||||
nameContainer: { display: 'flex', alignItems: 'center' },
|
||||
text: {
|
||||
color: 'white',
|
||||
fontSize: 16,
|
||||
marginTop: 6
|
||||
},
|
||||
buttonLayout: {
|
||||
flex: 0.2,
|
||||
alignItems: 'center',
|
||||
marginTop: 12
|
||||
marginVertical: 12
|
||||
},
|
||||
userPostsCard: {
|
||||
flex: 1
|
||||
|
||||
@@ -56,10 +56,16 @@ export const PostComponent: React.FC<PostComponentProps> = ({ post, fetchData })
|
||||
<Text style={{ color: '#fff' }}>{post.notes}</Text>
|
||||
{user?.publicMetadata.role !== 'admin' && (
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'space-between', marginTop: 10 }}>
|
||||
{post.status === StatusEnum.Created && <Text style={styles.created}>Created</Text>}
|
||||
{post.status === StatusEnum.Pending && <Text style={styles.pending}>Pending</Text>}
|
||||
{post.status === StatusEnum.Denied && <Text style={styles.denied}>Denied</Text>}
|
||||
{post.status === StatusEnum.Accepted && <Text style={styles.accepted}>Accepted</Text>}
|
||||
{post.status === StatusEnum.Created && (
|
||||
<Text style={[styles.created, styles.statusTag]}>Created</Text>
|
||||
)}
|
||||
{post.status === StatusEnum.Pending && (
|
||||
<Text style={[styles.pending, styles.statusTag]}>Pending</Text>
|
||||
)}
|
||||
{post.status === StatusEnum.Denied && <Text style={[styles.denied, styles.statusTag]}>Denied</Text>}
|
||||
{post.status === StatusEnum.Approved && (
|
||||
<Text style={[styles.approved, styles.statusTag]}>Approved</Text>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
{user?.publicMetadata.role === 'admin' && (
|
||||
@@ -111,8 +117,9 @@ const styles = StyleSheet.create({
|
||||
padding: 10,
|
||||
width: '100%'
|
||||
},
|
||||
created: { color: 'white' },
|
||||
pending: { color: 'yellow' },
|
||||
denied: { color: 'red' },
|
||||
accepted: { color: 'green' }
|
||||
statusTag: { paddingVertical: 3, paddingHorizontal: 10, borderRadius: 6 },
|
||||
created: { backgroundColor: '#0d6efd', color: '#ffffff' },
|
||||
pending: { backgroundColor: '#ffc107', color: '#000000' },
|
||||
denied: { backgroundColor: '#dc3545', color: '#ffffff' },
|
||||
approved: { backgroundColor: '#198754', color: '#ffffff' }
|
||||
})
|
||||
|
||||
@@ -26,7 +26,7 @@ export const SignOutButton = () => {
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
width: '40%',
|
||||
height: '100%',
|
||||
height: 35,
|
||||
backgroundColor: 'rgba(192, 196, 199, 1)',
|
||||
borderRadius: 5,
|
||||
alignItems: 'center',
|
||||
|
||||
@@ -10,5 +10,5 @@ export enum StatusEnum {
|
||||
Created = 'created',
|
||||
Pending = 'pending',
|
||||
Denied = 'denied',
|
||||
Accepted = 'accepted'
|
||||
Approved = 'approved'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user