diff --git a/apps/frontend/src/ui/components/overlay/cluster/ClusterCreationModal.tsx b/apps/frontend/src/ui/components/overlay/cluster/ClusterCreationModal.tsx index 4d850ef..66ed658 100644 --- a/apps/frontend/src/ui/components/overlay/cluster/ClusterCreationModal.tsx +++ b/apps/frontend/src/ui/components/overlay/cluster/ClusterCreationModal.tsx @@ -1,32 +1,32 @@ -import type { CreateCluster } from '@onelauncher/client/bindings'; import { ArrowRightIcon, Server01Icon } from '@untitled-theme/icons-solid'; -import { bridge } from '~imports'; import Button from '~ui/components/base/Button'; -import { type Accessor, type Context, createContext, createSignal, type JSX, type ParentProps, type Setter, Show, untrack, useContext } from 'solid-js'; +import { type Accessor, type Context, createContext, createSignal, type JSX, type ParentProps, type Setter, Show, useContext } from 'solid-js'; import { createStore } from 'solid-js/store'; import HeaderImage from '../../../../assets/images/header.png'; import { createModal } from '../Modal'; import ClusterGameSetup from './ClusterGameSetup'; -import ClusterProviderSelection from './ClusterProviderSelection'; +import ClusterImportSelection from './ClusterImportSelection'; +import ClusterProviderSelection, { type ClusterCreationProvider } from './ClusterProviderSelection'; export enum CreationStage { - PROVIDER_SELECTION = 0, - GAME_SETUP = 1, - IMPORT_SELECTION = 2, + PROVIDER_SELECTION, + GAME_SETUP, + IMPORT_SELECTION, } -type PartialCluster = Partial; -type PartialClusterUpdateFunc = (key: K, value: PartialCluster[K]) => void; +type FinishFn = () => Promise | boolean; interface ClusterModalController { step: Accessor; setStep: (step: CreationStage | number | undefined) => void; - partialCluster: Accessor; - setPartialCluster: Setter; - updatePartialCluster: PartialClusterUpdateFunc; + provider: Accessor; + setProvider: Setter; - start: () => Promise; + finishFunction: Accessor; + setFinishFunction: Setter; + + start: () => void; previous: () => void; cancel: () => void; finish: () => void; @@ -35,14 +35,13 @@ interface ClusterModalController { const ClusterModalContext = createContext() as Context; export function ClusterModalControllerProvider(props: ParentProps) { - const [partialCluster, setPartialCluster] = createSignal({}); - const requiredProps: (keyof PartialCluster)[] = ['name', 'mc_version', 'mod_loader']; - + const [provider, setProvider] = createSignal(); + const [finishFunction, setFinishFunction] = createSignal(() => true); const [steps, setSteps] = createSignal([]); const [stepComponents] = createStore<{ [key in CreationStage]: () => JSX.Element }>({ [CreationStage.PROVIDER_SELECTION]: ClusterProviderSelection, [CreationStage.GAME_SETUP]: ClusterGameSetup, - [CreationStage.IMPORT_SELECTION]: () => <>, + [CreationStage.IMPORT_SELECTION]: ClusterImportSelection, }); const controller: ClusterModalController = { @@ -64,12 +63,13 @@ export function ClusterModalControllerProvider(props: ParentProps) { }); }, - partialCluster, - setPartialCluster, - updatePartialCluster: (key, value) => setPartialCluster(prev => ({ ...prev, [key]: value })), + provider, + setProvider, - async start() { - setPartialCluster({}); + finishFunction, + setFinishFunction, + + start() { controller.setStep(CreationStage.PROVIDER_SELECTION); modal.show(); }, @@ -83,24 +83,17 @@ export function ClusterModalControllerProvider(props: ParentProps) { modal.hide(); }, - async finish() { - const untracked = untrack(partialCluster); - - for (const prop of requiredProps) - if (!untracked[prop]) - throw new Error(`Missing required property ${prop}`); + finish() { + const fn = finishFunction()(); - modal.hide(); - - await bridge.commands.createCluster({ - icon: null, - icon_url: null, - loader_version: null, - package_data: null, - skip: null, - skip_watch: null, - ...untracked, - } as CreateCluster); + if (fn instanceof Promise) + fn.then((res) => { + if (res === true) + modal.hide(); + }); + else + if (fn === true) + modal.hide(); }, }; diff --git a/apps/frontend/src/ui/components/overlay/cluster/ClusterGameSetup.tsx b/apps/frontend/src/ui/components/overlay/cluster/ClusterGameSetup.tsx index c4b2c06..c7eb127 100644 --- a/apps/frontend/src/ui/components/overlay/cluster/ClusterGameSetup.tsx +++ b/apps/frontend/src/ui/components/overlay/cluster/ClusterGameSetup.tsx @@ -1,4 +1,4 @@ -import type { Loader, VersionType } from '@onelauncher/client/bindings'; +import type { CreateCluster, Loader, VersionType } from '@onelauncher/client/bindings'; import { TextInputIcon } from '@untitled-theme/icons-solid'; import { bridge } from '~imports'; import Checkbox from '~ui/components/base/Checkbox'; @@ -8,32 +8,79 @@ import TextField from '~ui/components/base/TextField'; import LoaderIcon from '~ui/components/game/LoaderIcon'; import useCommand from '~ui/hooks/useCommand'; import { formatVersionRelease, LOADERS } from '~utils'; -import { createEffect, createSignal, For, Index, type JSX, onMount, Show, splitProps, untrack } from 'solid-js'; +import { createEffect, createMemo, createSignal, For, Index, type JSX, onMount, Show, splitProps, untrack } from 'solid-js'; import { type ClusterStepProps, createClusterStep } from './ClusterCreationModal'; +type PartialCluster = Partial; +type PartialClusterUpdateFunc = (key: K, value: PartialCluster[K]) => void; + export default createClusterStep({ message: 'Game Setup', buttonType: 'create', Component: ClusterGameSetup, }); +function _epicHardcodedLoaderVersionFilter(loader: Loader, version: string): boolean { + if (loader === 'vanilla') + return true; + + const split = version.split('.')[1]; + if (split === undefined) + return true; + + const minor = Number.parseInt(split); + if (minor < 13) + return loader === 'forge' || loader === 'legacyfabric'; + else + return loader === 'forge' || loader === 'fabric' || loader === 'neoforge' || loader === 'quilt'; +} + function ClusterGameSetup(props: ClusterStepProps) { - const check = () => { - const hasName = (props.controller.partialCluster().name?.length ?? 0) > 0; - const hasVersion = (props.controller.partialCluster().mc_version?.length ?? 0) > 0; - const hasLoader = (props.controller.partialCluster().mod_loader?.length ?? 0) > 0; + const [partialCluster, setPartialCluster] = createSignal({ + mod_loader: 'vanilla', + }); - props.setCanGoForward(hasName && hasVersion && hasLoader); - }; + const requiredProps: (keyof PartialCluster)[] = ['name', 'mc_version', 'mod_loader']; - createEffect(check); + const updatePartialCluster: PartialClusterUpdateFunc = (key, value) => setPartialCluster(prev => ({ ...prev, [key]: value })); + const setName = (name: string) => updatePartialCluster('name', name); + const setVersion = (version: string) => updatePartialCluster('mc_version', version); + const setLoader = (loader: Loader | string) => updatePartialCluster('mod_loader', loader.toLowerCase() as Loader); - const setName = (name: string) => props.controller.updatePartialCluster('name', name); - const setVersion = (version: string) => props.controller.updatePartialCluster('mc_version', version); - const setLoader = (loader: Loader | string) => props.controller.updatePartialCluster('mod_loader', loader.toLowerCase() as Loader); + const getLoaders = createMemo(() => { + const version = partialCluster().mc_version ?? ''; + + return LOADERS.filter(loader => _epicHardcodedLoaderVersionFilter(loader, version)); + }); + + createEffect(() => { + const hasName = (partialCluster().name?.length ?? 0) > 0; + const hasVersion = (partialCluster().mc_version?.length ?? 0) > 0; + const hasLoader = (partialCluster().mod_loader?.length ?? 0) > 0; + + props.setCanGoForward(hasName && hasVersion && hasLoader); + }); onMount(() => { - setLoader('vanilla'); + props.controller.setFinishFunction(() => async () => { + const untracked = untrack(partialCluster); + + for (const prop of requiredProps) + if (!untracked[prop]) + throw new Error(`Missing required property ${prop}`); + + await bridge.commands.createCluster({ + icon: null, + icon_url: null, + loader_version: null, + package_data: null, + skip: null, + skip_watch: null, + ...untracked, + } as CreateCluster); + + return true; + }); }); return ( @@ -51,8 +98,8 @@ function ClusterGameSetup(props: ClusterStepProps) {