Skip to content

Commit

Permalink
implement search user and toggle follow user in app b00tc4mp#84
Browse files Browse the repository at this point in the history
  • Loading branch information
Eden23 committed Aug 17, 2024
1 parent 8431628 commit 4301b97
Show file tree
Hide file tree
Showing 13 changed files with 135 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default (req, res, next) => {
const { targetUserId } = req.params

try {
logic.toggleAddGame(userId, targetUserId)
logic.toggleFollowUser(userId, targetUserId)
.then(() => res.status(204).json())
.catch(error => next(error))
} catch (error) {
Expand Down
14 changes: 7 additions & 7 deletions staff/marti-herms/project/V-HUB/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,19 @@ mongoose.connect(process.env.MONGODB_URI)

api.post('/users/auth', jsonBodyParser, handle.authenticateUser)

api.get('/users/:targetUserId', jwtVerifier, handle.getUser)
api.patch('/users/username', jwtVerifier, jsonBodyParser, handle.editUserUsername)

api.get('/users/:targetUserId/username', jwtVerifier, handle.getUserUsername)
api.patch('/users/avatar', jwtVerifier, jsonBodyParser, handle.editUserAvatar)

api.get('/users/:targetUserId/avatar', jwtVerifier, handle.getUserAvatar)
api.get('/users/search', jwtVerifier, handle.searchUser)

api.patch('/users/username', jwtVerifier, jsonBodyParser, handle.editUserUsername)
api.get('/users/:targetUserId', jwtVerifier, handle.getUser)

api.patch('/users/avatar', jwtVerifier, jsonBodyParser, handle.editUserAvatar)
api.get('/users/:targetUserId/username', jwtVerifier, handle.getUserUsername)

api.get('/users/search', jwtVerifier, handle.searchGame)
api.get('/users/:targetUserId/avatar', jwtVerifier, handle.getUserAvatar)

api.patch('/users/:targetUserId/following', jwtVerifier, handle.toggleFavGame)
api.patch('/users/:targetUserId/following', jwtVerifier, handle.toggleFollowUser)

api.post('/games', jwtVerifier, jsonBodyParser, handle.registerGame)

Expand Down
1 change: 1 addition & 0 deletions staff/marti-herms/project/V-HUB/api/test/search-user.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
curl -v http://localhost:8080/users/search?q=eden -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2NmMwYWUyMjkzMGY5NWM5ODU0MjdlY2UiLCJyb2xlIjoiZGV2IiwiaWF0IjoxNzIzOTA0NDY4fQ.hkqpFXcQxJetKBYQQH2FsEdkIeq7001sjuwGFcpEps8"
32 changes: 18 additions & 14 deletions staff/marti-herms/project/V-HUB/app/logic/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import isUserLoggedIn from './isUserLoggedIn.js'
import getUserUsername from './getUserUsername.js'
import registerGame from './registerGame.js'
import searchGame from './searchGame.js'
import searchUser from './searchUser.js'
import getUserLibrary from './getUserLibrary.js'
import getUserFavs from './getUserFavs.js'
import getGameById from './getGameById.js'
import getGameReviews from './getGameReviews.js'
import getDevUserGames from './getDevUserGames.js'
import toggleAddGame from './toggleAddGame.js'
import toggleFavGame from './toggleFavGame.js'
import toggleFollowUser from './toggleFollowUser.js'
import makeReview from './makeReview.js'
import deleteReview from './deleteReview.js'
import getUserAvatar from './getUserAvatar.js'
Expand All @@ -20,26 +22,28 @@ import editUserAvatar from './editUserAvatar.js'
import editUserUsername from './editUserUsername.js'

const logic = {
deleteReview,
editUserAvatar,
editUserUsername,
getDevUserGames,
getGameById,
getGameReviews,
getUser,
getUserAvatar,
getUserFavs,
getUserLibrary,
getUserUsername,
isUserLoggedIn,
loginUser,
logoutUser,
registerUser,
isUserLoggedIn,
getUserUsername,
makeReview,
registerGame,
registerUser,
searchGame,
getUserLibrary,
getUserFavs,
getGameById,
getGameReviews,
getDevUserGames,
searchUser,
toggleAddGame,
toggleFavGame,
makeReview,
deleteReview,
getUserAvatar,
getUser,
editUserAvatar,
editUserUsername
toggleFollowUser,
}

export default logic
32 changes: 32 additions & 0 deletions staff/marti-herms/project/V-HUB/app/logic/searchUser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { validate, errors } from 'com'

const { SystemError } = errors

export default (query) => {
validate.string(query, 'query')

if (query)
return fetch(`${import.meta.env.VITE_API_URL}/users/search?q=${query}`, {
headers: { Authorization: `Bearer ${sessionStorage.token}` }
})
.catch(error => { throw new SystemError(error.message) })
.then(response => {
const { status } = response

if (status === 200) {
return response.json()
.then(users => users)
}

return response.json()
.then(body => {
const { error, message } = body

const constructor = errors[error]

throw new constructor(message)
})
})

return Promise.all([])
}
27 changes: 27 additions & 0 deletions staff/marti-herms/project/V-HUB/app/logic/toggleFollowUser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { validate, errors } from 'com'

const { SystemError } = errors

export default (targetUserId) => {
validate.string(targetUserId, 'targetUserId')

return fetch(`${import.meta.env.VITE_API_URL}/users/${targetUserId}/following`, {
method: 'PATCH',
headers: { Authorization: `Bearer ${sessionStorage.token}` }
})
.catch(error => { throw new SystemError(error.message) })
.then(response => {
const { status } = response

if (status === 204) return

return response.json()
.then(body => {
const { error, message } = body

const constructor = errors[error]

throw new constructor(message)
})
})
}
4 changes: 2 additions & 2 deletions staff/marti-herms/project/V-HUB/app/src/home/Footer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ export default function Footer({ makeReviewVisibility, onSearchGame, onRegisterG
<Routes>
<Route path='/' element={<>
{role === 'dev' && <button className='border border-solid border-slate-500 bg-white px-2 rounded' onClick={onRegisterGame}>Register Game</button>}
<button className='border border-solid border-slate-500 bg-white px-2 rounded' onClick={onSearchGame}>Search Game</button>
<button className='border border-solid border-slate-500 bg-white px-2 rounded' onClick={onSearchGame}>Search</button>
</>} />
<Route path='/profile/:userId' element={<button className='border border-solid border-slate-500 bg-white px-2 rounded' onClick={onHome}>HOME</button>} />
<Route path='/games/register' element={<button className='border border-solid border-slate-500 bg-white px-2 rounded' onClick={onHome}>HOME</button>} />
<Route path='/games/search' element={<button className='border border-solid border-slate-500 bg-white px-2 rounded' onClick={onHome}>HOME</button>} />
<Route path='/search' element={<button className='border border-solid border-slate-500 bg-white px-2 rounded' onClick={onHome}>HOME</button>} />
<Route path='/games/:gameId' element={<>
<button className='border border-solid border-slate-500 bg-white px-2 rounded' onClick={onHome}>HOME</button>
{makeReviewVisibility ? <button className='border border-solid border-slate-500 bg-white px-2 rounded' onClick={onCancel}>Cancel</button> :
Expand Down
35 changes: 18 additions & 17 deletions staff/marti-herms/project/V-HUB/app/src/home/Game.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,24 @@ export default function Game({ makeReviewVisibility, onCancel }) {
const [value, setValue] = useState(0)

useEffect(() => {
try {
logic.getGameById(gameId)
.then(game => {
setGame(game)

loadReviews()
})
.catch(error => {
console.error(error)

alert(error.message)
})
} catch (error) {
console.error(error)

alert(error.message)
}
if (gameId.length >= 10)
try {
logic.getGameById(gameId)
.then(game => {
setGame(game)

loadReviews()
})
.catch(error => {
console.error(error)

alert(error.message)
})
} catch (error) {
console.error(error)

alert(error.message)
}
}, [])

useEffect(() => calculateRating(), [reviews])
Expand Down
4 changes: 2 additions & 2 deletions staff/marti-herms/project/V-HUB/app/src/home/GameBanner.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ export default function GameBanner({ game, onInteraction, onGameClick, collectio
}
}

return <article className='flex flex-row items-center border border-solid border-slate-700 dark:bg-black'>
return <article className='flex flex-row items-center border-y border-solid border-slate-700 dark:bg-black'>
<button className='bg-transparent border-0' onClick={handleGameClick}>
<Container className='flex flex-col'>
<Container className='flex flex-row items-center'>
<Image className='w-2/12 h-2/12' src={game.image} />
<Paragraph>{game.name}</Paragraph>
</Container>
<Container>
<Paragraph>{game.description.slice(0, 25) + '...'}</Paragraph>
<Paragraph>{game.description && game.description.slice(0, 25) + '...'}</Paragraph>
</Container>
</Container>
</button>
Expand Down
6 changes: 3 additions & 3 deletions staff/marti-herms/project/V-HUB/app/src/home/Search.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ export default function Search({ onChange }) {
const { value: query } = form.q

if (!query.trim())
navigate('/games/search')
else if (location.pathname !== '/games/search')
navigate(`/games/search?q=${query}`)
navigate('/search')
else if (location.pathname !== '/search')
navigate(`/search?q=${query}`)
else
setSearchParams({ q: query })

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import logic from '../../logic'
import useContext from '../context'

import GameBanner from './GameBanner'
import UserBanner from './UserBanner'

export default function SearchResults({ refreshStamp, onGameClick, onUserClick }) {
const { alert } = useContext()
Expand Down Expand Up @@ -57,7 +58,7 @@ export default function SearchResults({ refreshStamp, onGameClick, onUserClick }

return <section className='flex flex-col gap-4'>
{q.startsWith('@') ?
results.map(user => <UserBanner hey={user.id} user={user} onInteraction={loadUsers} onUserClick={onUserClick} />) :
results.map(user => <UserBanner key={user.id} user={user} onInteraction={loadUsers} onUserClick={onUserClick} />) :
results.map(game => <GameBanner key={game.id} game={game} onInteraction={loadGames} onGameClick={onGameClick} collectionType={'search'} />)}
</section>
}
15 changes: 10 additions & 5 deletions staff/marti-herms/project/V-HUB/app/src/home/UserBanner.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ import useContext from '../context'

import logic from '../../logic'

import defaultAvatar from '../../images/defaultAvatar.svg'
import extractPayloadFromToken from '../../util/extractPayloadFromToken'

export default function GameBanner({ user, onInteraction, onUserClick }) {
const { alert } = useContext()

const { sub: currentUserId } = extractPayloadFromToken(sessionStorage.token)

const handleUserClick = () => {
onUserClick(user.id)
}
Expand All @@ -29,17 +34,17 @@ export default function GameBanner({ user, onInteraction, onUserClick }) {
}
}

return <article className='flex flex-row items-center border border-solid border-slate-700 dark:bg-black'>
return <article className='flex flex-row items-center justify-between border-y border-solid border-slate-700 dark:bg-black'>
<button className='bg-transparent border-0' onClick={handleUserClick}>
<Container className='flex flex-col'>
<Container className='flex flex-row items-center'>
<Avatar className='w-2/12 h-2/12' src={user.avatar} />
<Avatar className='w-2/12 h-2/12' url={user.avatar || defaultAvatar} />
<Paragraph>{user.username}</Paragraph>
</Container>
</Container>
</button>
<Container className='flex flex-col gap-2 mr-2'>
<button className='bg-gray-500 rounded' onClick={handleFollowUser}>{user.following ? 'Unfollow' : 'Follow'}</button>
</Container>
{user.id !== currentUserId && <Container className='flex flex-col gap-2 mr-2'>
<button className='bg-gray-500 rounded' onClick={handleFollowUser}>{user.followed ? 'Unfollow' : 'Follow'}</button>
</Container>}
</article>
}
17 changes: 12 additions & 5 deletions staff/marti-herms/project/V-HUB/app/src/home/index.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState, useEffect } from 'react'
import { Route, Routes, useNavigate } from 'react-router-dom'
import { Route, Routes, useNavigate, useLocation } from 'react-router-dom'

import Header from './Header'
import Library from './Library'
Expand All @@ -16,8 +16,15 @@ export default function Home({ onLogout }) {
const [refreshStamp, setRefreshStamp] = useState(null)
const [makeReviewVisibility, setMakeReviewVisibility] = useState(false)

const location = useLocation()
const navigate = useNavigate()

useEffect(() => {
if (location.pathname === '/games/search' || location.pathname === '/users/search') {
navigate('/search')
}
}, [location])

const handleHomeClick = () => {
setRefreshStamp(Date.now())
navigate('/')
Expand All @@ -27,9 +34,9 @@ export default function Home({ onLogout }) {
navigate('/games/register')
}

const handleSearchGameClick = () => {
const handleSearchClick = () => {
setRefreshStamp(Date.now())
navigate('/games/search')
navigate('/search')
}

const handleInputChange = () => {
Expand Down Expand Up @@ -72,13 +79,13 @@ export default function Home({ onLogout }) {
<Route path='/' element={<Library onGameClick={handleGame} />} />
<Route path='/profile/:userId' element={<Profile refreshStamp={refreshStamp} onChange={handleSearchUser} />} />
<Route path='/games/register' element={<GameRegister onGameRegister={handleRegisterGame} />} />
<Route path='/games/search' element={<><Search onChange={handleInputChange} /> <SearchResults refreshStamp={refreshStamp} onGameClick={handleGame} onUserClick={handleSearchUser} /></>} />
<Route path='/search' element={<><Search onChange={handleInputChange} /> <SearchResults refreshStamp={refreshStamp} onGameClick={handleGame} onUserClick={handleSearchUser} /></>} />
<Route path='/games/:gameId' element={<Game makeReviewVisibility={makeReviewVisibility} onCancel={handleCancelReview} />} />
</Routes>
</main>

<Footer makeReviewVisibility={makeReviewVisibility}
onSearchGame={handleSearchGameClick}
onSearchGame={handleSearchClick}
onRegisterGame={handleRegisterGameClick}
onHome={handleHomeClick}
onAddReview={handleAddReview}
Expand Down

0 comments on commit 4301b97

Please sign in to comment.