Skip to content

Commit 42c05c2

Browse files
committed
allow unauthenticated requests to github api
1 parent ddfb6cf commit 42c05c2

File tree

2 files changed

+197
-116
lines changed

2 files changed

+197
-116
lines changed

app/page.tsx

Lines changed: 119 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
} from "@/components/ui/alert-dialog"
2222
import { toast } from "@/components/ui/custom-toast"
2323
import { Toaster } from "@/components/ui/toaster"
24+
import { Button } from "@/components/ui/button"
2425

2526
// Cache expiration time (24 hours in milliseconds)
2627
const CACHE_EXPIRATION = 24 * 60 * 60 * 1000
@@ -29,6 +30,7 @@ const REPOS_PER_PAGE = 10
2930

3031
export default function Home() {
3132
const [apiKey, setApiKey] = useState<string>("")
33+
const [noApiKey, setNoApiKey] = useState(false)
3234
const [collections, setCollections] = useState<Collection[]>([])
3335
const [selectedCollection, setSelectedCollection] = useState<Collection | null>(null)
3436
const [repositories, setRepositories] = useState<Repository[]>([])
@@ -181,6 +183,12 @@ export default function Home() {
181183

182184
// Load repositories for the imported collection
183185
loadRepositoriesForCollection(collection)
186+
toast({
187+
id: `import-${Date.now()}`,
188+
title: "Collection Imported",
189+
description: "The collection has been successfully imported.",
190+
variant: "default"
191+
})
184192
}
185193

186194
const isCacheValid = (collectionId: number) => {
@@ -192,8 +200,6 @@ export default function Home() {
192200
}
193201

194202
const loadRepositoriesForCollection = async (collection: Collection) => {
195-
if (!apiKey) return
196-
197203
// Reset pagination
198204
setPage(1)
199205
setHasMore(true)
@@ -228,7 +234,7 @@ export default function Home() {
228234
setProgressiveLoadingStatus(`Loading ${topic.name} (${i + 1}/${collection.topics.length})...`)
229235

230236
try {
231-
const repos = await fetchRepositoriesByTopic(apiKey, topic.name, minStars)
237+
const repos = await fetchRepositoriesByTopic(apiKey || undefined, topic.name, minStars)
232238

233239
// Process repositories as they come in
234240
for (const repo of repos) {
@@ -244,12 +250,31 @@ export default function Home() {
244250
}
245251
} catch (error) {
246252
console.error(`Error fetching repositories for topic ${topic.name}:`, error)
247-
toast({
248-
title: "Error",
249-
description: `Failed to load repositories for topic: ${topic.name}`,
250-
variant: "destructive",
251-
className: "bg-red-50 border-red-200 dark:bg-red-900/50 dark:border-red-800",
252-
})
253+
if (error instanceof Error) {
254+
if (error.message.includes('rate limit exceeded')) {
255+
toast({
256+
id: `rate-limit-${Date.now()}`,
257+
title: "Rate Limit Exceeded",
258+
description: error.message,
259+
variant: "destructive"
260+
})
261+
break
262+
} else if (error.message.includes('Consider adding a GitHub API key')) {
263+
toast({
264+
id: `api-key-recommended-${Date.now()}`,
265+
title: "API Key Recommended",
266+
description: "Adding a GitHub API key will provide better rate limits and reliability.",
267+
variant: "default"
268+
})
269+
} else {
270+
toast({
271+
id: `error-${Date.now()}`,
272+
title: "Error",
273+
description: `Failed to load repositories for topic: ${topic.name}`,
274+
variant: "destructive"
275+
})
276+
}
277+
}
253278
}
254279
}
255280

@@ -273,12 +298,14 @@ export default function Home() {
273298
setProgressiveLoadingStatus("")
274299
} catch (error) {
275300
console.error("Error fetching repositories:", error)
276-
toast({
277-
title: "Error",
278-
description: "Failed to load repositories",
279-
variant: "destructive",
280-
className: "bg-red-50 border-red-200 dark:bg-red-900/50 dark:border-red-800",
281-
})
301+
if (error instanceof Error) {
302+
toast({
303+
id: `error-${Date.now()}`,
304+
title: "Error",
305+
description: error.message,
306+
variant: "destructive"
307+
})
308+
}
282309
} finally {
283310
setIsLoading(false)
284311
}
@@ -342,26 +369,22 @@ export default function Home() {
342369
const handleToggleFavorite = (repoId: number) => {
343370
if (!selectedCollection) return
344371

345-
const collectionId = selectedCollection.id.toString()
346-
const currentFavorites = collectionFavorites[collectionId] || []
372+
const currentFavorites = collectionFavorites[selectedCollection.id] || []
347373

348-
// Check if the repository is already in favorites
349-
const existingIndex = currentFavorites.findIndex((fav) => fav.id === repoId)
350-
351-
if (existingIndex >= 0) {
374+
if (currentFavorites.some((repo) => repo.id === repoId)) {
352375
// Remove from favorites
353-
const updatedFavorites = [...currentFavorites]
354-
updatedFavorites.splice(existingIndex, 1)
376+
const updatedFavorites = currentFavorites.filter((repo) => repo.id !== repoId)
355377

356378
setCollectionFavorites({
357379
...collectionFavorites,
358-
[collectionId]: updatedFavorites,
380+
[selectedCollection.id]: updatedFavorites,
359381
})
360382

361383
toast({
384+
id: `remove-favorite-${Date.now()}`,
362385
title: "Removed from favorites",
363386
description: "Repository removed from favorites",
364-
className: "bg-purple-50 border-purple-200 dark:bg-purple-900/50 dark:border-purple-800",
387+
variant: "default"
365388
})
366389
} else {
367390
// Find the repository in either current or seen repositories
@@ -371,13 +394,14 @@ export default function Home() {
371394
// Add to favorites
372395
setCollectionFavorites({
373396
...collectionFavorites,
374-
[collectionId]: [...currentFavorites, repo],
397+
[selectedCollection.id]: [...currentFavorites, repo],
375398
})
376399

377400
toast({
401+
id: `add-favorite-${Date.now()}`,
378402
title: "Added to favorites",
379403
description: "Repository added to favorites",
380-
className: "bg-purple-50 border-purple-200 dark:bg-purple-900/50 dark:border-purple-800",
404+
variant: "default"
381405
})
382406
}
383407
}
@@ -389,9 +413,10 @@ export default function Home() {
389413
// Check if topic already exists in collection
390414
if (selectedCollection.topics.some((t) => t.name === topic)) {
391415
toast({
416+
id: `topic-exists-${Date.now()}`,
392417
title: "Topic already exists",
393418
description: `The topic "${topic}" is already in this collection.`,
394-
className: "bg-purple-50 border-purple-200 dark:bg-purple-900/50 dark:border-purple-800",
419+
variant: "default"
395420
})
396421
return
397422
}
@@ -414,44 +439,42 @@ export default function Home() {
414439
setSelectedCollection(updatedCollection)
415440

416441
toast({
442+
id: `topic-added-${Date.now()}`,
417443
title: "Topic added",
418444
description: `Added "${topic}" to collection. Click Refresh to load repositories.`,
419-
className: "bg-purple-50 border-purple-200 dark:bg-purple-900/50 dark:border-purple-800",
445+
variant: "default"
420446
})
421447
}
422448

423-
const handleDeleteCollection = (id: number) => {
424-
setCollectionToDelete(id)
449+
const handleDeleteCollection = (collectionId: number) => {
450+
setCollectionToDelete(collectionId)
425451
}
426452

427-
const confirmDeleteCollection = () => {
453+
const handleConfirmDelete = () => {
428454
if (collectionToDelete === null) return
429455

430456
const updatedCollections = collections.filter((c) => c.id !== collectionToDelete)
431457
setCollections(updatedCollections)
432458

433-
// If we deleted the selected collection, clear selection
459+
// If the deleted collection was selected, select the first available collection or null
434460
if (selectedCollection?.id === collectionToDelete) {
435-
setSelectedCollection(null)
436-
setRepositories([])
437-
setSeenRepositories([])
438-
}
439-
440-
// Remove from stored repositories
441-
if (storedRepos[collectionToDelete]) {
442-
const updatedStoredRepos = { ...storedRepos }
443-
delete updatedStoredRepos[collectionToDelete]
444-
setStoredRepos(updatedStoredRepos)
461+
setSelectedCollection(updatedCollections[0] || null)
445462
}
446463

447-
// Remove from collection favorites
448-
if (collectionFavorites[collectionToDelete]) {
449-
const updatedFavorites = { ...collectionFavorites }
450-
delete updatedFavorites[collectionToDelete]
451-
setCollectionFavorites(updatedFavorites)
452-
}
464+
// Remove from cache
465+
setStoredRepos((prev) => {
466+
const newStoredRepos = { ...prev }
467+
delete newStoredRepos[collectionToDelete]
468+
return newStoredRepos
469+
})
453470

454471
setCollectionToDelete(null)
472+
toast({
473+
id: `delete-${Date.now()}`,
474+
title: "Collection Deleted",
475+
description: "The collection has been successfully deleted.",
476+
variant: "default"
477+
})
455478
}
456479

457480
const openCreateModal = () => {
@@ -464,10 +487,24 @@ export default function Home() {
464487
setIsModalOpen(true)
465488
}
466489

467-
const handleRefresh = () => {
468-
if (selectedCollection) {
469-
loadRepositoriesForCollection(selectedCollection)
470-
}
490+
const handleRefresh = async () => {
491+
if (!selectedCollection) return
492+
493+
// Clear the cache for this collection
494+
setStoredRepos((prev) => {
495+
const newStoredRepos = { ...prev }
496+
delete newStoredRepos[selectedCollection.id]
497+
return newStoredRepos
498+
})
499+
500+
// Reload repositories
501+
await loadRepositoriesForCollection(selectedCollection)
502+
toast({
503+
id: `refresh-${Date.now()}`,
504+
title: "Collection Refreshed",
505+
description: "The collection has been refreshed with the latest data.",
506+
variant: "default"
507+
})
471508
}
472509

473510
// Get current collection favorites
@@ -478,26 +515,38 @@ export default function Home() {
478515

479516
return (
480517
<main className="min-h-screen bg-gray-50 dark:bg-gray-900 flex flex-col">
481-
{apiKey ? (
482-
<Navbar
483-
collections={collections}
484-
selectedCollection={selectedCollection}
485-
onSelectCollection={handleSelectCollection}
486-
onCreateCollection={openCreateModal}
487-
onEditCollection={openEditModal}
488-
onDeleteCollection={handleDeleteCollection}
489-
onRefresh={handleRefresh}
490-
onExportCollection={() => setIsExportModalOpen(true)}
491-
onImportCollection={() => setIsImportModalOpen(true)}
492-
isLoading={isLoading}
493-
/>
494-
) : null}
518+
<Navbar
519+
collections={collections}
520+
selectedCollection={selectedCollection}
521+
onSelectCollection={handleSelectCollection}
522+
onCreateCollection={openCreateModal}
523+
onEditCollection={openEditModal}
524+
onDeleteCollection={handleDeleteCollection}
525+
onRefresh={handleRefresh}
526+
onExportCollection={() => setIsExportModalOpen(true)}
527+
onImportCollection={() => setIsImportModalOpen(true)}
528+
isLoading={isLoading}
529+
/>
495530

496531
<div className="container mx-auto px-4 py-8 flex-grow">
497-
{!apiKey ? (
532+
{!apiKey && !noApiKey ? (
498533
<div className="max-w-md mx-auto">
499534
<h1 className="text-3xl font-bold mb-8 text-center text-purple-700 dark:text-purple-400">GitHubie</h1>
500-
<GitHubApiKeyForm onSubmit={handleApiKeySubmit} />
535+
<div className="space-y-4">
536+
<p className="text-gray-600 dark:text-gray-300 text-center">
537+
Add a GitHub API key for better rate limits and reliability, or continue without one.
538+
</p>
539+
<GitHubApiKeyForm onSubmit={handleApiKeySubmit} />
540+
<div className="flex justify-center">
541+
<Button
542+
onClick={() => setNoApiKey(true)}
543+
variant="outline"
544+
className="w-full"
545+
>
546+
Continue without API key
547+
</Button>
548+
</div>
549+
</div>
501550
</div>
502551
) : (
503552
<div className="space-y-6">
@@ -568,7 +617,7 @@ export default function Home() {
568617
Cancel
569618
</AlertDialogCancel>
570619
<AlertDialogAction
571-
onClick={confirmDeleteCollection}
620+
onClick={handleConfirmDelete}
572621
className="bg-red-500 hover:bg-red-600 dark:bg-red-600 dark:hover:bg-red-700"
573622
>
574623
Delete

0 commit comments

Comments
 (0)