From 62f01802cac633f6cb18c866f354d545aa71509a Mon Sep 17 00:00:00 2001 From: jmlee337 Date: Tue, 16 Jul 2024 14:27:51 +0900 Subject: [PATCH] ctrl-f search for challonge/manual as well #55 --- src/common/constants.ts | 1 + src/common/types.ts | 6 ++ src/renderer/App.tsx | 39 +++++--- src/renderer/ChallongeView.tsx | 171 +++++++++++++++++++-------------- src/renderer/DragAndDrop.tsx | 43 +++++++-- src/renderer/ManualView.tsx | 25 ++++- src/renderer/SearchBox.tsx | 84 ++++++++++++++++ src/renderer/SetView.tsx | 4 +- src/renderer/StartggView.tsx | 126 +----------------------- src/renderer/filterSets.ts | 54 +++++++++++ 10 files changed, 332 insertions(+), 221 deletions(-) create mode 100644 src/renderer/SearchBox.tsx create mode 100644 src/renderer/filterSets.ts diff --git a/src/common/constants.ts b/src/common/constants.ts index 5589775..119f86d 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -139,3 +139,4 @@ export const startggStageIds = new Map( ); export const frameMsDivisor = 0.05994; +export const highlightColor = '#ffee58'; diff --git a/src/common/types.ts b/src/common/types.ts index c9f3677..977af3b 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -234,3 +234,9 @@ export type NameWithHighlight = { }; name: string; }; + +export type SetWithNames = { + set: Set; + entrant1Names: NameWithHighlight[]; + entrant2Names: NameWithHighlight[]; +}; diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 13f451b..b32a419 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -74,6 +74,7 @@ import { import ManualView from './ManualView'; import ManualBar from './ManualBar'; import ChallongeView from './ChallongeView'; +import SearchBox from './SearchBox'; const Bottom = styled(Paper)` height: 147px; @@ -457,6 +458,7 @@ function Hello() { const [slugDialogOpen, setSlugDialogOpen] = useState(false); const [gettingTournament, setGettingTournament] = useState(false); + const [searchSubstr, setSearchSubstr] = useState(''); // Challonge tournament view const [challongeTournaments, setChallongeTournaments] = useState( @@ -1692,8 +1694,15 @@ function Hello() {
+ {mode === Mode.STARTGG && ( getEvent(id, true)} @@ -1711,6 +1720,7 @@ function Hello() { (challongeTournament) => ( getChallongeTournament(challongeTournament.slug) @@ -1724,6 +1734,7 @@ function Hello() { {mode === Mode.MANUAL && ( @@ -1807,10 +1818,11 @@ function Hello() { {selectedSet.entrant1Participants.length > 1 && ( {selectedSet.entrant2Participants.length > 1 && ( void; }) { return ( @@ -29,22 +30,18 @@ function SetView({ dense disableGutters onClick={() => { - selectSet(set); + selectSet(setWithNames.set); }} > ({ - name: participant.displayName, - }))} - entrant1Score={set.entrant1Score} - entrant1Win={set.entrant1Id === set.winnerId} - entrant2Names={set.entrant2Participants.map((participant) => ({ - name: participant.displayName, - }))} - entrant2Score={set.entrant2Score} - fullRoundText={set.fullRoundText} - state={set.state} - showScores={set.state === State.COMPLETED} + entrant1Names={setWithNames.entrant1Names} + entrant1Score={setWithNames.set.entrant1Score} + entrant1Win={setWithNames.set.entrant1Id === setWithNames.set.winnerId} + entrant2Names={setWithNames.entrant2Names} + entrant2Score={setWithNames.set.entrant2Score} + fullRoundText={setWithNames.set.fullRoundText} + state={setWithNames.set.state} + showScores={setWithNames.set.state === State.COMPLETED} wasReported={false} /> @@ -52,10 +49,12 @@ function SetView({ } export default function ChallongeView({ + searchSubstr, tournament, getChallongeTournament, selectSet, }: { + searchSubstr: string; tournament: ChallongeTournament; getChallongeTournament: () => Promise; selectSet: (set: Set) => void; @@ -68,69 +67,93 @@ export default function ChallongeView({ await getChallongeTournament(); setGetting(false); }; + const pendingSetsToShow = filterSets( + tournament.sets.pendingSets, + searchSubstr, + ); + const completedSetsToShow = filterSets( + tournament.sets.completedSets, + searchSubstr, + ); return ( - - { - setOpen(!open); - }} - sx={{ typography: 'caption' }} - > - {open ? : } -
0 || + completedSetsToShow.length > 0) && ( + + { + setOpen(!open); }} + sx={{ typography: 'caption' }} > - {tournament.name} -
- - { - ev.stopPropagation(); - get(); + {open ? : } +
- {getting ? : } - - - - -
- {tournament.sets.pendingSets.map((set) => ( - - ))} - {tournament.sets.completedSets.length > 0 && ( - <> - setCompletedOpen(!completedOpen)} - > - + + { + ev.stopPropagation(); + get(); + }} + size="small" + > + {getting ? : } + + + + +
+ {pendingSetsToShow.map((setWithNames) => ( + + ))} + {completedSetsToShow.length > 0 && ( + <> + setCompletedOpen(!completedOpen)} > - completed - {completedOpen ? : } - - - - {tournament.sets.completedSets.map((set) => ( - - ))} - - - )} -
-
- + + completed + {completedOpen ? ( + + ) : ( + + )} + + + + {completedSetsToShow.map((setWithNames) => ( + + ))} + + + )} +
+
+ + ) ); } diff --git a/src/renderer/DragAndDrop.tsx b/src/renderer/DragAndDrop.tsx index 1860ce9..9234b01 100644 --- a/src/renderer/DragAndDrop.tsx +++ b/src/renderer/DragAndDrop.tsx @@ -6,18 +6,42 @@ import { createTheme, } from '@mui/material'; import { CSSProperties, DragEvent, ReactElement } from 'react'; -import { PlayerOverrides } from '../common/types'; +import { NameWithHighlight, PlayerOverrides } from '../common/types'; +import { highlightColor } from '../common/constants'; + +function Name({ nameWithHighlight }: { nameWithHighlight: NameWithHighlight }) { + if (!nameWithHighlight.highlight) { + return nameWithHighlight.name; + } + + return ( + <> + + {nameWithHighlight.name.substring(0, nameWithHighlight.highlight.start)} + + + {nameWithHighlight.name.substring( + nameWithHighlight.highlight.start, + nameWithHighlight.highlight.end, + )} + + + {nameWithHighlight.name.substring(nameWithHighlight.highlight.end)} + + + ); +} export function DraggableChip({ - displayName, entrantId, + nameWithHighlight, prefix, pronouns, selectedChipData, setSelectedChipData, }: { - displayName: string; entrantId: number; + nameWithHighlight: NameWithHighlight; prefix: string; pronouns: string; selectedChipData: PlayerOverrides; @@ -45,7 +69,7 @@ export function DraggableChip({ ); }; const selected = - selectedChipData.displayName === displayName && + selectedChipData.displayName === nameWithHighlight.name && selectedChipData.entrantId === entrantId; return ( @@ -70,7 +94,7 @@ export function DraggableChip({ > } sx={{ zIndex: (theme) => theme.zIndex.drawer + 2 }} variant={selected ? 'filled' : 'outlined'} /> diff --git a/src/renderer/ManualView.tsx b/src/renderer/ManualView.tsx index fe2d22e..8575056 100644 --- a/src/renderer/ManualView.tsx +++ b/src/renderer/ManualView.tsx @@ -1,23 +1,40 @@ import { Stack } from '@mui/material'; import { DraggableChip } from './DragAndDrop'; -import { PlayerOverrides } from '../common/types'; +import { NameWithHighlight, PlayerOverrides } from '../common/types'; export default function ManualView({ manualNames, + searchSubstr, selectedChipData, setSelectedChipData, }: { manualNames: string[]; + searchSubstr: string; selectedChipData: PlayerOverrides; setSelectedChipData: (newSelectedChipData: PlayerOverrides) => void; }) { + const namesWithHighlights: NameWithHighlight[] = []; + manualNames.forEach((name) => { + if (!searchSubstr) { + namesWithHighlights.push({ name }); + return; + } + + const start = name.toLowerCase().indexOf(searchSubstr.toLowerCase()); + if (start >= 0) { + namesWithHighlights.push({ + highlight: { start, end: start + searchSubstr.length }, + name, + }); + } + }); return ( - {manualNames.map((manualName, i) => ( + {namesWithHighlights.map((nameWithHighlight, i) => ( void; + vlerkMode: boolean; +}) { + const searchInputRef = useRef(); + const [showSearch, setShowSearch] = useState(false); + const clearSearch = () => { + setSearchSubstr(''); + setShowSearch(false); + }; + return ( + <> + {(showSearch || (vlerkMode && mode === Mode.STARTGG)) && ( + + { + setSearchSubstr(event.target.value); + }} + onKeyDown={(event) => { + if (event.key === 'Escape') { + clearSearch(); + } + }} + inputRef={searchInputRef} + size="small" + value={searchSubstr} + InputProps={{ + endAdornment: ( + + + { + clearSearch(); + }} + > + + + + + ), + }} + /> + + )} + { + clearSearch(); + }, + FIND: () => { + setShowSearch(true); + searchInputRef.current?.focus(); + }, + }} + /> + + ); +} diff --git a/src/renderer/SetView.tsx b/src/renderer/SetView.tsx index 284a27b..e5eb0be 100644 --- a/src/renderer/SetView.tsx +++ b/src/renderer/SetView.tsx @@ -2,8 +2,8 @@ import styled from '@emotion/styled'; import { Backup, CheckBox, HourglassTop } from '@mui/icons-material'; import { Box, Stack, Tooltip } from '@mui/material'; import { NameWithHighlight } from '../common/types'; +import { highlightColor } from '../common/constants'; -const HIGHLIGHT_COLOR = '#ffee58'; const EntrantNames = styled(Stack)` flex-grow: 1; min-width: 0; @@ -35,7 +35,7 @@ function EntrantName({ entrantName }: { entrantName: NameWithHighlight }) { return ( {entrantName.name.substring(0, entrantName.highlight.start)} - + {entrantName.name.substring( entrantName.highlight.start, entrantName.highlight.end, diff --git a/src/renderer/StartggView.tsx b/src/renderer/StartggView.tsx index 619b1a5..d9c47af 100644 --- a/src/renderer/StartggView.tsx +++ b/src/renderer/StartggView.tsx @@ -4,21 +4,17 @@ import { CircularProgress, Collapse, IconButton, - InputAdornment, ListItemButton, - TextField, Tooltip, Typography, } from '@mui/material'; import { - Clear, KeyboardArrowDown, KeyboardArrowRight, KeyboardArrowUp, Refresh, } from '@mui/icons-material'; -import { useRef, useState } from 'react'; -import { GlobalHotKeys } from 'react-hotkeys'; +import { useState } from 'react'; import { Event, NameWithHighlight, @@ -29,6 +25,7 @@ import { Tournament, } from '../common/types'; import SetViewInner from './SetView'; +import filterSets from './filterSets'; const Block = styled.div` padding-left: 8px; @@ -40,12 +37,6 @@ const Name = styled.div` white-space: nowrap; `; -type SetWithNames = { - set: Set; - entrant1Names: NameWithHighlight[]; - entrant2Names: NameWithHighlight[]; -}; - function SetView({ set, entrant1Names, @@ -162,59 +153,6 @@ function PhaseGroupView({ setGetting(false); }; - const filterSets = (sets: Set[], substr: string): SetWithNames[] => { - const setsToShow: SetWithNames[] = []; - sets.forEach((set) => { - if (!substr) { - setsToShow.push({ - set, - entrant1Names: set.entrant1Participants.map((participant) => ({ - name: participant.displayName, - })), - entrant2Names: set.entrant2Participants.map((participant) => ({ - name: participant.displayName, - })), - }); - } else { - const entrant1Names: NameWithHighlight[] = []; - const entrant2Names: NameWithHighlight[] = []; - const includeStr = substr.toLowerCase(); - let toShow = false; - set.entrant1Participants.forEach((participant) => { - const start = participant.displayName - .toLowerCase() - .indexOf(includeStr); - if (start < 0) { - entrant1Names.push({ name: participant.displayName }); - } else { - toShow = true; - entrant1Names.push({ - highlight: { start, end: start + includeStr.length }, - name: participant.displayName, - }); - } - }); - set.entrant2Participants.forEach((participant) => { - const start = participant.displayName - .toLowerCase() - .indexOf(includeStr); - if (start < 0) { - entrant2Names.push({ name: participant.displayName }); - } else { - toShow = true; - entrant2Names.push({ - highlight: { start, end: start + includeStr.length }, - name: participant.displayName, - }); - } - }); - if (toShow) { - setsToShow.push({ set, entrant1Names, entrant2Names }); - } - } - }); - return setsToShow; - }; const pendingSetsToShow = filterSets( phaseGroup.sets.pendingSets, searchSubstr, @@ -516,6 +454,7 @@ function EventView({ } export default function StartggView({ + searchSubstr, tournament, vlerkMode, getEvent, @@ -523,6 +462,7 @@ export default function StartggView({ getPhaseGroup, selectSet, }: { + searchSubstr: string; tournament: Tournament; vlerkMode: boolean; getEvent: (id: number) => Promise; @@ -543,51 +483,8 @@ export default function StartggView({ eventSlug: string, ) => void; }) { - const searchInputRef = useRef(null); - const [searchSubstr, setSearchSubstr] = useState(''); - const [showSearch, setShowSearch] = useState(false); - const clearSearch = () => { - setSearchSubstr(''); - setShowSearch(false); - }; - return ( - {(showSearch || vlerkMode) && ( - - { - setSearchSubstr(event.target.value); - }} - onKeyDown={(event) => { - if (event.key === 'Escape') { - clearSearch(); - } - }} - inputRef={searchInputRef} - size="small" - value={searchSubstr} - InputProps={{ - endAdornment: ( - - - { - clearSearch(); - }} - > - - - - - ), - }} - /> - - )} {tournament.events.map((event) => ( ))} - { - clearSearch(); - }, - FIND: () => { - setShowSearch(true); - searchInputRef.current?.focus(); - }, - }} - /> ); } diff --git a/src/renderer/filterSets.ts b/src/renderer/filterSets.ts new file mode 100644 index 0000000..9709603 --- /dev/null +++ b/src/renderer/filterSets.ts @@ -0,0 +1,54 @@ +import { NameWithHighlight, Set, SetWithNames } from '../common/types'; + +export default function filterSets( + sets: Set[], + substr: string, +): SetWithNames[] { + const setsToShow: SetWithNames[] = []; + sets.forEach((set) => { + if (!substr) { + setsToShow.push({ + set, + entrant1Names: set.entrant1Participants.map((participant) => ({ + name: participant.displayName, + })), + entrant2Names: set.entrant2Participants.map((participant) => ({ + name: participant.displayName, + })), + }); + } else { + const entrant1Names: NameWithHighlight[] = []; + const entrant2Names: NameWithHighlight[] = []; + const includeStr = substr.toLowerCase(); + let toShow = false; + set.entrant1Participants.forEach((participant) => { + const start = participant.displayName.toLowerCase().indexOf(includeStr); + if (start < 0) { + entrant1Names.push({ name: participant.displayName }); + } else { + toShow = true; + entrant1Names.push({ + highlight: { start, end: start + includeStr.length }, + name: participant.displayName, + }); + } + }); + set.entrant2Participants.forEach((participant) => { + const start = participant.displayName.toLowerCase().indexOf(includeStr); + if (start < 0) { + entrant2Names.push({ name: participant.displayName }); + } else { + toShow = true; + entrant2Names.push({ + highlight: { start, end: start + includeStr.length }, + name: participant.displayName, + }); + } + }); + if (toShow) { + setsToShow.push({ set, entrant1Names, entrant2Names }); + } + } + }); + return setsToShow; +}