From 878e18941ec279afd5de65a4b0528b7e09139866 Mon Sep 17 00:00:00 2001 From: Will Baumbach Date: Wed, 6 Aug 2025 13:36:47 -0500 Subject: [PATCH] fixing image display for posts on profile page, adding success confirmation modal, changing posts display, --- api/controllers/postsController.js | 10 ++-- app/(tabs)/home.tsx | 66 ++++++++++++++++++++++- app/(tabs)/posts.tsx | 26 ++++----- app/(tabs)/profile.tsx | 56 +++++++++++++++---- app/components/PostComponent.tsx | 86 ++++++++++++++++++------------ app/login.tsx | 78 --------------------------- app/models/postModel.ts | 10 ++-- app/useStorageState.tsx | 65 ---------------------- 8 files changed, 187 insertions(+), 210 deletions(-) delete mode 100644 app/login.tsx delete mode 100644 app/useStorageState.tsx diff --git a/api/controllers/postsController.js b/api/controllers/postsController.js index 09f6b48..b00a8ae 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({ 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 }) } diff --git a/app/(tabs)/home.tsx b/app/(tabs)/home.tsx index dd83d59..a7a1b5e 100644 --- a/app/(tabs)/home.tsx +++ b/app/(tabs)/home.tsx @@ -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(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() { )} + { + setShowConfirmation(!showConfirmation) + }} + > + + + Your post has been submitted and will be reviewed within 3 business days. Thanks! + + setShowConfirmation(!showConfirmation)} + > + Close + + + ) } @@ -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 }, diff --git a/app/(tabs)/posts.tsx b/app/(tabs)/posts.tsx index dab81b3..c326c04 100644 --- a/app/(tabs)/posts.tsx +++ b/app/(tabs)/posts.tsx @@ -30,16 +30,14 @@ export default function PostsScreen() { return ( - - Posts - {posts?.length ? ( - posts.map((el) => ) - ) : ( - - All caught up! - - )} - + Posts + {posts?.length ? ( + posts.map((el) => ) + ) : ( + + All caught up! + + )} ) } @@ -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' }, diff --git a/app/(tabs)/profile.tsx b/app/(tabs)/profile.tsx index 6089d36..321b5cc 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/6883ddb2640ebaa1a12e3791`) + .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,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' } }) diff --git a/app/components/PostComponent.tsx b/app/components/PostComponent.tsx index 2322cf0..38c1b8d 100644 --- a/app/components/PostComponent.tsx +++ b/app/components/PostComponent.tsx @@ -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 = ({ 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 = ({ post, fetchData }) /> {post.notes} - - denyPost(post._id)}> - - Deny - - - approvePost(post._id)}> - - Approve - - - + {user?.publicMetadata.role !== 'admin' && ( + + {post.status === StatusEnum.Created && Created} + {post.status === StatusEnum.Pending && Pending} + {post.status === StatusEnum.Denied && Denied} + {post.status === StatusEnum.Accepted && Accepted} + + )} + {user?.publicMetadata.role === 'admin' && ( + + denyPost(post._id)}> + + Deny + + + approvePost(post._id)}> + + Approve + + + + )} ) } @@ -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' } }) 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..fb31572 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', + Accepted = 'accepted' +} 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] -}