diff --git a/src/react/api/hackernews.ts b/src/react/api/hackernews.ts new file mode 100644 index 00000000..e0d7900c --- /dev/null +++ b/src/react/api/hackernews.ts @@ -0,0 +1,46 @@ +import { FeedName } from '../models/feed-name.type'; +import { PollResult } from '../models/poll-result'; +import { Story } from '../models/story'; +import { User } from '../models/user'; + +const BASE_URL = 'https://node-hnapi.herokuapp.com'; + +async function getJson(path: string): Promise { + const response = await fetch(`${BASE_URL}${path}`); + + if (!response.ok) { + throw new Error(`Request failed with status ${response.status}`); + } + + return response.json() as Promise; +} + +export function fetchFeed(feedName: FeedName, page: number): Promise { + return getJson(`/${feedName}?page=${page}`); +} + +export async function fetchItemContent(id: number): Promise { + const story = await getJson(`/item/${id}`); + + if (story.type === 'poll') { + story.poll_votes_count = 0; + const pollResults = await Promise.all( + story.poll.map((_, index) => fetchPollContent(story.id + index + 1)) + ); + + pollResults.forEach((pollResult, index) => { + story.poll[index] = pollResult; + story.poll_votes_count += pollResult.points; + }); + } + + return story; +} + +export function fetchPollContent(id: number): Promise { + return getJson(`/item/${id}`); +} + +export function fetchUser(id: string): Promise { + return getJson(`/user/${id}`); +} diff --git a/src/react/models/comment.ts b/src/react/models/comment.ts new file mode 100644 index 00000000..afc289a2 --- /dev/null +++ b/src/react/models/comment.ts @@ -0,0 +1,10 @@ +export class Comment { + id: number; + level: number; + user: string; + time: number; + time_ago: string; + content: string; + deleted: boolean; + comments: Comment[]; +} diff --git a/src/react/models/feed-name.type.ts b/src/react/models/feed-name.type.ts new file mode 100644 index 00000000..a87b6b76 --- /dev/null +++ b/src/react/models/feed-name.type.ts @@ -0,0 +1 @@ +export type FeedName = 'news' | 'newest' | 'show' | 'ask' | 'jobs'; diff --git a/src/react/models/feed-type.type.ts b/src/react/models/feed-type.type.ts new file mode 100644 index 00000000..5d3c15ba --- /dev/null +++ b/src/react/models/feed-type.type.ts @@ -0,0 +1 @@ +export type FeedType = 'poll' | 'story' | 'job'; diff --git a/src/react/models/poll-result.ts b/src/react/models/poll-result.ts new file mode 100644 index 00000000..bd8977d4 --- /dev/null +++ b/src/react/models/poll-result.ts @@ -0,0 +1,4 @@ +export class PollResult { + points: number; + content: string; +} diff --git a/src/react/models/settings.ts b/src/react/models/settings.ts new file mode 100644 index 00000000..cf08c432 --- /dev/null +++ b/src/react/models/settings.ts @@ -0,0 +1,7 @@ +export interface Settings { + showSettings: boolean; + openLinkInNewTab: boolean; + theme: string; + titleFontSize: string; + listSpacing: string; +} diff --git a/src/react/models/story.ts b/src/react/models/story.ts new file mode 100644 index 00000000..dab22e18 --- /dev/null +++ b/src/react/models/story.ts @@ -0,0 +1,21 @@ +import { Comment } from './comment'; +import { FeedType } from './feed-type.type'; +import { PollResult } from './poll-result'; + +export class Story { + id: number; + title: string; + points: number; + user: string; + time: number; + time_ago: number; + type: FeedType; + url: string; + domain: string; + comments: Comment[]; + comments_count: number; + poll: PollResult[]; + poll_votes_count: number; + deleted: boolean; + dead: boolean; +} diff --git a/src/react/models/user.ts b/src/react/models/user.ts new file mode 100644 index 00000000..33f0a7aa --- /dev/null +++ b/src/react/models/user.ts @@ -0,0 +1,8 @@ +export class User { + id: string; + crated_time: number; + created: string; + karma: number; + avg: number; + about: string; +} diff --git a/src/react/utils/comments.ts b/src/react/utils/comments.ts new file mode 100644 index 00000000..b2ed1f5f --- /dev/null +++ b/src/react/utils/comments.ts @@ -0,0 +1,8 @@ +export function formatComments(count?: number): string { + if (count !== undefined && count > 0) { + const label = count === 1 ? 'comment' : 'comments'; + return `${count} ${label}`; + } + + return 'discuss'; +} diff --git a/tsconfig.react.json b/tsconfig.react.json index 0435e7ec..53b11cb2 100644 --- a/tsconfig.react.json +++ b/tsconfig.react.json @@ -14,8 +14,9 @@ "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", - "types": ["react", "react-dom"] + "types": ["react", "react-dom"], + "strictPropertyInitialization": false }, - "include": ["src/main.tsx", "src/react/**/*.tsx"], + "include": ["src/main.tsx", "src/react/**/*.ts", "src/react/**/*.tsx"], "exclude": ["node_modules"] }