feature: migrate Angular 9 HN PWA to React 18 + Vite + TypeScript#324
feature: migrate Angular 9 HN PWA to React 18 + Vite + TypeScript#324devin-ai-integration[bot] wants to merge 2 commits into
Conversation
Co-Authored-By: Lukas Burger <lukaskburger@gmail.com>
🤖 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:
|
| useEffect(() => { | ||
| const media = window.matchMedia('(prefers-color-scheme: dark)'); | ||
| const handleChange = (event: MediaQueryListEvent) => { | ||
| setTheme(event.matches ? 'night' : 'default'); | ||
| }; | ||
| media.addEventListener('change', handleChange); | ||
| return () => media.removeEventListener('change', handleChange); | ||
| }, [setTheme]); |
There was a problem hiding this comment.
🚩 System color scheme listener overrides user's explicitly saved theme
The useEffect at src/contexts/SettingsContext.tsx:53-60 subscribes to system color scheme changes and calls setTheme(), which writes to localStorage. If a user explicitly selected the 'amoledblack' theme and later the OS switches between light/dark mode, the listener will overwrite their saved preference with either 'night' or 'default'. This was the same behavior in the old Angular SettingsService (src/app/shared/services/settings.service.ts:28-35), so it's a pre-existing design issue carried forward. A fix would be to only react to system changes when no explicit user preference is saved.
Was this helpful? React with 👍 or 👎 to provide feedback.
Debug
There was a problem hiding this comment.
Intentionally preserving this behavior for now. As you noted, it mirrors the original Angular SettingsService (src/app/shared/services/settings.service.ts:28-35), and the migration spec explicitly asks to replicate that logic 1:1, so I kept it identical to avoid behavioral drift in this PR. The "only react to system changes when no explicit preference is saved" improvement is a reasonable follow-up, but it's a behavior change beyond the scope of this migration — happy to do it in a separate PR if desired.
Co-Authored-By: Lukas Burger <lukaskburger@gmail.com>
| import type { User } from '../models/user'; | ||
| import type { PollResult } from '../models/poll-result'; | ||
|
|
||
| const baseUrl = 'https://node-hnapi.herokuapp.com'; |
There was a problem hiding this comment.
🚩 API base URL points to Heroku free-tier which was sunset in 2022
The baseUrl at src/services/hackernews-api.ts:5 is https://node-hnapi.herokuapp.com. Heroku removed its free tier in November 2022, so this endpoint may be unavailable. This is a pre-existing issue (the old Angular service used the same URL), not introduced by this PR, but the migration would have been a good opportunity to switch to a maintained API (e.g., the official HN Firebase API at https://hacker-news.firebaseio.com).
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Keeping this as-is. The base URL is specified verbatim in the migration spec, and it's still live — I verified end-to-end during testing that feeds, item details, comments, and user pages all load real data from https://node-hnapi.herokuapp.com. Switching to the official HN Firebase API would change the data-fetching shape (it returns item IDs requiring per-item fan-out rather than ready-made feed/comment trees), which is out of scope for a 1:1 migration. Good candidate for a follow-up if the endpoint is ever retired.
Summary
Full rewrite of the Angular 9 Hacker News PWA to React 18 + TypeScript + Vite, replacing the Angular CLI/Webpack toolchain, RxJS-based services, and Angular Router with modern React equivalents. Behavior, routing, theming, and PWA support are preserved 1:1.
Key mappings:
HackerNewsAPIService(RxJSObservable+unfetch)src/services/hackernews-api.ts— plainasync/await+ nativefetchSettingsService(RxJSBehaviorSubject)src/contexts/SettingsContext.tsx— Context +useSettings()hookcomment.pipe.tssrc/utils/formatComment.tsapp.routes.ts)react-router-domv6<Routes>insrc/App.tsxclassmodelsinterface/typeinsrc/models/.ts+.html+.scsssrc/components&src/pages@vitejs/plugin-reactmanifest.jsonvite-plugin-pwaNotable details:
default/night/amoledblackclass is applied to the root<div>from settings, so the existing theme SCSS mixins continue to work unchanged.prefers-color-scheme: darkis subscribed viauseEffect+matchMedia.fetchItemContentis preserved — fortype === 'poll'items it fetches each option and sumspoll_votes_countviaPromise.all.Comment.tsxrenderscomment.commentsrecursively with a local collapseuseState; HTML content rendered viadangerouslySetInnerHTML(matching Angular's[innerHTML])./item/:idand/user/:idareReact.lazy+<Suspense fallback={<Loader />}>.usePageViewshook callsga('send', 'pageview')onuseLocation()changes.Removed Angular-specific files:
angular.json,karma.conf.js,tslint.json,e2e/,src/polyfills.ts,src/test.ts,src/main.ts, and thesrc/app/tree.Verification
npm run lint,npm run typecheck,npm run buildall pass.Link to Devin session: https://app.devin.ai/sessions/7e2b9c9ef4ff4c8eb5587812e54efa52
Requested by: @lburgers
Devin Review
b97dcbc