Skip to content

Commit

Permalink
Merge pull request #38 from HunterX18/main
Browse files Browse the repository at this point in the history
Sorting repos on the basis of stars and forks
  • Loading branch information
version0chiro authored Jun 25, 2021
2 parents e65c901 + b6722dc commit 65d405c
Show file tree
Hide file tree
Showing 4 changed files with 259 additions and 166 deletions.
71 changes: 50 additions & 21 deletions client/src/App.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,59 @@
import React,{ useContext, useState} from "react";
import React, { useContext, useState } from "react";

import './App.css';
import "./App.css";

import 'bootstrap/dist/css/bootstrap.min.css';
import "bootstrap/dist/css/bootstrap.min.css";
//Context
import {ThemeContext} from './Context/themeContext'
import { ThemeContext } from "./Context/themeContext";
//Components
import Header from './components/Header';
import CardSet from './components/CardSet';
import Navigation from './components/Navigation';
import Header from "./components/Header";
import CardSet from "./components/CardSet";
import Navigation from "./components/Navigation";

function App() {
const [language,setLanguage] = useState("Javascript");
const [pageNumber,setPageNumber] = useState(1);
const [maxPageNumber,setMaxPageNumber] = useState(100);
const [inputSearch, setInputSearch] = useState("");
const {theme} = useContext(ThemeContext)
function App() {
const [language, setLanguage] = useState("Javascript");
const [pageNumber, setPageNumber] = useState(1);
const [maxPageNumber, setMaxPageNumber] = useState(100);
const [inputSearch, setInputSearch] = useState("");
const [sortByForks, setSortByForks] = useState("desc");
const [sortByStars, setSortByStars] = useState("desc");
const { theme } = useContext(ThemeContext);

return (
<div className="App" style={{ backgroundColor: theme.bg, color: theme.color}}>
<Header setLanguage={setLanguage} setInputSearch={setInputSearch} inputSearch={inputSearch}/>
<Navigation setPageNumber={setPageNumber} pageNumber={pageNumber} maxPageNumber={maxPageNumber} />
<CardSet pageNumber={pageNumber} language={language} key={language+pageNumber} setMaxPageNumber={setMaxPageNumber} inputSearch={inputSearch}/>
<Navigation setPageNumber={setPageNumber} pageNumber={pageNumber} maxPageNumber={maxPageNumber} />
</div>
);
return (
<div
className="App"
style={{ backgroundColor: theme.bg, color: theme.color }}
>
<Header
setLanguage={setLanguage}
setInputSearch={setInputSearch}
inputSearch={inputSearch}
sortByStars={sortByStars}
setSortByStars={setSortByStars}
sortByForks={sortByForks}
setSortByForks={setSortByForks}
/>
<Navigation
setPageNumber={setPageNumber}
pageNumber={pageNumber}
maxPageNumber={maxPageNumber}
/>
<CardSet
pageNumber={pageNumber}
language={language}
key={language + pageNumber}
setMaxPageNumber={setMaxPageNumber}
sortByForks={sortByForks}
sortByStars={sortByStars}
inputSearch={inputSearch}
/>
<Navigation
setPageNumber={setPageNumber}
pageNumber={pageNumber}
maxPageNumber={maxPageNumber}
/>
</div>
);
}

export default App;
132 changes: 79 additions & 53 deletions client/src/components/CardSet.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,68 +2,94 @@ import React, { useState, useEffect, useContext } from "react";
import SingleCard from "./SingleCard";
import { makeStyles } from "@material-ui/core/styles";
import axios from "axios";
import {isEmpty} from 'lodash'
import { isEmpty } from "lodash";
//Context
import {ThemeContext} from '../Context/themeContext'
import { ThemeContext } from "../Context/themeContext";

const useStyles = makeStyles((theme) => ({
cardSet: {
margin: "15px",
},
cardSet: {
margin: "15px",
},
}));

const CardSet = (props) => {
const [repositores, setRepositories] = useState([])
const classes = useStyles()
const [isLoading, setIsLoading] = useState(false)
const [wasRejected, setWasRejected] = useState(false)
const {theme} = useContext(ThemeContext)
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`
const [repositores, setRepositories] = useState([]);
const classes = useStyles();
const [isLoading, setIsLoading] = useState(false);
const [wasRejected, setWasRejected] = useState(false);
const { theme } = useContext(ThemeContext);
let url = `https://api.github.com/search/repositories?q=good-first-issues:>0+language:${
props.language
}${props.inputSearch !== "" ? `:${props.inputSearch}+in%3Atitle` : ""}&page=${
props.pageNumber
}&per_page=10`;

useEffect(() => {
setIsLoading(true)
// GET request using axios inside useEffect React hook
axios.get(url)
.then(response => {
let maxPageNumber = Math.floor(response.data.total_count / 10);
props.setMaxPageNumber(maxPageNumber);
setRepositories(response.data.items);
setWasRejected(false)
setIsLoading(false)
}, rejection => {
if(rejection.response.status === 403) setWasRejected(true)
//console.log(rejection.response.data)
})
.catch(errors => {
setIsLoading(false)
//catch all (show some message)
//console.log(errors)
})
// empty dependency array means this effect will only run once (like componentDidMount in classes)
}, [props, url]);
let urlSuffix = "";
if (props.sortByStars == "desc") urlSuffix = "&sort=stars&order=desc";
else if (props.sortByStars == "asc") urlSuffix = "&sort=stars&order=asc";
else if (props.sortByForks == "desc") urlSuffix = "&sort=forks&order=desc";
else if (props.sortByForks == "asc") urlSuffix = "&sort=forks&order=asc";

useEffect(() => {
// console.log("stars", props.sortByStars, "forks", props.sortByForks);
url += urlSuffix;
console.log(url);
setIsLoading(true);
// GET request using axios inside useEffect React hook
axios
.get(url)
.then(
async (response) => {
// console.log(response.data.items);
let maxPageNumber = Math.floor(response.data.total_count / 10);
props.setMaxPageNumber(maxPageNumber);
setRepositories(response.data.items);

return (
<div style={{ backgroundColor: theme.bg, color: theme.color}}>
{isLoading ?
<div className="loader-container">
<div className="loader"></div>
<h5>Fetching some good first issues for you...</h5>
{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>}
</div>
:
<div className={classes.cardSet}>
{isEmpty(repositores) ? (
<div>
<h5>No issues to be shown at the moment, please try again later.</h5>
</div>
) : (
!isEmpty(repositores) && repositores.map((repo) => (<SingleCard key={repo.id} repo={repo} />))
)}
</div>
}
</div>
);
setWasRejected(false);
setIsLoading(false);
},
(rejection) => {
if (rejection.response.status === 403) setWasRejected(true);
//console.log(rejection.response.data)
}
)
.catch((errors) => {
setIsLoading(false);
//catch all (show some message)
//console.log(errors)
});
// empty dependency array means this effect will only run once (like componentDidMount in classes)
}, [props, url]);

return (
<div style={{ backgroundColor: theme.bg, color: theme.color }}>
{isLoading ? (
<div className="loader-container">
<div className="loader"></div>
<h5>Fetching some good first issues for you...</h5>
{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>
)}
</div>
) : (
<div className={classes.cardSet}>
{isEmpty(repositores) ? (
<div>
<h5>
No issues to be shown at the moment, please try again later.
</h5>
</div>
) : (
!isEmpty(repositores) &&
repositores.map((repo) => <SingleCard key={repo.id} repo={repo} />)
)}
</div>
)}
</div>
);
};

export default CardSet;
154 changes: 100 additions & 54 deletions client/src/components/Header.js
Original file line number Diff line number Diff line change
@@ -1,66 +1,112 @@
import { Navbar, Nav, NavDropdown, Form, Button } from "react-bootstrap";
import { useContext, useState } from "react";
import langugagesData from "../data/languages.json";
import { useDebouncedCallback } from 'use-debounce';
import { useDebouncedCallback } from "use-debounce";
//Context
import {ThemeContext} from '../Context/themeContext'
import { ThemeContext } from "../Context/themeContext";

const Header = (props) => {
const [language, setLanguage] = useState("javascript");
const {theme, changeTheme} = useContext(ThemeContext)
const [inputSearch, setInputSearch] = useState("");
const [language, setLanguage] = useState("javascript");
const { theme, changeTheme } = useContext(ThemeContext);
const [inputSearch, setInputSearch] = useState("");

const debouncedInput = useDebouncedCallback((value) => {
props.setInputSearch(value);
},
// delay in ms
1000
);
const debouncedInput = useDebouncedCallback(
(value) => {
props.setInputSearch(value);
},
// delay in ms
1000
);

const handleInputSearch = (inputValue) => {
setInputSearch(inputValue);
debouncedInput(inputValue);
}
const handleInputSearch = (inputValue) => {
setInputSearch(inputValue);
debouncedInput(inputValue);
};

return (
<div style={{color: theme.color}}>
<Navbar bg={theme.mode} variant={theme.mode} expand="lg">
<Navbar.Brand href="#home">Find Me Issues</Navbar.Brand>
{theme.mode === 'light' ?
<Button onClick={changeTheme}><i className="fa fa-moon-o" aria-hidden="true"></i></Button>
:
<Button onClick={changeTheme}><i className="fa fa-sun-o" aria-hidden="true"></i></Button>
}
<Nav className="mr-auto"></Nav>
<Form inline>
<label className="mr-sm-3">
<span>Find specific content in the project description</span>
<input type="text" value={inputSearch} onChange={(e) => handleInputSearch(e.target.value)} />
</label>
<div id="outlined-basic" className="mr-sm-2">{language}</div>
<NavDropdown title="Select Language" id="basic-nav-dropdown">
<div style={{height:"400px", overflowY:"auto"}}>
{langugagesData.languages.map((lang, index) => {
return (
<div key={index}>
<NavDropdown.Item
onClick={() => {
setLanguage(lang);
props.setLanguage(lang);
}}
>
{lang}
</NavDropdown.Item>
<NavDropdown.Divider />
</div>
);
})}
</div>
</NavDropdown>
</Form>
</Navbar>
</div>
);
const handleSortByStars = () => {
props.setSortByForks("");
if (props.sortByStars == "desc") props.setSortByStars("asc");
else props.setSortByStars("desc");
};
const handleSortByForks = () => {
props.setSortByStars("");
if (props.sortByForks == "desc") props.setSortByForks("asc");
else props.setSortByForks("desc");
};

return (
<div style={{ color: theme.color }}>
<Navbar bg={theme.mode} variant={theme.mode} expand="lg">
<Navbar.Brand href="#home">Find Me Issues</Navbar.Brand>
{theme.mode === "light" ? (
<Button onClick={changeTheme}>
<i className="fa fa-moon-o" aria-hidden="true"></i>
</Button>
) : (
<Button onClick={changeTheme}>
<i className="fa fa-sun-o" aria-hidden="true"></i>
</Button>
)}
<Nav className="mr-auto"></Nav>

<button
style={{
margin: "3px",
borderRadius: "5px",
backgroundColor: "blue",
color: "white",
}}
onClick={handleSortByStars}
>
sort by stars
</button>
<button
style={{
margin: "3px",
borderRadius: "5px",
backgroundColor: "blue",
color: "white",
}}
onClick={handleSortByForks}
>
sort by forks
</button>

<Form inline>
<label className="mr-sm-3">
<span>Find specific content in the project description</span>
<input
type="text"
value={inputSearch}
onChange={(e) => handleInputSearch(e.target.value)}
/>
</label>
<div id="outlined-basic" className="mr-sm-2">
{language}
</div>
<NavDropdown title="Select Language" id="basic-nav-dropdown">
<div style={{ height: "400px", overflowY: "auto" }}>
{langugagesData.languages.map((lang, index) => {
return (
<div key={index}>
<NavDropdown.Item
onClick={() => {
setLanguage(lang);
props.setLanguage(lang);
}}
>
{lang}
</NavDropdown.Item>
<NavDropdown.Divider />
</div>
);
})}
</div>
</NavDropdown>
</Form>
</Navbar>
</div>
);
};

export default Header;
Loading

0 comments on commit 65d405c

Please sign in to comment.