Skip to content

Commit 65d405c

Browse files
Merge pull request #38 from HunterX18/main
Sorting repos on the basis of stars and forks
2 parents e65c901 + b6722dc commit 65d405c

File tree

4 files changed

+259
-166
lines changed

4 files changed

+259
-166
lines changed

client/src/App.js

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,59 @@
1-
import React,{ useContext, useState} from "react";
1+
import React, { useContext, useState } from "react";
22

3-
import './App.css';
3+
import "./App.css";
44

5-
import 'bootstrap/dist/css/bootstrap.min.css';
5+
import "bootstrap/dist/css/bootstrap.min.css";
66
//Context
7-
import {ThemeContext} from './Context/themeContext'
7+
import { ThemeContext } from "./Context/themeContext";
88
//Components
9-
import Header from './components/Header';
10-
import CardSet from './components/CardSet';
11-
import Navigation from './components/Navigation';
9+
import Header from "./components/Header";
10+
import CardSet from "./components/CardSet";
11+
import Navigation from "./components/Navigation";
1212

13-
function App() {
14-
const [language,setLanguage] = useState("Javascript");
15-
const [pageNumber,setPageNumber] = useState(1);
16-
const [maxPageNumber,setMaxPageNumber] = useState(100);
17-
const [inputSearch, setInputSearch] = useState("");
18-
const {theme} = useContext(ThemeContext)
13+
function App() {
14+
const [language, setLanguage] = useState("Javascript");
15+
const [pageNumber, setPageNumber] = useState(1);
16+
const [maxPageNumber, setMaxPageNumber] = useState(100);
17+
const [inputSearch, setInputSearch] = useState("");
18+
const [sortByForks, setSortByForks] = useState("desc");
19+
const [sortByStars, setSortByStars] = useState("desc");
20+
const { theme } = useContext(ThemeContext);
1921

20-
return (
21-
<div className="App" style={{ backgroundColor: theme.bg, color: theme.color}}>
22-
<Header setLanguage={setLanguage} setInputSearch={setInputSearch} inputSearch={inputSearch}/>
23-
<Navigation setPageNumber={setPageNumber} pageNumber={pageNumber} maxPageNumber={maxPageNumber} />
24-
<CardSet pageNumber={pageNumber} language={language} key={language+pageNumber} setMaxPageNumber={setMaxPageNumber} inputSearch={inputSearch}/>
25-
<Navigation setPageNumber={setPageNumber} pageNumber={pageNumber} maxPageNumber={maxPageNumber} />
26-
</div>
27-
);
22+
return (
23+
<div
24+
className="App"
25+
style={{ backgroundColor: theme.bg, color: theme.color }}
26+
>
27+
<Header
28+
setLanguage={setLanguage}
29+
setInputSearch={setInputSearch}
30+
inputSearch={inputSearch}
31+
sortByStars={sortByStars}
32+
setSortByStars={setSortByStars}
33+
sortByForks={sortByForks}
34+
setSortByForks={setSortByForks}
35+
/>
36+
<Navigation
37+
setPageNumber={setPageNumber}
38+
pageNumber={pageNumber}
39+
maxPageNumber={maxPageNumber}
40+
/>
41+
<CardSet
42+
pageNumber={pageNumber}
43+
language={language}
44+
key={language + pageNumber}
45+
setMaxPageNumber={setMaxPageNumber}
46+
sortByForks={sortByForks}
47+
sortByStars={sortByStars}
48+
inputSearch={inputSearch}
49+
/>
50+
<Navigation
51+
setPageNumber={setPageNumber}
52+
pageNumber={pageNumber}
53+
maxPageNumber={maxPageNumber}
54+
/>
55+
</div>
56+
);
2857
}
2958

3059
export default App;

client/src/components/CardSet.js

Lines changed: 79 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,68 +2,94 @@ import React, { useState, useEffect, useContext } from "react";
22
import SingleCard from "./SingleCard";
33
import { makeStyles } from "@material-ui/core/styles";
44
import axios from "axios";
5-
import {isEmpty} from 'lodash'
5+
import { isEmpty } from "lodash";
66
//Context
7-
import {ThemeContext} from '../Context/themeContext'
7+
import { ThemeContext } from "../Context/themeContext";
88

99
const useStyles = makeStyles((theme) => ({
10-
cardSet: {
11-
margin: "15px",
12-
},
10+
cardSet: {
11+
margin: "15px",
12+
},
1313
}));
1414

1515
const CardSet = (props) => {
16-
const [repositores, setRepositories] = useState([])
17-
const classes = useStyles()
18-
const [isLoading, setIsLoading] = useState(false)
19-
const [wasRejected, setWasRejected] = useState(false)
20-
const {theme} = useContext(ThemeContext)
21-
const url = `https://api.github.com/search/issues?q=state:open+label:good-first-issue+language:${props.language}${props.inputSearch !== "" ? `:${props.inputSearch}+in%3Atitle` : ''}&page=${props.pageNumber}&per_page=10`
16+
const [repositores, setRepositories] = useState([]);
17+
const classes = useStyles();
18+
const [isLoading, setIsLoading] = useState(false);
19+
const [wasRejected, setWasRejected] = useState(false);
20+
const { theme } = useContext(ThemeContext);
21+
let url = `https://api.github.com/search/repositories?q=good-first-issues:>0+language:${
22+
props.language
23+
}${props.inputSearch !== "" ? `:${props.inputSearch}+in%3Atitle` : ""}&page=${
24+
props.pageNumber
25+
}&per_page=10`;
2226

23-
useEffect(() => {
24-
setIsLoading(true)
25-
// GET request using axios inside useEffect React hook
26-
axios.get(url)
27-
.then(response => {
28-
let maxPageNumber = Math.floor(response.data.total_count / 10);
29-
props.setMaxPageNumber(maxPageNumber);
30-
setRepositories(response.data.items);
31-
setWasRejected(false)
32-
setIsLoading(false)
33-
}, rejection => {
34-
if(rejection.response.status === 403) setWasRejected(true)
35-
//console.log(rejection.response.data)
36-
})
37-
.catch(errors => {
38-
setIsLoading(false)
39-
//catch all (show some message)
40-
//console.log(errors)
41-
})
42-
// empty dependency array means this effect will only run once (like componentDidMount in classes)
43-
}, [props, url]);
27+
let urlSuffix = "";
28+
if (props.sortByStars == "desc") urlSuffix = "&sort=stars&order=desc";
29+
else if (props.sortByStars == "asc") urlSuffix = "&sort=stars&order=asc";
30+
else if (props.sortByForks == "desc") urlSuffix = "&sort=forks&order=desc";
31+
else if (props.sortByForks == "asc") urlSuffix = "&sort=forks&order=asc";
4432

33+
useEffect(() => {
34+
// console.log("stars", props.sortByStars, "forks", props.sortByForks);
35+
url += urlSuffix;
36+
console.log(url);
37+
setIsLoading(true);
38+
// GET request using axios inside useEffect React hook
39+
axios
40+
.get(url)
41+
.then(
42+
async (response) => {
43+
// console.log(response.data.items);
44+
let maxPageNumber = Math.floor(response.data.total_count / 10);
45+
props.setMaxPageNumber(maxPageNumber);
46+
setRepositories(response.data.items);
4547

46-
return (
47-
<div style={{ backgroundColor: theme.bg, color: theme.color}}>
48-
{isLoading ?
49-
<div className="loader-container">
50-
<div className="loader"></div>
51-
<h5>Fetching some good first issues for you...</h5>
52-
{wasRejected && <h5 style={{color:"red"}}>You are seeing this message because github imposes rate limit on requests. Please refresh the page or wait a couple of minutes.</h5>}
53-
</div>
54-
:
55-
<div className={classes.cardSet}>
56-
{isEmpty(repositores) ? (
57-
<div>
58-
<h5>No issues to be shown at the moment, please try again later.</h5>
59-
</div>
60-
) : (
61-
!isEmpty(repositores) && repositores.map((repo) => (<SingleCard key={repo.id} repo={repo} />))
62-
)}
63-
</div>
64-
}
65-
</div>
66-
);
48+
setWasRejected(false);
49+
setIsLoading(false);
50+
},
51+
(rejection) => {
52+
if (rejection.response.status === 403) setWasRejected(true);
53+
//console.log(rejection.response.data)
54+
}
55+
)
56+
.catch((errors) => {
57+
setIsLoading(false);
58+
//catch all (show some message)
59+
//console.log(errors)
60+
});
61+
// empty dependency array means this effect will only run once (like componentDidMount in classes)
62+
}, [props, url]);
63+
64+
return (
65+
<div style={{ backgroundColor: theme.bg, color: theme.color }}>
66+
{isLoading ? (
67+
<div className="loader-container">
68+
<div className="loader"></div>
69+
<h5>Fetching some good first issues for you...</h5>
70+
{wasRejected && (
71+
<h5 style={{ color: "red" }}>
72+
You are seeing this message because github imposes rate limit on
73+
requests. Please refresh the page or wait a couple of minutes.
74+
</h5>
75+
)}
76+
</div>
77+
) : (
78+
<div className={classes.cardSet}>
79+
{isEmpty(repositores) ? (
80+
<div>
81+
<h5>
82+
No issues to be shown at the moment, please try again later.
83+
</h5>
84+
</div>
85+
) : (
86+
!isEmpty(repositores) &&
87+
repositores.map((repo) => <SingleCard key={repo.id} repo={repo} />)
88+
)}
89+
</div>
90+
)}
91+
</div>
92+
);
6793
};
6894

6995
export default CardSet;

client/src/components/Header.js

Lines changed: 100 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,112 @@
11
import { Navbar, Nav, NavDropdown, Form, Button } from "react-bootstrap";
22
import { useContext, useState } from "react";
33
import langugagesData from "../data/languages.json";
4-
import { useDebouncedCallback } from 'use-debounce';
4+
import { useDebouncedCallback } from "use-debounce";
55
//Context
6-
import {ThemeContext} from '../Context/themeContext'
6+
import { ThemeContext } from "../Context/themeContext";
77

88
const Header = (props) => {
9-
const [language, setLanguage] = useState("javascript");
10-
const {theme, changeTheme} = useContext(ThemeContext)
11-
const [inputSearch, setInputSearch] = useState("");
9+
const [language, setLanguage] = useState("javascript");
10+
const { theme, changeTheme } = useContext(ThemeContext);
11+
const [inputSearch, setInputSearch] = useState("");
1212

13-
const debouncedInput = useDebouncedCallback((value) => {
14-
props.setInputSearch(value);
15-
},
16-
// delay in ms
17-
1000
18-
);
13+
const debouncedInput = useDebouncedCallback(
14+
(value) => {
15+
props.setInputSearch(value);
16+
},
17+
// delay in ms
18+
1000
19+
);
1920

20-
const handleInputSearch = (inputValue) => {
21-
setInputSearch(inputValue);
22-
debouncedInput(inputValue);
23-
}
21+
const handleInputSearch = (inputValue) => {
22+
setInputSearch(inputValue);
23+
debouncedInput(inputValue);
24+
};
2425

25-
return (
26-
<div style={{color: theme.color}}>
27-
<Navbar bg={theme.mode} variant={theme.mode} expand="lg">
28-
<Navbar.Brand href="#home">Find Me Issues</Navbar.Brand>
29-
{theme.mode === 'light' ?
30-
<Button onClick={changeTheme}><i className="fa fa-moon-o" aria-hidden="true"></i></Button>
31-
:
32-
<Button onClick={changeTheme}><i className="fa fa-sun-o" aria-hidden="true"></i></Button>
33-
}
34-
<Nav className="mr-auto"></Nav>
35-
<Form inline>
36-
<label className="mr-sm-3">
37-
<span>Find specific content in the project description</span>
38-
<input type="text" value={inputSearch} onChange={(e) => handleInputSearch(e.target.value)} />
39-
</label>
40-
<div id="outlined-basic" className="mr-sm-2">{language}</div>
41-
<NavDropdown title="Select Language" id="basic-nav-dropdown">
42-
<div style={{height:"400px", overflowY:"auto"}}>
43-
{langugagesData.languages.map((lang, index) => {
44-
return (
45-
<div key={index}>
46-
<NavDropdown.Item
47-
onClick={() => {
48-
setLanguage(lang);
49-
props.setLanguage(lang);
50-
}}
51-
>
52-
{lang}
53-
</NavDropdown.Item>
54-
<NavDropdown.Divider />
55-
</div>
56-
);
57-
})}
58-
</div>
59-
</NavDropdown>
60-
</Form>
61-
</Navbar>
62-
</div>
63-
);
26+
const handleSortByStars = () => {
27+
props.setSortByForks("");
28+
if (props.sortByStars == "desc") props.setSortByStars("asc");
29+
else props.setSortByStars("desc");
30+
};
31+
const handleSortByForks = () => {
32+
props.setSortByStars("");
33+
if (props.sortByForks == "desc") props.setSortByForks("asc");
34+
else props.setSortByForks("desc");
35+
};
36+
37+
return (
38+
<div style={{ color: theme.color }}>
39+
<Navbar bg={theme.mode} variant={theme.mode} expand="lg">
40+
<Navbar.Brand href="#home">Find Me Issues</Navbar.Brand>
41+
{theme.mode === "light" ? (
42+
<Button onClick={changeTheme}>
43+
<i className="fa fa-moon-o" aria-hidden="true"></i>
44+
</Button>
45+
) : (
46+
<Button onClick={changeTheme}>
47+
<i className="fa fa-sun-o" aria-hidden="true"></i>
48+
</Button>
49+
)}
50+
<Nav className="mr-auto"></Nav>
51+
52+
<button
53+
style={{
54+
margin: "3px",
55+
borderRadius: "5px",
56+
backgroundColor: "blue",
57+
color: "white",
58+
}}
59+
onClick={handleSortByStars}
60+
>
61+
sort by stars
62+
</button>
63+
<button
64+
style={{
65+
margin: "3px",
66+
borderRadius: "5px",
67+
backgroundColor: "blue",
68+
color: "white",
69+
}}
70+
onClick={handleSortByForks}
71+
>
72+
sort by forks
73+
</button>
74+
75+
<Form inline>
76+
<label className="mr-sm-3">
77+
<span>Find specific content in the project description</span>
78+
<input
79+
type="text"
80+
value={inputSearch}
81+
onChange={(e) => handleInputSearch(e.target.value)}
82+
/>
83+
</label>
84+
<div id="outlined-basic" className="mr-sm-2">
85+
{language}
86+
</div>
87+
<NavDropdown title="Select Language" id="basic-nav-dropdown">
88+
<div style={{ height: "400px", overflowY: "auto" }}>
89+
{langugagesData.languages.map((lang, index) => {
90+
return (
91+
<div key={index}>
92+
<NavDropdown.Item
93+
onClick={() => {
94+
setLanguage(lang);
95+
props.setLanguage(lang);
96+
}}
97+
>
98+
{lang}
99+
</NavDropdown.Item>
100+
<NavDropdown.Divider />
101+
</div>
102+
);
103+
})}
104+
</div>
105+
</NavDropdown>
106+
</Form>
107+
</Navbar>
108+
</div>
109+
);
64110
};
65111

66112
export default Header;

0 commit comments

Comments
 (0)