Skip to content

Commit d3932f4

Browse files
committed
react-rxjs
1 parent e32606f commit d3932f4

File tree

14 files changed

+366
-417
lines changed

14 files changed

+366
-417
lines changed

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"version": "0.1.0",
44
"private": true,
55
"dependencies": {
6+
"@react-rxjs/core": "^0.3.0",
67
"@types/classnames": "^2.2.10",
78
"@types/jest": "26.0.10",
89
"@types/node": "14.6.2",
@@ -11,14 +12,15 @@
1112
"@types/react-dom": "16.9.8",
1213
"@types/react-paginate": "^6.2.1",
1314
"@types/webpack-env": "^1.15.2",
14-
"axios": "^0.19.0",
1515
"classnames": "^2.2.6",
1616
"parse-link-header": "^1.0.1",
1717
"react": "^16.13.1",
1818
"react-dom": "^16.13.1",
19+
"react-error-boundary": "^2.3.1",
1920
"react-markdown": "^4.3.1",
2021
"react-paginate": "^6.3.2",
2122
"react-scripts": "^3.4.3",
23+
"rxjs": "^6.6.3",
2224
"typescript": "3.5.3"
2325
},
2426
"scripts": {

src/api/githubAPI.tsx

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import axios from 'axios'
1+
import { ajax } from 'rxjs/ajax'
22
import parseLink, { Links } from 'parse-link-header'
3+
import { map, pluck } from 'rxjs/operators'
4+
import { Observable } from 'rxjs'
35

46
export interface Label {
57
id: number
@@ -64,47 +66,40 @@ const getPageCount = (pageLinks: Links) => {
6466
}
6567
}
6668

67-
export async function getIssues(
69+
export function getIssues(
6870
org: string,
6971
repo: string,
7072
page = 1
71-
): Promise<IssuesResult> {
73+
): Observable<IssuesResult> {
7274
const url = `https://api.github.com/repos/${org}/${repo}/issues?per_page=25&page=${page}`
73-
74-
try {
75-
const issuesResponse = await axios.get<Issue[]>(url)
76-
let pageCount = 0
77-
const pageLinks = parseLink(issuesResponse.headers.link)
78-
79-
if (pageLinks !== null) {
80-
pageCount = getPageCount(pageLinks)
81-
}
82-
83-
return {
84-
pageLinks,
85-
pageCount,
86-
issues: issuesResponse.data,
87-
}
88-
} catch (err) {
89-
throw err
90-
}
75+
return ajax.get(url).pipe(
76+
map((r) => {
77+
let pageCount = 0
78+
const pageLinks = parseLink(r.xhr.getResponseHeader('link') as string)
79+
80+
if (pageLinks !== null) {
81+
pageCount = getPageCount(pageLinks)
82+
}
83+
84+
return {
85+
pageLinks,
86+
pageCount,
87+
issues: r.response as Issue[],
88+
}
89+
})
90+
)
9191
}
9292

93-
export async function getRepoDetails(org: string, repo: string) {
93+
export function getRepoOpenIssuesCount(org: string, repo: string) {
9494
const url = `https://api.github.com/repos/${org}/${repo}`
95-
96-
const { data } = await axios.get<RepoDetails>(url)
97-
return data
95+
return ajax.getJSON<RepoDetails>(url).pipe(pluck('open_issues_count'))
9896
}
9997

100-
export async function getIssue(org: string, repo: string, number: number) {
98+
export function getIssue(org: string, repo: string, number: number) {
10199
const url = `https://api.github.com/repos/${org}/${repo}/issues/${number}`
102-
103-
const { data } = await axios.get<Issue>(url)
104-
return data
100+
return ajax.getJSON<Issue>(url)
105101
}
106102

107-
export async function getComments(url: string) {
108-
const { data } = await axios.get<Comment[]>(url)
109-
return data
103+
export function getComments(url: string) {
104+
return ajax.getJSON<Comment[]>(url)
110105
}

src/app/App.tsx

Lines changed: 17 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,23 @@
1-
import React, { useState } from 'react'
1+
import React from 'react'
22
import './App.css'
3-
43
import { RepoSearchForm } from 'features/repoSearch/RepoSearchForm'
54
import { IssuesListPage } from 'features/issuesList/IssuesListPage'
65
import { IssueDetailsPage } from 'features/issueDetails/IssueDetailsPage'
7-
8-
const ORG = 'rails'
9-
const REPO = 'rails'
10-
11-
type CurrentDisplay =
12-
| {
13-
type: 'issues'
14-
}
15-
| {
16-
type: 'comments'
17-
issueId: number
18-
}
19-
20-
const App: React.FC = () => {
21-
const [org, setOrg] = useState(ORG)
22-
const [repo, setRepo] = useState(REPO)
23-
const [page, setPage] = useState(1)
24-
const [currentDisplay, setCurrentDisplay] = useState<CurrentDisplay>({
25-
type: 'issues',
26-
})
27-
28-
const setOrgAndRepo = (org: string, repo: string) => {
29-
setOrg(org)
30-
setRepo(repo)
31-
}
32-
33-
const setJumpToPage = (page: number) => {
34-
setPage(page)
35-
}
36-
37-
const showIssuesList = () => {
38-
setCurrentDisplay({ type: 'issues' })
39-
}
40-
41-
const showIssueComments = (issueId: number) => {
42-
setCurrentDisplay({ type: 'comments', issueId })
43-
}
44-
45-
let content
46-
47-
if (currentDisplay.type === 'issues') {
48-
content = (
49-
<React.Fragment>
50-
<RepoSearchForm
51-
org={org}
52-
repo={repo}
53-
setOrgAndRepo={setOrgAndRepo}
54-
setJumpToPage={setJumpToPage}
55-
/>
56-
<IssuesListPage
57-
org={org}
58-
repo={repo}
59-
page={page}
60-
setJumpToPage={setJumpToPage}
61-
showIssueComments={showIssueComments}
62-
/>
63-
</React.Fragment>
64-
)
65-
} else {
66-
const { issueId } = currentDisplay
67-
const key = `${org}/${repo}/${issueId}`
68-
content = (
69-
<IssueDetailsPage
70-
key={key}
71-
org={org}
72-
repo={repo}
73-
issueId={issueId}
74-
showIssuesList={showIssuesList}
75-
/>
76-
)
77-
}
78-
79-
return <div className="App">{content}</div>
80-
}
6+
import { useSelectedIssueId } from 'state'
7+
8+
const List: React.FC = () =>
9+
useSelectedIssueId() === null ? (
10+
<>
11+
<RepoSearchForm />
12+
<IssuesListPage />
13+
</>
14+
) : null
15+
16+
const App: React.FC = () => (
17+
<div className="App">
18+
<List />
19+
<IssueDetailsPage />
20+
</div>
21+
)
8122

8223
export default App

src/features/issueDetails/IssueComments.tsx

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,11 @@ import React from 'react'
22
import ReactMarkdown from 'react-markdown'
33

44
import { insertMentionLinks } from 'utils/stringUtils'
5-
import { Issue, Comment } from 'api/githubAPI'
5+
import { Comment } from 'api/githubAPI'
66
import { UserWithAvatar } from 'components/UserWithAvatar'
77

88
import styles from './IssueComments.module.css'
9-
10-
interface ICLProps {
11-
issue: Issue
12-
comments: Comment[]
13-
}
9+
import { useIssueComments } from 'state'
1410

1511
interface ICProps {
1612
comment: Comment
@@ -35,20 +31,8 @@ function IssueComment({ comment }: ICProps) {
3531
)
3632
}
3733

38-
export function IssueComments({ comments = [], issue }: ICLProps) {
39-
// The issue has no comments
40-
if (issue.comments === 0) {
41-
return <div className="issue-detail--no-comments">No comments</div>
42-
}
43-
44-
// The issue has comments, but they're not loaded yet
45-
if (!comments || comments.length === 0) {
46-
return (
47-
<div className="issue-detail--comments-loading">Comments loading...</div>
48-
)
49-
}
50-
51-
// Comments are loaded
34+
export const IssueComments: React.FC = () => {
35+
const comments = useIssueComments()
5236
return (
5337
<ul className={styles.commentsList}>
5438
{comments.map((comment) => (

0 commit comments

Comments
 (0)