Flags and startup: a flag on every platform, a faster first paint, the screen kept on reload - #36
Merged
Merged
Conversation
On Windows and minimal Linux a regional-indicator pair has no colour glyph, so the Places list lost its flags and the map's flag markers turned into empty boxes. A flag-only Twemoji font (78 KB woff2, CC-BY 4.0) now leads the page and canvas font stacks behind a unicode-range, so it is fetched only once a flag shows, precached for offline use, and the map redraws its flag markers once it has loaded. Credited in Settings' data sources.
Every place list showed the country's flag beside a name except the global search results and the map's place card, where the country was named without one. Both lead with it now, hidden from assistive tech so the option and card keep the place's name. The card's country line also lost the stray leading dot it carried when opened from a list or a search.
At world zoom a country's visited cities sat on top of each other, so Europe became a pile of identical flags over the neighbours' ones, a logged airport's pill covered its country's only flag, and with markers allowed to overlap the sort key drew favourites at the bottom of the pile. A country's own flags now stack into one while they would overlap (a favourite or custom place always keeps its own), each country's main city outranks any second city, the pile's top is its highest priority, airport pills sit under the flags and shrink at world zoom, and the flag glyph is sized to fill its marker.
At 375px the brand's wordmark and four action buttons left the search field 85px wide, its placeholder clipped to "Sea"; below 480px the brand now shows its pin mark only (the wordmark stays its accessible name) and the field gets 158px. The search, GitHub star, map mode and map favourite icons were emoji, which draw as nothing without a colour emoji font (every Windows browser): they are inline SVG or canvas shapes now. Chromium's built-in clear button no longer doubles the field's own ✕.
A reload always landed on the map and the address stayed `/`, so a city page, a country page or a tab could not survive a reload or be linked, and the browser's Forward did nothing. The tab and the open place page now live in the hash (`#/places/country/FR`), restored before the first render; each navigation pushes an entry carrying its position, so Back keeps its rules (dialog first, sub-view, the app's own history, never quitting at home), walks the screens from before a reload, and Forward re-opens what Back left. The trip composer, an unsaved form, stays out.
The reminder to export a backup, the one thing between a lost phone and lost travels, only existed 2,226 px down Settings. Once a backup is due (data and none for 30 days, not snoozed) the same reminder, with the same wording, Back up now and Later, now heads Places as one slim line; the Settings one is the same component.
The Twemoji flag glyph fills about 85% of the em a colour-emoji flag fills, so every flag in a list came out a size smaller than the layout was drawn for. The font face now carries size-adjust: 118%, which also covers canvases, so the map marker goes back to its original font size.
On a 4G phone with a 4x slower CPU nothing painted for about 7.5 s: the entry imported MapLibre's chunk (its stylesheet was assigned to that chunk, and the trip composer's route map imported it statically), so all of MapLibre downloaded and ran before the app started, and the first render then waited for every reference file while five preloads fought the bundle for bandwidth. MapLibre now loads with the map, index.html carries the top bar as a static shell, the first render waits only for the cities and their regions (the two files still preloaded), and airports, heritage sites, languages and article names merge in behind it with the gazetteer event; search, Places and the map refresh on it, and a reload straight onto Trips, the Journal or a place page waits for them. Measured on that profile (median of 3): first paint 7.6 s to 1.6 s, app usable 8.5 s to 4.9 s, map 8.5 s to 7.0 s.
Writing the screen into the address rewrote every load to `#/map`: a move to the new address lost the query's fragment it carried over, and a handoff's cleaned-up address gained a hash (moved.spec). The home screen is the bare address now, a fragment that names no screen counts as home, and the address is left as it came at load.
Resolutions: - referenceData.ts: the railway stations (5 MB) join the data that lands after the first paint, beside the airports and heritage sites; the first screen, the map in any mode, needs only the cities. A station source picked in Settings before they land is kept, not overwritten. - App.tsx: the address and Back keep this branch's history sync; the postcard composer, like the trip composer, stays out of the address and waits for the late data. The journal long-press joins the SVG star imports. - MapScreen.tsx: the stations segment of the map-mode pill takes a train SVG icon, like the other segments. - MapView.tsx: logged stations sit under your flags with the airports, and their chips draw with the flag font stack; main's resize-on-load guard stays. - useUi.ts, route.ts: opening a screen from the address closes the postcard composer too; station pages are place pages, so their address already opens them (e2e added). - Tests: the startup test waits for and checks the stations; the new map-resize fake gains listImages.
The full unit run ended in three unhandled rejections from mapLoad.spec, which fail the run even with every test green: the map redraws its flag markers once the flag font loads, through map.listImages, and the fake map had no such method.
With the stations loaded after the first paint, a logged station had no marker until its visits changed: the map resolved it against an empty station set, and the key guarding the source rebuild could not see the data land. Reset that key with the others when the reference data changes, which also clears the markers when Settings switches the station source to None.
A reload onto the map's Stations, Airports or Monuments mode could show "Nothing in this view" until the map moved: the list was built when the map first reported its view, which can come before those datasets land after the first paint, and it rebuilt only on a view or mode change. It now rebuilds when they land.
Picking a place from search or Places scrolled the map's list to its row with a smooth glide even when the system asks for reduced motion; the map's own camera already honoured that setting. The scroll now jumps there instead.
When the app's entry script failed to download, the reload message went below the static shell index.html now paints into #root: a full-height app frame, so the message sat out of view on a blank-looking page. It now takes the shell's place.
…paint The note on the preloads in index.html listed what follows the first render; the stations joined that list in the merge of main.
The stations joined the reference data that follows the first paint, but they are its biggest file (5 MB, about 1 MB compressed): fetched with the rest, their parse landed before the first render on a fast network and held the app's frame back by 200 to 400 ms on a CPU slowed fourfold. They are now fetched once the page is idle, and the airports and heritage sites no longer wait for them.
Fetched on idle, the 1 MB stations download still shared a throttled connection with MapLibre's code and the map's outlines, and the map drew about half a second later than before the stations existed. On the map they are now asked for from the map's load event (or when the map cannot start); the Stations mode and every other screen, which may read them and have no map to compete with, ask at once.
Backup keeps the shared reminder component, whose two buttons take the titles the accessibility pass gave the inline version.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Based on #31, whose branch it builds on; merge that first.
On Windows and on a Linux without a colour-emoji font, every country flag in the app renders as two letters or an empty box, because flags are Unicode flag characters and those systems have no glyphs for them. The app now ships the Twemoji country flags as a 78 KB font limited to the flag characters, so a browser fetches it only once a flag is on screen, and the service worker keeps it for offline use. Its CC-BY licence and source sit beside it in
public/fonts, and Settings credits it with the other data sources. Search results and the map's place card now lead with the flag, and at world zoom a country's own flags stack into one while they would overlap, so Europe no longer piles up.On a phone over 4G with a slow processor, the first screen painted after 7.6 seconds, because MapLibre and five reference files loaded before anything drew. MapLibre now loads with the map, the top bar paints from the HTML at once, and only the city and region lists are awaited; airports, monuments and the rest merge in after the first render. First paint went from 7.6 to 1.6 seconds and the app is usable at 4.9 instead of 8.5, the median of three runs each. The bytes transferred stay the same, only their order changed.
A reload used to land on the map whatever screen was open. The tab and the open city or country page now sit in the address, so a reload, Back and Forward return to them, while the home screen keeps its bare address.
The backup reminder also heads Places, not only the middle of Settings. On a 375 px phone the search field was 85 px wide and showed "Sea"; the brand collapses to its pin mark there, which gives the field 158 px. The search, star and map-mode icons are drawn as SVG, so they no longer vanish without an emoji font.