Reachy_Mini / src /context /AppsContext.jsx
tfrere's picture
tfrere HF Staff
Initial commit: Reachy Mini Website
5c85958
raw
history blame
3.28 kB
import { createContext, useContext, useState, useEffect } from 'react';
// URLs for fetching apps
const OFFICIAL_APP_LIST_URL = 'https://huggingface.co/datasets/pollen-robotics/reachy-mini-official-app-store/raw/main/app-list.json';
const HF_SPACES_API = 'https://huggingface.co/api/spaces';
// Context
const AppsContext = createContext(null);
// Fetch all spaces with reachy_mini tag from HuggingFace API
async function fetchAllReachyMiniSpaces() {
try {
const response = await fetch(`${HF_SPACES_API}?filter=reachy_mini&full=true&limit=100`);
if (!response.ok) {
console.warn('Failed to fetch spaces with tag:', response.status);
return [];
}
return await response.json();
} catch (err) {
console.error('Error fetching reachy_mini spaces:', err);
return [];
}
}
// Provider component
export function AppsProvider({ children }) {
const [apps, setApps] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [hasFetched, setHasFetched] = useState(false);
useEffect(() => {
// Only fetch once
if (hasFetched) return;
async function fetchApps() {
setLoading(true);
setError(null);
try {
// 1. Fetch official app IDs from the curated list
const officialResponse = await fetch(OFFICIAL_APP_LIST_URL);
let officialIdList = [];
if (officialResponse.ok) {
officialIdList = await officialResponse.json();
}
const officialSet = new Set(officialIdList.map(id => id.toLowerCase()));
// 2. Fetch ALL spaces with reachy_mini tag from HuggingFace API
const allSpaces = await fetchAllReachyMiniSpaces();
console.log(`[AppsContext] Fetched ${allSpaces.length} spaces with reachy_mini tag`);
// 3. Build apps list with isOfficial flag
const allApps = allSpaces.map(space => {
const spaceId = space.id || '';
const isOfficial = officialSet.has(spaceId.toLowerCase());
return {
id: spaceId,
name: spaceId.split('/').pop(),
description: space.cardData?.short_description || '',
cardData: space.cardData || {},
likes: space.likes || 0,
lastModified: space.lastModified,
author: spaceId.split('/')[0],
isOfficial,
};
});
// Sort: official first, then by likes
allApps.sort((a, b) => {
if (a.isOfficial !== b.isOfficial) {
return a.isOfficial ? -1 : 1;
}
return (b.likes || 0) - (a.likes || 0);
});
setApps(allApps);
setHasFetched(true);
} catch (err) {
console.error('Failed to fetch apps:', err);
setError('Failed to load apps. Please try again later.');
} finally {
setLoading(false);
}
}
fetchApps();
}, [hasFetched]);
return (
<AppsContext.Provider value={{ apps, loading, error }}>
{children}
</AppsContext.Provider>
);
}
// Hook to use the apps context
export function useApps() {
const context = useContext(AppsContext);
if (!context) {
throw new Error('useApps must be used within an AppsProvider');
}
return context;
}