Always show map + feed info #42

Closed
opened 2026-03-28 13:06:26 +00:00 by maxtkc · 0 comments
Owner

Approach

Always initialize an empty feed on app startup (if no existing data), so the invariant "there is always a feed" holds throughout the app lifecycle. This eliminates all conditional "no feed loaded" branches and simplifies state management significantly.


Phase 1: Always initialize empty feed on startup

  • In src/index.ts (~line 217): remove the hasExistingRows branch entirely
  • Replace it with: always call initializeEmpty() if no existing rows, then unconditionally call updateFileList(), updateMap(), browseNavigation.refresh(), and enable the export button — same as the hasExistingRows === true path
  • Remove the welcome notification ('Welcome to edit.gtfs.zone! Create a new GTFS feed...')
  • createNewFeed() in src/modules/ui.ts (~line 1144) no longer needs hideMapOverlay() — remove that call

Phase 2: Remove the welcome overlay

  • Delete the #map-overlay <div> from src/index.html (~lines 336–351)
  • Delete showMapOverlay(), hideMapOverlay(), and showLoading() methods from src/modules/map-controller.ts (~lines 630–660)
  • Remove all call sites: hideMapOverlay() in ui.ts (~lines 265, 350, 1150) and any remaining references in index.ts
  • Remove the // Keep welcome overlay visible initially comment block in map-controller.ts (~lines 117–120)

There were more hideMapOverlay() / showLoading() call sites than the plan listed (also at the error path in loadGTFSFile and in loadGTFSFromURL), all removed.

Phase 3: File list — show all files unconditionally

  • In updateFileList() (src/modules/ui.ts ~line 409): remove the if (required.length > 0) / if (optional.length > 0) guards — always render required and optional sections (the other section retains its guard since empty feeds have no "other" files)
  • Remove the "No GTFS files loaded" placeholder text from src/index.html (~lines 454–459) — updateFileList() is now called at startup so it will always be populated
  • The export button should always be enabled — removed the hasFiles disabled check from updateFileList() and the export-btn.disabled = false calls in loadGTFSFile() and loadGTFSFromURL(); it is set enabled once at startup in index.ts

Phase 4: Always show Feed Information block

  • In src/modules/page-content-renderer.ts: change getFeedInfo() to return {} (empty object) instead of null when no rows exist — change return type from Record<string, unknown> | null to Record<string, unknown>
  • In renderHomePage() (~line 287): change ${feedInfo ? this.renderFeedInfoProperties(feedInfo) : ''} to ${this.renderFeedInfoProperties(feedInfo)} (unconditional)

Phase 5: Remove dead "load data first" guards

  • In toggleAddStopMode() (src/modules/ui.ts ~line 1285): remove the !this.gtfsParser.getFileDataSync('stops.txt') check and the "Load GTFS data first" warning — stops.txt always exists after empty init
  • In toggleEditStopsMode() (~line 1337): same removal

Bug fix: feed_info edits lost on refresh (discovered during testing)

After completing all phases, testing revealed that editing a feed_info field (e.g. feed_publisher_name) would show the correct notification and appear in the Changes tab, but the text box would immediately clear, and everything — including the patch — was gone after a page refresh.

Root cause: two bugs in initializeEmpty() in src/modules/gtfs-parser.ts:

  1. Array mismatch: gtfsData[filename].data = [] and the [] passed to setupVirtual were two separate array instances. persistDirtyBlobs reads gtfsData.data, while the virtual table mutates its own flat array — so blob saves always saw an empty array and were silently skipped.

  2. No initial feed_info row: The virtual table update handler returns early when byId has no entry for the key. With an empty feed_info table, every field edit was a no-op in memory. On reload, patch replay also silently failed for the same reason — leaving hasExistingRows = false and causing initializeEmpty() to call clearDatabase(), destroying the patches.

Fix (committed d313755): share the same array reference between gtfsData.data and setupVirtual; seed feed_info with a schema-keyed all-empty-string row; flush the seed blob to IDB immediately (not deferred) so patch replay always has a row to land on even after a quick refresh.


Relevant files

  • src/index.ts — startup initialization (~lines 217–241)
  • src/index.html — welcome overlay (~336–351), file list placeholder (~454–459)
  • src/modules/map-controller.ts — overlay methods (~630–660), init comment (~117–120)
  • src/modules/ui.tsupdateFileList() (~409), loadGTFSFile() (~265), loadGTFSFromURL() (~350), createNewFeed() (~1144), toggleAddStopMode() (~1285), toggleEditStopsMode() (~1337)
  • src/modules/page-content-renderer.tsgetFeedInfo() (~361), renderHomePage() (~287)
  • src/modules/gtfs-parser.tsinitializeEmpty() (~517), persistDirtyBlobs() (~399)

Original Issue

Hiding things causes problems, lets keep it simple

## Approach Always initialize an empty feed on app startup (if no existing data), so the invariant "there is always a feed" holds throughout the app lifecycle. This eliminates all conditional "no feed loaded" branches and simplifies state management significantly. --- ## Phase 1: Always initialize empty feed on startup - [x] In `src/index.ts` (~line 217): remove the `hasExistingRows` branch entirely - [x] Replace it with: always call `initializeEmpty()` if no existing rows, then unconditionally call `updateFileList()`, `updateMap()`, `browseNavigation.refresh()`, and enable the export button — same as the `hasExistingRows === true` path - [x] Remove the welcome notification (`'Welcome to edit.gtfs.zone! Create a new GTFS feed...'`) - [x] `createNewFeed()` in `src/modules/ui.ts` (~line 1144) no longer needs `hideMapOverlay()` — remove that call ## Phase 2: Remove the welcome overlay - [x] Delete the `#map-overlay` `<div>` from `src/index.html` (~lines 336–351) - [x] Delete `showMapOverlay()`, `hideMapOverlay()`, and `showLoading()` methods from `src/modules/map-controller.ts` (~lines 630–660) - [x] Remove all call sites: `hideMapOverlay()` in `ui.ts` (~lines 265, 350, 1150) and any remaining references in `index.ts` - [x] Remove the `// Keep welcome overlay visible initially` comment block in `map-controller.ts` (~lines 117–120) There were more `hideMapOverlay()` / `showLoading()` call sites than the plan listed (also at the error path in `loadGTFSFile` and in `loadGTFSFromURL`), all removed. ## Phase 3: File list — show all files unconditionally - [x] In `updateFileList()` (`src/modules/ui.ts` ~line 409): remove the `if (required.length > 0)` / `if (optional.length > 0)` guards — always render required and optional sections (the `other` section retains its guard since empty feeds have no "other" files) - [x] Remove the "No GTFS files loaded" placeholder text from `src/index.html` (~lines 454–459) — `updateFileList()` is now called at startup so it will always be populated - [x] The export button should always be enabled — removed the `hasFiles` disabled check from `updateFileList()` and the `export-btn.disabled = false` calls in `loadGTFSFile()` and `loadGTFSFromURL()`; it is set enabled once at startup in `index.ts` ## Phase 4: Always show Feed Information block - [x] In `src/modules/page-content-renderer.ts`: change `getFeedInfo()` to return `{}` (empty object) instead of `null` when no rows exist — change return type from `Record<string, unknown> | null` to `Record<string, unknown>` - [x] In `renderHomePage()` (~line 287): change `${feedInfo ? this.renderFeedInfoProperties(feedInfo) : ''}` to `${this.renderFeedInfoProperties(feedInfo)}` (unconditional) ## Phase 5: Remove dead "load data first" guards - [x] In `toggleAddStopMode()` (`src/modules/ui.ts` ~line 1285): remove the `!this.gtfsParser.getFileDataSync('stops.txt')` check and the "Load GTFS data first" warning — `stops.txt` always exists after empty init - [x] In `toggleEditStopsMode()` (~line 1337): same removal ## Bug fix: feed_info edits lost on refresh (discovered during testing) After completing all phases, testing revealed that editing a `feed_info` field (e.g. `feed_publisher_name`) would show the correct notification and appear in the Changes tab, but the text box would immediately clear, and everything — including the patch — was gone after a page refresh. Root cause: two bugs in `initializeEmpty()` in `src/modules/gtfs-parser.ts`: 1. **Array mismatch**: `gtfsData[filename].data = []` and the `[]` passed to `setupVirtual` were two separate array instances. `persistDirtyBlobs` reads `gtfsData.data`, while the virtual table mutates its own `flat` array — so blob saves always saw an empty array and were silently skipped. 2. **No initial `feed_info` row**: The virtual table `update` handler returns early when `byId` has no entry for the key. With an empty `feed_info` table, every field edit was a no-op in memory. On reload, patch replay also silently failed for the same reason — leaving `hasExistingRows = false` and causing `initializeEmpty()` to call `clearDatabase()`, destroying the patches. Fix (committed `d313755`): share the same array reference between `gtfsData.data` and `setupVirtual`; seed `feed_info` with a schema-keyed all-empty-string row; flush the seed blob to IDB immediately (not deferred) so patch replay always has a row to land on even after a quick refresh. --- ## Relevant files - `src/index.ts` — startup initialization (~lines 217–241) - `src/index.html` — welcome overlay (~336–351), file list placeholder (~454–459) - `src/modules/map-controller.ts` — overlay methods (~630–660), init comment (~117–120) - `src/modules/ui.ts` — `updateFileList()` (~409), `loadGTFSFile()` (~265), `loadGTFSFromURL()` (~350), `createNewFeed()` (~1144), `toggleAddStopMode()` (~1285), `toggleEditStopsMode()` (~1337) - `src/modules/page-content-renderer.ts` — `getFeedInfo()` (~361), `renderHomePage()` (~287) - `src/modules/gtfs-parser.ts` — `initializeEmpty()` (~517), `persistDirtyBlobs()` (~399) --- ## Original Issue Hiding things causes problems, lets keep it simple
maxtkc self-assigned this 2026-03-28 13:06:26 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
gtfs.zone/coloring-book#42
No description provided.