import { useEffect, useState } from "react"; import chartXkcd from "chart.xkcd"; function transformLikesData(likesData) { // Step 1 likesData.sort((a, b) => new Date(a.likedAt) - new Date(b.likedAt)); // Step 2 const cumulativeLikes = {}; let cumulativeCount = 0; // Step 3 likesData.forEach(like => { const date = like.likedAt cumulativeCount++; cumulativeLikes[date] = cumulativeCount; }); // Step 4 const transformedData = Object.keys(cumulativeLikes).map(date => ({ x: date, y: cumulativeLikes[date].toString() })); return transformedData; } function getProjectsFromHash() { let hash = window.location.hash; console.log('hash', hash) const projects = hash.replace("#", "").split('&').filter(project => project !== ''); return projects; } const initProjects = getProjectsFromHash(); function App() { const [projectType, setProjectType] = useState("models"); const [projectName, setProjectName] = useState(""); const [hasGraph, setHasGraph] = useState(false); const [isLoading, setIsLoading] = useState(false); const [datasets, setDatasets] = useState([]); function setHash() { const hashes = datasets.map(dataset => dataset.label).join('&'); if (window.parent && window.parent.postMessage) { window.parent.postMessage({ hash: hashes, }, "*"); } window.location.hash = hashes } async function getLikeHistory(projectPath) { const res = await fetch(`https://huggingface.co/api/${projectPath}/likers?expand[]=likeAt`) /** * Format: * [{"user": "timqian", "likedAt": "2021-07-01T00:00:00.000Z"}, {"user": "yy", "likedAt": "2021-07-02T00:00:00.000Z"}] */ const likers = await res.json() let likeHistory = transformLikesData(likers) if (likeHistory.length > 40) { // sample 20 points const sampledLikeHistory = [] const step = Math.floor(likeHistory.length / 20) for (let i = 0; i < likeHistory.length; i += step) { sampledLikeHistory.push(likeHistory[i]) } // Add the last point if it's not included if (sampledLikeHistory[sampledLikeHistory.length - 1].x !== likeHistory[likeHistory.length - 1].x) { sampledLikeHistory.push(likeHistory[likeHistory.length - 1]) } likeHistory = sampledLikeHistory } return likeHistory; } const onSubmit = async () => { setIsLoading(true) const likeHistory = await getLikeHistory(`${projectType}/${projectName}`); // if likeHistory is empty, show error message if (likeHistory.length === 0) { setIsLoading(false) alert("No like history found") return } setDatasets([...datasets, { label: `${projectType !== 'models' ? `${projectType}/` : ''}${projectName}`, data: likeHistory, }]) setHasGraph(true) setIsLoading(false) setProjectName("") } useEffect(() => { const svg = document.querySelector('.line-chart') if (datasets.length === 0) { svg.innerHTML = '' setHash() return } // draw chart in next tick new chartXkcd.XY(svg, { title: 'Like History', xLabel: 'Time', yLabel: 'Likes', data: { datasets, }, options: { // unxkcdify: true, xTickCount: 3, yTickCount: 4, legendPosition: chartXkcd.config.positionType.upLeft, showLine: true, timeFormat: 'MM/DD/YYYY', dotSize: 0.5, dataColors: [ "#FBBF24", // Warm Yellow "#60A5FA", // Light Blue "#14B8A6", // Teal "#A78BFA", // Soft Purple "#FF8C00", // Orange "#64748B", // Slate Gray "#FB7185", // Coral Pink "#6EE7B7", // Mint Green "#2563EB", // Deep Blue "#374151" // Charcoal ] }, }); setHash() }, [datasets]) useEffect(() => { function handleReceiveMessage(event) { // You might want to check event.origin here for security if needed // and ensure that event.data contains the properties you expect if (event.data && typeof event.data === 'object' && 'hash' in event.data) { // Update the hash of the parent window's URL window.location.hash = event.data.hash; console.log('hash') console.log(window.location.hash) } } // Add event listener for 'message' events window.addEventListener('message', handleReceiveMessage); // Clean up the event listener on component unmount return () => { window.removeEventListener('message', handleReceiveMessage); }; }, []); useEffect(() => { const projects = initProjects; if (projects.length <= 0) return; async function getLikeHistoryAndDisplay() { console.log('hi') setIsLoading(true); for (const project of projects) { let projectPath = project.startsWith('spaces/') || project.startsWith('datasets/') ? project : `models/${project}` const likeHistory = await getLikeHistory(projectPath); setDatasets(prevDatasets => [...prevDatasets, { label: project, data: likeHistory, }]) } setIsLoading(false); } getLikeHistoryAndDisplay() }, []) return (