Skip to content

Commit 6b73c52

Browse files
committed
load RSS feed from anaconda.org
1 parent c245d61 commit 6b73c52

File tree

1 file changed

+152
-65
lines changed

1 file changed

+152
-65
lines changed

src/components/Packages/index.jsx

Lines changed: 152 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { useState, useEffect } from "react";
2+
import Admonition from "@docusaurus/theme-classic/lib/theme/Admonition";
23

34
// Function to calculate the Levenshtein distance between two strings
45
function levenshteinDistance(a, b) {
@@ -49,13 +50,16 @@ function highlightSubstring(str, substr) {
4950
}
5051

5152
const Packages = () => {
52-
const [packages, setPackages] = useState([]);
53+
const [allPackages, setAllPackages] = useState([]);
54+
const [latestPackages, setLatestPackages] = useState([]);
5355
const [searchTerm, setSearchTerm] = useState("");
5456

5557
useEffect(() => {
56-
const fetchData = async () => {
58+
const fetchAllData = async () => {
5759
try {
58-
const response = await fetch("https://raw.githubusercontent.com/conda-forge/feedstock-outputs/gh-pages/feedstock-outputs.json");
60+
const response = await fetch(
61+
"https://raw.githubusercontent.com/conda-forge/feedstock-outputs/single-file/feedstock-outputs.json"
62+
);
5963
const data = await response.json();
6064

6165
if (typeof data === "object" && data !== null) {
@@ -65,29 +69,56 @@ const Packages = () => {
6569
repos,
6670
}));
6771

68-
setPackages(packagesArray);
72+
setAllPackages(packagesArray);
6973
} else {
7074
console.error("Invalid data format. Expected an object.");
7175
}
7276
} catch (error) {
7377
console.error("Error fetching packages:", error);
7478
}
7579
};
76-
77-
fetchData();
80+
const fetchLatestData = async () => {
81+
try {
82+
const response = await fetch(
83+
"https://conda.anaconda.org/conda-forge/rss.xml"
84+
);
85+
// parse the RSS feed into an XML document
86+
const xml = await response.text();
87+
const parser = new DOMParser();
88+
const doc = parser.parseFromString(xml, "text/xml");
89+
const titles = doc.querySelectorAll("title");
90+
const dates = doc.querySelectorAll("pubDate");
91+
// Convert the object into an array of { name, date } objects
92+
var latestPackagesArray = [];
93+
// The first 'title' element is the feed title, so we skip it
94+
titles.forEach(
95+
(title, index) =>
96+
index &&
97+
latestPackagesArray.push({
98+
name: title.textContent.split(" ")[0],
99+
date: dates[index - 1].textContent,
100+
})
101+
);
102+
setLatestPackages(latestPackagesArray);
103+
} catch (error) {
104+
console.error("Error fetching latest packages:", error);
105+
}
106+
};
107+
fetchLatestData();
108+
fetchAllData();
78109
}, []);
79110

80111
const searchTermLower = searchTerm.toLowerCase();
81112
var filteredPackages = [];
82113
if (searchTerm.length >= 3) {
83114
// For queries with three or more characters, search the entire string for a match
84-
filteredPackages = packages.filter((pkg) =>
115+
filteredPackages = allPackages.filter((pkg) =>
85116
pkg.name.toLowerCase().includes(searchTermLower)
86117
);
87118
} else if (searchTerm.length > 0) {
88119
// For queries with less than three characters,
89120
// only search if the package name starts with the query for performance reasons
90-
filteredPackages = packages.filter((pkg) =>
121+
filteredPackages = allPackages.filter((pkg) =>
91122
pkg.name.toLowerCase().startsWith(searchTermLower)
92123
);
93124
}
@@ -108,78 +139,134 @@ const Packages = () => {
108139
setSearchTerm(event.target.value);
109140
};
110141

142+
var renderResultsBlock;
143+
var resultsPill;
144+
if (searchTerm.length) {
145+
// This is the results table, displayed when the user enters a search term
146+
renderResultsBlock = (
147+
<table>
148+
<thead>
149+
<tr>
150+
<th>Package</th>
151+
<th>Feedstock(s)</th>
152+
</tr>
153+
</thead>
154+
<tbody>
155+
{(filteredPackages.length &&
156+
filteredPackages.map((pkg) => (
157+
<tr key={pkg.name}>
158+
<td>
159+
<a
160+
href={`https://anaconda.org/conda-forge/${pkg.name}`}
161+
target="_blank"
162+
title={`View ${pkg.name} on anaconda.org`}
163+
>
164+
{highlightSubstring(pkg.name, searchTermLower)}
165+
</a>
166+
</td>
167+
<td>
168+
{pkg.repos.map((repo) => (
169+
<span>
170+
<a
171+
href={`https://github.com/conda-forge/${repo}-feedstock`}
172+
target="_blank"
173+
title={`View ${repo}-feedstock on GitHub`}
174+
>
175+
{repo}-feedstock
176+
</a>
177+
178+
<br />
179+
</span>
180+
))}
181+
</td>
182+
</tr>
183+
))) || (
184+
<tr>
185+
<td colSpan="2">No packages found</td>
186+
</tr>
187+
)}
188+
</tbody>
189+
</table>
190+
);
191+
resultsPill = (
192+
<span className="badge badge--info margin-left--sm">
193+
{filteredPackages.length} package(s) found
194+
</span>
195+
);
196+
} else {
197+
// Without a search term, display the most recently updated feedstocks
198+
renderResultsBlock = (
199+
<div>
200+
<Admonition type="tip" coll>
201+
<p>
202+
The following packages have been published to{" "}
203+
<a href="https://anaconda.org/conda-forge" target="_blank">
204+
Anaconda.org
205+
</a>{" "}
206+
recently.
207+
Check{" "}
208+
<a href="https://github.com/conda-forge/feedstock/commits">
209+
conda-forge/feedstocks
210+
</a>{" "}
211+
for the last updates in our feedstocks.
212+
</p>
213+
</Admonition>
214+
<table>
215+
<thead>
216+
<tr>
217+
<th>#</th>
218+
<th>Package</th>
219+
<th>Feedstock</th>
220+
<th>Last updated</th>
221+
</tr>
222+
</thead>
223+
<tbody>
224+
{latestPackages.map((item, index) => (
225+
<tr key={item.name}>
226+
<td>{index + 1}</td>
227+
<td>
228+
<a
229+
href={`https://anaconda.org/conda-forge/${item.name}`}
230+
target="_blank"
231+
>
232+
{item.name}
233+
</a>
234+
</td>
235+
<td>...</td>
236+
<td>{item.date}</td>
237+
</tr>
238+
))}
239+
</tbody>
240+
</table>
241+
</div>
242+
);
243+
resultsPill = (
244+
<span className="badge badge--success margin-left--sm">
245+
{allPackages.length} packages loaded
246+
</span>
247+
);
248+
}
249+
111250
return (
112-
<div
113-
className={["container", "margin-vert--lg"].join(" ")}
114-
>
251+
<div className={["container", "margin-vert--lg"].join(" ")}>
115252
<div className="row">
116253
<main className="col col--12">
117254
<h1>Packages in conda-forge</h1>
118-
<form className="margin-vert--md">
255+
<form id="filterPackages" className="margin-vert--md">
119256
<div className="navbar__search">
120-
<label htmlFor="search">
257+
<label htmlFor="filterPackages">
121258
<input
122259
type="text"
123260
placeholder="Filter items..."
124261
value={searchTerm}
125262
onChange={handleSearchChange}
126263
className="navbar__search-input"
127264
/>
128-
{(searchTerm.length && (
129-
<span class="badge badge--info margin-left--sm">
130-
{filteredPackages.length} package(s) found
131-
</span>
132-
)) || (
133-
<span class="badge badge--success margin-left--sm">
134-
{packages.length} packages loaded
135-
</span>
136-
)}
265+
{resultsPill}
137266
</label>
138267
</div>
139268
</form>
140-
<table>
141-
<thead>
142-
<tr>
143-
<th>Package</th>
144-
<th>Feedstock(s)</th>
145-
</tr>
146-
</thead>
147-
<tbody>
148-
{(filteredPackages.length &&
149-
filteredPackages.map((pkg) => (
150-
<tr key={pkg.name}>
151-
<td>
152-
<a
153-
href={`https://anaconda.org/conda-forge/${pkg.name}`}
154-
target="_blank"
155-
title={`View ${pkg.name} on anaconda.org`}
156-
>
157-
{highlightSubstring(pkg.name, searchTermLower)}
158-
</a>
159-
</td>
160-
<td>
161-
{pkg.repos.map((repo) => (
162-
<span>
163-
<a
164-
href={`https://github.com/conda-forge/${repo}-feedstock`}
165-
target="_blank"
166-
title={`View ${repo}-feedstock on GitHub`}
167-
>
168-
{repo}-feedstock
169-
</a>
170-
171-
<br />
172-
</span>
173-
))}
174-
</td>
175-
</tr>
176-
))) || (
177-
<tr>
178-
<td colSpan="2">Use the search bar to find packages</td>
179-
</tr>
180-
)}
181-
</tbody>
182-
</table>
269+
{renderResultsBlock}
183270
</main>
184271
</div>
185272
</div>

0 commit comments

Comments
 (0)