feat: render the bundled-scenarios list on the home page
This commit is contained in:
@@ -9,10 +9,15 @@ use BattleForge\Http\Response;
|
||||
|
||||
final class GetHomePage
|
||||
{
|
||||
public function __construct(private readonly string $scenariosDir = '')
|
||||
{
|
||||
}
|
||||
|
||||
/** @param array<string, string> $params */
|
||||
public function handle(Request $request, array $params): Response
|
||||
{
|
||||
$csrf = $request->cookies['__csrf'] ?? '';
|
||||
$bundled = $this->loadBundledScenarios();
|
||||
|
||||
ob_start();
|
||||
require_once __DIR__ . '/../../Views/layout.php';
|
||||
@@ -21,4 +26,35 @@ final class GetHomePage
|
||||
|
||||
return Response::html(200, $body);
|
||||
}
|
||||
|
||||
/** @return list<array{id: string, name: string, summary: string}> */
|
||||
private function loadBundledScenarios(): array
|
||||
{
|
||||
if ($this->scenariosDir === '' || !is_file($this->scenariosDir . '/manifest.json')) {
|
||||
return [];
|
||||
}
|
||||
$decoded = json_decode((string) file_get_contents($this->scenariosDir . '/manifest.json'), true);
|
||||
if (!is_array($decoded)) {
|
||||
return [];
|
||||
}
|
||||
$entries = [];
|
||||
foreach ($decoded as $id => $entry) {
|
||||
if (!is_string($id) || !preg_match('/^[a-z0-9-]+$/', $id) || !is_array($entry)) {
|
||||
continue;
|
||||
}
|
||||
if (!isset($entry['name'], $entry['summary'])) {
|
||||
continue;
|
||||
}
|
||||
if (!is_file($this->scenariosDir . '/' . $id . '.json')) {
|
||||
continue;
|
||||
}
|
||||
$entries[] = [
|
||||
'id' => $id,
|
||||
'name' => (string) $entry['name'],
|
||||
'summary' => (string) $entry['summary'],
|
||||
];
|
||||
}
|
||||
usort($entries, static fn (array $a, array $b): int => strcmp($a['id'], $b['id']));
|
||||
return $entries;
|
||||
}
|
||||
}
|
||||
|
||||
+13
-2
@@ -5,15 +5,26 @@ declare(strict_types=1);
|
||||
use BattleForge\Http\Escape;
|
||||
|
||||
/** @var string $csrf Provided by the front controller. */
|
||||
/** @var list<array{id: string, name: string, summary: string}> $bundled Provided by GetHomePage. */
|
||||
?><?php
|
||||
render_layout(static function () use ($csrf): void {
|
||||
render_layout(static function () use ($csrf, $bundled): void {
|
||||
$e = static fn (string $v): string => Escape::html($v);
|
||||
$a = static fn (string $v): string => Escape::attr($v);
|
||||
?>
|
||||
<h1>BattleForge</h1>
|
||||
<p><a href="<?= $a('/scenarios/new/edit/team') ?>">New scenario</a></p>
|
||||
<h2>Play bundled</h2>
|
||||
<ul id="bf-bundled">
|
||||
<?php foreach ($bundled as $entry) : ?>
|
||||
<li>
|
||||
<button type="button" data-bf-bundle="<?= $a($entry['id']) ?>">
|
||||
<strong><?= $e($entry['name']) ?></strong> — <?= $e($entry['summary']) ?>
|
||||
</button>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<h2>Recent scenarios</h2>
|
||||
<div id="recent"><p class="bf-empty">No saved scenarios yet.</p></div>
|
||||
<script type="module" src="<?= $a('/js/storage.js') ?>"></script>
|
||||
<?php
|
||||
}, $csrf, 'BattleForge');
|
||||
}, $csrf, 'BattleForge');
|
||||
@@ -23,4 +23,37 @@ final class GetHomePageTest extends TestCase
|
||||
self::assertStringContainsString('default-src', $response->headers['Content-Security-Policy']);
|
||||
self::assertStringContainsString('<a href="/scenarios/new/edit/team">', $response->body);
|
||||
}
|
||||
|
||||
public function testItRendersTheBundledScenariosListFromTheManifest(): void
|
||||
{
|
||||
$tmpDir = sys_get_temp_dir() . '/bf-home-bundled-' . bin2hex(random_bytes(4));
|
||||
mkdir($tmpDir, 0700, true);
|
||||
file_put_contents(
|
||||
$tmpDir . '/manifest.json',
|
||||
json_encode([
|
||||
'skirmish' => ['name' => 'Skirmish', 'summary' => 'Open ground.'],
|
||||
'hold-the-line' => ['name' => 'Hold the Line', 'summary' => 'Defend.'],
|
||||
], JSON_THROW_ON_ERROR),
|
||||
);
|
||||
foreach (['skirmish.json', 'hold-the-line.json'] as $file) {
|
||||
file_put_contents($tmpDir . '/' . $file, '{}');
|
||||
}
|
||||
|
||||
$handler = new GetHomePage($tmpDir);
|
||||
$request = new Request([], [], [], [], [], '', 'GET', '/', 'text/html', '');
|
||||
$response = $handler->handle($request, []);
|
||||
|
||||
self::assertSame(200, $response->status);
|
||||
self::assertStringContainsString('id="bf-bundled"', $response->body);
|
||||
self::assertStringContainsString('data-bf-bundle="skirmish"', $response->body);
|
||||
self::assertStringContainsString('data-bf-bundle="hold-the-line"', $response->body);
|
||||
self::assertStringContainsString('Open ground.', $response->body);
|
||||
self::assertStringContainsString('Defend.', $response->body);
|
||||
|
||||
unlink($tmpDir . '/manifest.json');
|
||||
foreach (['skirmish.json', 'hold-the-line.json'] as $file) {
|
||||
unlink($tmpDir . '/' . $file);
|
||||
}
|
||||
rmdir($tmpDir);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user