Make the lesson map the floor screen of the typing tab
The aquarium was a separate landing screen in front of the map, showing the pets collected so far and a "Weiter üben" button. But the pets already swim behind every screen in the tab, so the screen mostly restated what was visible anyway, at the cost of one extra step between opening the tab and typing. The map absorbs what was worth keeping: the "Weiter üben" button now floats over it, and the pets, streak and best animal move into the header. One Escape from the map leaves the tab entirely, where it used to go back a screen first. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,14 @@
|
||||
/** The map: a single winding path, one world section at a time.
|
||||
*
|
||||
* The floor screen of the tippen tab - the swimming aquarium creatures already show
|
||||
* behind it, so there is no separate landing screen in front of this one anymore.
|
||||
*
|
||||
* Locked lessons are dimmed rather than hidden - seeing that "Große Buchstaben" is
|
||||
* waiting is half the reason to finish the world that is open. Each node carries its own
|
||||
* best animal, star count and a badge for which game it plays, so the map doubles as
|
||||
* both a path forward and a trophy cabinet. */
|
||||
* both a path forward and a trophy cabinet. A second badge marks a lesson that unlocks
|
||||
* real music or an audiobook chapter, shown whether or not the lesson itself is locked
|
||||
* yet - same reasoning as the dimmed-not-hidden lessons: seeing it coming is the point. */
|
||||
|
||||
import type { Lesson, World } from "../../lib/tippen/curriculum";
|
||||
import { animalById } from "../../lib/tippen/grading";
|
||||
@@ -18,6 +23,9 @@ interface Props {
|
||||
/** Which card the keyboard selection is on. */
|
||||
selected: number;
|
||||
onPick: (lesson: Lesson) => void;
|
||||
/** The lesson the floating "Weiter üben" button jumps to - the first unfinished one. */
|
||||
nextLesson: Lesson | null;
|
||||
onContinue: () => void;
|
||||
}
|
||||
|
||||
const NODE_SIZE = 88;
|
||||
@@ -32,7 +40,7 @@ const CONSOLIDATION_LABEL: Record<"fragments" | "words" | "sentences", string> =
|
||||
sentences: "Sätze",
|
||||
};
|
||||
|
||||
export function LessonMap({ worlds, lessons, progress, selected, onPick }: Props) {
|
||||
export function LessonMap({ worlds, lessons, progress, selected, onPick, nextLesson, onContinue }: Props) {
|
||||
return (
|
||||
<div
|
||||
className="view-enter"
|
||||
@@ -128,6 +136,16 @@ export function LessonMap({ worlds, lessons, progress, selected, onPick }: Props
|
||||
{modeInfo.emoji}
|
||||
</span>
|
||||
|
||||
{lesson.reward.resolved && (
|
||||
<span
|
||||
aria-hidden
|
||||
style={{ position: "absolute", top: -4, left: -4, fontSize: 15, opacity: 0.9 }}
|
||||
title="Schaltet neue Musik frei!"
|
||||
>
|
||||
🎁
|
||||
</span>
|
||||
)}
|
||||
|
||||
<span style={{ fontSize: 24 }}>{locked ? "🔒" : (animal?.emoji ?? "·")}</span>
|
||||
|
||||
{/* The keys themselves: for a pre-reader this is the real label, the
|
||||
@@ -167,6 +185,29 @@ export function LessonMap({ worlds, lessons, progress, selected, onPick }: Props
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{nextLesson && (
|
||||
<button
|
||||
onClick={onContinue}
|
||||
style={{
|
||||
position: "fixed",
|
||||
bottom: 24,
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
border: "none",
|
||||
borderRadius: 999,
|
||||
padding: "15px 32px",
|
||||
fontSize: 19,
|
||||
fontWeight: 900,
|
||||
cursor: "pointer",
|
||||
background: "var(--accent)",
|
||||
color: "var(--paper)",
|
||||
boxShadow: "0 8px 24px var(--shadow)",
|
||||
}}
|
||||
>
|
||||
▶ {nextLesson.title}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user