diff --git a/api/controllers/postsController.js b/api/controllers/postsController.js
index 09f6b48..87d41e9 100644
--- a/api/controllers/postsController.js
+++ b/api/controllers/postsController.js
@@ -36,13 +36,17 @@ export const getPost = async (req, res, next) => {
}
export const getAllPostsByUser = async (req, res, next) => {
- const post = await Post.find({ email: req.params.user }).exec()
- if (!post) {
+ console.log(req.params)
+ const posts = await Post.find({ clerkUserID: req.params.id }).exec()
+ posts.map((el) => {
+ el.photo = process.env.CLOUDFRONT_URL + el.photo
+ })
+ if (!posts) {
return next('No document found with that id', 404)
}
res.status(200).json({
status: 'success',
- data: post
+ data: posts
})
}
diff --git a/api/models/postModel.js b/api/models/postModel.js
index 6b77959..a73642d 100644
--- a/api/models/postModel.js
+++ b/api/models/postModel.js
@@ -1,7 +1,7 @@
import mongoose from 'mongoose'
const postSchema = new mongoose.Schema({
- userID: {
+ clerkUserID: {
type: String,
required: true
},
diff --git a/api/models/userModel.js b/api/models/userModel.js
index 69136a0..f500aaf 100644
--- a/api/models/userModel.js
+++ b/api/models/userModel.js
@@ -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!'
-// }
-// }
\ No newline at end of file
+export default User
diff --git a/api/routes/posts.js b/api/routes/posts.js
index 7ad308a..7be4a0d 100644
--- a/api/routes/posts.js
+++ b/api/routes/posts.js
@@ -14,7 +14,7 @@ router.route('/').get(getAllPosts).post(createPost)
router.route('/:id').get(getPost).patch(updatePost).delete(deletePost)
-router.route('/user/:user').get(getAllPostsByUser)
+router.route('/user/:id').get(getAllPostsByUser)
router.route('/status/:status').get(getAllPostsByStatus)
diff --git a/app/(auth)/sign-in.tsx b/app/(auth)/sign-in.tsx
index 640cccb..a0d82b1 100644
--- a/app/(auth)/sign-in.tsx
+++ b/app/(auth)/sign-in.tsx
@@ -1,22 +1,63 @@
-import { SignIn } from '@clerk/clerk-react'
-import { dark } from '@clerk/themes'
+import { useSignIn } from '@clerk/clerk-expo'
+import { router } from 'expo-router'
import React from 'react'
-import { StyleSheet, View } from 'react-native'
+import { StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native'
+
+export default function LoginScreen() {
+ const { signIn, setActive, isLoaded } = useSignIn()
+ const [username, setUsername] = React.useState('')
+ const [password, setPassword] = React.useState('')
+
+ async function handleSignIn() {
+ if (!isLoaded) return
+
+ try {
+ const signInAttempt = await signIn.create({
+ identifier: username,
+ password
+ })
+
+ if (signInAttempt.status === 'complete') {
+ await setActive({ session: signInAttempt.createdSessionId })
+ router.replace('/')
+ }
+ } catch (e) {
+ console.log(JSON.stringify(e))
+ }
+ }
-export default function SignInScreen() {
return (
-
+ Login
+
+ Username
+
+ Password
+
+ {
+ handleSignIn()
+ }}
+ >
+ Login
+
+ Don't have an account?
+ {
+ router.navigate('/sign-up')
+ }}
+ >
+ Sign Up
+ {' '}
+
)
}
@@ -25,7 +66,35 @@ const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#25292e',
- padding: 5,
+ justifyContent: 'center',
+ alignItems: 'center'
+ },
+ text: {
+ color: '#fff'
+ },
+ textCenter: {
+ color: '#ffffff',
+ alignSelf: 'center',
+ marginBottom: 6
+ },
+ textlabel: {
+ color: '#fff',
+ marginBottom: 3
+ },
+ input: {
+ backgroundColor: '#fff',
+ borderRadius: 5,
+ padding: 10,
+ marginBottom: 15
+ },
+ button: {
+ backgroundColor: '#1e90ff',
+ padding: 12,
+ borderRadius: 5,
+ alignItems: 'center',
+ marginBottom: 15
+ },
+ signup: {
alignItems: 'center'
}
})
diff --git a/app/(auth)/sign-up.tsx b/app/(auth)/sign-up.tsx
index 202a8d8..80f9f46 100644
--- a/app/(auth)/sign-up.tsx
+++ b/app/(auth)/sign-up.tsx
@@ -1,22 +1,164 @@
-import { SignUp } from '@clerk/clerk-expo/web'
-import { dark } from '@clerk/themes'
-import * as React from 'react'
-import { StyleSheet, View } from 'react-native'
+import { isClerkAPIResponseError, useSignUp } from '@clerk/clerk-expo'
+import { ClerkAPIError } from '@clerk/types'
+import { router } from 'expo-router'
+import { useState } from 'react'
+import { StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native'
export default function SignUpScreen() {
+ const { signUp, setActive, isLoaded } = useSignUp()
+ const [pendingVerification, setPendingVerification] = useState(false)
+ const [code, setCode] = useState('')
+ const [errors, setErrors] = useState([])
+ const [fname, setFname] = useState('')
+ const [lname, setLname] = useState('')
+ const [email, setUsername] = useState('')
+ const [password, setPassword] = useState('')
+
+ async function onSignUpPress() {
+ if (!isLoaded) return
+ setErrors([])
+
+ try {
+ // Start Auth
+ await signUp.create({
+ firstName: fname,
+ lastName: lname,
+ emailAddress: email,
+ password
+ })
+
+ // Set confirmation
+ await signUp.prepareEmailAddressVerification()
+ setPendingVerification(true)
+ } catch (e) {
+ if (isClerkAPIResponseError(e)) setErrors(e.errors)
+ console.log(JSON.stringify(e))
+ }
+ }
+
+ 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 (
+
+ Enter the verification code we sent to {email}
+ setCode(code)}
+ />
+
+ Verify
+
+ {errors.map((error) => (
+
+ {error.longMessage}
+
+ ))}
+
+ )
+ }
+
return (
-
+ <>
+ Create an account
+
+ First name
+
+ Last name
+
+ Email
+
+ Password
+
+ {
+ onSignUpPress()
+ }}
+ >
+ Sign Up
+
+ Already have an account?
+ {
+ router.navigate('/sign-in')
+ }}
+ >
+ Sign In
+ {' '}
+
+ {errors.map((error) => (
+
+ {error.longMessage}
+
+ ))}
+ >
)
}
@@ -25,7 +167,35 @@ const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#25292e',
- padding: 5,
+ justifyContent: 'center',
+ alignItems: 'center'
+ },
+ text: {
+ color: '#fff'
+ },
+ textCenter: {
+ color: '#ffffff',
+ alignSelf: 'center',
+ marginBottom: 12
+ },
+ textlabel: {
+ color: '#fff',
+ marginBottom: 3
+ },
+ input: {
+ backgroundColor: '#fff',
+ borderRadius: 5,
+ padding: 10,
+ marginBottom: 15
+ },
+ button: {
+ backgroundColor: '#1e90ff',
+ padding: 12,
+ borderRadius: 5,
+ alignItems: 'center',
+ marginBottom: 15
+ },
+ signIn: {
alignItems: 'center'
}
})
diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx
index a00d0d2..0aec797 100644
--- a/app/(tabs)/_layout.tsx
+++ b/app/(tabs)/_layout.tsx
@@ -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 (
(
- )
+ ),
+ tabBarItemStyle: { display: user?.publicMetadata.role === 'admin' ? 'flex' : 'none' }
}}
/>
(null)
async function resetState() {
@@ -34,7 +37,7 @@ export default function App() {
'Content-Type': 'application/json'
},
body: JSON.stringify({
- userID: '6883ddb2640ebaa1a12e3791',
+ clerkUserID: user?.id,
date: new Date(),
photo,
address,
@@ -43,6 +46,7 @@ export default function App() {
}).then((res) => {
console.log(res.status)
if (res.status === 200) {
+ setShowConfirmation(true)
resetState()
}
})
@@ -109,6 +113,26 @@ export default function App() {
>
)}
+ {
+ setShowConfirmation(!showConfirmation)
+ }}
+ >
+
+
+ Your post has been submitted and will be reviewed within 3 business days. Thanks!
+
+ setShowConfirmation(!showConfirmation)}
+ >
+ Close
+
+
+
)
}
@@ -121,6 +145,48 @@ const styles = StyleSheet.create({
paddingHorizontal: 25,
paddingVertical: 200
},
+ centeredView: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center'
+ },
+ modalView: {
+ flex: 1,
+ top: '80%',
+ height: 100,
+ backgroundColor: '#363c43ff',
+ borderRadius: 12,
+ padding: 35,
+ shadowColor: '#000',
+ shadowOffset: {
+ width: 0,
+ height: 2
+ },
+ shadowOpacity: 0.25,
+ shadowRadius: 4,
+ elevation: 5
+ },
+ modalText: {
+ marginBottom: 15,
+ textAlign: 'center',
+ color: 'white'
+ },
+ modalButton: {
+ borderRadius: 20,
+ padding: 10,
+ elevation: 2
+ },
+ buttonClose: {
+ backgroundColor: '#96a5b1ff',
+ paddingHorizontal: 20,
+ paddingVertical: 5,
+ borderRadius: 12
+ },
+ textStyle: {
+ color: 'white',
+ fontWeight: 'bold',
+ textAlign: 'center'
+ },
cameraContainer: {
flex: 1
},
diff --git a/app/(tabs)/posts.tsx b/app/(tabs)/posts.tsx
index 153a9ec..c326c04 100644
--- a/app/(tabs)/posts.tsx
+++ b/app/(tabs)/posts.tsx
@@ -1,6 +1,7 @@
import { useFocusEffect } from 'expo-router'
import React, { useState } from 'react'
-import { Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native'
+import { StyleSheet, Text, View } from 'react-native'
+import { PostComponent } from '../components/PostComponent'
import { Post } from '../models/postModel'
export default function PostsScreen() {
@@ -27,95 +28,16 @@ export default function PostsScreen() {
})
}
- async function approvePost(postID: string) {
- // add code to update post to approved status
- console.log('Approving post ' + postID)
- await fetch(`http://localhost:3000/api/v1/posts/${postID}`, {
- method: 'PATCH',
- headers: {
- Accept: 'application/json, text/plain, */*',
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- status: 'approved'
- })
- }).then(() => {
- fetchData()
- })
- }
-
- async function denyPost(postID: string) {
- // add code to update post to remove status
- console.log('Denying post ' + postID)
- await fetch(`http://localhost:3000/api/v1/posts/${postID}`, {
- method: 'PATCH',
- headers: {
- Accept: 'application/json, text/plain, */*',
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- status: 'denied'
- })
- }).then(() => {
- fetchData()
- })
- }
-
return (
-
- Posts
- {posts?.length ? (
- posts.map((el) => (
-
- {el._id}
-
-
-
- {el.notes}
-
- denyPost(el._id)}>
-
- Deny
-
-
- approvePost(el._id)}
- >
-
- Approve
-
-
-
-
- ))
- ) : (
-
- All caught up!
-
- )}
-
+ Posts
+ {posts?.length ? (
+ posts.map((el) => )
+ ) : (
+
+ All caught up!
+
+ )}
)
}
@@ -123,14 +45,12 @@ export default function PostsScreen() {
const styles = StyleSheet.create({
wrapper: {
flex: 1,
- backgroundColor: '#25292e'
- },
- container: {
- flex: 1,
+ backgroundColor: '#25292e',
+ padding: 16,
alignItems: 'center',
overflow: 'scroll'
},
- text: {
+ title: {
color: '#fff',
justifyContent: 'center'
},
diff --git a/app/(tabs)/profile.tsx b/app/(tabs)/profile.tsx
index 6089d36..0afde3c 100644
--- a/app/(tabs)/profile.tsx
+++ b/app/(tabs)/profile.tsx
@@ -1,10 +1,35 @@
import { useUser } from '@clerk/clerk-react'
-import React from 'react'
+import { useFocusEffect } from 'expo-router'
+import React, { useState } from 'react'
import { Image, StyleSheet, Text, View } from 'react-native'
+import { PostComponent } from '../components/PostComponent'
import { SignOutButton } from '../components/SignOutButton'
+import { Post } from '../models/postModel'
export default function PostsScreen() {
const { user } = useUser()
+ const [posts, setPosts] = useState()
+
+ useFocusEffect(
+ React.useCallback(() => {
+ // Do something when the screen is focused
+ // TODO: add endpoint to get only non approved or denied status posts
+ fetchData()
+ return () => {
+ // Do something when the screen is unfocused
+ // Useful for cleanup functions
+ }
+ }, [])
+ )
+
+ async function fetchData() {
+ fetch(`http://localhost:3000/api/v1/posts/user/${user?.id}`)
+ .then((res) => res.json())
+ .then((json) => {
+ console.log(json)
+ setPosts(json.data)
+ })
+ }
return (
<>
@@ -23,7 +48,15 @@ export default function PostsScreen() {
-
+
+ {posts?.length ? (
+ posts.map((el) => )
+ ) : (
+
+ Your posts will show up here!
+
+ )}
+
>
)
@@ -33,11 +66,11 @@ const styles = StyleSheet.create({
wrapper: {
flex: 1,
backgroundColor: '#25292e',
- padding: 16
+ padding: 16,
+ overflow: 'scroll'
},
profileInfoCard: {
backgroundColor: '#373d44ff',
- flex: 0.25,
marginTop: 12,
borderColor: '#626e7aff',
borderStyle: 'solid',
@@ -45,33 +78,34 @@ 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: {
- backgroundColor: '#373d44ff',
- flex: 0.75,
- marginTop: 12,
- borderColor: '#626e7aff',
- borderStyle: 'solid',
- borderWidth: 1,
- borderRadius: 12
+ flex: 1
+ },
+ noPostsContainer: {
+ flex: 1,
+ alignItems: 'center',
+ justifyContent: 'center'
+ },
+ noPosts: {
+ color: '#787b80ff'
}
})
diff --git a/app/components/Post.tsx b/app/components/Post.tsx
deleted file mode 100644
index 2594dae..0000000
--- a/app/components/Post.tsx
+++ /dev/null
@@ -1,3 +0,0 @@
-export const Post = () => {
- return null
-}
diff --git a/app/components/PostComponent.tsx b/app/components/PostComponent.tsx
new file mode 100644
index 0000000..dcfa786
--- /dev/null
+++ b/app/components/PostComponent.tsx
@@ -0,0 +1,125 @@
+import { useUser } from '@clerk/clerk-react'
+import React from 'react'
+import { Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native'
+import { Post, StatusEnum } from '../models/postModel'
+
+type PostComponentProps = {
+ post: Post
+ fetchData: () => void
+}
+
+export const PostComponent: React.FC = ({ post, fetchData }) => {
+ const { user } = useUser()
+
+ async function approvePost(postID: string) {
+ console.log('Approving post ' + postID)
+ await fetch(`http://localhost:3000/api/v1/posts/${postID}`, {
+ method: 'PATCH',
+ headers: {
+ Accept: 'application/json, text/plain, */*',
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ status: 'approved'
+ })
+ }).then(() => {
+ fetchData()
+ })
+ }
+
+ async function denyPost(postID: string) {
+ console.log('Denying post ' + postID)
+ await fetch(`http://localhost:3000/api/v1/posts/${postID}`, {
+ method: 'PATCH',
+ headers: {
+ Accept: 'application/json, text/plain, */*',
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ status: 'denied'
+ })
+ }).then(() => {
+ fetchData()
+ })
+ }
+
+ return (
+
+ {post._id}
+
+
+
+ {post.notes}
+ {user?.publicMetadata.role !== 'admin' && (
+
+ {post.status === StatusEnum.Created && (
+ Created
+ )}
+ {post.status === StatusEnum.Pending && (
+ Pending
+ )}
+ {post.status === StatusEnum.Denied && Denied}
+ {post.status === StatusEnum.Approved && (
+ Approved
+ )}
+
+ )}
+ {user?.publicMetadata.role === 'admin' && (
+
+ denyPost(post._id)}>
+
+ Deny
+
+
+ approvePost(post._id)}>
+
+ Approve
+
+
+
+ )}
+
+ )
+}
+
+const styles = StyleSheet.create({
+ text: {
+ color: '#fff',
+ justifyContent: 'center'
+ },
+ posts: {
+ marginTop: 10,
+ backgroundColor: '#373d44ff',
+ borderColor: '#626e7aff',
+ borderStyle: 'solid',
+ borderWidth: 1,
+ borderRadius: 12,
+ padding: 10,
+ width: '100%'
+ },
+ 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' }
+})
diff --git a/app/components/SignOutButton.tsx b/app/components/SignOutButton.tsx
index 79cdb79..f11aa3d 100644
--- a/app/components/SignOutButton.tsx
+++ b/app/components/SignOutButton.tsx
@@ -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',
diff --git a/app/login.tsx b/app/login.tsx
deleted file mode 100644
index 92cfe67..0000000
--- a/app/login.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import React from 'react'
-import { StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native'
-
-export default function LoginScreen() {
- const [username, setUsername] = React.useState('')
- const [password, setPassword] = React.useState('')
-
- return (
-
- Login
-
- Username
-
- Password
-
- {
- console.log('test')
- }}
- >
- Login
-
- {/* {
- router.navigate('/signup')
- }}
- >
- Sign Up
- */}
-
-
- )
-}
-
-const styles = StyleSheet.create({
- container: {
- flex: 1,
- backgroundColor: '#25292e',
- justifyContent: 'center',
- alignItems: 'center'
- },
- text: {
- color: '#fff'
- },
- textlabel: {
- color: '#fff',
- marginBottom: 3
- },
- input: {
- backgroundColor: '#fff',
- borderRadius: 5,
- padding: 10,
- marginBottom: 15
- },
- button: {
- backgroundColor: '#1e90ff',
- padding: 12,
- borderRadius: 5,
- alignItems: 'center',
- marginBottom: 12
- },
- signup: {
- alignItems: 'center'
- }
-})
diff --git a/app/models/postModel.ts b/app/models/postModel.ts
index 8a2a3b7..e3052b7 100644
--- a/app/models/postModel.ts
+++ b/app/models/postModel.ts
@@ -7,8 +7,8 @@ export interface Post {
}
export enum StatusEnum {
- Created = "created",
- Pending = "pending",
- Denied = "denied",
- Accepted = "accepted"
-}
\ No newline at end of file
+ Created = 'created',
+ Pending = 'pending',
+ Denied = 'denied',
+ Approved = 'approved'
+}
diff --git a/app/useStorageState.tsx b/app/useStorageState.tsx
deleted file mode 100644
index d99532d..0000000
--- a/app/useStorageState.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import * as SecureStore from 'expo-secure-store'
-import { useCallback, useEffect, useReducer } from 'react'
-import { Platform } from 'react-native'
-
-type UseStateHook = [[boolean, T | null], (value: T | null) => void]
-
-function useAsyncState(initialValue: [boolean, T | null] = [true, null]): UseStateHook {
- return useReducer(
- (state: [boolean, T | null], action: T | null = null): [boolean, T | null] => [false, action],
- initialValue
- ) as UseStateHook
-}
-
-export async function setStorageItemAsync(key: string, value: string | null) {
- if (Platform.OS === 'web') {
- try {
- if (value === null) {
- localStorage.removeItem(key)
- } else {
- localStorage.setItem(key, value)
- }
- } catch (e) {
- console.error('Local storage is unavailable:', e)
- }
- } else {
- if (value == null) {
- await SecureStore.deleteItemAsync(key)
- } else {
- await SecureStore.setItemAsync(key, value)
- }
- }
-}
-
-export function useStorageState(key: string): UseStateHook {
- // Public
- const [state, setState] = useAsyncState()
-
- // Get
- useEffect(() => {
- if (Platform.OS === 'web') {
- try {
- if (typeof localStorage !== 'undefined') {
- setState(localStorage.getItem(key))
- }
- } catch (e) {
- console.error('Local storage is unavailable:', e)
- }
- } else {
- SecureStore.getItemAsync(key).then((value) => {
- setState(value)
- })
- }
- }, [key, setState])
-
- // Set
- const setValue = useCallback(
- (value: string | null) => {
- setState(value)
- setStorageItemAsync(key, value)
- },
- [key, setState]
- )
-
- return [state, setValue]
-}