Skip to content

Commit 49b0354

Browse files
authored
Stream query (#4)
* Query with pagination * Filter by stream name
1 parent 63bb61f commit 49b0354

File tree

2 files changed

+206
-98
lines changed

2 files changed

+206
-98
lines changed

src/renderer/common/StartggQueries.tsx

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,52 @@ export const GET_SETS_AT_STATION = gql`
4040
}
4141
`;
4242

43+
export const GET_SETS_AT_STREAM_STATION = gql`
44+
query SetsAtStation($eventId: String!, $page: Int) {
45+
event(slug: $eventId) {
46+
id
47+
name
48+
tournament {
49+
name
50+
}
51+
sets(page: $page, perPage: 5) {
52+
pageInfo {
53+
totalPages
54+
}
55+
nodes {
56+
id
57+
station {
58+
id
59+
number
60+
}
61+
slots {
62+
id
63+
entrant {
64+
id
65+
name
66+
}
67+
}
68+
startedAt
69+
completedAt
70+
fullRoundText
71+
games {
72+
selections {
73+
selectionValue
74+
selectionType
75+
entrant {
76+
id
77+
}
78+
}
79+
}
80+
stream {
81+
streamName
82+
}
83+
}
84+
}
85+
}
86+
}
87+
`
88+
4389
export const GET_ALL_SETS_AT_EVENT = gql`
4490
query SetsAtStation($eventId: String!) {
4591
event(slug: $eventId) {

src/renderer/components/VideoSearch.tsx

Lines changed: 160 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import { createRef, useState, useEffect, ReactInstance } from 'react';
22
import { Button, Box, TextField } from '@mui/material';
3-
import { GET_SETS_AT_STATION } from 'renderer/common/StartggQueries';
4-
import { useLazyQuery } from '@apollo/client';
3+
import {
4+
GET_SETS_AT_STATION,
5+
GET_SETS_AT_STREAM_STATION,
6+
} from 'renderer/common/StartggQueries';
7+
import { useLazyQuery, useApolloClient } from '@apollo/client';
58
import { useTheme } from '@mui/material/styles';
69
import { PropagateLoader } from 'react-spinners';
710
import { useNavigate } from 'react-router-dom';
@@ -20,16 +23,22 @@ export interface VODMetadata {
2023
tournamentName: string;
2124
}
2225

26+
function isNumber(value: string | number): boolean {
27+
return value != null && value !== '' && !isNaN(Number(value.toString()));
28+
}
29+
2330
const RetrieveSets = (
2431
eventId: string,
2532
vodUrl: string,
26-
stationNumber: number,
33+
station: string,
2734
buttonDisabled: boolean,
28-
setButtonDisabled: React.Dispatch<React.SetStateAction<boolean>>,
35+
setButtonDisabled: React.Dispatch<React.SetStateAction<boolean>>
2936
): JSX.Element => {
3037
const navigate = useNavigate();
3138
const theme = useTheme();
39+
const client = useApolloClient();
3240
let options = {};
41+
let pageNum = 1;
3342
let apikey = window.electron.store.get('apikey');
3443
if (apikey != '') {
3544
options = {
@@ -44,98 +53,122 @@ const RetrieveSets = (
4453
const [waiting, setWaiting] = useState(false);
4554
const [errorMessage, setErrorMessage] = useState('');
4655
const [errorOpen, setErrorOpen] = useState(false);
56+
let totalData: any[] = [];
4757

48-
const [getSets, { loading, error, data }] = useLazyQuery(
49-
GET_SETS_AT_STATION,
50-
options
51-
);
52-
53-
useEffect(() => {
54-
if (loading || waiting) {
55-
setButtonDisabled(true)
56-
} else if (error) {
57-
setButtonDisabled(false)
58-
}
59-
})
58+
let query;
59+
isNumber(station)
60+
? (query = GET_SETS_AT_STATION)
61+
: (query = GET_SETS_AT_STREAM_STATION);
6062

61-
useEffect(() => {
62-
if (data) {
63-
window.electron.ipcRenderer
64-
.retrieveVideoInformation({ vodUrl: vodUrl })
65-
.then((timestamp) => {
66-
let characterMap = window.electron.store.get('characterMap');
67-
const formattedSets = data.event.sets.nodes.map((set: any) => {
68-
let characterStrings = ['', ''];
69-
let characters = ['Random Character', 'Random Character'];
70-
if (set.games != null) {
71-
let characterArrays: string[][] = [[], []];
72-
for (const game of set.games) {
73-
if (game.selections != null) {
74-
let entrantIds = [
75-
set.slots[0].entrant.id,
76-
set.slots[1].entrant.id,
77-
];
78-
for (let i = 0; i < 2; i++) {
79-
for (let j = 0; j < 2; j++) {
80-
if (game.selections[j].entrant.id == entrantIds[i]) {
81-
let character: string =
82-
characterMap[game.selections[j].selectionValue];
83-
characterArrays[i].indexOf(character) === -1
84-
? characterArrays[i].push(character)
85-
: null;
86-
}
63+
const formatSetData = (responseData: any) => {
64+
window.electron.ipcRenderer
65+
.retrieveVideoInformation({ vodUrl: vodUrl })
66+
.then((timestamp) => {
67+
let characterMap = window.electron.store.get('characterMap');
68+
const formattedSets = responseData.event.sets.nodes.map((set: any) => {
69+
let characterStrings = ['', ''];
70+
let characters = ['Random Character', 'Random Character'];
71+
if (set.games != null) {
72+
let characterArrays: string[][] = [[], []];
73+
for (const game of set.games) {
74+
if (game.selections != null) {
75+
let entrantIds = [
76+
set.slots[0].entrant.id,
77+
set.slots[1].entrant.id,
78+
];
79+
for (let i = 0; i < 2; i++) {
80+
for (let j = 0; j < 2; j++) {
81+
if (game.selections[j].entrant.id == entrantIds[i]) {
82+
let character: string =
83+
characterMap[game.selections[j].selectionValue];
84+
characterArrays[i].indexOf(character) === -1
85+
? characterArrays[i].push(character)
86+
: null;
8787
}
8888
}
8989
}
9090
}
91-
characterStrings[0] = ' (' + characterArrays[0].join(', ') + ')';
92-
characterStrings[1] = ' (' + characterArrays[1].join(', ') + ')';
93-
characters[0] = characterArrays[0][0]
94-
characters[1] = characterArrays[1][0]
9591
}
96-
let metadata: VODMetadata = {
97-
title:
98-
set.slots[0].entrant.name.split('|').pop().trim() +
99-
characterStrings[0] +
100-
' vs ' +
101-
set.slots[1].entrant.name.split('|').pop().trim() +
102-
characterStrings[1] +
103-
' - ' +
104-
set.fullRoundText +
105-
' - ' +
106-
data.event.tournament.name,
107-
startTime: new Date((set.startedAt - timestamp) * 1000)
108-
.toISOString()
109-
.slice(11, 19),
110-
endTime: new Date((set.completedAt - timestamp) * 1000)
111-
.toISOString()
112-
.slice(11, 19),
113-
download: true,
114-
player1: set.slots[0].entrant.name.split('|').pop().trim(),
115-
player2: set.slots[1].entrant.name.split('|').pop().trim(),
116-
character1: characters[0],
117-
character2: characters[1],
118-
tournamentName: data.event.tournament.name,
119-
};
120-
return metadata;
121-
});
122-
setWaiting(false);
123-
console.log("All sets", formattedSets)
124-
navigate('/SetsView', {
125-
state: {
126-
sets: formattedSets,
127-
vodUrl: vodUrl,
128-
tournamentName: data.event.tournament.name,
129-
},
130-
});
131-
}).catch((err) => {
132-
setErrorMessage("Error retrieving sets: " + err.message);
133-
setErrorOpen(true);
134-
setWaiting(false);
92+
characterStrings[0] = ' (' + characterArrays[0].join(', ') + ')';
93+
characterStrings[1] = ' (' + characterArrays[1].join(', ') + ')';
94+
characters[0] = characterArrays[0][0];
95+
characters[1] = characterArrays[1][0];
96+
}
97+
let metadata: VODMetadata = {
98+
title:
99+
set.slots[0].entrant.name.split('|').pop().trim() +
100+
characterStrings[0] +
101+
' vs ' +
102+
set.slots[1].entrant.name.split('|').pop().trim() +
103+
characterStrings[1] +
104+
' - ' +
105+
set.fullRoundText +
106+
' - ' +
107+
responseData.event.tournament.name,
108+
startTime: new Date((set.startedAt - timestamp) * 1000)
109+
.toISOString()
110+
.slice(11, 19),
111+
endTime: new Date((set.completedAt - timestamp) * 1000)
112+
.toISOString()
113+
.slice(11, 19),
114+
download: true,
115+
player1: set.slots[0].entrant.name.split('|').pop().trim(),
116+
player2: set.slots[1].entrant.name.split('|').pop().trim(),
117+
character1: characters[0],
118+
character2: characters[1],
119+
tournamentName: responseData.event.tournament.name,
120+
};
121+
return metadata;
135122
});
123+
setWaiting(false);
124+
console.log('All sets', formattedSets);
125+
navigate('/SetsView', {
126+
state: {
127+
sets: formattedSets,
128+
vodUrl: vodUrl,
129+
tournamentName: responseData.event.tournament.name,
130+
},
131+
});
132+
})
133+
.catch((err) => {
134+
setErrorMessage('Error retrieving sets: ' + err.message);
135+
setErrorOpen(true);
136+
setWaiting(false);
137+
});
138+
};
139+
140+
const [getSets, { loading, error, data }] = useLazyQuery(query, options);
141+
142+
useEffect(() => {
143+
if (loading || waiting) {
144+
setButtonDisabled(true);
145+
} else if (error) {
146+
setButtonDisabled(false);
147+
}
148+
});
149+
150+
useEffect(() => {
151+
if (data && isNumber(station)) {
152+
formatSetData(data);
136153
}
137154
}, [data]);
138155

156+
const getNextPage = async () => {
157+
await client
158+
.query({
159+
query: GET_SETS_AT_STREAM_STATION,
160+
variables: { eventId: eventId, page: pageNum },
161+
})
162+
.then((res: any) => {
163+
totalData.push(
164+
res.data.event.sets.nodes.filter(
165+
(set: any) => set.stream && set.stream.streamName == station
166+
)
167+
);
168+
pageNum++;
169+
});
170+
};
171+
139172
return (
140173
<Box
141174
sx={{
@@ -151,9 +184,31 @@ const RetrieveSets = (
151184
variant="contained"
152185
onClick={() => {
153186
setWaiting(true);
154-
getSets({
155-
variables: { eventId: eventId, stationNumbers: [stationNumber] },
156-
});
187+
if (isNumber(station)) {
188+
getSets({
189+
variables: { eventId: eventId, stationNumbers: [station] },
190+
});
191+
} else {
192+
client
193+
.query({
194+
query: GET_SETS_AT_STREAM_STATION,
195+
variables: { eventId: eventId, page: pageNum },
196+
})
197+
.then(async (res: any) => {
198+
totalData.push(
199+
res.data.event.sets.nodes.filter(
200+
(set: any) => set.stream && set.stream.streamName == station
201+
)
202+
);
203+
pageNum++;
204+
while (pageNum < res.data.event.sets.pageInfo.totalPages) {
205+
await getNextPage();
206+
}
207+
let dataCopy = structuredClone(res.data);
208+
dataCopy.event.sets.nodes = totalData.flat();
209+
formatSetData(dataCopy);
210+
});
211+
}
157212
}}
158213
sx={{ marginBottom: '25px', width: '100%' }}
159214
disabled={buttonDisabled}
@@ -172,7 +227,7 @@ const VideoSearch = () => {
172227
const [vodUrl, setVodUrl] = useState('');
173228
const [eventId, setEventId] = useState('');
174229
const [buttonDisabled, setButtonDisabled] = useState(true);
175-
const [stationNumber, setStationNumber] = useState(0);
230+
const [station, setStation] = useState('');
176231
const [urlError, setUrlError] = useState(false);
177232
const [slugError, setSlugError] = useState(false);
178233

@@ -184,7 +239,7 @@ const VideoSearch = () => {
184239
if (
185240
eventId != '' &&
186241
vodUrl != '' &&
187-
stationNumber != 0 &&
242+
station != '' &&
188243
window.electron.store.get('apikey') &&
189244
!urlError &&
190245
!slugError
@@ -196,9 +251,11 @@ const VideoSearch = () => {
196251
});
197252

198253
function isValidURL(url: string) {
199-
var res = url.match(/(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g);
200-
return (res !== null)
201-
};
254+
var res = url.match(
255+
/(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g
256+
);
257+
return res !== null;
258+
}
202259

203260
useEffect(() => {
204261
if (isValidURL(vodUrl) || vodUrl == '') {
@@ -246,14 +303,19 @@ const VideoSearch = () => {
246303
/>
247304
<TextField
248305
className="textfield"
249-
label="Stream Station Number"
250-
type="number"
306+
label="Stream Station"
251307
variant="filled"
252-
onChange={(event) => setStationNumber(parseInt(event.target.value))}
253-
helperText="The station number the stream is assigned to."
308+
onChange={(event) => setStation(event.target.value)}
309+
helperText="Can be name of twitch stream or a number."
254310
sx={{ marginBottom: '25px' }}
255311
/>
256-
{RetrieveSets(eventId, vodUrl, stationNumber, buttonDisabled, setButtonDisabled)}
312+
{RetrieveSets(
313+
eventId,
314+
vodUrl,
315+
station,
316+
buttonDisabled,
317+
setButtonDisabled
318+
)}
257319
</Box>
258320
);
259321
};

0 commit comments

Comments
 (0)