Paint the bar around the Dynamic Island in the theme's background (#147)
The page named no theme-color, so the status bar iOS draws around the Dynamic Island was its own black or white, a band across the top of every theme. The page and the admin page now carry one, set to the page's background whenever the theme or mode changes; Android's toolbar takes it too. Not seen on a phone yet: the browser test checks the colour matches, not what iOS does with it. A browser test at 360px covers this and #146's row of buttons. The stub DOM gains getComputedStyle, which every browser has. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- On an iPhone, the bar around the Dynamic Island is the theme's background, not the system's own black or white.
|
||||||
- A feed's buttons stay on one row on a narrow phone, and the reader's buttons stay together instead of breaking across lines under a long feed name.
|
- A feed's buttons stay on one row on a narrow phone, and the reader's buttons stay together instead of breaking across lines under a long feed name.
|
||||||
- A moment's DNS failure no longer marks a feed as failing for a day or calls it gone: iPX tries it again in ten minutes. One failed lookup one morning left 205 podcasts unread, uncategorised and labelled gone until the next day. A name that does not resolve is said to be maybe gone, and only once it has been failing a day, on its Directory page as on its own.
|
- A moment's DNS failure no longer marks a feed as failing for a day or calls it gone: iPX tries it again in ten minutes. One failed lookup one morning left 205 podcasts unread, uncategorised and labelled gone until the next day. A name that does not resolve is said to be maybe gone, and only once it has been failing a day, on its Directory page as on its own.
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ function makeContext(html, script, { media = false } = {}) {
|
|||||||
EventSource: function () { this.close = () => {}; },
|
EventSource: function () { this.close = () => {}; },
|
||||||
MediaMetadata: function () {},
|
MediaMetadata: function () {},
|
||||||
Blob: function () {},
|
Blob: function () {},
|
||||||
|
getComputedStyle: () => ({ backgroundColor: '' }),
|
||||||
setTimeout, clearTimeout, setInterval, clearInterval,
|
setTimeout, clearTimeout, setInterval, clearInterval,
|
||||||
confirm: () => false, prompt: () => null, alert(){},
|
confirm: () => false, prompt: () => null, alert(){},
|
||||||
Date, Math, JSON, Object, Array, String, Number, Promise, Error, FormData: function(){},
|
Date, Math, JSON, Object, Array, String, Number, Promise, Error, FormData: function(){},
|
||||||
|
|||||||
@@ -1656,3 +1656,20 @@ test('a refresh button turns while what it checks is being checked', async ({ pa
|
|||||||
await page.evaluate(() => setScanning('multi-show', false));
|
await page.evaluate(() => setScanning('multi-show', false));
|
||||||
expect(await spin('#scanAll .i')).toBe('none');
|
expect(await spin('#scanAll .i')).toBe('none');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.describe('on a narrow phone', () => {
|
||||||
|
test.use({ viewport: { width: 360, height: 760 } });
|
||||||
|
|
||||||
|
test("a feed's buttons stay on one row, and the bar round the island is the page's colour", async ({ page }) => {
|
||||||
|
// Seven 44px buttons did not fit 360px, and Unsubscribe went onto a row of its own (#146).
|
||||||
|
await page.locator('#burger').click();
|
||||||
|
await page.locator('.feed', { hasText: 'Test Show' }).first().click();
|
||||||
|
const tops = await page.locator('#content .acts .btn').evaluateAll(bs => bs.map(b => b.getBoundingClientRect().top));
|
||||||
|
expect(tops.length).toBe(7);
|
||||||
|
expect(new Set(tops).size).toBe(1);
|
||||||
|
// iOS paints the status bar round the Dynamic Island in theme-color, its own black or white
|
||||||
|
// without one (#147).
|
||||||
|
expect(await page.evaluate(() => document.querySelector('meta[name=theme-color]').content))
|
||||||
|
.toBe(await page.evaluate(() => getComputedStyle(document.body).backgroundColor));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="color-scheme" content="dark light">
|
<meta name="color-scheme" content="dark light">
|
||||||
|
<meta name="theme-color" content="#0a1726">
|
||||||
<title>iPX admin</title>
|
<title>iPX admin</title>
|
||||||
<link rel="icon" type="image/png" sizes="128x128" id="favicon" href="/favicon.png" data-light="/favicon.png" data-dark="/favicon-dark.png">
|
<link rel="icon" type="image/png" sizes="128x128" id="favicon" href="/favicon.png" data-light="/favicon.png" data-dark="/favicon-dark.png">
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||||
<meta name="color-scheme" content="dark light">
|
<meta name="color-scheme" content="dark light">
|
||||||
|
<meta name="theme-color" content="#0a1726">
|
||||||
<title>iPX</title>
|
<title>iPX</title>
|
||||||
<link rel="icon" type="image/png" sizes="128x128" id="favicon" href="/favicon.png" data-light="/favicon.png" data-dark="/favicon-dark.png">
|
<link rel="icon" type="image/png" sizes="128x128" id="favicon" href="/favicon.png" data-light="/favicon.png" data-dark="/favicon-dark.png">
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ function setTheme(name = theme.name, mode = theme.mode, save = false){
|
|||||||
: theme.mode === 'auto' ? (systemDark && !systemDark.matches ? 'light' : 'dark') : theme.mode;
|
: theme.mode === 'auto' ? (systemDark && !systemDark.matches ? 'light' : 'dark') : theme.mode;
|
||||||
// The tab's icon in the same variant as the logo on the page.
|
// The tab's icon in the same variant as the logo on the page.
|
||||||
const fav = $('#favicon'); if(fav) fav.href = fav.dataset[root.dataset.mode];
|
const fav = $('#favicon'); if(fav) fav.href = fav.dataset[root.dataset.mode];
|
||||||
|
// The bar iOS draws round the Dynamic Island takes this colour, as Android's toolbar does.
|
||||||
|
// Without it the bar was the system's own black or white, a band across the top of the page.
|
||||||
|
const tc = $('meta[name=theme-color]'); if(tc) tc.content = getComputedStyle(document.body).backgroundColor;
|
||||||
const sel = $('#stheme'); if(sel) sel.value = theme.name;
|
const sel = $('#stheme'); if(sel) sel.value = theme.name;
|
||||||
const ms = $('#smode'); if(ms) ms.value = theme.mode;
|
const ms = $('#smode'); if(ms) ms.value = theme.mode;
|
||||||
const mf = $('#smodefield'); if(mf) mf.hidden = !both;
|
const mf = $('#smodefield'); if(mf) mf.hidden = !both;
|
||||||
|
|||||||
Reference in New Issue
Block a user