Skip to content

Commit c6d6c9b

Browse files
authored
Merge pull request #136 from Avasam/develop
Develop
2 parents cec40f4 + 28ad3af commit c6d6c9b

11 files changed

+223
-114
lines changed

.idea/speedrun.com_global_leaderboard_webapp.iml .idea/speedrun.com_global_scoreboard_webapp.iml

-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
UPDATE player
2-
SET last_update = DATEADD(dd, -6, CURRENT_TIMESTAMP)
3-
WHERE user_id = '';
2+
SET last_update = subdate(current_date, 7)
3+
WHERE last_update > subdate(current_date, 7)
4+
AND user_id = '';

global-scoreboard/package-lock.json

+123-75
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

global-scoreboard/package.json

+10-10
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@
1414
"eject": "react-scripts eject"
1515
},
1616
"dependencies": {
17-
"@fortawesome/fontawesome-svg-core": "^1.2.28",
18-
"@fortawesome/free-regular-svg-icons": "^5.13.0",
19-
"@fortawesome/free-solid-svg-icons": "^5.13.0",
20-
"@fortawesome/react-fontawesome": "^0.1.9",
17+
"@fortawesome/fontawesome-svg-core": "^1.2.30",
18+
"@fortawesome/free-regular-svg-icons": "^5.14.0",
19+
"@fortawesome/free-solid-svg-icons": "^5.14.0",
20+
"@fortawesome/react-fontawesome": "^0.1.11",
2121
"bootstrap": "^4.5.0",
2222
"react": "^16.13.1",
23-
"react-bootstrap": "^1.0.1",
24-
"react-bootstrap-table-next": "^4.0.2",
23+
"react-bootstrap": "^1.3.0",
24+
"react-bootstrap-table-next": "^4.0.3",
2525
"react-bootstrap-table2-filter": "^1.3.3",
2626
"react-bootstrap-table2-paginator": "^2.1.2",
2727
"react-bootstrap-table2-toolkit": "^2.1.3",
@@ -31,14 +31,14 @@
3131
},
3232
"devDependencies": {
3333
"@types/jest": "^24.0.23",
34-
"@types/node": "^13.13.9",
35-
"@types/react": "^16.9.35",
34+
"@types/node": "^13.13.15",
35+
"@types/react": "^16.9.43",
3636
"@types/react-dom": "^16.9.8",
3737
"eslint": "^6.8.0",
38-
"eslint-plugin-react": "^7.20.0",
38+
"eslint-plugin-react": "^7.20.3",
3939
"tslint": "^5.20.1",
4040
"tslint-react": "^4.2.0",
41-
"typescript": "^3.9.3"
41+
"typescript": "^3.9.7"
4242
},
4343
"browserslist": {
4444
"production": [

global-scoreboard/src/App.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,14 @@ const App: FC = () => {
5757
>Copyright</a> {new Date().getFullYear()} by <a
5858
href="https://github.com/Avasam/"
5959
target="about"
60-
>Samuel Therrien</a>.
60+
>Samuel Therrien</a> (
61+
<a href="https://www.twitch.tv/Avasam" target="about">
62+
Avasam<img
63+
height="14"
64+
alt="Twitch"
65+
src="https://static.twitchcdn.net/assets/favicon-32-d6025c14e900565d6177.png"
66+
></img>
67+
</a>).
6168
Powered by <a
6269
href="https://www.speedrun.com/"
6370
target="src"

global-scoreboard/src/Dashboard/Dashboard.tsx

-3
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,6 @@ const Dashboard = (props: DashboardProps) => {
223223
}
224224

225225
return <Container className="dashboard-container">
226-
<Alert variant="info">
227-
We have switched to a new URL! Makes sure to update your bookmark!
228-
</Alert>
229226
<Alert
230227
variant={alertVariant}
231228
style={{ visibility: alertMessage ? 'visible' : 'hidden' }}
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+
props.onSearch?.(searchText)
45+
}),
46+
// Note: 500 is double the default table update
47+
500)
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

user_updater.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ def __set_points(self) -> None:
126126
# TODO: This is not optimized
127127
pre_fix_worst_time = valid_runs[-1]["run"]["times"]["primary_t"]
128128
# TODO: Extract "cutoff everything after soft cutoff" to its own function
129-
# Find the time that's most often repeated in the leaderboard (after the 8th octile) and cut off after
129+
# Find the time that's most often repeated in the leaderboard (after the 80th percentile)
130+
# and cut off everything after that
130131
cut_off_80th_percentile: int = valid_runs[int(len(valid_runs)*0.8)]["run"]["times"]["primary_t"]
131132
count: int = 0
132133
most_repeated_time_pos: int = 0

utils.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import simplejson
88
import requests
99

10-
HTTP_RETRYABLE_ERRORS = [401, 420, 500, 502]
10+
HTTP_RETRYABLE_ERRORS = [401, 420, 502]
1111
HTTP_ERROR_RETRY_DELAY_MIN = 5
1212
HTTP_ERROR_RETRY_DELAY_MAX = 15
1313

0 commit comments

Comments
 (0)