Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions src/hooks/useGitHubAuth.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { useState, useMemo } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { Octokit } from '@octokit/core';

export const useGitHubAuth = () => {
const [username, setUsername] = useState('');
const [token, setToken] = useState('');
const octokitRef = useRef(new Octokit());

const octokit = useMemo(() => {
if (!username) return null;
useEffect(() => {
if(token){
return new Octokit({ auth: token });
octokitRef.current = new Octokit({ auth: token });
return;
}
return new Octokit();
}, [username, token]);
octokitRef.current = new Octokit();
}, [token]);

const getOctokit = () => octokit;
const getOctokit = useCallback(() => octokitRef.current, []);
Comment thread
Harshit-collab104 marked this conversation as resolved.

return {
username,
Expand Down
242 changes: 186 additions & 56 deletions src/hooks/useGitHubData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,169 @@ interface FetchFilters {
state?: string;
}

interface ContributionScore {
mergedPrs: number;
openPrs: number;
closedPrs: number;
issuesCreated: number;
total: number;
}

interface FetchPaginatedResult {
items: GitHubItem[];
total: number;
}

const SCORE_WEIGHTS = {
mergedPr: 5,
openPr: 2,
closedPr: 1,
issueCreated: 1,
};

const emptyContributionScore: ContributionScore = {
mergedPrs: 0,
openPrs: 0,
closedPrs: 0,
issuesCreated: 0,
total: 0,
};

const fetchPaginated = async (
octokit: Octokit,
username: string,
type: 'issue' | 'pr',
page = 1,
perPage = 10,
filters: FetchFilters = {}
): Promise<FetchPaginatedResult> => {
let q = `author:${username} is:${type}`;

if (filters.search) {
q += ` ${filters.search} in:title`;
}

if (filters.repo) {
q += ` repo:${filters.repo}`;
}

if (filters.startDate) {
q += ` created:>=${filters.startDate}`;
}

if (filters.endDate) {
q += ` created:<=${filters.endDate}`;
}

if (filters.state === 'open' || filters.state === 'closed') {
q += ` is:${filters.state}`;
}

if (filters.state === 'merged' && type === 'pr') {
q += ` is:merged`;
}

const response = await octokit.request(
'GET /search/issues',
{
q,
sort: 'created',
order: 'desc',
per_page: perPage,
page,
}
);

return {
items: response.data.items as GitHubItem[],
total: response.data.total_count,
};
};

const buildScoreQuery = (
username: string,
type: 'issue' | 'pr',
qualifiers: string[],
filters: FetchFilters = {}
) => {
let q = `author:${username} is:${type} ${qualifiers.join(' ')}`;

if (filters.search) {
q += ` ${filters.search} in:title`;
}

if (filters.repo) {
q += ` repo:${filters.repo}`;
}

if (filters.startDate) {
q += ` created:>=${filters.startDate}`;
}

if (filters.endDate) {
q += ` created:<=${filters.endDate}`;
}

return q.trim();
};

const fetchCount = async (octokit: Octokit, q: string) => {
const response = await octokit.request('GET /search/issues', {
q,
per_page: 1,
page: 1,
});

return response.data.total_count;
};

const fetchContributionScore = async (
octokit: Octokit,
username: string,
filters: FetchFilters = {}
): Promise<ContributionScore> => {
const [
mergedPrs,
openPrs,
closedPrs,
issuesCreated,
] = await Promise.all([
fetchCount(
octokit,
buildScoreQuery(username, 'pr', ['is:merged'], filters)
),
fetchCount(
octokit,
buildScoreQuery(username, 'pr', ['is:open'], filters)
),
fetchCount(
octokit,
buildScoreQuery(
username,
'pr',
['is:closed', '-is:merged'],
filters
)
),
fetchCount(
octokit,
buildScoreQuery(username, 'issue', [], filters)
),
]);

return {
mergedPrs,
openPrs,
closedPrs,
issuesCreated,
total:
mergedPrs * SCORE_WEIGHTS.mergedPr +
openPrs * SCORE_WEIGHTS.openPr +
closedPrs * SCORE_WEIGHTS.closedPr +
issuesCreated * SCORE_WEIGHTS.issueCreated,
};
};

export const useGitHubData = (
getOctokit: () => Octokit | null
) => {
Expand All @@ -30,67 +193,14 @@ export const useGitHubData = (
const [error, setError] = useState('');
const [totalIssues, setTotalIssues] = useState(0);
const [totalPrs, setTotalPrs] = useState(0);
const [contributionScore, setContributionScore] =
useState<ContributionScore>(emptyContributionScore);
const [rateLimited, setRateLimited] = useState(false);

// Prevent stale responses overwriting latest data
const lastRequestId = useRef(0);
const abortControllerRef = useRef<AbortController | null>(null);

const fetchPaginated = async (
octokit: Octokit,
username: string,
type: 'issue' | 'pr',
page = 1,
perPage = 10,
filters: FetchFilters = {},
signal?: AbortSignal
) => {
let q = `author:${username} is:${type}`;

if (filters.search) {
q += ` ${filters.search} in:title`;
}

if (filters.repo) {
q += ` repo:${filters.repo}`;
}

if (filters.startDate) {
q += ` created:>=${filters.startDate}`;
}

if (filters.endDate) {
q += ` created:<=${filters.endDate}`;
}

if (filters.state === 'open' || filters.state === 'closed') {
q += ` is:${filters.state}`;
}

if (filters.state === 'merged' && type === 'pr') {
q += ` is:merged`;
}

const response = await octokit.request(
'GET /search/issues',
{
q,
sort: 'created',
order: 'desc',
per_page: perPage,
page,
request: {
signal,
},
}
);

return {
items: response.data.items as GitHubItem[],
total: response.data.total_count,
};
};

const fetchData = useCallback(
async (
username: string,
Expand Down Expand Up @@ -125,7 +235,7 @@ export const useGitHubData = (
const shouldFetchPrs =
activeTab === 'pr' || activeTab === 'both';

const requests: Promise<unknown>[] = [];
const requests: Promise<FetchPaginatedResult>[] = [];

if (shouldFetchIssues) {
requests.push(
Expand Down Expand Up @@ -214,6 +324,25 @@ export const useGitHubData = (
);
}

try {
const score = await fetchContributionScore(
octokit,
username,
filters
);

if (requestId === lastRequestId.current) {
setContributionScore(score);
}
} catch {
if (requestId === lastRequestId.current) {
setContributionScore(emptyContributionScore);
setError(
'Some GitHub data could not be fetched completely.'
);
}
}
Comment thread
Harshit-collab104 marked this conversation as resolved.

setRateLimited(false);
} catch (err: unknown) {
if (requestId !== lastRequestId.current || abortControllerRef.current !== controller) {
Expand Down Expand Up @@ -287,6 +416,7 @@ export const useGitHubData = (
prs,
totalIssues,
totalPrs,
contributionScore,
loading,
error,
rateLimited,
Expand Down
Loading
Loading