Skip to content

Commit

Permalink
Url encode templated values by default
Browse files Browse the repository at this point in the history
(cherry picked from commit 40692ad)
  • Loading branch information
kunyavskiy authored and Kostya Bats committed Mar 1, 2024
1 parent 2f7dcae commit 0d1a03b
Show file tree
Hide file tree
Showing 11 changed files with 158 additions and 110 deletions.
2 changes: 1 addition & 1 deletion .run/overlay2-start-local.run.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</scripts>
<node-interpreter value="project" />
<envs>
<env name="VITE_WEBSOCKET_URL" value="ws://localhost:8080/api/overlay" />
<env name="VITE_WEBSOCKET_URL" value="wss://overlay.challenge.live.ismagilov.de/api/overlay" />
</envs>
<method v="2" />
</configuration>
Expand Down
160 changes: 76 additions & 84 deletions config/icpc-northern-eurasia/nef-2020-2021/onsite/advanced.json
Original file line number Diff line number Diff line change
@@ -1,91 +1,83 @@
{
"startTime": "2021-04-04 10:00:00 +03:00",
"freezeTimeSeconds": 14400,
"startTime": "now", "freezeTimeSeconds": 14400,
"teamOverrideTemplate": {
"medias": {
"screen": {
"type": "Video",
"url": "http://192.168.18.38:80{teamId}"
},
"camera": {
"type": "Video",
"url": "http://192.168.18.38:81{teamId}"
"type": "HLSVideo",
"url": "https://coach.icpc.asia/{ip}/stream.m3u8",
"jwtToken": "put token here"
}, "achievement": {
"type": "Photo",
"url": "http://localhost:8080/api/overlay/svgAchievement/generic_achivements.svg?University={orgDisplayName}&TeamName={funnyName}&Contestants={contestants}"
}
}
},
"teamOverrides": {
"486860": {"shortname": "SPb HSE 1: Lemon Tree", "groups": ["SPb HSE", "SPb"]},
"486861": {"shortname": "SPb ITMO: Insert your name", "groups": ["SPb ITMO", "SPb"]},
"486862": {"shortname": "HSE: Overtrained", "groups": ["HSE", "Moscow"]},
"486863": {"shortname": "HSE: Sleeveless shorts", "groups": ["HSE", "Moscow"]},
"486864": {"shortname": "Moscow IPT: Malaya Bronnaya", "groups": ["Moscow IPT", "Moscow"]},
"486865": {"shortname": "St. Petersburg SU: Cheba Kings", "groups": ["St. Petersburg SU", "SPb"]},
"486866": {"shortname": "St. Petersburg SU: LOUD Enough", "groups": ["St. Petersburg SU", "SPb"]},
"486867": {"shortname": "St. Petersburg SU: 25", "groups": ["St. Petersburg SU", "SPb"]},
"486868": {"shortname": "Belarusian SU: 1", "groups": ["Belarusian SU", "Belarus"]},
"486869": {"shortname": "Moscow IPT: LinkCat", "groups": ["Moscow IPT", "Moscow"]},
"486870": {"shortname": "St. Petersburg SU: glasses", "groups": ["St. Petersburg SU", "SPb"]},
"486871": {"shortname": "Belarusian SU: 3", "groups": ["Belarusian SU", "Belarus"]},
"486872": {"shortname": "SPb ITMO: pengzoo", "groups": ["SPb ITMO", "SPb"]},
"486873": {"shortname": "Moscow SU: Nonames", "groups": ["Moscow SU", "Moscow"]},
"486874": {"shortname": "St. Petersburg SU: computer", "groups": ["St. Petersburg SU", "SPb"]},
"486875": {"shortname": "SPb ITMO: 25", "groups": ["SPb ITMO", "SPb"]},
"486876": {"shortname": "IITU: 1", "groups": ["IITU", "Other"]},
"486877": {"shortname": "Saratov SU: N", "groups": ["Saratov SU", "Other"]},
"486878": {"shortname": "Tbilisi Freeuni: 1", "groups": ["Tbilisi Freeuni", "Other"]},
"486879": {"shortname": "Innopolis U: V", "groups": ["Innopolis U", "Other"]},
"486880": {"shortname": "Moscow IPT: 42NA", "groups": ["Moscow IPT", "Moscow"]},
"486881": {"shortname": "Belarusian SU: 2", "groups": ["Belarusian SU", "Belarus"]},
"486882": {"shortname": "SPb HSE: VLADBUK", "groups": ["SPb HSE", "SPb"]},
"486883": {"shortname": "SPb ITMO: Unexpected Value", "groups": ["SPb ITMO", "SPb"]},
"486884": {"shortname": "Yerevan SU: New Era", "groups": ["Yerevan SU", "Other"]},
"486885": {"shortname": "Moscow SU: boloto", "groups": ["Moscow SU", "Moscow"]},
"486886": {"shortname": "HSE: Smetana fans", "groups": ["HSE", "Moscow"]},
"486887": {"shortname": "Belarusian SUIR: #2", "groups": ["Belarusian SUIR", "Belarus"]},
"486888": {"shortname": "SDU 1: Qataly degen ne?", "groups": ["SDU", "Other"]},
"486889": {"shortname": "Tolyatti SU: A", "groups": ["Tolyatti SU", "Other"]},
"486890": {"shortname": "Penza SU: E", "groups": ["Penza SU", "Other"]},
"486891": {"shortname": "Kazan FU: AJ", "groups": ["Kazan FU", "Other"]},
"486892": {"shortname": "Ural FU: CheezeKEK", "groups": ["Ural FU", "Other"]},
"486893": {"shortname": "Kazakh-British TU: 1", "groups": ["Kazakh-British TU", "Kazakh"]},
"486894": {"shortname": "Nazarbayev U: 1", "groups": ["Nazarbayev U", "Kazakh"]},
"486895": {"shortname": "Izhevsk STU: Ne nado dumat", "groups": ["Izhevsk STU", "Other"]},
"486896": {"shortname": "NNSU: 1", "groups": ["NNSU", "Other"]},
"486897": {"shortname": "Moscow AI: #3", "groups": ["Moscow AI", "Moscow"]},
"486898": {"shortname": "Baltic FU 1: La Squadra", "groups": ["Baltic FU", "Other"]},
"486899": {"shortname": "Nizhny Novgorod HSE: 1", "groups": ["Nizhny Novgorod HSE", "Other"]},
"486900": {"shortname": "Siberian SUTI: Santa-Monika", "groups": ["Siberian SUTI", "Other"]},
"486901": {"shortname": "Latvia: LU 1", "groups": ["Latvia", "Other"]},
"486902": {"shortname": "TUIT: MathX", "groups": ["TUIT", "Other"]},
"486903": {"shortname": "MISiS: ToMcKoE 4aellutuE", "groups": ["MISiS", "Moscow"]},
"486904": {"shortname": "YaroslavlSU 1: Crblnge team", "groups": ["YaroslavlSU", "Other"]},
"486905": {"shortname": "Novosibirsk SU 1: Mufasa", "groups": ["Novosibirsk SU", "Other"]},
"486906": {"shortname": "AUCA: #1 Turtles", "groups": ["AUCA", "Other"]},
"486907": {"shortname": "MEPhI: SoZo", "groups": ["MEPhI", "Moscow"]},
"486908": {"shortname": "Ufa SATU: Electric Funeral", "groups": ["Ufa SATU", "Other"]},
"486910": {"shortname": "Budenny Mil Telecom A: 1", "groups": ["Budenny Mil Telecom A", "SPb"]}
},
"problemOverrides": {
"A":{"color":"#e6194B"},
"B":{"color":"#3cb44b"},
"C":{"color":"#ffe119"},
"D":{"color":"#4363d8"},
"E":{"color":"#f58231"},
"F":{"color":"#42d4f4"},
"G":{"color":"#f032e6"},
"H":{"color":"#fabed4"},
"I":{"color":"#469990"},
"J":{"color":"#dcbeff"},
"K":{"color":"#9A6324"}
},
"awardsSettings": {
"medals": [
{"id": "gold-medal", "citation": "Gold Medal","color": "GOLD", "maxRank": 4},
{"id": "silver-medal", "citation": "Solver Medal","color": "SILVER", "maxRank": 8},
{"id": "bronze-medal", "citation": "Bronze Medal","color": "BRONZE", "maxRank": 12}
]
},
"scoreboardOverrides": {
"penaltyPerWrongAttempt": 20
}
}, "teamNameRegexes": {
"customFields": {
"funnyName": {"[^:]*: (.*) \\([^)]*\\)": "$1"},
"contestants": {
"[^:]*: (.*) \\(([^)]*)\\)": "$2"
}
}, "organizationRegex": {"^([^:]*).*": "$1"}
}, "teamOverrides": {
"486860": {"shortname": "SPb HSE 1: Lemon Tree", "groups": ["SPb HSE", "SPb"]},
"486861": {"shortname": "SPb ITMO: Insert your name", "groups": ["SPb ITMO", "SPb"]},
"486862": {"shortname": "HSE: Overtrained", "groups": ["HSE", "Moscow"]},
"486863": {"shortname": "HSE: Sleeveless shorts", "groups": ["HSE", "Moscow"]},
"486864": {"shortname": "Moscow IPT: Malaya Bronnaya", "groups": ["Moscow IPT", "Moscow"]},
"486865": {"shortname": "St. Petersburg SU: Cheba Kings", "groups": ["St. Petersburg SU", "SPb"]},
"486866": {"shortname": "St. Petersburg SU: LOUD Enough", "groups": ["St. Petersburg SU", "SPb"]},
"486867": {"shortname": "St. Petersburg SU: 25", "groups": ["St. Petersburg SU", "SPb"]},
"486868": {"shortname": "Belarusian SU: 1", "groups": ["Belarusian SU", "Belarus"]},
"486869": {"shortname": "Moscow IPT: LinkCat", "groups": ["Moscow IPT", "Moscow"]},
"486870": {"shortname": "St. Petersburg SU: glasses", "groups": ["St. Petersburg SU", "SPb"]},
"486871": {"shortname": "Belarusian SU: 3", "groups": ["Belarusian SU", "Belarus"]},
"486872": {"shortname": "SPb ITMO: pengzoo", "groups": ["SPb ITMO", "SPb"]},
"486873": {"shortname": "Moscow SU: Nonames", "groups": ["Moscow SU", "Moscow"]},
"486874": {"shortname": "St. Petersburg SU: computer", "groups": ["St. Petersburg SU", "SPb"]},
"486875": {"shortname": "SPb ITMO: 25", "groups": ["SPb ITMO", "SPb"]},
"486876": {"shortname": "IITU: 1", "groups": ["IITU", "Other"]},
"486877": {"shortname": "Saratov SU: N", "groups": ["Saratov SU", "Other"]},
"486878": {"shortname": "Tbilisi Freeuni: 1", "groups": ["Tbilisi Freeuni", "Other"]},
"486879": {"shortname": "Innopolis U: V", "groups": ["Innopolis U", "Other"]},
"486880": {"shortname": "Moscow IPT: 42NA", "groups": ["Moscow IPT", "Moscow"]},
"486881": {"shortname": "Belarusian SU: 2", "groups": ["Belarusian SU", "Belarus"]},
"486882": {"shortname": "SPb HSE: VLADBUK", "groups": ["SPb HSE", "SPb"]},
"486883": {"shortname": "SPb ITMO: Unexpected Value", "groups": ["SPb ITMO", "SPb"]},
"486884": {"shortname": "Yerevan SU: New Era", "groups": ["Yerevan SU", "Other"]},
"486885": {"shortname": "Moscow SU: boloto", "groups": ["Moscow SU", "Moscow"]},
"486886": {"shortname": "HSE: Smetana fans", "groups": ["HSE", "Moscow"]},
"486887": {"shortname": "Belarusian SUIR: #2", "groups": ["Belarusian SUIR", "Belarus"]},
"486888": {"shortname": "SDU 1: Qataly degen ne?", "groups": ["SDU", "Other"]},
"486889": {"shortname": "Tolyatti SU: A", "groups": ["Tolyatti SU", "Other"]},
"486890": {"shortname": "Penza SU: E", "groups": ["Penza SU", "Other"]},
"486891": {"shortname": "Kazan FU: AJ", "groups": ["Kazan FU", "Other"]},
"486892": {"shortname": "Ural FU: CheezeKEK", "groups": ["Ural FU", "Other"]},
"486893": {"shortname": "Kazakh-British TU: 1", "groups": ["Kazakh-British TU", "Kazakh"]},
"486894": {"shortname": "Nazarbayev U: 1", "groups": ["Nazarbayev U", "Kazakh"]},
"486895": {"shortname": "Izhevsk STU: Ne nado dumat", "groups": ["Izhevsk STU", "Other"]},
"486896": {"shortname": "NNSU: 1", "groups": ["NNSU", "Other"]},
"486897": {"shortname": "Moscow AI: #3", "groups": ["Moscow AI", "Moscow"]},
"486898": {"shortname": "Baltic FU 1: La Squadra", "groups": ["Baltic FU", "Other"]},
"486899": {"shortname": "Nizhny Novgorod HSE: 1", "groups": ["Nizhny Novgorod HSE", "Other"]},
"486900": {"shortname": "Siberian SUTI: Santa-Monika", "groups": ["Siberian SUTI", "Other"]},
"486901": {"shortname": "Latvia: LU 1", "groups": ["Latvia", "Other"]},
"486902": {"shortname": "TUIT: MathX", "groups": ["TUIT", "Other"]},
"486903": {"shortname": "MISiS: ToMcKoE 4aellutuE", "groups": ["MISiS", "Moscow"]},
"486904": {"shortname": "YaroslavlSU 1: Crblnge team", "groups": ["YaroslavlSU", "Other"]},
"486905": {"shortname": "Novosibirsk SU 1: Mufasa", "groups": ["Novosibirsk SU", "Other"]},
"486906": {"shortname": "AUCA: #1 Turtles", "groups": ["AUCA", "Other"]},
"486907": {"shortname": "MEPhI: SoZo", "groups": ["MEPhI", "Moscow"]},
"486908": {"shortname": "Ufa SATU: Electric Funeral", "groups": ["Ufa SATU", "Other"]},
"486910": {"shortname": "Budenny Mil Telecom A: 1", "groups": ["Budenny Mil Telecom A", "SPb"]}
}, "problemOverrides": {
"A": {"color": "#e6194B"}, "B": {"color": "#3cb44b"}, "C": {"color": "#ffe119"}, "D": {"color": "#4363d8"},
"E": {"color": "#f58231"}, "F": {"color": "#42d4f4"}, "G": {"color": "#f032e6"}, "H": {"color": "#fabed4"},
"I": {"color": "#469990"}, "J": {"color": "#dcbeff"}, "K": {"color": "#9A6324"}
}, "awardsSettings": {
"medals": [
{"id": "gold-medal", "citation": "Gold Medal", "color": "GOLD", "maxRank": 4},
{"id": "silver-medal", "citation": "Solver Medal", "color": "SILVER", "maxRank": 8},
{"id": "bronze-medal", "citation": "Bronze Medal", "color": "BRONZE", "maxRank": 12}
]
}, "scoreboardOverrides": {"penaltyPerWrongAttempt": 20}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"type":"pcms",
"url":"https://nerc.itmo.ru/archive/2020/standings.xml"
"url":"https://nerc.itmo.ru/archive/2020/standings.xml",
"emulation": {"startTime": "now", "speed": 10}
}

14 changes: 7 additions & 7 deletions config/vkoshp/2023/advanced.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,13 @@
"screen": {
"type": "WebRTCGrabberConnection",
"peerName": "{grabberPeerName}",
"url": "{grabberUrl}",
"url": "http://{grabberIp}:8080",
"streamType": "desktop",
"credential": "live"
},
"camera": {
"type": "WebRTCGrabberConnection",
"url": "{grabberUrl}",
"url": "http://{grabberIp}:8080",
"peerName": "{grabberPeerName}",
"streamType": "webcam",
"credential": "live"
Expand Down Expand Up @@ -125,11 +125,11 @@
"nsk(\\d\\d\\d)": "N$1",
"kaz(\\d\\d\\d)": "K$1"
},
"grabberUrl": {
"spb(\\d\\d\\d)": "http://192.168.0.112:8000",
"geo(\\d\\d\\d)": "http://192.168.0.175:8000",
"nsk(\\d\\d\\d)": "http://192.168.0.175:8000",
"kaz(\\d\\d\\d)": "http://192.168.0.175:8000"
"grabberIp": {
"spb(\\d\\d\\d)": "192.168.0.112",
"geo(\\d\\d\\d)": "192.168.0.175",
"nsk(\\d\\d\\d)": "192.168.0.175",
"kaz(\\d\\d\\d)": "192.168.0.175"
}
},
"groupRegex": {
Expand Down
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ ktor-client-auth = { version.ref = "ktor", group = "io.ktor", name = "ktor
ktor-client-contentNegotiation = { version.ref = "ktor", group = "io.ktor", name = "ktor-client-content-negotiation" }

ktor-serialization-kotlinx-json = { version.ref = "ktor", group = "io.ktor", name = "ktor-serialization-kotlinx-json" }
ktor-http = { version.ref = "ktor", group = "io.ktor", name = "ktor-http" }

ktor-server-auth = { version.ref = "ktor", group = "io.ktor", name = "ktor-server-auth" }
ktor-server-autoHeadResponse = { version.ref = "ktor", group = "io.ktor", name = "ktor-server-auto-head-response" }
Expand Down
1 change: 1 addition & 0 deletions src/cds/core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ dependencies {
implementation(projects.cds.utils)
implementation(libs.kotlin.reflect)
implementation(libs.kotlinx.serialization.json5)
implementation(libs.ktor.http)
ksp(projects.ksp)
compileOnly(projects.ksp)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

package org.icpclive.cds.adapters

import io.ktor.http.*
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.*
Expand All @@ -21,22 +22,31 @@ private data object Trigger : AdvancedAdapterEvent

internal object AdvancedPropertiesAdapter

private val templateRegex = kotlin.text.Regex("\\{([a-z0-9A-Z_-]*)}")
private val templateRegex = kotlin.text.Regex("\\{(!?[a-z0-9A-Z_-]*)}")

private fun String.applyTemplate(valueProvider: (String) -> String?) =
replace(templateRegex) { valueProvider(it.groups[1]!!.value) ?: it.value }

private fun urlEncoded(block: (String) -> String?) : (String) -> String? = l@{
block(it.removePrefix("!"))?.run {
if (it.startsWith("!"))
this
else
encodeURLParameter()
}
}

private fun MediaType.applyTemplate(valueProvider: (String) -> String?) = when (this) {
is MediaType.Photo -> copy(url = url.applyTemplate(valueProvider))
is MediaType.Video -> copy(url = url.applyTemplate(valueProvider))
is MediaType.Photo -> copy(url = url.applyTemplate(urlEncoded(valueProvider)))
is MediaType.Video -> copy(url = url.applyTemplate(urlEncoded(valueProvider)))
is MediaType.HLSVideo -> copy(
url = url.applyTemplate(valueProvider),
url = url.applyTemplate(urlEncoded(valueProvider)),
jwtToken = jwtToken?.applyTemplate(valueProvider)
)
is MediaType.Object -> copy(url = url.applyTemplate(valueProvider))
is MediaType.WebRTCProxyConnection -> copy(url = url.applyTemplate(valueProvider))
is MediaType.Object -> copy(url = url.applyTemplate(urlEncoded(valueProvider)))
is MediaType.WebRTCProxyConnection -> copy(url = url.applyTemplate(urlEncoded(valueProvider)))
is MediaType.WebRTCGrabberConnection -> copy(
url = url.applyTemplate(valueProvider),
url = url.applyTemplate(urlEncoded(valueProvider)),
peerName = peerName.applyTemplate(valueProvider),
credential = credential?.applyTemplate(valueProvider)
)
Expand Down Expand Up @@ -143,15 +153,20 @@ private fun <K, V> mergeMaps(original: Map<K, V>, override: Map<K, V?>) = buildM
private fun TeamOverrideTemplate.instantiateTemplate(
teams: List<TeamInfo>,
valueProvider: TeamInfo.(String) -> String?,
) = teams.associate {
it.contestSystemId to TeamInfoOverride(
hashTag = hashTag?.applyTemplate { name -> it.valueProvider(name) },
fullName = fullName?.applyTemplate { name -> it.valueProvider(name) },
displayName = displayName?.applyTemplate { name -> it.valueProvider(name) },
medias = medias?.mapValues { (_, v) -> v?.applyTemplate { name -> it.valueProvider(name) } }
)
) = teams.associate { team ->
team.contestSystemId to instantiateTemplate { team.valueProvider(it) }
}

internal fun TeamOverrideTemplate.instantiateTemplate(
valueProvider: (String) -> String?,
): TeamInfoOverride = TeamInfoOverride(
hashTag = hashTag?.applyTemplate(valueProvider),
fullName = fullName?.applyTemplate(valueProvider),
displayName = displayName?.applyTemplate(valueProvider),
medias = medias?.mapValues { (_, v) -> v?.applyTemplate(valueProvider) }
)


private fun List<TeamInfo>.filterNotSubmitted(show: Boolean?, submittedTeams: Set<Int>) = if (show != false) {
this
} else {
Expand Down
38 changes: 38 additions & 0 deletions src/cds/core/src/test/kotlin/TeamInfoOverrideTemplateTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import org.icpclive.cds.adapters.instantiateTemplate
import org.icpclive.cds.api.MediaType
import org.icpclive.cds.api.TeamMediaType
import org.icpclive.cds.tunning.TeamOverrideTemplate
import kotlin.test.*

object TeamInfoOverrideTemplateTest {
private fun TeamOverrideTemplate.instantiate(map: Map<String, String>) = instantiateTemplate {
map[it]
}

@Test
fun `check url encodes`() {
val template = TeamOverrideTemplate(
displayName = "{teamName}",
medias = mapOf(
TeamMediaType.CAMERA to MediaType.Photo("http://photos-server/{teamName}"),
TeamMediaType.REACTION_VIDEO to MediaType.WebRTCGrabberConnection(
url = "{!grabberUrl}",
peerName = "{teamName}",
streamType = "",
credential = null
)
)
)
val teamName = "Team name with spaces & other / strange : symbols?"
val teamReplaced = "Team%20name%20with%20spaces%20%26%20other%20%2F%20strange%20%3A%20symbols%3F"
val url = "http://this-should-not-be-replaced:12345/url"
val instantiated = template.instantiate(mapOf(
"teamName" to teamName,
"grabberUrl" to url
))
assertEquals(teamName, instantiated.displayName)
assertEquals("http://photos-server/${teamReplaced}", (instantiated.medias?.get(TeamMediaType.CAMERA) as MediaType.Photo).url)
assertEquals(url, (instantiated.medias?.get(TeamMediaType.REACTION_VIDEO) as MediaType.WebRTCGrabberConnection).url)
assertEquals(teamName, (instantiated.medias?.get(TeamMediaType.REACTION_VIDEO) as MediaType.WebRTCGrabberConnection).peerName)
}
}
2 changes: 1 addition & 1 deletion src/frontend/admin/src/components/TeamView.js
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ const TeamViewManager = ({ singleService, pvpService, splitService }) => {
const [mediaTypes1, setMediaTypes1] = useState(undefined);
const [mediaTypes2, setMediaTypes2] = useState(undefined);
const [statusShown, setStatusShown] = useState(true);
const [achievementShown, setAchievementShown] = useState(false);
const [achievementShown, setAchievementShown] = useState(true);

const onShow = useCallback(() => {
const settings = {
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/admin/src/services/analytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const useAnalyticsService = () => {
case "AnalyticsMessageSnapshot":
setMessages(() => event.messages.reduce((ac, message) => ({ ...ac, [message.id]: message }), {}));
break;
case "ModifyAnalyticsMessage":
case "ModifyAnalyticsMessage": // :NOTE: почему мы ничего не делаем?
case "AddAnalyticsMessage":
setMessages(messages => ({ ...messages, [event.message.id]: event.message }));
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ const ScoreboardTableHeader = () => {
<ScoreboardTableHeaderNameCell>Name</ScoreboardTableHeaderNameCell>
<ScoreboardTableHeaderCell>Σ</ScoreboardTableHeaderCell>
{needPenalty && <ScoreboardTableHeaderCell>Penalty</ScoreboardTableHeaderCell>}
{problems && problems.map((probData) => <ScoreboardProblemLabel key={probData.name} letter={probData.letter}
{problems && problems.map((probData) => <ScoreboardProblemLabel key={probData.ordinal} letter={probData.letter}
problemColor={probData.color}/>
)}
</ScoreboardTableHeaderWrap>;
Expand Down

0 comments on commit 0d1a03b

Please sign in to comment.