├── 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 |
106 | 107 | 108 |
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 | --------------------------------------------------------------------------------