Skip to content

Commit fd72d8b

Browse files
committed
Optimistically populate components, then update
1 parent a9bb0e5 commit fd72d8b

File tree

5 files changed

+670
-247
lines changed

5 files changed

+670
-247
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
dist
22
node_modules
33
.env
4+
.astro

src/components/GitHubPRCount.tsx

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { useEffect, useState } from 'react';
2+
import { baseUrl } from '../lib/utils';
3+
4+
interface GitHubPRCountProps {
5+
initialCount: number | null;
6+
}
7+
8+
export default function GitHubPRCount({ initialCount }: GitHubPRCountProps) {
9+
const [prCount, setPrCount] = useState<number | null>(null);
10+
const [isLoading, setIsLoading] = useState(true);
11+
const [dataSource, setDataSource] = useState<'static' | 'live'>('static');
12+
13+
useEffect(() => {
14+
// Step 1: Initialize from Astro endpoint
15+
const initializeFromEndpoint = async () => {
16+
try {
17+
console.log('[Build Data] 🔄 Initializing PR count from static JSON file...');
18+
const response = await fetch(baseUrl('build-data-pr.json'));
19+
if (response.ok) {
20+
const data = await response.json();
21+
if (data.count !== null && data.count !== undefined) {
22+
setPrCount(data.count);
23+
setDataSource('static');
24+
setIsLoading(false);
25+
console.log(`[Build Data] ✓ Initialized from static JSON: ${data.count} open PRs`);
26+
return true;
27+
}
28+
}
29+
} catch (error) {
30+
console.error('[Build Data] ✗ Failed to initialize from static JSON:', error);
31+
}
32+
return false;
33+
};
34+
35+
// Step 2: Try to update with direct GitHub API call
36+
const updateFromGitHub = async () => {
37+
setIsLoading(true);
38+
console.log('[GitHub API] 🔄 Attempting to update PR count from GitHub API...');
39+
40+
try {
41+
const response = await fetch(
42+
'https://api.github.com/search/issues?q=repo:ipython/ipython+type:pr+state:open'
43+
);
44+
45+
if (response.ok) {
46+
const data = await response.json();
47+
const count = data.total_count || 0;
48+
setPrCount(count);
49+
setDataSource('live');
50+
console.log(`[GitHub API] ✓ Updated from GitHub: ${count} open PRs`);
51+
} else {
52+
console.error(`[GitHub API] ✗ Could not reach GitHub API: ${response.status} ${response.statusText}`);
53+
// Keep dataSource as 'static' since update failed
54+
setDataSource('static');
55+
}
56+
} catch (error) {
57+
console.error('[GitHub API] ✗ Could not reach GitHub API:', error);
58+
// Keep dataSource as 'static' since update failed
59+
setDataSource('static');
60+
} finally {
61+
setIsLoading(false);
62+
}
63+
};
64+
65+
// Initialize from endpoint, then try to update from GitHub
66+
initializeFromEndpoint().then(() => {
67+
// Try to update after a short delay
68+
setTimeout(updateFromGitHub, 100);
69+
});
70+
}, []);
71+
72+
if (prCount === null) {
73+
return null;
74+
}
75+
76+
return (
77+
<div className="gradient-stats-cyan-blue rounded-lg p-6 border border-theme-secondary/20">
78+
<div className="flex items-center justify-between">
79+
<div>
80+
<p className="text-sm text-gray-600 dark:text-gray-400 mb-1">
81+
Open Pull Requests
82+
{dataSource === 'static' && (
83+
<span className="ml-2 text-xs opacity-75">(static)</span>
84+
)}
85+
{dataSource === 'live' && (
86+
<span className="ml-2 text-xs opacity-75">(live)</span>
87+
)}
88+
{isLoading && (
89+
<span className="ml-2 text-xs opacity-75">(updating...)</span>
90+
)}
91+
</p>
92+
<p className="text-3xl font-bold text-gray-900 dark:text-white">{prCount}</p>
93+
</div>
94+
<a
95+
href="https://github.com/ipython/ipython/pulls"
96+
target="_blank"
97+
rel="noopener noreferrer"
98+
className="text-theme-secondary hover:text-theme-primary transition-colors"
99+
>
100+
<svg className="w-8 h-8" fill="currentColor" viewBox="0 0 16 16">
101+
<path d="M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z"></path>
102+
</svg>
103+
</a>
104+
</div>
105+
</div>
106+
);
107+
}

src/components/PyPIRelease.tsx

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import { useEffect, useState } from "react";
2+
import { baseUrl } from "../lib/utils";
3+
4+
interface PyPIReleaseProps {
5+
initialVersion: string | null;
6+
initialReleaseDate: string | null;
7+
}
8+
9+
export default function PyPIRelease({
10+
initialVersion,
11+
initialReleaseDate,
12+
}: PyPIReleaseProps) {
13+
const [version, setVersion] = useState<string | null>(null);
14+
const [releaseDate, setReleaseDate] = useState<string | null>(null);
15+
const [isLoading, setIsLoading] = useState(true);
16+
const [dataSource, setDataSource] = useState<"static" | "live">("static");
17+
18+
useEffect(() => {
19+
// Step 1: Initialize from static JSON file
20+
const initializeFromJSON = async () => {
21+
try {
22+
console.log(
23+
"[Build Data] 🔄 Initializing PyPI data from static JSON file..."
24+
);
25+
const response = await fetch(baseUrl("build-data-pypi.json"));
26+
if (response.ok) {
27+
const data = await response.json();
28+
if (data.version) {
29+
setVersion(data.version);
30+
setReleaseDate(data.releaseDate);
31+
setDataSource("static");
32+
setIsLoading(false);
33+
console.log(
34+
`[Build Data] ✓ Initialized from static JSON: v${data.version} (${
35+
data.releaseDate || "date unknown"
36+
})`
37+
);
38+
return true;
39+
}
40+
}
41+
} catch (error) {
42+
console.error(
43+
"[Build Data] ✗ Failed to initialize from static JSON:",
44+
error
45+
);
46+
}
47+
setIsLoading(false);
48+
return false;
49+
};
50+
51+
// Step 2: Try to update with direct PyPI API call
52+
const updateFromPyPI = async () => {
53+
setIsLoading(true);
54+
console.log(
55+
"[PyPI API] 🔄 Attempting to update PyPI data from PyPI API..."
56+
);
57+
58+
try {
59+
const response = await fetch("https://pypi.org/pypi/ipython/json");
60+
61+
if (response.ok) {
62+
const data = await response.json();
63+
const newVersion = data.info.version;
64+
const releases = data.releases[newVersion];
65+
let newReleaseDate: string | null = null;
66+
67+
if (releases && releases.length > 0) {
68+
const latestRelease = releases[releases.length - 1];
69+
if (latestRelease.upload_time) {
70+
const date = new Date(latestRelease.upload_time);
71+
newReleaseDate = date.toLocaleDateString("en-US", {
72+
year: "numeric",
73+
month: "long",
74+
day: "numeric",
75+
});
76+
}
77+
}
78+
79+
setVersion(newVersion);
80+
setReleaseDate(newReleaseDate);
81+
setDataSource("live");
82+
console.log(
83+
`[PyPI API] ✓ Updated from PyPI: v${newVersion} (${
84+
newReleaseDate || "date unknown"
85+
})`
86+
);
87+
} else {
88+
console.error(
89+
`[PyPI API] ✗ Could not reach PyPI API: ${response.status} ${response.statusText}`
90+
);
91+
// Don't change dataSource - keep it as 'static' since update failed
92+
}
93+
} catch (error) {
94+
console.error("[PyPI API] ✗ Could not reach PyPI API:", error);
95+
// Don't change dataSource - keep it as 'static' since update failed
96+
} finally {
97+
setIsLoading(false);
98+
}
99+
};
100+
101+
// Initialize from JSON, then try to update from PyPI
102+
initializeFromJSON().then((initialized) => {
103+
if (initialized) {
104+
// Only try to update if we successfully initialized
105+
setTimeout(updateFromPyPI, 100);
106+
}
107+
});
108+
}, []);
109+
110+
if (!version) {
111+
return null;
112+
}
113+
114+
return (
115+
<div className="gradient-stats-green-cyan rounded-lg p-6 border border-theme-accent/20">
116+
<div className="flex items-center justify-between">
117+
<div>
118+
<p className="text-sm text-gray-600 dark:text-gray-400 mb-1">
119+
Latest Release
120+
{dataSource === "static" && (
121+
<span className="ml-2 text-xs opacity-75">(static)</span>
122+
)}
123+
{dataSource === "live" && (
124+
<span className="ml-2 text-xs opacity-75">(live)</span>
125+
)}
126+
{isLoading && (
127+
<span className="ml-2 text-xs opacity-75">(updating...)</span>
128+
)}
129+
</p>
130+
<p className="text-2xl font-bold text-gray-900 dark:text-white mb-1">
131+
v{version}
132+
</p>
133+
{releaseDate && (
134+
<p className="text-sm text-gray-600 dark:text-gray-400">
135+
Released {releaseDate}
136+
</p>
137+
)}
138+
</div>
139+
<a
140+
href="https://pypi.org/project/ipython/"
141+
target="_blank"
142+
rel="noopener noreferrer"
143+
className="text-theme-secondary hover:text-theme-primary transition-colors"
144+
>
145+
<svg
146+
className="w-8 h-8"
147+
fill="none"
148+
stroke="currentColor"
149+
viewBox="0 0 24 24"
150+
>
151+
<path
152+
strokeLinecap="round"
153+
strokeLinejoin="round"
154+
strokeWidth="2"
155+
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
156+
></path>
157+
</svg>
158+
</a>
159+
</div>
160+
</div>
161+
);
162+
}

0 commit comments

Comments
 (0)