Merge branch 'fix/realm-vollbild'
All checks were successful
CI / test (push) Successful in 23s

This commit is contained in:
Lukas Müllner 2026-11-13 21:35:18 +01:00
commit 76dbed117f
9 changed files with 108 additions and 24 deletions

14
PLAN.md
View file

@ -284,3 +284,17 @@ Abgabe: **9. Dezember 2026, 03:00 MEZ**. Eingereicht wird in „Best Daily Game
Aufnahmen werden vorher nach VP9/WebM umgewandelt.
- Reddit selbst (Feed, Kommentar im Thread, Flair) nimmt der Besitzer mit seinem Konto auf (Reddit blockt
Automatisierung); optional als `dist/trailer/input/feed.*` / `comment.*`.
### 2026-11-13 – Realm im Vollbild (Bug vom Besitzer)
- **Bug:** Am Handy öffnete „🎨 Realm“ das Fenster im Inline-Post (512 px hoch). Die Liste ist höher, „Done“ lag
außerhalb, und scrollen darf ein Inline-Post nicht (Devvit-Regel „Scrolling within inline webviews is prohibited“,
Scroll-Falle im Feed) → man saß fest.
- **Lösung:** eigener Einstiegspunkt `realm` (`public/realm.html`, `src/client/realm-page.ts`), geöffnet mit
`requestExpandedMode(ev, 'realm', {title: 'Your realm'})`; „Done“ ruft `exitExpandedMode`. Der Knopf klebt unten
(`position: sticky`), damit er auch bei langer Liste sichtbar bleibt. Der Startbildschirm lädt beim `focus`-Ereignis
(Rückkehr aus dem Vollbild, laut Doku der Ersatz für `addWebViewModeListener`) Tag und Aussehen neu – damit zeigt er
nach dem Spielen auch gleich das eigene Ergebnis.
- Credits-Fenster im Post geprüft: passt auch bei 320 px Breite (Knopf endet bei 413 von 512 px).
- Pixelify-Ligatur „fi“ (sieht aus wie „A“) jetzt für die ganze App aus (`font-variant-ligatures: none`).
- **Fallstrick:** `devvit.json` geändert → laufenden Playtest stoppen und neu starten.

View file

@ -10,6 +10,9 @@
},
"game": {
"entry": "game.html"
},
"realm": {
"entry": "realm.html"
}
}
},

View file

@ -6,7 +6,7 @@
"type": "module",
"scripts": {
"build": "npm run build:client -- --metafile=dist/client.meta.json --minify && npm run build:server -- --metafile=dist/server.meta.json --minify",
"build:client": "esbuild --bundle --log-level=warning --sourcemap=linked --target=es2023 --format=esm --outdir=public --platform=browser --define:import.meta.env.MODE='\"reddit\"' --define:import.meta.env.BASE_URL='\"/\"' --define:__API_ORIGIN__='\"\"' --loader:.woff2=file --loader:.woff=file --asset-names=fonts/[name]-[hash] src/client/splash.ts src/client/game.ts",
"build:client": "esbuild --bundle --log-level=warning --sourcemap=linked --target=es2023 --format=esm --outdir=public --platform=browser --define:import.meta.env.MODE='\"reddit\"' --define:import.meta.env.BASE_URL='\"/\"' --define:__API_ORIGIN__='\"\"' --loader:.woff2=file --loader:.woff=file --asset-names=fonts/[name]-[hash] src/client/splash.ts src/client/game.ts src/client/realm-page.ts",
"build:server": "esbuild --bundle --log-level=warning --sourcemap=linked --target=es2023 --format=cjs --outdir=dist/server --platform=node src/server/index.ts",
"clean": "rm -rf dist/ public/*.js* public/*.css* public/fonts/",
"format": "npm run lint -- --fix",

14
public/realm.html Normal file
View file

@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Ninelands</title>
<link rel="stylesheet" href="realm-page.css">
<link rel="modulepreload" href="realm-page.js">
</head>
<body>
<div id="app"></div>
<script src="realm-page.js" type="module"></script>
</body>
</html>

View file

@ -349,17 +349,24 @@ async function scenes(page, o) {
'Streaks and stars unlock boards, peoples and banners.',
)
await wait(900)
const s2 = frameOf('splash.html')
await tap(s2.locator('.realm-btn'), 300)
await wait(700)
await tap(s2.locator('.realm-item', {hasText: 'Winter'}).first(), 400)
// Realm öffnet im Vollbild (requestExpandedMode gibt es hier nicht → nur antippen, Bühne wechselt)
await tap(frameOf('splash.html').locator('.realm-btn'), 300, false)
await wait(300)
await stage('finger', null)
await stage('show', 'game', '/realm.html')
const r = frameOf('realm.html')
await r.waitForSelector('.realm-item')
await wait(600)
await tap(r.locator('.realm-item', {hasText: 'Winter'}).first(), 400)
await wait(900)
await stage('finger', null)
await o.snap('05-realm')
await wait(800)
await tap(s2.locator('.overlay button.primary', {hasText: 'Done'}), 300)
await tap(r.locator('.overlay button.primary', {hasText: 'Done'}), 300, false)
await wait(300)
await stage('finger', null)
await wait(400)
await stage('show', 'post', '/splash.html')
await wait(700)
await stage(
'caption',

35
src/client/realm-page.ts Normal file
View file

@ -0,0 +1,35 @@
// „🎨 Realm“ im Vollbild (Einstiegspunkt „realm“): Aussehen wählen, „Done“ führt zurück in den Post.
import './style.css'
import {exitExpandedMode} from '@devvit/web/client'
import {dailyMap} from '../shared/daily.ts'
import {fetchDay} from './api.ts'
import {byId, h} from './dom.ts'
import {applyLook, openRealm} from './realm.ts'
const app = byId<HTMLDivElement>('app')
async function main(): Promise<void> {
app.replaceChildren(h('div.loading', 'Loading…'))
try {
const day = await fetchDay()
app.replaceChildren()
applyLook(day.look)
openRealm({
look: day.look,
progress: {
bestStreak: day.stats?.bestStreak ?? 0,
totalStars: day.stats?.totalStars ?? 0,
},
day: dailyMap(new Date()).day,
loggedIn: day.loggedIn,
onChange: look => applyLook(look),
onClose: ev => exitExpandedMode(ev),
})
} catch (err) {
app.replaceChildren(
h('div.error', `Could not load your realm. ${(err as Error).message}`),
)
}
}
void main()

View file

@ -75,6 +75,8 @@ export function openRealm(opts: {
day: string
loggedIn: boolean
onChange: (look: Look) => void
/** Statt nur das Fenster zu schließen (Vollbild-Seite: zurück in den Post) */
onClose?: (ev: MouseEvent) => void
}): void {
let look = opts.look
const body = h('div.realm-rows')
@ -94,7 +96,9 @@ export function openRealm(opts: {
h('div.row.end', close),
),
)
close.addEventListener('click', () => overlay.remove())
close.addEventListener('click', ev =>
opts.onClose ? opts.onClose(ev) : overlay.remove(),
)
const row = <T extends string>(
title: string,

View file

@ -9,7 +9,7 @@ import {dailyBoard, dailyMap, dayLabel} from '../shared/daily.ts'
import {fetchDay} from './api.ts'
import {byId, h} from './dom.ts'
import {appReady} from './journeys.ts'
import {applyLook, openRealm} from './realm.ts'
import {applyLook} from './realm.ts'
import {leaderTable} from './result.ts'
import {lawName, starsLine} from './texts.ts'
@ -28,6 +28,12 @@ async function main(): Promise<void> {
}
render(day)
appReady()
// Zurück aus dem Vollbild (Spiel oder Realm): Ergebnis und Aussehen neu holen
addEventListener('focus', () => {
fetchDay()
.then(render)
.catch(() => {})
})
}
function render(day: GetDayRsp): void {
@ -38,21 +44,10 @@ function render(day: GetDayRsp): void {
const realm = h('button.icon.realm-btn', '🎨 Realm')
realm.title = 'Customise your realm'
realm.addEventListener('click', () =>
openRealm({
look: day.look,
progress: {
bestStreak: day.stats?.bestStreak ?? 0,
totalStars: day.stats?.totalStars ?? 0,
},
day: dailyMap(new Date()).day,
loggedIn: day.loggedIn,
onChange: look => {
day.look = look
applyLook(look)
renderMiniBoard(mini, dailyBoard(map))
},
}),
// Im Vollbild: der Inline-Post ist nur 512 px hoch und darf nicht scrollen (Devvit-Regel) – sonst wäre
// „Done“ am Handy unerreichbar
realm.addEventListener('click', ev =>
requestExpandedMode(ev, 'realm', {title: 'Your realm'}),
)
const play = h('button.primary', day.mine ? 'Play again' : 'Play')

View file

@ -43,6 +43,8 @@ body {
body {
background-color: var(--px-grass-dark);
/* Pixelify verbindet „fi“ sonst zu einem Zeichen, das wie „A“ aussieht */
font-variant-ligatures: none;
/* feines Schachbrett wie Grasnarbe */
background-image: conic-gradient(
rgba(0, 0, 0, 0.08) 25%,
@ -219,6 +221,16 @@ button.icon {
}
/* ---------- Realm-Auswahl ---------- */
/* „Done“ bleibt unten sichtbar, auch wenn die Liste scrollt (Vollbild am Handy) */
.realm > .row.end {
position: sticky;
bottom: -1rem;
margin: 0 -1rem -1rem;
padding: 0.6rem 1rem;
background: var(--px-wood);
border-top: 2px solid var(--px-ink);
}
.realm h3 {
margin: 0 0 0.25rem;
font-family: var(--font-pixel);