diff --git a/assets/react/controllers/ActorPopover.jsx b/assets/react/controllers/ActorPopover.jsx index f085fd6..5d0d485 100644 --- a/assets/react/controllers/ActorPopover.jsx +++ b/assets/react/controllers/ActorPopover.jsx @@ -21,6 +21,8 @@ export default function ActorPopover({ hintType, hintText }) { const dismiss = useDismiss(context); const { getReferenceProps, getFloatingProps } = useInteractions([click, dismiss]); + if (!hintText) return null; + const iconClass = HINT_ICONS[hintType] || 'fa-solid fa-circle-question'; return ( diff --git a/src/Service/GameGridGenerator.php b/src/Service/GameGridGenerator.php index c7c39b0..8078d19 100644 --- a/src/Service/GameGridGenerator.php +++ b/src/Service/GameGridGenerator.php @@ -35,6 +35,7 @@ class GameGridGenerator $rowOrder = 0; $usedMovieRoleIds = []; $usedHintKeys = []; + $cachedAwards = null; foreach (str_split(strtolower($mainActor->getName())) as $char) { if (!preg_match('/[a-z]/', $char)) { @@ -57,7 +58,7 @@ class GameGridGenerator $row->setPosition(strpos(strtolower($actor->getName()), $char)); $row->setRowOrder($rowOrder); - $hint = $this->generateHint($mainActor, $usedMovieRoleIds, $usedHintKeys); + $hint = $this->generateHint($mainActor, $usedMovieRoleIds, $usedHintKeys, $cachedAwards); if ($hint !== null) { $row->setHintType($hint['type']); $row->setHintData($hint['data']); @@ -138,13 +139,13 @@ class GameGridGenerator * @param list $usedHintKeys Semantic keys like "film:42" to avoid duplicate hints * @return array{type: string, data: string}|null */ - private function generateHint(Actor $mainActor, array &$usedMovieRoleIds, array &$usedHintKeys): ?array + private function generateHint(Actor $mainActor, array &$usedMovieRoleIds, array &$usedHintKeys, ?array &$cachedAwards): ?array { $types = ['film', 'character', 'award']; shuffle($types); foreach ($types as $type) { - $hint = $this->resolveHint($type, $mainActor, $usedMovieRoleIds, $usedHintKeys); + $hint = $this->resolveHint($type, $mainActor, $usedMovieRoleIds, $usedHintKeys, $cachedAwards); if ($hint !== null) { return $hint; } @@ -158,7 +159,7 @@ class GameGridGenerator * @param list $usedHintKeys * @return array{type: string, data: string}|null */ - private function resolveHint(string $type, Actor $mainActor, array &$usedMovieRoleIds, array &$usedHintKeys): ?array + private function resolveHint(string $type, Actor $mainActor, array &$usedMovieRoleIds, array &$usedHintKeys, ?array &$cachedAwards): ?array { switch ($type) { case 'film': @@ -196,12 +197,15 @@ class GameGridGenerator return ['type' => 'character', 'data' => $roleId]; case 'award': - try { - $awards = $this->wikidataAwardGateway->getAwards($mainActor); - } catch (\Throwable) { - return null; + if ($cachedAwards === null) { + try { + $cachedAwards = $this->wikidataAwardGateway->getAwards($mainActor); + } catch (\Throwable) { + $cachedAwards = []; + return null; + } } - foreach ($awards as $award) { + foreach ($cachedAwards as $award) { $text = $award['name'] . ' (' . $award['year'] . ')'; $key = 'award:' . $text; if (!in_array($key, $usedHintKeys)) { diff --git a/src/Service/WikidataAwardGateway.php b/src/Service/WikidataAwardGateway.php index db19bc5..c933f30 100644 --- a/src/Service/WikidataAwardGateway.php +++ b/src/Service/WikidataAwardGateway.php @@ -33,6 +33,7 @@ class WikidataAwardGateway 'Accept' => 'application/sparql-results+json', 'User-Agent' => 'LtbxdActorle/1.0', ], + 'timeout' => 5, ]); $data = $response->toArray(); @@ -55,7 +56,7 @@ class WikidataAwardGateway private function buildQuery(string $actorName): string { - $escaped = str_replace('"', '\\"', $actorName); + $escaped = str_replace(['\\', '"', "\n", "\r"], ['\\\\', '\\"', '\\n', '\\r'], $actorName); return <<