1
- import React , { useState } from 'react' ;
1
+ import React , { useState , useEffect } from 'react' ;
2
2
import { Github , Heart } from 'lucide-react' ;
3
3
import SearchBar from './components/SearchBar' ;
4
4
import UserList from './components/UserList' ;
5
5
import { GitHubUser , SearchResults } from './types/github' ;
6
6
import { ThemeProvider , useTheme } from './contexts/ThemeContext' ;
7
7
import { ThemeSwitcher } from './components/ThemeSwitcher' ;
8
- import { createTokenAuth } from '@octokit/auth-token' ;
9
8
import { Octokit } from '@octokit/rest' ;
10
9
import { AuthProvider , useAuth } from './contexts/AuthContext' ;
11
10
@@ -20,12 +19,19 @@ function AppContent() {
20
19
const [ error , setError ] = useState < string | null > ( null ) ;
21
20
const [ loading , setLoading ] = useState ( false ) ;
22
21
22
+ // Redirect user to home screen after signing in
23
+ useEffect ( ( ) => {
24
+ if ( isAuthenticated ) {
25
+ window . history . replaceState ( { } , document . title , '/' ) ; // Remove code from URL
26
+ }
27
+ } , [ isAuthenticated ] ) ;
28
+
23
29
const searchUsers = async ( page = 1 ) => {
24
30
if ( ! keyword . trim ( ) ) return ;
25
-
31
+
26
32
setLoading ( true ) ;
27
33
setError ( null ) ;
28
-
34
+
29
35
try {
30
36
const token = accessToken || import . meta. env . VITE_GITHUB_TOKEN ;
31
37
if ( ! token ) {
@@ -38,9 +44,9 @@ function AppContent() {
38
44
const searchResponse = await octokit . search . users ( {
39
45
q : query ,
40
46
page,
41
- per_page : 10
47
+ per_page : 10 ,
42
48
} ) ;
43
-
49
+
44
50
if ( searchResponse . data . items . length === 0 ) {
45
51
setUsers ( [ ] ) ;
46
52
setTotalCount ( 0 ) ;
@@ -53,12 +59,12 @@ function AppContent() {
53
59
try {
54
60
const [ userData , mostUsedLanguage ] = await Promise . all ( [
55
61
octokit . users . getByUsername ( { username : user . login } ) ,
56
- getMostUsedLanguage ( user . login , token )
62
+ getMostUsedLanguage ( user . login , token ) ,
57
63
] ) ;
58
-
64
+
59
65
return {
60
66
...userData . data ,
61
- most_used_language : mostUsedLanguage
67
+ most_used_language : mostUsedLanguage ,
62
68
} ;
63
69
} catch ( err ) {
64
70
return {
@@ -71,7 +77,7 @@ function AppContent() {
71
77
followers : 0 ,
72
78
following : 0 ,
73
79
company : null ,
74
- most_used_language : null
80
+ most_used_language : null ,
75
81
} ;
76
82
}
77
83
} )
@@ -83,8 +89,8 @@ function AppContent() {
83
89
} catch ( err ) {
84
90
console . error ( 'Search error:' , err ) ;
85
91
setError (
86
- err instanceof Error
87
- ? `Error: ${ err . message } `
92
+ err instanceof Error
93
+ ? `Error: ${ err . message } `
88
94
: 'An error occurred while searching. Please try again later.'
89
95
) ;
90
96
setUsers ( [ ] ) ;
@@ -97,26 +103,25 @@ function AppContent() {
97
103
const getMostUsedLanguage = async ( username : string , token : string ) => {
98
104
try {
99
105
const octokit = new Octokit ( { auth : token } ) ;
100
-
106
+
101
107
const { data : repos } = await octokit . repos . listForUser ( {
102
108
username,
103
109
sort : 'pushed' ,
104
- per_page : 10
110
+ per_page : 10 ,
105
111
} ) ;
106
-
107
- const languages = repos . map ( repo => repo . language ) . filter ( Boolean ) ;
108
-
112
+
113
+ const languages = repos . map ( ( repo ) => repo . language ) . filter ( Boolean ) ;
114
+
109
115
if ( languages . length === 0 ) return null ;
110
-
116
+
111
117
const languageCounts = languages . reduce < Record < string , number > > ( ( acc , lang ) => {
112
118
if ( lang ) {
113
119
acc [ lang ] = ( acc [ lang ] || 0 ) + 1 ;
114
120
}
115
121
return acc ;
116
122
} , { } ) ;
117
-
118
- return Object . entries ( languageCounts )
119
- . sort ( ( [ , a ] , [ , b ] ) => b - a ) [ 0 ] [ 0 ] ;
123
+
124
+ return Object . entries ( languageCounts ) . sort ( ( [ , a ] , [ , b ] ) => b - a ) [ 0 ] [ 0 ] ;
120
125
} catch {
121
126
return null ;
122
127
}
@@ -129,17 +134,13 @@ function AppContent() {
129
134
const renderAuthButton = ( ) => {
130
135
return (
131
136
< div className = "flex flex-col items-end gap-2" >
132
- { authError && (
133
- < div className = "text-red-500 text-sm" >
134
- { authError }
135
- </ div >
136
- ) }
137
+ { authError && < div className = "text-red-500 text-sm" > { authError } </ div > }
137
138
{ ! isAuthenticated && (
138
139
< button
139
140
onClick = { login }
140
141
className = { `px-4 py-2 rounded-md ${
141
- theme === 'dark'
142
- ? 'bg-white text-black hover:bg-gray-200'
142
+ theme === 'dark'
143
+ ? 'bg-white text-black hover:bg-gray-200'
143
144
: 'bg-black text-white hover:bg-gray-800'
144
145
} `}
145
146
>
@@ -157,12 +158,14 @@ function AppContent() {
157
158
< ThemeSwitcher />
158
159
{ renderAuthButton ( ) }
159
160
</ div >
160
-
161
+
161
162
< div className = "flex flex-col items-center" >
162
163
< div className = "text-center mb-12" >
163
164
< div className = "flex items-center justify-center space-x-3 mb-4" >
164
165
< Github className = { `w-10 h-10 ${ theme === 'dark' ? 'text-white' : 'text-black' } ` } />
165
- < h1 className = { `text-4xl font-bold ${ theme === 'dark' ? 'text-white' : 'text-black' } tracking-tight` } >
166
+ < h1
167
+ className = { `text-4xl font-bold ${ theme === 'dark' ? 'text-white' : 'text-black' } tracking-tight` }
168
+ >
166
169
GitHub Bio Search
167
170
</ h1 >
168
171
</ div >
@@ -172,7 +175,7 @@ function AppContent() {
172
175
</ div >
173
176
174
177
< div className = "w-full max-w-2xl" >
175
- < SearchBar
178
+ < SearchBar
176
179
keyword = { keyword }
177
180
location = { location }
178
181
setKeyword = { setKeyword }
@@ -182,7 +185,7 @@ function AppContent() {
182
185
</ div >
183
186
184
187
< div className = "w-full mt-8" >
185
- < UserList
188
+ < UserList
186
189
users = { users }
187
190
totalCount = { totalCount }
188
191
currentPage = { currentPage }
@@ -194,26 +197,27 @@ function AppContent() {
194
197
</ div >
195
198
</ div >
196
199
</ div >
197
-
198
- < footer className = { `py-6 ${ theme === 'dark' ? 'bg-black border-gray-800' : 'bg-white border-gray-100' } border-t` } >
200
+
201
+ < footer
202
+ className = { `py-6 ${ theme === 'dark' ? 'bg-black border-gray-800' : 'bg-white border-gray-100' } border-t` }
203
+ >
199
204
< div className = "container mx-auto px-4 text-center max-w-5xl" >
200
- < p className = { `text-sm ${ theme === 'dark' ? 'text-gray-400' : 'text-gray-600' } flex items-center justify-center gap-2` } >
205
+ < p
206
+ className = { `text-sm ${ theme === 'dark' ? 'text-gray-400' : 'text-gray-600' } flex items-center justify-center gap-2` }
207
+ >
201
208
Built with{ ' ' }
202
- < a
209
+ < a
203
210
href = "https://cursor.sh"
204
211
target = "_blank"
205
212
rel = "noopener noreferrer"
206
213
className = { `hover:text-${ theme === 'dark' ? 'white' : 'black' } transition-colors` }
207
214
>
208
215
Cursor
209
- </ a >
210
- { ' & ' }
211
- < Heart
212
- className = "w-4 h-4 text-red-500 inline-block animate-pulse fill-current"
213
- aria-label = "love"
214
- />
215
- { ' by ' }
216
- < a
216
+ </ a > { ' ' }
217
+ & { ' ' }
218
+ < Heart className = "w-4 h-4 text-red-500 inline-block animate-pulse fill-current" aria-label = "love" /> { ' ' }
219
+ by{ ' ' }
220
+ < a
217
221
href = "https://linkedin.com/in/sourcingdenis"
218
222
target = "_blank"
219
223
rel = "noopener noreferrer"
@@ -238,4 +242,4 @@ function App() {
238
242
) ;
239
243
}
240
244
241
- export default App ;
245
+ export default App ;
0 commit comments