Models, fetch-based API, settings context (migration 2/8) - #690
Models, fetch-based API, settings context (migration 2/8)#690charityquinn-cognition wants to merge 1 commit into
Conversation
Co-Authored-By: Charity Quinn <charity.quinn@cognition.ai>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
| function getInitialSettings(): Settings { | ||
| const openLinkInNewTab = localStorage.getItem('openLinkInNewTab'); | ||
| const titleFontSize = localStorage.getItem('titleFontSize'); | ||
| const listSpacing = localStorage.getItem('listSpacing'); | ||
|
|
||
| return { | ||
| showSettings: false, | ||
| openLinkInNewTab: openLinkInNewTab ? JSON.parse(openLinkInNewTab) : false, | ||
| theme: 'default', | ||
| titleFontSize: titleFontSize || '16', | ||
| listSpacing: listSpacing || '0', | ||
| }; | ||
| } |
There was a problem hiding this comment.
📝 Info: Saved theme applied only after first paint
getInitialSettings hardcodes theme: 'default' and reads the persisted theme only inside a later useEffect (SettingsContext.tsx). The Angular service applied it in the constructor before render, so users with a saved night theme can see a brief default-theme flash.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Accurate. getInitialSettings deliberately doesn't read theme synchronously because the effect that applies it also subscribes to prefers-color-scheme, and the saved value has to win over the media query — doing it in one place avoided duplicating that precedence logic. The flash is real though; the fix is to seed theme from localStorage in the initializer and let the effect only handle the media-query default when nothing is saved. Flagging to the requester rather than folding a behavior tweak into this PR unprompted.
Summary
Stacked on #689. Ports the framework-agnostic layer: models, the HN API service, the settings service, and the comment pipe. No UI yet, so nothing is wired up — PR3 onward consumes this.
Models (
src-react/shared/models/) become plain interfaces/types, keeping the API's wire names verbatim (time_ago,comments_count,poll_votes_count, and the misspelledcrated_time) so no mapping layer is needed.Story.typekeeps the item-kind union'poll' | 'story' | 'job'; the feed names are a separate union, sincefeed-type.type.tswas overloading one type for both concerns:API (
shared/api/hackernewsApi.ts) drops RxJS forasync/awaitoverfetch, samebaseUrland endpoints. Two behavioral notes:fetchJsonnow throws on non-2xx; the Angular version resolved with the error body, so a 500 could reach the UI as a "story".story.pollafter the caller had already received it:Settings (
shared/settings/SettingsContext.tsx) becomes context + provider +useSettings(), preserving the localStorage keys (openLinkInNewTab,theme,titleFontSize,listSpacing), theprefers-color-scheme: darkdefault with a livematchMedialistener (cleaned up on unmount), and the same mutators (toggleSettings,toggleOpenLinksInNewTab,setTheme,setFont,setSpacing).useSettingsthrows outside the provider rather than returningundefined.comment.pipe.ts→formatComments(count)utility, same output strings.Verified:
yarn react:buildpasses;GET /news?page=1against the live API returns 30 stories through the new module.Devin-Org: engineering
Link to Devin session: https://app.devin.ai/sessions/1ff25c6cf2f949458f82cc596fc79c65
Requested by: @charityquinn-cognition
Devin Review