From eab167352803400ffd869954ea64e87cf0c1a3b3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 28 May 2026 03:23:11 +0530 Subject: [PATCH 1/2] fix(search): #169 migrate deprecated GitHub issue queries to advanced_search --- src/hooks/useGitHubData.ts | 27 +++++++++++++------ .../ContributorProfile/ContributorProfile.tsx | 7 ++++- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/hooks/useGitHubData.ts b/src/hooks/useGitHubData.ts index f4c78cf6..8acbd205 100644 --- a/src/hooks/useGitHubData.ts +++ b/src/hooks/useGitHubData.ts @@ -21,6 +21,11 @@ interface FetchFilters { state?: string; } +type PaginatedSearchResult = { + items: GitHubItem[]; + total: number; +}; + export const useGitHubData = ( getOctokit: () => Octokit | null ) => { @@ -43,36 +48,42 @@ export const useGitHubData = ( perPage = 10, filters: FetchFilters = {} ) => { - let q = `author:${username} is:${type}`; + const queryParts: string[] = [ + `author:${username}`, + `is:${type}`, + ]; if (filters.search) { - q += ` ${filters.search} in:title`; + queryParts.push(`${filters.search} in:title`); } if (filters.repo) { - q += ` repo:${filters.repo}`; + queryParts.push(`repo:${filters.repo}`); } if (filters.startDate) { - q += ` created:>=${filters.startDate}`; + queryParts.push(`created:>=${filters.startDate}`); } if (filters.endDate) { - q += ` created:<=${filters.endDate}`; + queryParts.push(`created:<=${filters.endDate}`); } if (filters.state === 'open' || filters.state === 'closed') { - q += ` is:${filters.state}`; + queryParts.push(`is:${filters.state}`); } if (filters.state === 'merged' && type === 'pr') { - q += ` is:merged`; + queryParts.push('is:merged'); } + const q = queryParts.join(' AND '); + const response = await octokit.request( 'GET /search/issues', { q, + advanced_search: true, sort: 'created', order: 'desc', per_page: perPage, @@ -112,7 +123,7 @@ export const useGitHubData = ( const shouldFetchPrs = activeTab === 'pr' || activeTab === 'both'; - const requests: Promise[] = []; + const requests: Promise[] = []; if (shouldFetchIssues) { requests.push( diff --git a/src/pages/ContributorProfile/ContributorProfile.tsx b/src/pages/ContributorProfile/ContributorProfile.tsx index ed1b714d..b2f81cd2 100644 --- a/src/pages/ContributorProfile/ContributorProfile.tsx +++ b/src/pages/ContributorProfile/ContributorProfile.tsx @@ -29,8 +29,13 @@ export default function ContributorProfile() { const userData = await userRes.json(); setProfile(userData); + const searchParams = new URLSearchParams({ + q: `author:${username} AND is:pr`, + advanced_search: "true", + }); + const prsRes = await fetch( - `https://api.github.com/search/issues?q=author:${username}+type:pr` + `https://api.github.com/search/issues?${searchParams.toString()}` ); const prsData = await prsRes.json(); setPRs(prsData.items); From 4612bd9515b699089817cad5ca5b398356b23239 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 28 May 2026 04:14:07 +0530 Subject: [PATCH 2/2] fix(search): migrate deprecated search flows to advanced_search with defensive query sanitization and resilient fetch handling --- src/hooks/useGitHubData.ts | 4 +++- src/pages/ContributorProfile/ContributorProfile.tsx | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/hooks/useGitHubData.ts b/src/hooks/useGitHubData.ts index 8acbd205..101f43d6 100644 --- a/src/hooks/useGitHubData.ts +++ b/src/hooks/useGitHubData.ts @@ -54,7 +54,9 @@ export const useGitHubData = ( ]; if (filters.search) { - queryParts.push(`${filters.search} in:title`); + const escapedSearch = + filters.search.trim().replace(/"/g, '\\"'); + queryParts.push(`in:title:"${escapedSearch}"`); } if (filters.repo) { diff --git a/src/pages/ContributorProfile/ContributorProfile.tsx b/src/pages/ContributorProfile/ContributorProfile.tsx index b2f81cd2..bd3af425 100644 --- a/src/pages/ContributorProfile/ContributorProfile.tsx +++ b/src/pages/ContributorProfile/ContributorProfile.tsx @@ -37,8 +37,14 @@ export default function ContributorProfile() { const prsRes = await fetch( `https://api.github.com/search/issues?${searchParams.toString()}` ); + + if (!prsRes.ok) { + setPRs([]); + return; + } + const prsData = await prsRes.json(); - setPRs(prsData.items); + setPRs(prsData.items || []); } catch { toast.error("Failed to fetch user data."); } finally {