Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module.exports = {
root: true,
env: { browser: true, es2020: true, node: true },
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
parser: '@typescript-eslint/parser',
plugins: ['react-hooks'],
ignorePatterns: ['dist', 'node_modules', 'dev-dist', 'coverage', 'playwright-report', '.eslintrc.cjs'],
rules: {
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
'@typescript-eslint/no-explicit-any': 'off',
},
};
83 changes: 83 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
name: CI

on:
push:
pull_request:

jobs:
build:
name: Lint, typecheck, test and build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Install dependencies
run: npm ci

- name: Typecheck
run: npx tsc --noEmit

- name: Lint
run: npx eslint .

- name: Unit tests
run: npm test

- name: Build
run: npm run build

e2e:
name: Playwright end-to-end tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Check for Playwright config
id: playwright_config
run: |
if [ -f playwright.config.ts ]; then
echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "exists=false" >> "$GITHUB_OUTPUT"
fi

- name: Install dependencies
if: steps.playwright_config.outputs.exists == 'true'
run: npm ci

- name: Install Playwright browsers
if: steps.playwright_config.outputs.exists == 'true'
run: npx playwright install --with-deps chromium

- name: Run end-to-end tests
if: steps.playwright_config.outputs.exists == 'true'
continue-on-error: false
run: npm run test:e2e

- name: Check for Playwright report
id: playwright_report
if: always() && steps.playwright_config.outputs.exists == 'true'
run: |
if [ -d playwright-report ]; then
echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "exists=false" >> "$GITHUB_OUTPUT"
fi

- name: Upload Playwright report
if: always() && steps.playwright_report.outputs.exists == 'true'
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report
retention-days: 7
52 changes: 7 additions & 45 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,46 +1,8 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.

# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out

# dependencies
/node_modules

# profiling files
chrome-profiler-events.json
speed-measure-plugin.json

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*

# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings

# System Files
node_modules
dist
dev-dist
coverage
playwright-report
test-results
.DS_Store
Thumbs.db
*.log
23 changes: 0 additions & 23 deletions .travis.yml

This file was deleted.

40 changes: 24 additions & 16 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,35 @@
# Contributing

Thank you for your interest in contributing! Please feel free to put up a PR for any issue or feature request.
Even if you have little to no experience with Angular, I'll be more than happy to help. :)
Even if you have little to no experience with React, I'll be more than happy to help. :)

## Setup

1. Fork the repo
2. Clone your fork
3. Make a branch for your feature or bug fix
4. If you don't have Angular CLI installed: `npm install -g angular-cli@latest`
5. `ng init`
6. Type `n` for each file to not overwrite any file changes
7. Run `npm start` and open `localhost:4200` in a browser
8. Work your magic
9. Run `npm run build` or `npm run static-serve` to kick off a production build and make sure nothing is broken
10. To test service worker changes:
* `npm run build` to kick off a fresh build and update the `dist/` directory
* `npm run precache` to generate the service worker file
* `npm run static-serve` to load the application along with the service worker asset using [live-server](https://github.com/tapio/live-server)
11. Add yourself to the [contributor's list](https://github.com/hdjirdeh/angular2-hn#contributors) in the README!
12. Commit your changes and reference the issue you're addressing (for example: `git commit -am 'Commit message. Closes #5'`)
13. Push your branch to your fork
14. Create a pull request from your branch on your fork to `master` on this repo
15. Have your branch get merged in! :star2:
4. `npm install`
5. Run `npm start` and open the printed URL (`http://localhost:5173` by default) in a browser
6. Work your magic
7. Before pushing, make sure the checks that run in CI pass locally:
* `npm run typecheck`
* `npm run lint`
* `npm test`
* `npm run build`
* `npm run test:e2e` if you touched behaviour covered by the Playwright suite
8. To test service worker changes (they are inactive in development):
* `npm run build` to generate `dist/`, including `sw.js` and `manifest.webmanifest`
* `npm run preview` to serve the production build with the service worker registered
9. Add yourself to the [contributor's list](https://github.com/hdjirdeh/angular2-hn#contributors) in the README!
10. Commit your changes and reference the issue you're addressing (for example: `git commit -am 'Commit message. Closes #5'`)
11. Push your branch to your fork
12. Create a pull request from your branch on your fork to `master` on this repo
13. Have your branch get merged in! :star2:

## Tests

Unit and component tests live next to the code they cover (`*.test.ts` / `*.test.tsx`) and run with Vitest, Testing Library and MSW. Shared test setup and fixtures are in `src/test`.

End-to-end tests run with Playwright via `npm run test:e2e`. Intercept network calls with `page.route` rather than relying on the live Hacker News API, which is often slow or unavailable.

If you experience a problem at any point, please don't hesitate to file an issue or send me a message!
112 changes: 66 additions & 46 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,88 +1,108 @@
<p align="center">
<a href="https://angular2-hn.firebaseapp.com">
<img alt="Angular 2 HN" title="Angular 2 HN" src="http://i.imgur.com/J303pQ4.png" width="150">
</a>
<img alt="React HN" title="React HN" src="http://i.imgur.com/J303pQ4.png" width="150">
</p>

<p align="center">
A progressive Hacker News client built with Angular
</p>

<p align="center">
<a href="https://angular2-hn.firebaseapp.com">View App</a>
A progressive Hacker News client built with React 18, TypeScript and Vite
</p>

<p align="center">
<a href="/CONTRIBUTING.md"><img alt="PRs Welcome" src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg"></a>
<a href="https://travis-ci.org/housseindjirdeh/angular2-hn"><img alt="Build Status" src="https://travis-ci.org/housseindjirdeh/angular2-hn.svg?branch=master"></a>
</p>

---

:zap: **Fast:** Service Worker App Shell + Dynamic Content model to achieve faster load times with and without a network.
:zap: **Fast:** Service worker precaching plus runtime caching of API responses for fast loads with and without a network.

:iphone: **Responsive:** Completely responsive UI that can be installed to your mobile home screen to provide a native feel.

:rocket: **Progressive:** [Lighthouse](https://github.com/GoogleChrome/lighthouse) score of 87/100.
:rocket: **Progressive:** Installable, offline-capable PWA with a web app manifest.

<p align="center">
<img src = "http://i.imgur.com/fzJzLFO.png" width=500>
</p>

## Mobile Preview
## Stack

<p align="center">
<img src = "http://i.imgur.com/ZloA1hn.gif">
</p>
- [React 18](https://react.dev/) with [TypeScript](https://www.typescriptlang.org/)
- [Vite](https://vitejs.dev/) for the dev server and production build
- [React Router](https://reactrouter.com/) for routing
- [Vitest](https://vitest.dev/) + [Testing Library](https://testing-library.com/) + [MSW](https://mswjs.io/) for unit and component tests
- [Playwright](https://playwright.dev/) for end-to-end tests
- [vite-plugin-pwa](https://vite-pwa-org.netlify.app/) (Workbox) for the service worker and manifest
- Sass for styling

## Laptop Preview
Data comes from the public [node-hnapi](https://github.com/cheeaun/node-hnapi) endpoint at `https://node-hnapi.herokuapp.com`.

<p align="center">
<img src = "http://i.imgur.com/MrKHaln.gif">
</p>
## Getting started

## Offline Support
```bash
npm install
npm start
```

This app uses [Workbox](https://workboxjs.org/) to generate a service worker as part of the build step to load quickly and work offline.
`npm start` runs the Vite dev server (printed URL, `http://localhost:5173` by default).

## Manifest
## Scripts

With Chromium based browsers for Android (Chrome, Opera, etc...), Angular 2 HN includes a Web App Manifest that allows you to install to your homescreen.
| Script | Description |
| --- | --- |
| `npm install` | Install dependencies |
| `npm start` | Start the Vite dev server (alias: `npm run dev`) |
| `npm run build` | Typecheck and build the production bundle into `dist/` (including the service worker) |
| `npm run preview` | Serve the production build locally, service worker included |
| `npm test` | Run the Vitest unit/component suite once |
| `npm run test:watch` | Run Vitest in watch mode |
| `npm run test:coverage` | Run the unit suite with a V8 coverage report |
| `npm run test:e2e` | Run the Playwright end-to-end suite |
| `npm run lint` | Run ESLint over the repository |
| `npm run typecheck` | Run `tsc --noEmit` |

<p align="center">
<img src = "http://i.imgur.com/1RaaNkr.png">
</p>
Service worker behaviour is not active in development. To exercise it, run `npm run build` followed by `npm run preview`.

## Themes
## Project layout

Built in theme engine!
```
public/assets Static icons and images served as-is
src/api Hacker News API client (hackerNewsApi.ts)
src/components
core Header, Footer and the Settings popup
feeds Feed list and individual story items
item-details Item page and recursive comment tree
shared Reusable Loader and ErrorMessage components
user User profile page
src/context React contexts, including SettingsContext
src/models Shared TypeScript models (story, comment, user, ...)
src/styles Global Sass, theme variables and media query helpers
src/test Test setup, MSW server, fixtures and render helpers
src/App.tsx Application shell, applies the active theme class
src/routes.tsx Route definitions
src/main.tsx Entry point
```

Current themes:
* Default
* Night
* Black (AMOLED)
### Routes

More to come!
- `/` redirects to `/news/1`
- `/news/:page`, `/newest/:page`, `/show/:page`, `/ask/:page`, `/jobs/:page` render the feed
- `/item/:id` renders an item with its comment tree (lazy loaded)
- `/user/:id` renders a user profile (lazy loaded)

## Areas of improvement
## Themes and settings

- Realtime updating using the Firebase SDK (may need to add option to settings so service worker can still rely on REST endpoints)
- Server side rendering
Settings live in `src/context/SettingsContext.tsx` and are persisted to `localStorage`. Open them from the cog in the header.

Feel free to send me feedback on [twitter](https://twitter.com/hdjirdeh) or [file an issue](https://github.com/hdjirdeh/angular2-hn/issues/new)! Feature requests are always welcome.
- **Theme:** `default`, `night` or `amoledblack`. The selected theme is applied as a class on the top-level element in `App.tsx`.
- **Open links in a new tab**
- **Title font size**
- **List spacing**

## Build process
## Offline support and manifest

Note: This project has been ejected (with AOT + production settings) in order to customize Webpack configurations.
`vite-plugin-pwa` generates `dist/sw.js` and `dist/manifest.webmanifest` during `npm run build`. The app shell is precached, and Hacker News API requests use a `NetworkFirst` runtime cache so recently viewed content stays available offline. The manifest allows the app to be installed to a mobile home screen.

- Clone or download the repo
- `npm install`
- `npm start` to run the application with webpack-dev-server or `npm build` to kick off a fresh build and update the output directory (`dist/`)
## Testing

Note: Any Service Worker changes will not be reflected when you run the application locally in development. To test service worker changes:
- `npm build`
- `npm run precache` to generate the service worker file
- `npm run static-serve` to load the application along with the service worker asset using [live-server](https://github.com/tapio/live-server)
Unit and component tests run in jsdom with Vitest and Testing Library; network calls are intercepted by MSW (`src/test/server.ts`) so tests never hit the live API. Playwright covers the end-to-end flows and should intercept network responses with `page.route` for the same reason — the public API is frequently slow or unavailable.

## Contributors

Expand Down
Loading
Loading