· 5 years ago · Jun 23, 2020, 02:46 PM
1import React, { useDebugValue, useEffect, useState } from 'react';
2import './App.scss';
3import Cell from './components/Cell';
4import Details from './components/Details';
5import Error from './components/Error';
6import Loading from './components/Loading';
7import Pagination from './components/Pagination';
8import Search from './components/Search';
9import Sort from './components/Sort';
10
11
12function App () {
13 const [ isLoading, setLoadingState ] = useState( false );
14 const [ errorMessage, setErrorMessage ] = useState( null );
15 const [ repositories, updateRepositories ] = useState( [] );
16 const [ selectedRepoDetails, updateSelectedDetails ] = useState( null );
17 const [ favoriteReposList, updateFavoriteReposList ] = useState( [] );
18 const [ page, setPage ] = useState( 0 );
19 const [ searchResultsCache, setSearchResultsCache ] = useState( new Map() );
20
21 // const searchResultsCache = new Map();
22 const maxCache = 10;
23
24 /**
25 * Adds the result to the cache Map and removes the surplus if the cache size is bigger than the max cache size.
26 * @param text
27 * @param result
28 */
29 function cacheResult ( text, result ) {
30 cache.set( text, result );
31
32 const keys = cache.keys();
33 for ( let i = 0; i < cache.size - maxCache; i++ ) {
34 cache.delete( keys.next().value );
35 }
36 }
37
38 /**
39 * Called when GitHub API calls finishes
40 * @param results - Object containing "error" and "repos" properties
41 * @type error - string or null
42 * @type repos - Object, contains "items" property, which is an array of RepoItem
43 * @type RepoItem - contains info about individual repo on github:
44 * id: repo id
45 * name: repo name
46 * created_at: timestamp when repo was created
47 * html_url: github url to the Repo
48 * stargazers_count: number of stars repo has
49 * owner: Object containing info about owner:
50 * login: github handle of the author
51 */
52 function onSearchResults ( results ) {
53
54 const { repos, error } = results;
55
56 if ( error ) {
57 setErrorMessage( error );
58 } else {
59 updateRepositories( repos || [] );
60 }
61 }
62
63 /**
64 *
65 * @param repoId - string, id of the repo to be added to favorites
66 */
67 function onAddToFavorite ( repoId ) {
68 const favList = [ ...favoriteReposList ];
69 const index = favList.indexOf( repoId );
70
71 if ( index > -1 ) {
72 favList.splice( index, 1 );
73 } else {
74 favList.push( repoId );
75 }
76
77 updateFavoriteReposList( favList );
78 }
79
80 const hasResults = !!repositories.length;
81
82 return (
83 <div className = "container">
84 <div className = "listContainer">
85 <Search
86 updateLoadingState = { setLoadingState }
87 onSearchResults = { onSearchResults }
88 searchResultsCache = { searchResultsCache }
89 maxCache = { maxCache }
90 />
91
92 { errorMessage && (
93 <Error onClear = { () => setErrorMessage( null ) } message = { errorMessage }/>
94 ) }
95
96 { isLoading && <Loading/> }
97
98 { hasResults && (
99 <div className = "resultContainer">
100 <Sort onSort = { updateRepositories } currentRepos = { repositories }/>
101 { repositories.slice( page * 20, page * 20 + 20 ).map( repo => (
102 <Cell
103 onAddToFavorite = { ( repoId ) => onAddToFavorite( repoId ) }
104 onPress = { () => updateSelectedDetails( repo ) }
105 key = { repo.id }
106 id = { repo.id }
107 avatar = { repo.avatar }
108 owner = { repo.owner }
109 title = { repo.title }
110 stars = { repo.stars }
111 timestamp = { repo.timestamp }
112 url = { repo.url }
113 isFavorite = { favoriteReposList.includes( repo.id ) }
114 />
115 ) ) }
116 { repositories.length > 20 && <Pagination
117 page = { page }
118 pagesCount = { Math.floor( repositories.length / 20 ) }
119 setPage = { setPage }
120 /> }
121 </div>
122 ) }
123 </div>
124
125 <div className = { `detailsContainer ${ selectedRepoDetails ? 'open' : '' }` }>
126 <Details
127 repo = { selectedRepoDetails }
128 onPressClose = { () => updateSelectedDetails( null ) }
129 />
130 </div>
131 </div>
132 );
133}
134
135export default App;