Skip to content

Commit

Permalink
feat(modrinth): selecting filters auto updates mods
Browse files Browse the repository at this point in the history
  • Loading branch information
olwalkey committed Dec 27, 2024
1 parent 66fb4a7 commit b0b4320
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 32 deletions.
4 changes: 3 additions & 1 deletion resources/scripts/components/elements/ScrollMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { Checkbox } from '@/components/elements/CheckboxLabel';

import { cn } from '@/lib/utils';

import { fetchNewProjects } from '../server/modrinth/config';

interface Props {
appVersion;
baseUrl: string;
Expand All @@ -28,7 +30,7 @@ const ScrollMenu = React.forwardRef<
return (
<div ref={ref} className={cn('relative', className)} {...props}>
<div className='overflow-y-auto max-h-48 scrollbar-thin scrollbar-thumb-[#FF343C] hover:scrollbar-thumb-[#F06F53] scrollbar-track-[#000000]'>
<ul>
<ul onClick={fetchNewProjects()}>
{items.map((item) => (
<li key={item}>
<Checkbox
Expand Down
44 changes: 44 additions & 0 deletions resources/scripts/components/server/modrinth/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import * as React from 'react';

import { cn } from '@/lib/utils';

const Checkbox = React.forwardRef<
React.ElementRef<'div'>,
React.ComponentPropsWithoutRef<'div'> & { label?: string; onChange?: () => void }
>(({ className, label, onChange, ...props }, ref) => {
const [checked, setChecked] = React.useState(false);

const toggleChecked = () => {
setChecked((prev) => {
const newCheckedState = !prev;
if (onChange) onChange(); // Call the onChange handler when the checkbox is toggled
return newCheckedState;
});
};

return (
<div className='flex items-center gap-2 select-none'>
{label && (
<span onClick={toggleChecked}>
<span
onClick={toggleChecked}
className={cn(
'inline-block rounded-lg w-full px-2 py-1 cursor-pointer transition-colors duration-200',
checked
? 'bg-green-800 text-white mb-2 select-none'
: 'border-transparent hover:bg-gray-700 mb-2 select-none',
)}
{...props}
ref={ref}
>
{label}
</span>
</span>
)}
</div>
);
});

Checkbox.displayName = 'Checkbox';

export { Checkbox };
37 changes: 20 additions & 17 deletions resources/scripts/components/server/modrinth/DisplayMods.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { toast } from 'sonner';
import ContentBox from '@/components/elements/ContentBox';
import HugeIconsDownload from '@/components/elements/hugeicons/Download';

// import { useProjects } from './FetchProjects';
import { apiEndpoints, offset, settings } from './config';

interface Project {
Expand All @@ -25,9 +26,6 @@ interface Props {
appVersion: string;
baseUrl: string;
nonApiUrl: string;
selectedLoaders?: string[];
selectedVersions?: string[];
selectedEnvironments?: string[];
}

const ProjectSelector: React.FC<Props> = ({ appVersion, baseUrl, nonApiUrl }) => {
Expand All @@ -37,8 +35,6 @@ const ProjectSelector: React.FC<Props> = ({ appVersion, baseUrl, nonApiUrl }) =>
const fetchProjects = async () => {
setIsLoading(true); // Start loading
try {
console.log('Fetching projects with appVersion:', appVersion);

const facets = [
settings.loaders.length > 0
? settings.loaders.map((loader) => `categories:${loader}`)
Expand All @@ -49,7 +45,7 @@ const ProjectSelector: React.FC<Props> = ({ appVersion, baseUrl, nonApiUrl }) =>
: ['project_type:mod'],
].filter(Boolean);

console.log('Constructed facets:', facets);
// console.log('Constructed facets:', facets);

const searchParams = new URLSearchParams({
facets: JSON.stringify(facets),
Expand All @@ -58,7 +54,7 @@ const ProjectSelector: React.FC<Props> = ({ appVersion, baseUrl, nonApiUrl }) =>
});

const apiUrl = `${baseUrl}${apiEndpoints.projects}?${searchParams.toString()}`;
console.log('Constructed API URL:', apiUrl);
// console.log('Constructed API URL:', apiUrl);

const response = await fetch(apiUrl, {
headers: {
Expand All @@ -67,13 +63,13 @@ const ProjectSelector: React.FC<Props> = ({ appVersion, baseUrl, nonApiUrl }) =>
},
});

console.log('Response status:', response.status);
// console.log('Response status:', response.status);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}

const data = await response.json();
console.log('Fetched projects data:', data);
// console.log('Fetched projects data:', data);

const updatedProjects = data.hits.map((project: Project) => ({
...project,
Expand All @@ -85,11 +81,14 @@ const ProjectSelector: React.FC<Props> = ({ appVersion, baseUrl, nonApiUrl }) =>
toast.error('Failed to fetch projects.');
console.error('Error fetching projects:', error);
} finally {
setIsLoading(false); // Stop loading
setIsLoading(false);
}
};

// Format the download number
useEffect(() => {
fetchProjects();
}, []);

const formatNumber = (num: number): string => {
if (num >= 1_000_000_000) {
return (num / 1_000_000_000).toFixed(1) + 'B';
Expand All @@ -109,11 +108,15 @@ const ProjectSelector: React.FC<Props> = ({ appVersion, baseUrl, nonApiUrl }) =>

return (
<div>
<button onClick={fetchProjects} className='btn btn-primary mb-4' disabled={isLoading}>
<button
onClick={fetchProjects}
id='fetchNewProjects'
className='btn btn-primary mb-4 flex hidden'
disabled={isLoading}
>
{isLoading ? 'Loading...' : 'Fetch Projects'}
</button>
<p></p>
<button onClick={getData}>Get Data</button>
{isLoading ? (
<p className='text-white'>Loading projects...</p>
) : projects.length > 0 ? (
Expand Down Expand Up @@ -159,17 +162,17 @@ const ProjectSelector: React.FC<Props> = ({ appVersion, baseUrl, nonApiUrl }) =>
</p>
<p className='text-sm text-gray-400'>{project.description}</p>
</div>
<div className='text-right flex flex-col py-2'>
<div className='text-right flex flex-col py-2 whitespace-nowrap'>
<p className='text-sm inline-block '>
<p className='text-gray-600 inline'>Downloads: </p>
{formatNumber(project.downloads)}
</p>
<p className='text-sm inline inline-block pt-2 '>
<p className='text-sm inline inline-block pt-2 whitespace-nowrap'>
<p className='text-gray-600 inline'>Latest: </p>
{project.versions[project.versions.length - 1]}
</p>
<a href='#' className='pt-3'>
<button className='flex text-right border-2 border-solid rounded py-1 px-6 border-brand hover:border-red-600'>
<a href='#' className='pt-4'>
<button className='flex text-right border-2 border-solid rounded py-1 px-6 border-brand hover:border-white transition ease-in-out delay-300 hover:bg-red-600 hover:scale-110'>
<HugeIconsDownload className='px-2 mx-2' fill='currentColor' />
Install
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import { CheckboxArrow } from '@/components/elements/CheckBoxArrow';
import { Checkbox } from '@/components/elements/CheckboxLabel';
import ArrowDownIcon from '@/components/elements/hugeicons/ArrowDown';

import { fetchNewProjects } from './config';

// import { fetchProjectsGlobal } from './DisplayMods';

interface Props {
items: string[];
onSelectionChange: (selectedItems: string[]) => void;
Expand All @@ -28,12 +32,14 @@ const EnvironmentSelector: React.FC<Props> = ({ items, onSelectionChange }) => {
<Checkbox key={item} label={item} onChange={() => handleToggle(item)} />
))}
{items.length > 5 && (
<div className='flex items-center gap-2 cursor-pointer mt-2' onClick={() => setShowAll(!showAll)}>
<CheckboxArrow label={showAll ? 'Show Less' : 'Show More'} />
<ArrowDownIcon
className={`transform transition-transform ${showAll ? 'rotate-180' : ''}`}
fill='currentColor'
/>
<div onClick={fetchNewProjects()}>
<div className='flex items-center gap-2 cursor-pointer mt-2' onClick={() => setShowAll(!showAll)}>
<CheckboxArrow label={showAll ? 'Show Less' : 'Show More'} />
<ArrowDownIcon
className={`transform transition-transform ${showAll ? 'rotate-180' : ''}`}
fill='currentColor'
/>
</div>
</div>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ScrollMenu } from '@/components/elements/ScrollMenu';
import Checkbox from '@/components/elements/inputs/Checkbox';

import { apiEndpoints, fetchHeaders, gameLoaders, settings } from './config';
import { fetchNewProjects } from './config';

interface GameVersion {
version: string;
Expand Down Expand Up @@ -70,6 +71,7 @@ const GameVersionSelector: React.FC<Props> = ({ appVersion, baseUrl }) => {
label='Show Snapshots'
checked={isSnapshotSelected}
onChange={() => setIsSnapshotSelected((prev) => !prev)}
onClick={fetchNewProjects()}
/>
</div>
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';
import { toast } from 'sonner';

import EnvironmentSelector from './EnvironmentSelector';
import { apiEndpoints, settings } from './config';
import { apiEndpoints, fetchNewProjects, settings } from './config';

interface GameLoaders {
icon: string; // SVG data (probably won't use this)
Expand All @@ -18,7 +18,6 @@ interface Props {
//! FIXME: We only want to show actual loaders like Fabric, Paper, Forge, not datapacks, Iris, Optifine
const LoaderSelector: React.FC<Props> = ({ appVersion, baseUrl }) => {
const [loaders, setLoaders] = useState<GameLoaders[]>([]);
const [selectedLoaders, setSelectedLoaders] = useState<string[]>([]);
const apiUrl = `${baseUrl}${apiEndpoints.loaders}`;

useEffect(() => {
Expand All @@ -32,6 +31,7 @@ const LoaderSelector: React.FC<Props> = ({ appVersion, baseUrl }) => {
});

if (!response.ok) {
toast(`HTTP Error! Status: ${response.status}`);
throw new Error(`HTTP error! Status: ${response.status}`);
}

Expand All @@ -51,6 +51,7 @@ const LoaderSelector: React.FC<Props> = ({ appVersion, baseUrl }) => {

const handleSelectionChange = (selectedItems: string[]) => {
settings.loaders = selectedItems;
// fetchProjectsGlobal(baseUrl, appVersion);
console.log('Selected loaders updated:', selectedItems);
};

Expand All @@ -59,7 +60,7 @@ const LoaderSelector: React.FC<Props> = ({ appVersion, baseUrl }) => {
});

return (
<div>
<div onClick={fetchNewProjects()}>
{filterLoaders.length > 0 ? (
<EnvironmentSelector
items={filterLoaders.map((loader) => loader.name)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { useEffect, useState } from 'react';
import { toast } from 'sonner';

import { Checkbox } from '@/components/elements/CheckboxLabel';
import ContentBox from '@/components/elements/ContentBox';
import { ModBox } from '@/components/elements/ModBox';
import PageContentBlock from '@/components/elements/PageContentBlock';
Expand All @@ -10,7 +9,7 @@ import ProjectSelector from './DisplayMods';
import EnvironmentSelector from './EnvironmentSelector';
import GameVersionSelector from './GameVersionSelector';
import LoaderSelector from './LoaderSelector';
import { apiEndpoints } from './config';
import { fetchNewProjects } from './config';

export default () => {
const [appVersion, setAppVersion] = useState<string | null>(null);
Expand Down Expand Up @@ -65,7 +64,7 @@ export default () => {
<LoaderSelector
appVersion={appVersion}
baseUrl={url}
onSelectionChange={(selectedLoaders) => updateSettings('loaders', selectedLoaders)}
// onSelectionChange={(selectedLoaders) => updateSettings('loaders', selectedLoaders)}
/>
</ContentBox>
</ModBox>
Expand All @@ -75,7 +74,7 @@ export default () => {
<GameVersionSelector
appVersion={appVersion}
baseUrl={url}
onSelectionChange={(selectedVersions) => updateSettings('versions', selectedVersions)}
// onSelectionChange={(selectedVersions) => updateSettings('versions', selectedVersions)}
/>
</ContentBox>
</ModBox>
Expand All @@ -96,7 +95,7 @@ export default () => {
className='p-8 bg-[#ffffff09] border-[1px] border-[#ffffff11] shadow-sm rounded-xl w-full md:w-4/5'
title='Modrinth'
>
<ProjectSelector appVersion={appVersion} baseUrl={url} nonApiUrl={nonApiUrl} settings={settings} />
<ProjectSelector appVersion={appVersion} baseUrl={url} nonApiUrl={nonApiUrl} />
</ContentBox>
</div>
</PageContentBlock>
Expand Down
8 changes: 8 additions & 0 deletions resources/scripts/components/server/modrinth/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,11 @@ export const fetchHeaders = (appVersion) => ({
'Content-Type': 'application/json',
'User-Agent': `pyrohost/pyrodactyl/${appVersion} (pyro.host)`,
});

// Function to programmatically click the button with id 'fetchNewProjects'
export const fetchNewProjects = () => {
const button = document.getElementById('fetchNewProjects');
if (button) {
button.click();
}
};

0 comments on commit b0b4320

Please sign in to comment.