diff --git a/CHANGELOG.md b/CHANGELOG.md index b1b2df6..ebf5a2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,8 +19,9 @@ The long form, with what was wrong before and how it was found, is in ### Changed -- A feed that fails to check gets a red ! in the feed list, and its page says why, in place of a - pop-up per failure that everyone saw during a scan of every feed. +- A feed that fails to check gets a red exclamation mark in the feed list, in the margin where a + folder's triangle sits, and its page says why, in place of a pop-up per failure that everyone + saw during a scan of every feed. A folder holding a failing feed has its triangle turn red. - The theme is chosen in Settings only; the button beside the iPodderX name is gone. - Add a feed asks only for the feed; Popular and Directory in the sidebar are where you browse. - On a phone, an item's files, with play and delete, sit above its show notes rather than below @@ -34,6 +35,10 @@ The long form, with what was wrong before and how it was found, is in - Switching tabs straight after marking everything read no longer shows the previous tab's items: of two lists asked for at once, only the later one is shown. +- ipx has a favicon: the logo, squared up, also at /favicon.ico for browsers that ask there on + their own, and on white for an iPhone's home screen. +- The file icon of an item not yet downloaded sits level with the rest of its row, instead of + higher than a downloaded one's. - An item you open stays read. A list refresh that crossed with marking it read could put its unread dot back until the next refresh. - On the Unread tab, the item you were reading leaves the list as soon as you move to the next diff --git a/src/web.rs b/src/web.rs index 65b0491..96e8c62 100644 --- a/src/web.rs +++ b/src/web.rs @@ -65,6 +65,9 @@ pub fn router(state: WebState) -> Router { .route("/login", get(login_page)) .route("/api/login", post(login)) .route("/icon.png", get(icon)) + .route("/favicon.ico", get(favicon)) + .route("/favicon.png", get(favicon)) + .route("/apple-touch-icon.png", get(touch_icon)) .route("/app.js", get(app_js)) .route("/login.js", get(login_js)) .route("/inter.woff2", get(inter)) @@ -477,6 +480,25 @@ async fn icon() -> impl IntoResponse { ) } +/// The logo, squared up with transparent padding: it is 128x121, and a tab icon that is not +/// square can be passed over. /favicon.ico is the same PNG, for a browser that asks for that +/// on its own; behind the auth layer it answered 401, and the tab stayed blank. +async fn favicon() -> impl IntoResponse { + ( + [(header::CONTENT_TYPE, "image/png"), (header::CACHE_CONTROL, "max-age=86400")], + include_bytes!("../web/favicon.png").as_slice(), + ) +} + +/// For an iPhone's home screen, which paints a transparent icon's background black, so this +/// one is on white. +async fn touch_icon() -> impl IntoResponse { + ( + [(header::CONTENT_TYPE, "image/png"), (header::CACHE_CONTROL, "max-age=86400")], + include_bytes!("../web/apple-touch-icon.png").as_slice(), + ) +} + /// Inter, the pages' typeface, served from the binary as the icon is, so neither page loads /// anything from anyone else. Outside the auth layer for the sign-in page. Its licence, the SIL /// Open Font License, is web/Inter-LICENSE.txt. diff --git a/tests/ui/app.spec.js b/tests/ui/app.spec.js index 8a35e5b..226bc33 100644 --- a/tests/ui/app.spec.js +++ b/tests/ui/app.spec.js @@ -1085,3 +1085,47 @@ test.describe('on a phone', () => { .toBe(true); }); }); + +test('a file not yet downloaded has its icon in line with the rest of its row', async ({ page }) => { + await page.locator('#feedlist .place', { hasText: 'All Subscriptions' }).click(); + await expect(page.locator('.ep .dlbar').first()).toBeAttached({ timeout: 20_000 }); + // The icon's middle against the date's, downloaded or not. The download bar used to take a + // line of its own and lift the icon of every pending file (issue #31). + const offsets = await page.$$eval('.ep', rows => rows.map(r => { + const k = r.querySelector('.file .kind'), d = r.querySelector('.date'); + if (!k || !d) return null; + const a = k.getBoundingClientRect(), b = d.getBoundingClientRect(); + return Math.round((a.top + a.height / 2) - (b.top + b.height / 2)); + }).filter(x => x !== null)); + expect(offsets.length).toBeGreaterThan(1); + for (const o of offsets) expect(Math.abs(o)).toBeLessThanOrEqual(1); +}); + +test('the favicon is the logo, square, from both pages', async ({ page }) => { + await expect(page.locator('link[rel="icon"]')).toHaveAttribute('href', '/favicon.png'); + // A browser asks for /favicon.ico on its own, signed in or not. + for (const path of ['/favicon.ico', '/favicon.png', '/apple-touch-icon.png']) { + const r = await page.request.get(path, { headers: { cookie: '' } }); + expect(r.status(), path).toBe(200); + expect(r.headers()['content-type'], path).toBe('image/png'); + } +}); + +test('a feed error is marked in the same column as the folder triangles', async ({ page }) => { + await expect(page.locator('.feed.group .chev').first()).toBeVisible(); + if ((await page.locator('.feed.group .chev').first().getAttribute('aria-expanded')) !== 'true') + await page.locator('.feed.group .chev').first().click(); + // Faked in the page: no fixture feed fails. A feed on its own, and one inside a folder. + await page.evaluate(() => { + S.feeds.find(f => f.group).last_error = 'HTTP 404'; + S.feeds.find(f => !f.group && !S.feeds.some(c => c.group === f.id)).last_error = 'timed out'; + renderFeeds(); + }); + await expect(page.locator('.ferr')).toHaveCount(2); + await expect(page.locator('.ferr svg')).toHaveCount(2); // the icon, not a "!" + await expect(page.locator('.chev.bad')).toHaveCount(1); // the folder holding one + const xs = await page.$$eval('.chev, .ferr', els => + els.map(e => { const r = e.getBoundingClientRect(); return Math.round(r.left + r.width / 2); })); + expect(new Set(xs).size, JSON.stringify(xs)).toBe(1); + await page.reload(); // put the real list back +}); diff --git a/web/apple-touch-icon.png b/web/apple-touch-icon.png new file mode 100644 index 0000000..789eb10 Binary files /dev/null and b/web/apple-touch-icon.png differ diff --git a/web/favicon.png b/web/favicon.png new file mode 100644 index 0000000..c4616ec Binary files /dev/null and b/web/favicon.png differ diff --git a/web/index.html b/web/index.html index 93436ba..629627c 100644 --- a/web/index.html +++ b/web/index.html @@ -5,7 +5,8 @@