├── README.md
├── public
├── robots.txt
├── favicon.ico
├── logo192.png
├── logo512.png
├── manifest.json
└── index.html
├── src
├── index.js
├── index.css
├── client.js
├── graphql.js
└── App.js
├── .gitignore
└── package.json
/README.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tchmr/udemy-grahpql-with-react-intro-search-repos/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tchmr/udemy-grahpql-with-react-intro-search-repos/HEAD/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tchmr/udemy-grahpql-with-react-intro-search-repos/HEAD/public/logo512.png
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 |
6 | ReactDOM.render(, document.getElementById('root'));
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/src/client.js:
--------------------------------------------------------------------------------
1 | import { ApolloClient } from 'apollo-client'
2 | import { ApolloLink } from 'apollo-link'
3 | import { HttpLink } from 'apollo-link-http'
4 | import { InMemoryCache } from 'apollo-cache-inmemory'
5 |
6 | const GITHUB_TOKEN = process.env.REACT_APP_GITHUB_TOKEN
7 |
8 | const headersLink = new ApolloLink((operation, forward) => {
9 | operation.setContext({
10 | headers: {
11 | Authorization: `Bearer ${GITHUB_TOKEN}`
12 | }
13 | })
14 | return forward(operation)
15 | })
16 |
17 | const endpoint = 'https://api.github.com/graphql'
18 | const httpLink = new HttpLink({ uri: endpoint })
19 | const link = ApolloLink.from([headersLink, httpLink])
20 |
21 | export default new ApolloClient({
22 | link,
23 | cache: new InMemoryCache()
24 | })
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "udemy-grahpql-with-react-intro-search-repos",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^4.2.4",
7 | "@testing-library/react": "^9.3.2",
8 | "@testing-library/user-event": "^7.1.2",
9 | "apollo-boost": "^0.1.16",
10 | "graphql": "14.0.2",
11 | "react": "^16.12.0",
12 | "react-apollo": "2.1.11",
13 | "react-dom": "^16.12.0",
14 | "react-scripts": "3.3.0"
15 | },
16 | "scripts": {
17 | "start": "react-scripts start",
18 | "build": "react-scripts build",
19 | "test": "react-scripts test",
20 | "eject": "react-scripts eject"
21 | },
22 | "eslintConfig": {
23 | "extends": "react-app"
24 | },
25 | "browserslist": {
26 | "production": [
27 | ">0.2%",
28 | "not dead",
29 | "not op_mini all"
30 | ],
31 | "development": [
32 | "last 1 chrome version",
33 | "last 1 firefox version",
34 | "last 1 safari version"
35 | ]
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/graphql.js:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag'
2 |
3 | export const SEARCH_REPOSITORIES = gql`
4 | query searchRepositories($after: String, $before: String, $first: Int, $last: Int, $query: String!) {
5 | search(after: $after, before: $before, first: $first, last: $last, query: $query, type: REPOSITORY) {
6 | repositoryCount
7 | pageInfo {
8 | endCursor
9 | hasNextPage
10 | hasPreviousPage
11 | startCursor
12 | }
13 | edges {
14 | cursor
15 | node {
16 | ... on Repository {
17 | id
18 | name
19 | url
20 | stargazers {
21 | totalCount
22 | }
23 | viewerHasStarred
24 | }
25 | }
26 | }
27 | }
28 | }
29 | `
30 |
31 | export const ADD_STAR = gql`
32 | mutation addStar($input: AddStarInput!) {
33 | addStar(input: $input) {
34 | starrable {
35 | id
36 | viewerHasStarred
37 | }
38 | }
39 | }
40 | `
41 |
42 | export const REMOVE_STAR = gql`
43 | mutation removeStar($input: RemoveStarInput!) {
44 | removeStar(input: $input) {
45 | starrable {
46 | id
47 | viewerHasStarred
48 | }
49 | }
50 | }
51 | `
52 |
53 | export const ME = gql`
54 | query me {
55 | user(login: "iteachonudemy") {
56 | id
57 | name
58 | }
59 | }
60 | `
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { ApolloProvider, Mutation, Query } from 'react-apollo'
3 | import client from './client.js'
4 | import { ADD_STAR, REMOVE_STAR, SEARCH_REPOSITORIES } from './graphql'
5 |
6 | const StarButton = props => {
7 | const { node, query, first, last, before, after } = props
8 | const totalCount = node.stargazers.totalCount
9 | const viewerHasStarred = node.viewerHasStarred
10 | const starCount = totalCount === 1 ? "1 star" : `${totalCount} stars`
11 | const StarStatus = ({addOrRemoveStar}) => {
12 | return (
13 |
43 | )
44 | }
45 |
46 | return (
47 |
50 | {
51 | addOrRemoveStar =>
52 | }
53 |
54 | )
55 | }
56 |
57 | const PER_PAGE = 5
58 | const DEFAULT_STATE = {
59 | after: null,
60 | before: null,
61 | first: PER_PAGE,
62 | last: null,
63 | query: ""
64 | }
65 |
66 | class App extends React.Component {
67 | constructor(props) {
68 | super(props)
69 | this.state = DEFAULT_STATE
70 |
71 | this.myRef = React.createRef()
72 | this.handleSubmit = this.handleSubmit.bind(this)
73 | }
74 |
75 | handleSubmit(event) {
76 | event.preventDefault()
77 |
78 | this.setState({
79 | query: this.myRef.current.value
80 | })
81 | }
82 |
83 | goNext(search) {
84 | this.setState({
85 | first: PER_PAGE,
86 | after: search.pageInfo.endCursor,
87 | last: null,
88 | before: null
89 | })
90 | }
91 |
92 | goPrevious(search) {
93 | this.setState({
94 | first: null,
95 | after: null,
96 | last: PER_PAGE,
97 | before: search.pageInfo.startCursor
98 | })
99 | }
100 |
101 | render() {
102 | const { query, first, last, before, after } = this.state
103 | return (
104 |
105 |
109 |
113 | {
114 | ({ loading, error, data }) => {
115 | if (loading) return 'Loading...'
116 | if (error) return `Error! ${error.message}`
117 |
118 | const search = data.search
119 | const repositoryCount = search.repositoryCount
120 | const repositoryUnit = repositoryCount === 1 ? 'Repository' : 'Repositories'
121 | const title = `GitHub Repositories Search Result - ${data.search.repositoryCount} ${repositoryUnit}`
122 |
123 | return (
124 |
125 | {title}
126 |
127 | {
128 | search.edges.map(edge => {
129 | const node = edge.node
130 | return (
131 | -
132 | {node.name}
133 |
134 |
135 |
136 | )
137 | })
138 | }
139 |
140 | {
141 | search.pageInfo.hasPreviousPage ?
142 |
145 | :
146 | null
147 | }
148 | {
149 | search.pageInfo.hasNextPage ?
150 |
153 | :
154 | null
155 | }
156 |
157 | )
158 | }
159 | }
160 |
161 |
162 | )
163 | }
164 | }
165 |
166 | export default App
167 |
--------------------------------------------------------------------------------