diff options
-rw-r--r-- | src/components/Header/Header.tsx | 2 | ||||
-rw-r--r-- | src/components/Header/SearchBar.tsx | 90 | ||||
-rw-r--r-- | src/components/UserStrip/UserStrip.tsx | 2 | ||||
-rw-r--r-- | src/pages/ProfilePage/ProfileInfo.tsx | 22 |
4 files changed, 104 insertions, 12 deletions
diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index 2e3fc20..1825647 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -52,7 +52,7 @@ const Header: React.FC<PropTypes> = ({ navigate, userImage }) => { <Typography variant="h5" className={classes.logo}> Which </Typography> - <SearchBar /> + <SearchBar navigate={navigate} /> <div> <IconButton onClick={handleHome}> <HomeIcon /> diff --git a/src/components/Header/SearchBar.tsx b/src/components/Header/SearchBar.tsx index 182a1a4..2be8f6f 100644 --- a/src/components/Header/SearchBar.tsx +++ b/src/components/Header/SearchBar.tsx @@ -1,27 +1,111 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import SearchIcon from '@material-ui/icons/Search'; -import { InputBase } from '@material-ui/core'; +import { + InputBase, + List, + ListItem, + Paper, + Divider, + ClickAwayListener +} from '@material-ui/core'; import { makeStyles } from '@material-ui/core/styles'; +import { User } from 'which-types'; +import { get } from '../../requests'; +import UserStrip from '../UserStrip/UserStrip'; + +interface PropTypes { + navigate: (prefix: string, id: string) => void; +} + +const INTERVAL = 300; const useStyles = makeStyles(theme => ({ root: { + position: 'relative', background: 'rgba(255, 255, 255, 0.5)', borderRadius: '2px', padding: theme.spacing(0.5), display: 'flex', alignItems: 'center' + }, + results: { + position: 'absolute', + width: '100%', + top: theme.spacing(5) } })); -const SearchBar: React.FC = () => { +const SearchBar: React.FC<PropTypes> = ({ navigate }) => { + const [query, setQuery] = useState<string>(''); + const [results, setResults] = useState<User[]>([]); + const [isReady, setIsReady] = useState<boolean>(true); + const [shouldRefetch, setShouldRefetch] = useState<boolean>(false); const classes = useStyles(); + const sleep = () => { + setIsReady(false); + setTimeout(() => setIsReady(true), INTERVAL); + }; + + const fetchPolls = () => { + sleep(); + get(`/users?username[$regex]=${query}`).then(response => { + setResults(response.data); + }); + }; + + useEffect(() => { + if (isReady && shouldRefetch) { + fetchPolls(); + setShouldRefetch(false); + } + }, [isReady]); + + + const handleChange = (event: React.ChangeEvent<HTMLInputElement>): void => { + setQuery(event.target.value); + if (isReady) fetchPolls(); + else setShouldRefetch(true); + }; + + const handleClose = () => { + setQuery(''); + setResults([]); + }; + + const handleNavigate = (index: number) => () => { + navigate('profile', results[index]._id); + handleClose(); + }; + + const SearchResults = ( + <ClickAwayListener onClickAway={handleClose}> + <Paper className={classes.results}> + <List> + { + results.map((result, index) => ( + <> + <ListItem button onClick={handleNavigate(index)}> + <UserStrip user={result} navigate={navigate} /> + </ListItem> + {(index < results.length - 1) && <Divider />} + </> + )) + } + </List> + </Paper> + </ClickAwayListener> + ); + return ( <div className={classes.root}> <SearchIcon /> <InputBase placeholder="Search..." + value={query} + onChange={handleChange} /> + {results.length > 0 && SearchResults} </div> ); }; diff --git a/src/components/UserStrip/UserStrip.tsx b/src/components/UserStrip/UserStrip.tsx index 6e84768..f02adc3 100644 --- a/src/components/UserStrip/UserStrip.tsx +++ b/src/components/UserStrip/UserStrip.tsx @@ -10,8 +10,8 @@ import { User } from 'which-types'; interface PropTypes { user: User; - info: string | JSX.Element navigate: (prefix: string, id: string) => void; + info?: string | JSX.Element } diff --git a/src/pages/ProfilePage/ProfileInfo.tsx b/src/pages/ProfilePage/ProfileInfo.tsx index f52e374..2b9227e 100644 --- a/src/pages/ProfilePage/ProfileInfo.tsx +++ b/src/pages/ProfilePage/ProfileInfo.tsx @@ -1,8 +1,9 @@ import React, { useState } from 'react'; -import { Avatar, Badge } from '@material-ui/core/'; +import { Avatar, Badge, Typography } from '@material-ui/core/'; import { makeStyles } from '@material-ui/core/styles'; import { User } from 'which-types'; import CameraAltIcon from '@material-ui/icons/CameraAlt'; +import VerifiedIcon from '@material-ui/icons/CheckCircleOutline'; import MoreMenu from './MoreMenu'; import Highlight from './Highlight'; import UploadImage from '../../components/UploadImage/UploadImage'; @@ -27,9 +28,15 @@ const useStyles = makeStyles(theme => ({ margin: '0 auto' }, name: { - fontSize: 20, - textAlign: 'center', - margin: '10px 0' + margin: theme.spacing(1, 0), + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }, + verified: { + marginLeft: theme.spacing(0.5), + width: theme.spacing(3), + height: theme.spacing(3) }, profileMenu: { display: 'flex', @@ -110,13 +117,14 @@ const ProfileInfo: React.FC<PropTypes> = ({ ) : <Avatar className={classes.avatar} src={user?.avatarUrl} /> } - <div className={classes.name}> + <Typography variant="h5" className={classes.name}> {user?.username} - </div> + {user?.verified && <VerifiedIcon className={classes.verified} color="primary" />} + </Typography> <div className={classes.profileMenu}> <Highlight text="Polls" value={savedPolls} /> <Highlight text="Since" value={dateSince} /> - <Highlight text="Total" value={totalVotes} /> + <Highlight text="Total votes" value={totalVotes} /> </div> </div> ); |