diff options
| -rw-r--r-- | components/ui.tsx | 17 | ||||
| -rw-r--r-- | package.json | 1 | ||||
| -rw-r--r-- | pages/game.tsx | 47 | ||||
| -rw-r--r-- | styles/game.css | 20 | ||||
| -rw-r--r-- | yarn.lock | 5 | 
5 files changed, 86 insertions, 4 deletions
diff --git a/components/ui.tsx b/components/ui.tsx index 7474240..1e9997a 100644 --- a/components/ui.tsx +++ b/components/ui.tsx @@ -1,4 +1,5 @@  import { ReactNode, useEffect, useState } from 'react'; +import { v4 as uuid } from 'uuid';  import CheckBoxIcon from '@material-ui/icons/CheckBox';  import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank'; @@ -79,6 +80,7 @@ export function Input(props: {  	autocomplete?: string;  	autofocus?: boolean;  	className?: string; +	onChange?: () => void;  }) {  	return <input  		id={props.id} @@ -91,16 +93,25 @@ export function Input(props: {  		className={'input' + ' ' + (props.dark ? 'dark' : 'light') + ' ' + props.className}  		autoComplete={props.autocomplete}  		autoFocus={props.autofocus} +		onChange={props.onChange}  	/>;  } -export function SearchBar(props: { label?: string; }) { -	return <div className='searchBar round-t fullwidth'> +export function SearchBar(props: { +	label?: string; +	search?: (query: string) => void; +}) { +	var id = uuid(); + +	var getQuery = () => (document.getElementById(id).children[0] as HTMLInputElement).value; + +	return <div className='searchBar round-t fullwidth' id={id}>  		<Input  			label={props.label}  			className='pad-m bg-700' +			onChange={() => props.search && props.search(getQuery())}  		/> -		<Button className='dispinbl valigntop'> +		<Button className='dispinbl valigntop' onclick={() => props.search && props.search(getQuery())}>  			<SearchIcon className='icon' />  		</Button>  	</div>; diff --git a/package.json b/package.json index 75fa8b1..d0e01e0 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@  		"copy-to-clipboard": "^3.3.1",  		"email-validator": "^2.0.4",  		"friendly-time": "^1.1.1", +		"fuse.js": "^6.4.6",  		"image-blob-reduce": "^2.2.2",  		"micromark": "^2.11.4",  		"next": "^10.0.5", diff --git a/pages/game.tsx b/pages/game.tsx index de2c089..f6c6f5b 100644 --- a/pages/game.tsx +++ b/pages/game.tsx @@ -1,11 +1,13 @@  import Icon from '@mdi/react';  import axios from 'axios';  import copy from 'copy-to-clipboard'; +import Fuse from 'fuse.js';  import { useContext, useEffect, useState } from 'react';  import * as cookies from 'react-cookies';  import { Socket } from 'socket.io-client';  import { SocketContext } from '../components/socketContext'; +import { userInfo } from '../api/api';  import { DialogBox } from '../components/dialogBox';  import { GameBar } from '../components/gameBar';  import { CurrentGameSettings } from '../components/gameSettings'; @@ -15,11 +17,13 @@ import { ToastContext, toastType } from '../components/toast';  import { Button, IconLabelButton, SearchBar } from '../components/ui';  import { VoerBord } from '../components/voerBord'; +import AddIcon from '@material-ui/icons/Add';  import FlagOutlinedIcon from '@material-ui/icons/FlagOutlined';  import LinkRoundedIcon from '@material-ui/icons/LinkRounded';  import RefreshIcon from '@material-ui/icons/Refresh';  import WifiTetheringRoundedIcon from '@material-ui/icons/WifiTetheringRounded';  import { mdiContentCopy } from '@mdi/js'; +import { AccountAvatar } from '../components/account';  function VoerGame(props: {  	gameID: string; @@ -139,11 +143,28 @@ function GameOutcomeDialog(props: {  	</DialogBox>;  } +function InviteableFriend(props: { user?: userInfo; }) { +	return <div className='round-t bg-700 inviteableFriend drop-1 posrel'> +		<AccountAvatar size={44} id={props.user?.id} /> +		<span className='username nosel posabs abscenterv pad-m'>{props.user?.username}</span> +		<Button className='floatr dispinbl' children={<AddIcon />} /> +	</div>; +} +  export default function GamePage() {  	var [gameID, setGameID] = useState('');  	var [player1, setPlayer1] = useState(true);  	var [active, setActive] = useState(false);  	var [gameIDUrl, setGameIDUrl] = useState(''); +	var [friendList, setFriendList] = useState<userInfo[]>([]); + +	var [query, setQuery] = useState(''); +	var [visibleFriends, setVisibleFriends] = useState<userInfo[]>([]); + +	var fuse = new Fuse(friendList, { +		keys: ['username'], +		isCaseSensitive: false, +	});  	var { io } = useContext(SocketContext);  	var { toast } = useContext(ToastContext); @@ -174,6 +195,27 @@ export default function GamePage() {  	}, []);  	useEffect(() => { +		axios.request<{ friends: Array<userInfo>; }>({ +			method: 'get', +			url: '/api/social/list/friends', +		}) +			.then(response => { +				console.log(response.data.friends); +				setFriendList(response.data.friends); +			}) +			.catch(err => { +				toast({ message: 'error', type: 'error', description: err.toString() }); +			}); +	}, []); + +	useEffect(() => { +		var fuseSearch = fuse.search(query); +		var results = fuseSearch.map(res => res.item).slice(0, 5); + +		setVisibleFriends(results); +	}, [query]); + +	useEffect(() => {  		io.on('gameStart', () => setActive(true));  	}, []); @@ -246,7 +288,10 @@ export default function GamePage() {  						<h2 className='label center posabs abscenterh nosel'>Uitnodigen via link</h2>  					</Button>  				</div> -				<SearchBar label='Zoeken in vriendenlijst' /> +				<div className='inviteFromFriendsList'> +					<SearchBar label='Zoeken in vriendenlijst' search={q => setQuery(q)} /> +					{visibleFriends.map(user => <InviteableFriend user={user} />)} +				</div>  			</DialogBox>  		</CenteredPage>  	</div>; diff --git a/styles/game.css b/styles/game.css index d482f9b..5954991 100644 --- a/styles/game.css +++ b/styles/game.css @@ -74,3 +74,23 @@ html.dark .newGameDialog .inviteButton.random .icon { color: var(--confirm); }  html.dark .newGameDialog .inviteButton.link .icon { color: var(--error); }  html.dark .newGameDialog .searchBar .input { background-color: var(--gray-800); } +.inviteFromFriendsList { +	overflow-y: visible; +	height: 44px; +} + +html.dark .inviteFromFriendsList .inviteableFriend { +	background-color: var(--gray-800); +} +.inviteFromFriendsList .inviteableFriend { +	margin-top: var(--spacing-small); +	overflow: hidden; +	cursor: pointer; +} + +.inviteFromFriendsList .inviteableFriend .button { +	background-color: transparent; +	color: var(--foreground); +	padding: 10px; +} + @@ -5922,6 +5922,11 @@ functional-red-black-tree@^1.0.1:    resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"    integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +fuse.js@^6.4.6: +  version "6.4.6" +  resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-6.4.6.tgz#62f216c110e5aa22486aff20be7896d19a059b79" +  integrity sha512-/gYxR/0VpXmWSfZOIPS3rWwU8SHgsRTwWuXhyb2O6s7aRuVtHtxCkR33bNYu3wyLyNx/Wpv0vU7FZy8Vj53VNw== +  gauge@~2.7.3:    version "2.7.4"    resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"  |