Skip to content
Open
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
2 changes: 2 additions & 0 deletions services/cacheService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ export const CacheKeys = {
prDetails: (owner: string, repo: string, prNumber: number) => `pr_${owner}_${repo}_${prNumber}`,
issueExpandedData: (owner: string, repo: string, issueNumber: number) => `expanded_${owner}_${repo}_${issueNumber}`,
workflowFiles: () => 'workflow_files',
deploymentsBySha: (owner: string, repo: string, sha: string) => `deployments_${owner}_${repo}_${sha}`,
deploymentStatuses: (owner: string, repo: string, deploymentId: number) => `deployment_statuses_${owner}_${repo}_${deploymentId}`,
};

// Type for cached expanded issue data (all data needed for expanded view)
Expand Down
60 changes: 60 additions & 0 deletions services/startupDataService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Startup data prefetcher for top repos
import { Repository, Issue } from '../types';
import { fetchIssues, fetchPullRequestDetails, fetchWorkflowRuns, fetchDeploymentsBySha, fetchDeploymentStatuses } from './githubService';
import { setCache, CacheKeys, getCached } from './cacheService';

export async function prefetchTopReposData(token: string, repos: Repository[]): Promise<void> {
const topRepos = repos.slice(0, 4);

for (const repo of topRepos) {
try {
// Fetch all issues (including PRs)
const issues = await fetchIssues(token, repo.owner.login, repo.name);
setCache(CacheKeys.repoIssues(repo.owner.login, repo.name), issues);

// Get PRs from issues
const prs = issues.filter(issue => issue.pull_request).slice(0, 10); // Limit to 10 PRs per repo

// Fetch PR details and related data for each PR
for (const pr of prs) {
const prNumber = pr.number;

// Fetch PR details
const prDetails = await fetchPullRequestDetails(token, repo.owner.login, repo.name, prNumber);
setCache(CacheKeys.prDetails(repo.owner.login, repo.name, prNumber), prDetails);

// Fetch deployments for the PR's head SHA
if (prDetails.head?.sha) {
try {
const deployments = await fetchDeploymentsBySha(token, repo.owner.login, repo.name, prDetails.head.sha);
// Cache deployments keyed by SHA
setCache(CacheKeys.deploymentsBySha(repo.owner.login, repo.name, prDetails.head.sha), deployments);

// Fetch statuses for each deployment
for (const deployment of deployments) {
try {
const statuses = await fetchDeploymentStatuses(token, repo.owner.login, repo.name, deployment.id);
setCache(CacheKeys.deploymentStatuses(repo.owner.login, repo.name, deployment.id), statuses);
} catch (statusErr) {
console.warn('Failed to fetch deployment statuses:', statusErr);
}
}
} catch (deployErr) {
console.warn('Failed to fetch deployments:', deployErr);
}
}
}

// Fetch workflow runs for the repo
try {
const workflowRuns = await fetchWorkflowRuns(token, repo.owner.login, repo.name);
setCache(CacheKeys.workflowRuns(repo.owner.login, repo.name), workflowRuns);
} catch (workflowErr) {
console.warn('Failed to fetch workflow runs:', workflowErr);
}

} catch (err) {
console.warn(`Failed to prefetch data for repo ${repo.full_name}:`, err);
}
}
}
24 changes: 14 additions & 10 deletions views/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ToastContainer, useToast } from '../components/Toast';
import { ThemeToggle } from '../components/ThemeToggle';
import { LogOut, RefreshCw, Plus, X, Lock, Globe, AlertTriangle, Key, FileCode, Sparkles } from 'lucide-react';
import { getCached, setCache, CacheKeys } from '../services/cacheService';
import { prefetchTopReposData } from '../services/startupDataService';

interface DashboardProps {
token: string;
Expand Down Expand Up @@ -158,16 +159,19 @@ export const Dashboard: React.FC<DashboardProps> = ({ token, user, onRepoSelect,
// If not cached, leave empty - issues will be cached when user visits repo detail
}

setRepoIssues(issuesMap);
} catch (err) {
// Only show error if we don't have cached data to display
if (!hasCachedData) {
setError('Failed to load repositories.');
}
} finally {
setLoading(false);
setIsRefreshing(false);
}
setRepoIssues(issuesMap);

// Prefetch expanded data for top repos in background for instant issue loading
prefetchTopReposData(token, data).catch(err => console.warn('Prefetch failed:', err));
} catch (err) {
// Only show error if we don't have cached data to display
if (!hasCachedData) {
setError('Failed to load repositories.');
}
} finally {
setLoading(false);
setIsRefreshing(false);
}
}, [token, repos.length]);

useEffect(() => {
Expand Down