Skip to content

Commit f2070bd

Browse files
committed
Custom game values search with SRC search and debounce
1 parent 308ced1 commit f2070bd

File tree

3 files changed

+76
-18
lines changed

3 files changed

+76
-18
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { FormControl, FormLabel } from 'react-bootstrap'
2+
import { SearchFieldProps, SearchProps } from 'react-bootstrap-table2-toolkit'
3+
import React from 'react'
4+
import { apiGet } from '../fetchers/api'
5+
6+
type IdToNameMap = { [key: string]: string }
7+
8+
type GameCategorySearchProps =
9+
SearchProps
10+
& SearchFieldProps
11+
& {
12+
setGameMap: React.Dispatch<React.SetStateAction<IdToNameMap>>
13+
}
14+
15+
function debounce<T>(fn: (...args: T[]) => void, time: number) {
16+
let timeout: NodeJS.Timeout | null
17+
return wrapper
18+
function wrapper(...args: T[]) {
19+
if (timeout) {
20+
clearTimeout(timeout)
21+
}
22+
timeout = setTimeout(() => {
23+
timeout = null
24+
fn(...args)
25+
}, time)
26+
}
27+
}
28+
29+
const GameCategorySearch = (props: GameCategorySearchProps) => {
30+
const handleOnChange = debounce(
31+
(searchText: string) =>
32+
!searchText
33+
? props.onClear?.()
34+
: apiGet('https://www.speedrun.com/api/v1/games', { name: searchText, max: 200 }, false)
35+
.then(res => res.json())
36+
.then<{ id: string, names: { international: string } }[]>(res => res.data)
37+
.then<IdToNameMap>(games => Object.fromEntries(games.map(game => [game.id, game.names.international])))
38+
.then(games => {
39+
props.setGameMap(prevGames => {
40+
const newGames = { ...prevGames, ...games }
41+
localStorage.setItem('games', JSON.stringify(newGames))
42+
return newGames
43+
})
44+
return props.onSearch?.(searchText)
45+
}),
46+
500)
47+
// Note: 500 is double the default table update
48+
49+
return <FormLabel>
50+
<FormControl
51+
className={props.className}
52+
placeholder={props.placeholder}
53+
onChange={e => handleOnChange(e.currentTarget.value)}
54+
/>
55+
</FormLabel>
56+
57+
}
58+
59+
export default GameCategorySearch

global-scoreboard/src/GameSearch/GameSearch.tsx

+11-14
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@ import 'react-bootstrap-table2-paginator/dist/react-bootstrap-table2-paginator.m
55
import './GameSearch.css'
66
import BootstrapTable, { Column } from 'react-bootstrap-table-next'
77
import { Container, Dropdown, DropdownButton, FormControl, InputGroup, Spinner } from 'react-bootstrap'
8-
import React, { Component, useEffect, useRef, useState } from 'react'
9-
import ToolkitProvider, { Search, SearchProps, ToolkitProviderProps } from 'react-bootstrap-table2-toolkit'
8+
import React, { useEffect, useState } from 'react'
9+
import ToolkitProvider, { ToolkitProviderProps } from 'react-bootstrap-table2-toolkit'
1010
import filterFactory, { Comparator, multiSelectFilter, numberFilter } from 'react-bootstrap-table2-filter'
1111
import paginationFactory, { PaginationListStandalone, PaginationProvider, PaginationTotalStandalone, SizePerPageDropdownStandalone } from 'react-bootstrap-table2-paginator'
1212
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
13+
import GameCategorySearch from './GameCategorySearchBar'
1314
import { Picky } from 'react-picky'
1415
import { apiGet } from '../fetchers/api'
1516
import { faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons'
1617
import { faLongArrowAltDown } from '@fortawesome/free-solid-svg-icons'
1718
import { faLongArrowAltUp } from '@fortawesome/free-solid-svg-icons'
18-
const { SearchBar } = Search
1919

2020
interface PlatformDto {
2121
id: string
@@ -274,8 +274,6 @@ const fetchValueNamesForRun = async (runId: string) => {
274274
}
275275

276276
const GameSearch = () => {
277-
const searchBarRef = useRef<Component<SearchProps>>(null)
278-
const boostrapTableRef = useRef<BootstrapTable>(null)
279277
const [gameValues, setGameValues] = useState<GameValueRow[]>([])
280278
const [platforms, setPlatforms] = useState<IdToNameMap>()
281279
const [selectedPlatforms, setSelectedPlatforms] = useState<PlatformSelectOption[]>([])
@@ -294,13 +292,13 @@ const GameSearch = () => {
294292
getAllPlatforms().then(setPlatforms)
295293
}, [])
296294

297-
const doPlatformSelection = (platforms: PlatformSelectOption[]) => {
298-
platformFilter(platforms.map(platform => platform.id))
299-
setSelectedPlatforms(platforms)
295+
const doPlatformSelection = (selectedPlatformOptions: PlatformSelectOption[]) => {
296+
platformFilter(selectedPlatformOptions.map(platform => platform.id))
297+
setSelectedPlatforms(selectedPlatformOptions)
300298
}
301-
const handlePlatformSelection = (platforms: PlatformSelectOption[]) => {
302-
doPlatformSelection(platforms)
303-
localStorage.setItem('selectedPlatforms', JSON.stringify(platforms))
299+
const handlePlatformSelection = (selectedPlatformOptions: PlatformSelectOption[]) => {
300+
doPlatformSelection(selectedPlatformOptions)
301+
localStorage.setItem('selectedPlatforms', JSON.stringify(selectedPlatformOptions))
304302
}
305303

306304
const doMinTimeChange = (minTime: string) => {
@@ -353,10 +351,10 @@ const GameSearch = () => {
353351
>
354352
{(({ paginationProps, paginationTableProps }) =>
355353
<div>
356-
<SearchBar
357-
ref={searchBarRef}
354+
<GameCategorySearch
358355
{...toolkitprops.searchProps}
359356
placeholder="Game / Category search"
357+
setGameMap={setGameMap}
360358
/>
361359
<Picky
362360
id="platform-multiselect"
@@ -401,7 +399,6 @@ const GameSearch = () => {
401399
</div>
402400
<SizePerPageDropdownStandalone {...paginationProps} />
403401
<BootstrapTable
404-
ref={boostrapTableRef}
405402
wrapperClasses="table-responsive"
406403
striped
407404
{...toolkitprops.baseProps}

global-scoreboard/src/react-bootstrap-table-next.d.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
// - CustomFilterProps extends
2727
// - NumberFIlterFunction
2828
// - FilterProps.getFilter exists
29+
// - export SearchFieldProps and SearchProps
30+
// - onSearch has first parameter searchText: string
2931

3032
type RowFieldValue = ReactText | Date | TODO
3133
type TODO = any
@@ -324,15 +326,15 @@ declare module 'react-bootstrap-table2-toolkit' {
324326
className?: string
325327
style?: {}
326328
}
327-
interface SearchFieldProps {
329+
export interface SearchFieldProps {
328330
className?: string
329331
placeholder?: string
330-
ref: SearchBar
332+
ref?: SearchBar
331333
}
332-
interface SearchProps {
334+
export interface SearchProps {
333335
searchText?: string
334336
onClear?(): void
335-
onSearch?(): void
337+
onSearch?(searchText: string): void
336338
}
337339
export class ColumnToggle {
338340
static ToggleList(props: ColumnToggleProps): ReactElement

0 commit comments

Comments
 (0)