Aero music: the button works on the podcast player
"The music thing button with the Frutiger Aero doesn't work on the podcasts." The podcast page is a full-screen overlay at z-45 and the control sat at z-40: visible through the transparent page, but every tap landed on the overlay. The control now takes a data-raised="player" position on /podcast/[id] - z-index 46, docked to the header's corner (top under the header pill, right mirroring the max-w-3xl geometry) where neither the transport bar nor the voice panel lives - and the transcript gained pt-32 so the episode card clears it. Study/review/exam feeds keep their "steps aside" behaviour. aero-music-test: a podcast section asserts the control exists, that elementFromPoint at its centre hits the button, and that a tap expands the credit bar. 31 checks. Version 0.7.9, SW v101.
This commit is contained in:
parent
08a145b087
commit
724a068bec
7 changed files with 74 additions and 4 deletions
11
README.md
11
README.md
|
|
@ -1492,6 +1492,17 @@ Things I chose while building, with the reasoning:
|
|||
into view as you type. No strings, so no i18n; the additive change sits on top of the merged
|
||||
scene-heat rework unchanged.
|
||||
|
||||
107. **The Aero soundtrack button rides above the podcast player.** "The music thing button with
|
||||
the Frutiger Aero doesn't work on the podcasts": the podcast page is a full-screen overlay at
|
||||
`z-45` and the music control sat at `z-40` — visible through the transparent page, but every
|
||||
tap landed on the overlay. The control now takes a `data-raised="player"` position on
|
||||
`/podcast/[id]`: `z-index: 46`, docked to the header's corner (`top` under the header pill,
|
||||
`right` mirroring the `max-w-3xl` geometry) where neither the transport bar nor the voice panel
|
||||
lives, and the transcript gained `pt-32` so the episode card clears it. The study/review/exam
|
||||
feeds keep the "steps aside" behaviour — their bottom corners are spoken for. aero-music-test
|
||||
grew a podcast section that asserts the control is there, that `elementFromPoint` at its centre
|
||||
hits the button (nothing covers it) and that a tap expands the credit bar (31 checks).
|
||||
|
||||
106. **When the browser has no speech engine at all.** "The podcasts don't even work on my Linux
|
||||
setup on Firefox": Firefox on Linux speaks through speech-dispatcher, and without it
|
||||
`speechSynthesis` is *present* (so `speechAvailable()` says yes) but empty and silent —
|
||||
|
|
|
|||
|
|
@ -933,6 +933,15 @@ body:has(.overlay-open) :is(.app-header, .segmented, .tabbar, .cookie-notice) {
|
|||
.aero-music[data-raised="wizard"] {
|
||||
bottom: calc(5.5rem + env(safe-area-inset-bottom, 0px));
|
||||
}
|
||||
/* The podcast player is a full-screen overlay (z-45): the music control has
|
||||
to ride above it, and it moves to the header's corner — the transport and
|
||||
the voice panel own the bottom of that screen. */
|
||||
.aero-music[data-raised="player"] {
|
||||
top: calc(max(env(safe-area-inset-top), 0.75rem) + 3.9rem);
|
||||
right: max(0.75rem, calc((100vw - 48rem) / 2 + 0.75rem));
|
||||
bottom: auto;
|
||||
z-index: 46;
|
||||
}
|
||||
body:has(.overlay-open) .aero-music,
|
||||
/* The one-time notice shares this corner; it wins while it is up. */
|
||||
body:has(.cookie-notice) .aero-music {
|
||||
|
|
|
|||
|
|
@ -205,7 +205,14 @@ export function AeroMusic() {
|
|||
|
||||
/* The composer sits at the bottom of the chat screen and the wizard has its
|
||||
own action bar; everywhere else the phone tab bar is the only neighbour. */
|
||||
const raised = pathname === "/chat" ? "chat" : pathname === "/create" || pathname === "/homework" ? "wizard" : undefined;
|
||||
const raised =
|
||||
pathname.startsWith("/podcast/")
|
||||
? "player"
|
||||
: pathname === "/chat"
|
||||
? "chat"
|
||||
: pathname === "/create" || pathname === "/homework"
|
||||
? "wizard"
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<div className="aero-music" data-testid="aero-music" data-raised={raised}>
|
||||
|
|
|
|||
|
|
@ -585,7 +585,9 @@ export function PodcastPlayer({ episode }: { episode: Podcast }) {
|
|||
const index = Number(target.dataset.word);
|
||||
if (Number.isInteger(index)) jumpWord(index);
|
||||
}}
|
||||
className="no-scrollbar min-h-0 flex-1 overflow-y-auto overscroll-contain px-4 pb-40 pt-24"
|
||||
/* pt-32 clears the Aero music control, which rides above this overlay
|
||||
in the header's corner while the theme is on. */
|
||||
className="no-scrollbar min-h-0 flex-1 overflow-y-auto overscroll-contain px-4 pb-40 pt-32"
|
||||
>
|
||||
<div className="mx-auto w-full max-w-3xl space-y-6">
|
||||
{naturalError ? (
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "freezmo",
|
||||
"version": "0.7.8",
|
||||
"version": "0.7.9",
|
||||
"private": true,
|
||||
"license": "GPL-3.0-only",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
*
|
||||
* Bumping CACHE_VERSION is how a new deploy invalidates old assets.
|
||||
*/
|
||||
const CACHE_VERSION = "freezmo-v100";
|
||||
const CACHE_VERSION = "freezmo-v101";
|
||||
const SHELL_CACHE = `${CACHE_VERSION}-shell`;
|
||||
const ASSET_CACHE = `${CACHE_VERSION}-assets`;
|
||||
|
||||
|
|
|
|||
|
|
@ -208,6 +208,47 @@ async function stubSoundCloud(page) {
|
|||
await context.close();
|
||||
}
|
||||
|
||||
/* 4b. the podcast player: the control rides above that overlay -------- */
|
||||
{
|
||||
const { context, page } = await makePage(browser, { width: 390, height: 844, deviceScaleFactor: 1, isMobile: true, hasTouch: true });
|
||||
await stubSoundCloud(page);
|
||||
await seed(page, {
|
||||
theme: "aero",
|
||||
decks: [],
|
||||
extra: {
|
||||
"freezmo.podcasts.v1": JSON.stringify([
|
||||
{
|
||||
id: "pod_music",
|
||||
title: "While you listen",
|
||||
summary: "",
|
||||
language: "English",
|
||||
sourceText: "x",
|
||||
createdAt: 1,
|
||||
updatedAt: 1,
|
||||
sections: [{ heading: "One", text: "Look up on a clear day." }],
|
||||
},
|
||||
]),
|
||||
},
|
||||
});
|
||||
await page.goto(`${BASE}/podcast/pod_music`, { waitUntil: "networkidle" });
|
||||
await page.waitForTimeout(1500);
|
||||
|
||||
const circle = page.getByRole("button", { name: "Frutiger Aero music" });
|
||||
check("podcast: the music control is there while listening", (await circle.count()) === 1);
|
||||
const reachable = await page.evaluate(() => {
|
||||
const button = document.querySelector("[data-testid=aero-music] button");
|
||||
if (!button) return "missing";
|
||||
const box = button.getBoundingClientRect();
|
||||
const top = document.elementFromPoint(box.x + box.width / 2, box.y + box.height / 2);
|
||||
return button.contains(top) || top === button ? "on-top" : `covered by ${top?.className?.toString().slice(0, 40)}`;
|
||||
});
|
||||
check("podcast: nothing covers it (it rides above the overlay)", reachable === "on-top", reachable);
|
||||
await circle.click();
|
||||
await page.waitForTimeout(400);
|
||||
check("podcast: tapping it expands the credit bar", (await page.getByText("SoundCloud ·", { exact: false }).count()) >= 1);
|
||||
await context.close();
|
||||
}
|
||||
|
||||
/* 5. immersive overlays step aside, the music keeps playing ----------- */
|
||||
{
|
||||
const { context, page } = await makePage(browser, { width: 1280, height: 731 });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue