fixing image display for posts on profile page, adding success confirmation modal, changing posts display,
This commit is contained in:
@@ -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({ userID: req.params.user }).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
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { CameraView, useCameraPermissions } from 'expo-camera'
|
||||
import React, { useRef, useState } from 'react'
|
||||
import { Button, Image, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native'
|
||||
import { Button, Image, Modal, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native'
|
||||
|
||||
export default function App() {
|
||||
const [permission, requestPermission] = useCameraPermissions()
|
||||
const [photo, setPhoto] = useState('')
|
||||
const [address, setAddress] = useState('')
|
||||
const [notes, setNotes] = useState('')
|
||||
const [showConfirmation, setShowConfirmation] = useState(false)
|
||||
const cameraRef = useRef<CameraView>(null)
|
||||
|
||||
async function resetState() {
|
||||
@@ -43,6 +44,7 @@ export default function App() {
|
||||
}).then((res) => {
|
||||
console.log(res.status)
|
||||
if (res.status === 200) {
|
||||
setShowConfirmation(true)
|
||||
resetState()
|
||||
}
|
||||
})
|
||||
@@ -109,6 +111,26 @@ export default function App() {
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
<Modal
|
||||
animationType='fade'
|
||||
transparent={true}
|
||||
visible={showConfirmation}
|
||||
onRequestClose={() => {
|
||||
setShowConfirmation(!showConfirmation)
|
||||
}}
|
||||
>
|
||||
<View style={styles.modalView}>
|
||||
<Text style={styles.modalText}>
|
||||
Your post has been submitted and will be reviewed within 3 business days. Thanks!
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
style={[styles.modalButton, styles.buttonClose]}
|
||||
onPress={() => setShowConfirmation(!showConfirmation)}
|
||||
>
|
||||
<Text style={styles.textStyle}>Close</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</Modal>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -121,6 +143,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
|
||||
},
|
||||
|
||||
@@ -30,16 +30,14 @@ export default function PostsScreen() {
|
||||
|
||||
return (
|
||||
<View style={styles.wrapper}>
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.text}>Posts</Text>
|
||||
{posts?.length ? (
|
||||
posts.map((el) => <PostComponent key={el._id} post={el} fetchData={fetchData} />)
|
||||
) : (
|
||||
<View style={styles.caughtUpContainer}>
|
||||
<Text style={styles.caughtUpText}>All caught up!</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
<Text style={styles.title}>Posts</Text>
|
||||
{posts?.length ? (
|
||||
posts.map((el) => <PostComponent key={el._id} post={el} fetchData={fetchData} />)
|
||||
) : (
|
||||
<View style={styles.caughtUpContainer}>
|
||||
<Text style={styles.caughtUpText}>All caught up!</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -47,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'
|
||||
},
|
||||
|
||||
@@ -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<Post[]>()
|
||||
|
||||
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/6883ddb2640ebaa1a12e3791`)
|
||||
.then((res) => res.json())
|
||||
.then((json) => {
|
||||
console.log(json)
|
||||
setPosts(json.data)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -23,7 +48,15 @@ export default function PostsScreen() {
|
||||
<SignOutButton></SignOutButton>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.userPostsCard}></View>
|
||||
<View style={styles.userPostsCard}>
|
||||
{posts?.length ? (
|
||||
posts.map((el) => <PostComponent key={el._id} post={el} fetchData={fetchData} />)
|
||||
) : (
|
||||
<View style={styles.noPostsContainer}>
|
||||
<Text style={styles.noPosts}>Your posts will show up here!</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
)
|
||||
@@ -33,7 +66,8 @@ const styles = StyleSheet.create({
|
||||
wrapper: {
|
||||
flex: 1,
|
||||
backgroundColor: '#25292e',
|
||||
padding: 16
|
||||
padding: 16,
|
||||
overflow: 'scroll'
|
||||
},
|
||||
profileInfoCard: {
|
||||
backgroundColor: '#373d44ff',
|
||||
@@ -66,12 +100,14 @@ const styles = StyleSheet.create({
|
||||
marginTop: 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'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useUser } from '@clerk/clerk-react'
|
||||
import React from 'react'
|
||||
import { Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native'
|
||||
import { Post } from '../models/postModel'
|
||||
import { Post, StatusEnum } from '../models/postModel'
|
||||
|
||||
type PostComponentProps = {
|
||||
post: Post
|
||||
@@ -8,6 +9,8 @@ type PostComponentProps = {
|
||||
}
|
||||
|
||||
export const PostComponent: React.FC<PostComponentProps> = ({ post, fetchData }) => {
|
||||
const { user } = useUser()
|
||||
|
||||
async function approvePost(postID: string) {
|
||||
console.log('Approving post ' + postID)
|
||||
await fetch(`http://localhost:3000/api/v1/posts/${postID}`, {
|
||||
@@ -51,34 +54,44 @@ export const PostComponent: React.FC<PostComponentProps> = ({ post, fetchData })
|
||||
/>
|
||||
</View>
|
||||
<Text style={{ color: '#fff' }}>{post.notes}</Text>
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'space-between', marginTop: 10 }}>
|
||||
<TouchableOpacity style={{ flex: 1, marginRight: 5 }} onPress={() => denyPost(post._id)}>
|
||||
<Text
|
||||
style={{
|
||||
backgroundColor: '#bf3636ff',
|
||||
color: '#fff',
|
||||
textAlign: 'center',
|
||||
padding: 8,
|
||||
borderRadius: 4
|
||||
}}
|
||||
>
|
||||
Deny
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={{ flex: 1, marginLeft: 5 }} onPress={() => approvePost(post._id)}>
|
||||
<Text
|
||||
style={{
|
||||
backgroundColor: '#17be3bff',
|
||||
color: '#fff',
|
||||
textAlign: 'center',
|
||||
padding: 8,
|
||||
borderRadius: 4
|
||||
}}
|
||||
>
|
||||
Approve
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
{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>}
|
||||
</View>
|
||||
)}
|
||||
{user?.publicMetadata.role === 'admin' && (
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'space-between', marginTop: 10 }}>
|
||||
<TouchableOpacity style={{ flex: 1, marginRight: 5 }} onPress={() => denyPost(post._id)}>
|
||||
<Text
|
||||
style={{
|
||||
backgroundColor: '#bf3636ff',
|
||||
color: '#fff',
|
||||
textAlign: 'center',
|
||||
padding: 8,
|
||||
borderRadius: 4
|
||||
}}
|
||||
>
|
||||
Deny
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={{ flex: 1, marginLeft: 5 }} onPress={() => approvePost(post._id)}>
|
||||
<Text
|
||||
style={{
|
||||
backgroundColor: '#17be3bff',
|
||||
color: '#fff',
|
||||
textAlign: 'center',
|
||||
padding: 8,
|
||||
borderRadius: 4
|
||||
}}
|
||||
>
|
||||
Approve
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -90,9 +103,16 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
posts: {
|
||||
marginTop: 10,
|
||||
backgroundColor: '#363c43ff',
|
||||
backgroundColor: '#373d44ff',
|
||||
borderColor: '#626e7aff',
|
||||
borderStyle: 'solid',
|
||||
borderWidth: 1,
|
||||
borderRadius: 12,
|
||||
padding: 10,
|
||||
width: '90%',
|
||||
borderRadius: 5
|
||||
}
|
||||
width: '100%'
|
||||
},
|
||||
created: { color: 'white' },
|
||||
pending: { color: 'yellow' },
|
||||
denied: { color: 'red' },
|
||||
accepted: { color: 'green' }
|
||||
})
|
||||
|
||||
@@ -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 (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.text}>Login</Text>
|
||||
<View style={{ width: '80%', marginTop: 20 }}>
|
||||
<Text style={styles.textlabel}>Username</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
value={username}
|
||||
placeholder='Enter username'
|
||||
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={() => {
|
||||
console.log('test')
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: '#fff', fontWeight: 'bold' }}>Login</Text>
|
||||
</TouchableOpacity>
|
||||
{/* <TouchableOpacity
|
||||
style={styles.signup}
|
||||
onPress={() => {
|
||||
router.navigate('/signup')
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: '#fff' }}>Sign Up</Text>
|
||||
</TouchableOpacity> */}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
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'
|
||||
}
|
||||
})
|
||||
@@ -7,8 +7,8 @@ export interface Post {
|
||||
}
|
||||
|
||||
export enum StatusEnum {
|
||||
Created = "created",
|
||||
Pending = "pending",
|
||||
Denied = "denied",
|
||||
Accepted = "accepted"
|
||||
}
|
||||
Created = 'created',
|
||||
Pending = 'pending',
|
||||
Denied = 'denied',
|
||||
Accepted = 'accepted'
|
||||
}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import * as SecureStore from 'expo-secure-store'
|
||||
import { useCallback, useEffect, useReducer } from 'react'
|
||||
import { Platform } from 'react-native'
|
||||
|
||||
type UseStateHook<T> = [[boolean, T | null], (value: T | null) => void]
|
||||
|
||||
function useAsyncState<T>(initialValue: [boolean, T | null] = [true, null]): UseStateHook<T> {
|
||||
return useReducer(
|
||||
(state: [boolean, T | null], action: T | null = null): [boolean, T | null] => [false, action],
|
||||
initialValue
|
||||
) as UseStateHook<T>
|
||||
}
|
||||
|
||||
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<string> {
|
||||
// Public
|
||||
const [state, setState] = useAsyncState<string>()
|
||||
|
||||
// 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]
|
||||
}
|
||||
Reference in New Issue
Block a user