Skip to content

Commit

Permalink
Add caching and headers to API fetch operations
Browse files Browse the repository at this point in the history
  • Loading branch information
MelanX committed Aug 24, 2024
1 parent 9d86872 commit 98a88f1
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 34 deletions.
111 changes: 88 additions & 23 deletions src/components/DependencyTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,35 @@ interface Project {
name: string;
}

// Define a global constant for the headers
const HEADERS = {
'User-Agent': 'GitHub@ChaoticTrials/Website'
};

const CACHE_EXPIRATION_MS = 5 * 60 * 1000;

const setCache = (key: string, data: any) => {
const timestampedData = {
timestamp: Date.now(),
data
};

localStorage.setItem(key, JSON.stringify(timestampedData));
};

const getCache = (key: string) => {
const cachedItem = localStorage.getItem(key);
if (!cachedItem) return null;

const { timestamp, data } = JSON.parse(cachedItem);
if (Date.now() - timestamp > CACHE_EXPIRATION_MS) {
localStorage.removeItem(key);
return null;
}

return data;
};

const DependencyTable: React.FC<Props> = ({ slug }) => {
const [projectData, setProjectData] = React.useState<any>(null);
const [versionsData, setVersionsData] = React.useState<any>(null);
Expand All @@ -18,12 +47,21 @@ const DependencyTable: React.FC<Props> = ({ slug }) => {

React.useEffect(() => {
const fetchProjectData = async () => {
try {
const response = await fetch(`https://api.modrinth.com/v3/project/${slug}`);
const data = await response.json();
setProjectData(data);
} catch (error) {
console.error('Error fetching project data:', error);
const cacheKey = `projectData_${slug}`;
const cachedData = getCache(cacheKey);
if (cachedData) {
setProjectData(cachedData);
} else {
try {
const response = await fetch(`https://api.modrinth.com/v3/project/${slug}`, {
headers: HEADERS
});
const data = await response.json();
setProjectData(data);
setCache(cacheKey, data);
} catch (error) {
console.error('Error fetching project data:', error);
}
}
};

Expand All @@ -33,41 +71,68 @@ const DependencyTable: React.FC<Props> = ({ slug }) => {
React.useEffect(() => {
if (projectData && projectData.versions) {
const fetchVersionsData = async () => {
try {
const idsParam = JSON.stringify(projectData.versions);
const response = await fetch(`https://api.modrinth.com/v3/versions?ids=${idsParam}`);
const versions = await response.json();
setVersionsData(versions);

const cacheKey = `versionsData_${slug}`;
const cachedData = getCache(cacheKey);
if (cachedData) {
setVersionsData(cachedData);
const projectIds: { [key: string]: boolean } = {};
versions.forEach((version: any) => {
cachedData.forEach((version: any) => {
if (version.dependencies) {
version.dependencies.forEach((dependency: any) => {
projectIds[dependency.project_id] = dependency.dependency_type === "required";
});
}
});
setUniqueProjectIds(projectIds);
} catch (error) {
console.error('Error fetching versions data:', error);
} else {
try {
const idsParam = JSON.stringify(projectData.versions);
const response = await fetch(`https://api.modrinth.com/v3/versions?ids=${idsParam}`, {
headers: HEADERS
});
const versions = await response.json();
setVersionsData(versions);
setCache(cacheKey, versions);

const projectIds: { [key: string]: boolean } = {};
versions.forEach((version: any) => {
if (version.dependencies) {
version.dependencies.forEach((dependency: any) => {
projectIds[dependency.project_id] = dependency.dependency_type === "required";
});
}
});
setUniqueProjectIds(projectIds);
} catch (error) {
console.error('Error fetching versions data:', error);
}
}
};

fetchVersionsData();
}
}, [projectData]);
}, [projectData, slug]);

React.useEffect(() => {
const projectIdsArray = Object.keys(uniqueProjectIds);
if (projectIdsArray.length > 0) {
const fetchProjectsData = async () => {
try {
const idsParam = JSON.stringify(projectIdsArray);
const response = await fetch(`https://api.modrinth.com/v3/projects?ids=${idsParam}`);
const data = await response.json();
setProjectsData(data.sort((a: Project, b: Project) => a.name.localeCompare(b.name)));
} catch (error) {
console.error('Error fetching projects data:', error);
const cacheKey = `projectsData_${JSON.stringify(projectIdsArray)}`;
const cachedData = getCache(cacheKey);
if (cachedData) {
setProjectsData(cachedData.sort((a: Project, b: Project) => a.name.localeCompare(b.name)));
} else {
try {
const idsParam = JSON.stringify(projectIdsArray);
const response = await fetch(`https://api.modrinth.com/v3/projects?ids=${idsParam}`, {
headers: HEADERS
});
const data = await response.json();
setProjectsData(data.sort((a: Project, b: Project) => a.name.localeCompare(b.name)));
setCache(cacheKey, data);
} catch (error) {
console.error('Error fetching projects data:', error);
}
}
};

Expand Down
53 changes: 42 additions & 11 deletions src/components/LoadProjectData/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useEffect, useState} from 'react';
import React, { useEffect, useState } from 'react';

export interface Project {
name: string;
Expand Down Expand Up @@ -36,20 +36,51 @@ interface FetchModDataProps {
children: (data: ProjectMetadata) => React.ReactNode;
}

const FetchModData: React.FC<FetchModDataProps> = ({children}) => {
const CACHE_EXPIRATION_MS = 5 * 60 * 1000;
const DATA_CACHE_KEY = 'modMetaData';

const setCache = (key: string, data: any) => {
const timestampedData = {
timestamp: Date.now(),
data
};

localStorage.setItem(key, JSON.stringify(timestampedData));
};

const getCache = (key: string) => {
const cachedItem = localStorage.getItem(key);
if (!cachedItem) return null;

const { timestamp, data } = JSON.parse(cachedItem);
if (Date.now() - timestamp > CACHE_EXPIRATION_MS) {
localStorage.removeItem(key);
return null;
}

return data;
};

const FetchModData: React.FC<FetchModDataProps> = ({ children }) => {
const [data, setData] = useState<ProjectMetadata | null>(null);

useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch('https://raw.githubusercontent.com/ChaoticTrials/ModMeta/HEAD/data/projects.json'); // todo find better location
if (!response.ok) {
throw new Error('Network response was not ok');
const cachedData = getCache(DATA_CACHE_KEY);
if (cachedData) {
setData(cachedData);
} else {
try {
const response = await fetch('https://raw.githubusercontent.com/ChaoticTrials/ModMeta/HEAD/data/projects.json');
if (!response.ok) {
throw new Error('Network response was not ok');
}
const jsonData: ProjectMetadata = await response.json();
setData(jsonData);
setCache(DATA_CACHE_KEY, jsonData);
} catch (error) {
console.error('Fetch error:', error);
}
const jsonData: ProjectMetadata = await response.json();
setData(jsonData);
} catch (error) {
console.error('Fetch error:', error);
}
};

Expand All @@ -63,4 +94,4 @@ const FetchModData: React.FC<FetchModDataProps> = ({children}) => {
);
};

export default FetchModData;
export default FetchModData;

0 comments on commit 98a88f1

Please sign in to comment.