refactor: ClusterCreationModal, prepare for multi stage onboarding and integrate with ModalController

This commit is contained in:
LynithDev 2024-08-09 22:01:26 +02:00
parent 7eb2f74297
commit fcb7df0430
9 changed files with 278 additions and 282 deletions

View file

@ -6,10 +6,10 @@ import ErrorBoundary from './components/ErrorBoundary';
import AnimatedRoutes from './components/AnimatedRoutes';
import NotificationOverlay from './components/overlay/notifications/NotificationOverlay';
import { SettingsProvider } from './hooks/useSettings';
import { ClusterModalController } from './components/overlay/cluster/ClusterCreationModal';
import { ClusterModalControllerProvider } from './components/overlay/cluster/ClusterCreationModal';
import { MultiProvider } from './components/MultiProvider';
import { AccountControllerProvider } from './components/overlay/account/AddAccountModal';
import { ModalProvider } from './components/overlay/Modal';
import { ModalProvider, ModalRenderer } from './components/overlay/Modal';
import { PROGRAM_INFO } from '~bindings';
function App(props: ParentProps) {
@ -37,6 +37,7 @@ function App(props: ParentProps) {
</div>
<NotificationOverlay />
<ModalRenderer />
</main>
</GlobalContexts>
);
@ -51,7 +52,7 @@ function GlobalContexts(props: ParentProps) {
ModalProvider,
SettingsProvider,
AccountControllerProvider,
ClusterModalController,
ClusterModalControllerProvider,
]}
>
{props.children}

View file

@ -1,22 +1,23 @@
import { type JSX, splitProps } from 'solid-js';
import fabric from '~assets/logos/fabric.png';
import forge from '~assets/logos/forge.png';
import quilt from '~assets/logos/quilt.png';
import vanilla from '~assets/logos/vanilla.png';
import type { Loader } from '~bindings';
import FabricImage from '~assets/logos/fabric.png';
import ForgeImage from '~assets/logos/forge.png';
import QuiltImage from '~assets/logos/quilt.png';
import VanillaImage from '~assets/logos/vanilla.png';
import type { Cluster, Loader } from '~bindings';
export function getLoaderIcon(loader: Loader): string {
switch (loader) {
case 'forge':
return forge;
case 'fabric':
return fabric;
case 'quilt':
return quilt;
case 'vanilla':
default:
return vanilla;
}
export function getLoaderLogoSrc(loader: Cluster | Loader): string {
const loaderName = (typeof loader === 'string' ? loader : loader.meta.loader)?.toLowerCase() as Loader;
const mapping: Record<Loader, string> = {
vanilla: VanillaImage,
fabric: FabricImage,
legacyfabric: FabricImage,
forge: ForgeImage,
neoforge: ForgeImage,
quilt: QuiltImage,
};
return mapping[loaderName];
}
type LoaderIconProp = JSX.HTMLAttributes<HTMLImageElement> & {
@ -26,7 +27,7 @@ type LoaderIconProp = JSX.HTMLAttributes<HTMLImageElement> & {
function LoaderIcon(props: LoaderIconProp) {
const [{ loader = 'vanilla' }, rest] = splitProps(props, ['loader']);
return (
<img {...rest} src={getLoaderIcon(loader)} alt={loader} />
<img {...rest} src={getLoaderLogoSrc(loader)} alt={`${loader}'s logo`} />
);
}

View file

@ -4,7 +4,7 @@ import type {
ParentProps,
Ref,
} from 'solid-js';
import { For, createContext, createSignal, onCleanup, onMount, splitProps, useContext } from 'solid-js';
import { For, Show, createContext, createSignal, onCleanup, onMount, splitProps, useContext } from 'solid-js';
import { mergeRefs } from '@solid-primitives/refs';
import { createStore } from 'solid-js/store';
import { Transition } from 'solid-transition-group';
@ -12,18 +12,23 @@ import Button from '../base/Button';
import FullscreenOverlay from './FullscreenOverlay';
import type { MakeOptional } from '~types.ts';
type ModalsList = (() => JSX.Element)[];
interface ModalContextType {
modals: () => ModalsList;
displayModal: (modal: () => JSX.Element) => number;
closeModal: (index?: number) => void;
isVisible: (index: number) => boolean;
};
const ModalContext = createContext() as Context<ModalContextType>;
type ModalsList = (() => JSX.Element)[];
export function ModalProvider(props: ParentProps) {
const [modals, setModals] = createStore<ModalsList>([]);
const context: ModalContextType = {
modals: () => modals,
displayModal: (modal) => {
setModals(prev => [...prev, modal]);
return modals.length - 1;
@ -40,34 +45,47 @@ export function ModalProvider(props: ParentProps) {
setModals(prev => prev.slice(0, -1));
},
isVisible: (index) => {
return modals.length > index;
},
};
return (
<ModalContext.Provider value={context}>
{props.children}
<FullscreenOverlay
visible={() => modals.length > 0}
setVisible={(value) => {
if (value === false)
context.closeModal();
}}
>
<Transition
mode="outin"
enterClass="modal-animation-enter"
enterActiveClass="modal-animation-enter-active"
enterToClass="modal-animation-enter-to"
exitClass="modal-animation-leave"
exitActiveClass="modal-animation-leave-active"
exitToClass="modal-animation-leave-to"
>
<>{modals.length > 0 && modals[modals.length - 1]!()}</>
</Transition>
</FullscreenOverlay>
</ModalContext.Provider>
);
}
export function ModalRenderer() {
const controller = useModalController();
return (
<FullscreenOverlay
visible={() => controller.modals().length > 0}
setVisible={(value) => {
if (value === false)
controller.closeModal();
}}
>
<Transition
mode="outin"
enterClass="modal-animation-enter"
enterActiveClass="modal-animation-enter-active"
enterToClass="modal-animation-enter-to"
exitClass="modal-animation-leave"
exitActiveClass="modal-animation-leave-active"
exitToClass="modal-animation-leave-to"
>
<Show when={controller.modals().length > 0}>
{controller.modals()[controller.modals().length - 1]!()}
</Show>
</Transition>
</FullscreenOverlay>
);
}
export function useModalController() {
const context = useContext(ModalContext);
@ -77,16 +95,16 @@ export function useModalController() {
return context;
}
interface CreateModal {
export interface ModalController {
show: () => void;
hide: () => void;
};
export function createModal(el: (props: CreateModal) => JSX.Element): CreateModal {
export function createModal(el: (props: ModalController) => JSX.Element): ModalController {
const controller = useModalController();
const [index, setIndex] = createSignal<number>();
const ctx: CreateModal = {
const ctx: ModalController = {
show: () => {
setIndex(controller.displayModal(() => el(ctx)));
},
@ -100,7 +118,7 @@ export function createModal(el: (props: CreateModal) => JSX.Element): CreateModa
export type ModalProps = {
ref?: Ref<HTMLDivElement> | undefined;
} & ParentProps & CreateModal;
} & ParentProps & ModalController;
function Modal(props: ModalProps) {
return (
@ -110,7 +128,7 @@ function Modal(props: ModalProps) {
);
}
Modal.SplitProps = function <T extends CreateModal>(props: T) {
Modal.SplitProps = function <T extends ModalController>(props: T) {
return splitProps(props, ['hide', 'show']);
};

View file

@ -1,238 +1,223 @@
import { type Accessor, type Context, Match, type ParentProps, type Setter, Switch, createContext, createEffect, createSignal, onCleanup, untrack, useContext } from 'solid-js';
import { type Accessor, type Context, type JSX, type ParentProps, type Setter, Show, createContext, createEffect, createSignal, onMount, untrack, useContext } from 'solid-js';
import { ArrowRightIcon, Server01Icon } from '@untitled-theme/icons-solid';
import { createStore } from 'solid-js/store';
import HeaderImage from '../../../../assets/images/header.png';
import { createModal } from '../Modal';
import ClusterStepOne from './ClusterStepOne';
import { ClusterStepTwo } from './ClusterStepTwo';
import ClusterGameSetup from './ClusterGameSetup';
import ClusterProviderSelection from './ClusterProviderSelection';
import Button from '~ui/components/base/Button';
import type { CreateCluster } from '~bindings';
import { bridge } from '~imports';
import type { FixedArray, UnionToArray } from '~types.ts';
export enum CreationStage {
PROVIDER_SELECTION = 0,
GAME_SETUP = 1,
IMPORT_SELECTION = 2,
}
type PartialCluster = Partial<CreateCluster>;
type PartialClusterUpdateFunc = <K extends keyof PartialCluster>(key: K, value: PartialCluster[K]) => any;
// Why the fuck do I need to use a context for all this???
// TODO: Rewrite for use with the new Modal stacking system
interface ClusterModalContextFunc {
step: Accessor<number>;
setStep: Setter<number>;
interface ClusterModalController {
step: Accessor<CreationStage | undefined>;
setStep: (step: CreationStage | undefined) => void;
partialCluster: Accessor<PartialCluster>;
setPartialCluster: Setter<PartialCluster>;
updatePartialCluster: PartialClusterUpdateFunc;
start: () => Promise<void>; // Returns true if its finished (created) successfully
start: () => Promise<void>;
previous: () => void;
cancel: () => void;
finish: () => void;
}
const ClusterModalContext = createContext<ClusterModalContextFunc>() as Context<ClusterModalContextFunc>;
const ClusterModalContext = createContext() as Context<ClusterModalController>;
export enum ClusterModalStages {
STAGE_1_PROVIDER = 0,
STAGE_2_GAME_SETUP = 1,
}
type ClusterModalStagesLength = UnionToArray<keyof typeof ClusterModalStages>['length'];
export function ClusterModalController(props: ParentProps) {
const [step, setStep] = createSignal<number>(ClusterModalStages.STAGE_1_PROVIDER);
export function ClusterModalControllerProvider(props: ParentProps) {
const [partialCluster, setPartialCluster] = createSignal<PartialCluster>({});
const [inProgress, setInProgress] = createSignal(false);
const [error, setError] = createSignal<string | undefined>(undefined);
const requiredProps: (keyof PartialCluster)[] = ['name', 'mc_version', 'mod_loader'];
const updatePartialCluster: PartialClusterUpdateFunc = (key, value) => {
setPartialCluster((prev) => {
return {
...prev,
[key]: value,
};
});
};
const [steps, setSteps] = createSignal<CreationStage[]>([]);
const [stepComponents] = createStore<{ [key in CreationStage]: () => JSX.Element }>({
[CreationStage.PROVIDER_SELECTION]: ClusterProviderSelection,
[CreationStage.GAME_SETUP]: ClusterGameSetup,
[CreationStage.IMPORT_SELECTION]: () => <></>,
});
const modal = createModal(() => (
// eslint-disable-next-line ts/no-use-before-define -- circular dependency should be refactored later
<ClusterCreationModal controller={context} />
));
const controller: ClusterModalController = {
step: () => {
return steps()[steps().length - 1];
},
setStep: (stage) => {
setSteps((prev) => {
if (stage === undefined)
return prev.slice(0, -1);
return [...prev, stage];
});
},
const context: ClusterModalContextFunc = {
step,
setStep,
partialCluster,
setPartialCluster,
updatePartialCluster,
start() {
setPartialCluster({});
setStep(ClusterModalStages.STAGE_1_PROVIDER);
setError(undefined);
setInProgress(true);
modal.show();
return new Promise<void>((resolve, reject) => {
createEffect(() => {
const result = error();
if (inProgress() === true)
return;
if (result === undefined || result.length === 0)
resolve();
else
reject(result);
});
updatePartialCluster: (key, value) => {
setPartialCluster((prev) => {
return {
...prev,
[key]: value,
};
});
},
async start() {
setPartialCluster({});
controller.setStep(CreationStage.PROVIDER_SELECTION);
},
previous() {
controller.setStep(undefined);
},
cancel() {
setSteps([]);
},
async finish() {
const untracked = untrack(partialCluster);
if (untracked.name === undefined || untracked.mc_version === undefined || untracked.mod_loader === undefined)
throw new Error('Cluster is missing required fields');
modal.hide();
for (const prop of requiredProps)
if (!untracked[prop])
throw new Error(`Missing required property ${prop}`);
// @ts-expect-error -- TODO: Do this properly
await bridge.commands.createCluster({
name: untracked.name!,
mod_loader: untracked.mod_loader!,
mc_version: untracked.mc_version!,
// TODO: Implement the rest of the fields
icon: null,
icon_url: null,
loader_version: null,
package_data: null,
skip: null,
skip_watch: null,
...untracked,
});
setInProgress(false);
},
};
onCleanup(() => {
setStep(ClusterModalStages.STAGE_1_PROVIDER);
const modal = createModal(() => (
<>{stepComponents[controller.step()!]()}</>
));
createEffect(() => {
if (steps().length > 0)
modal.show();
else
modal.hide();
});
return (
<ClusterModalContext.Provider value={context}>
<ClusterModalContext.Provider value={controller}>
{props.children}
</ClusterModalContext.Provider>
);
}
export function useClusterModalController() {
export function useClusterCreator() {
const ctx = useContext(ClusterModalContext);
if (!ctx)
throw new Error('useClusterModalController must be used within a ClusterModalController');
throw new Error('useClusterCreator must be used within a ClusterModalControllerProvider');
return ctx;
}
type ButtonType = 'next' | 'create';
export interface ClusterStep {
stage: CreationStage;
component: (props: ClusterStepProps) => JSX.Element;
}
export interface ClusterStepProps {
isVisible: Accessor<boolean>;
setCanGoForward: Setter<boolean>;
controller: Accessor<ClusterModalContextFunc>;
setNextStage: Setter<CreationStage | undefined>;
controller: ClusterModalController;
};
function ClusterCreationModal(props: { controller: ClusterModalContextFunc }) {
const buttonIsNext = () => props.controller.step() !== ((Object.keys(ClusterModalStages).length / 2) - 1);
interface CreateClusterStepType {
message: string;
buttonType: ButtonType;
Component: (props: ClusterStepProps) => JSX.Element;
}
const [canGoForward, setCanGoForward] = createSignal(false);
// Indexed by the current step
const messages: FixedArray<string, ClusterModalStagesLength> = [
'Provider selection',
'Game Setup',
];
export function createClusterStep(props: CreateClusterStepType): () => JSX.Element {
return () => {
const controller = useClusterCreator();
const [canGoForward, setCanGoForward] = createSignal(false);
const [nextStage, setNextStage] = createSignal<CreationStage | undefined>();
const stepComponents = [
ClusterStepOne,
ClusterStepTwo,
].map((Component, index) => (
Component({
setCanGoForward,
isVisible: () => props.controller.step() === index,
controller: () => props.controller,
})
));
const previousButtonText = () => {
if (controller.step() === CreationStage.PROVIDER_SELECTION)
return 'Cancel';
else
return 'Previous';
};
const Step = (props: { step: number }) => <>{stepComponents[props.step]}</>;
function previousBtnClick() {
if (controller.step() === CreationStage.PROVIDER_SELECTION)
controller.cancel();
else
controller.previous();
}
function cancel() {
// controller.setVisible(false);
}
function btnClick() {
if (props.buttonType === 'create')
controller.finish();
else if (props.buttonType === 'next')
controller.setStep(nextStage());
}
function prev() {
props.controller.setStep(prev => prev - 1);
}
function next() {
props.controller.setStep(prev => prev + 1);
}
function finish() {
props.controller.finish();
}
return (
<div class="bg-primary rounded-lg text-center flex flex-col min-w-sm">
<div class="flex relative h-25">
<div class="absolute top-0 left-0 w-full h-full">
<img class="h-full w-full rounded-t-lg" src={HeaderImage} alt="Header Image" />
return (
<div class="bg-primary rounded-lg text-center flex flex-col min-w-sm">
<div class="flex relative h-25">
<div class="absolute top-0 left-0 w-full h-full">
<img class="h-full w-full rounded-t-lg" src={HeaderImage} alt="Header Image" />
</div>
<div
class="absolute top-0 left-0 px-10 h-full flex flex-row justify-start items-center gap-x-4 bg-[radial-gradient(at_center,#00000077,transparent)]"
>
<Server01Icon class="w-8 h-8" />
<div class="flex flex-col items-start justify-center">
<h1 class="h-10 -mt-2">New Cluster</h1>
<span>{props.message}</span>
</div>
</div>
</div>
<div
class="absolute top-0 left-0 px-10 h-full flex flex-row justify-start items-center gap-x-4 bg-[radial-gradient(at_center,#00000077,transparent)]"
>
<Server01Icon class="w-8 h-8" />
<div class="flex flex-col items-start justify-center">
{/** weird positioning?? */}
<h1 class="h-10 -mt-2">New Cluster</h1>
<span>{messages[props.controller.step()]}</span>
<div class="flex flex-col border border-white/5 rounded-b-lg">
<div class="p-3">
<props.Component
controller={controller}
setCanGoForward={setCanGoForward}
setNextStage={setNextStage}
/>
</div>
<div class="flex flex-row gap-x-2 justify-end pt-0 p-3">
<Button
class="capitalize"
children={previousButtonText()}
buttonStyle="ghost"
onClick={previousBtnClick}
/>
<Button
children={props.buttonType}
class="capitalize"
buttonStyle="primary"
disabled={canGoForward() !== true}
iconRight={<ArrowRightIcon />}
onClick={btnClick}
/>
</div>
</div>
</div>
<div class="flex flex-col border border-white/5 rounded-b-lg">
<div class="p-3">
<Step step={props.controller.step()} />
</div>
<div class="flex flex-row gap-x-2 justify-end pt-0 p-3">
<Switch>
<Match when={props.controller.step() === 0}>
<Button
children="Cancel"
buttonStyle="ghost"
onClick={cancel}
/>
</Match>
<Match when={props.controller.step() >= 1}>
<Button
children="Previous"
buttonStyle="ghost"
onClick={prev}
/>
</Match>
</Switch>
<Switch>
<Match when={buttonIsNext() === true}>
<Button
children="Next"
buttonStyle="primary"
disabled={!canGoForward()}
iconRight={<ArrowRightIcon />}
onClick={next}
/>
</Match>
<Match when={buttonIsNext() === false}>
<Button
children="Create"
buttonStyle="primary"
disabled={!canGoForward()}
iconRight={<ArrowRightIcon />}
onClick={finish}
/>
</Match>
</Switch>
</div>
</div>
</div>
);
);
};
}

View file

@ -1,59 +1,40 @@
import { TextInputIcon } from '@untitled-theme/icons-solid';
import { For, Index, type JSX, Show, createEffect, createSignal, on, onMount, splitProps, untrack } from 'solid-js';
import type { ClusterStepProps } from './ClusterCreationModal';
import { For, Index, type JSX, Show, createEffect, createSignal, onMount, splitProps, untrack } from 'solid-js';
import { type ClusterStepProps, createClusterStep } from './ClusterCreationModal';
import Dropdown from '~ui/components/base/Dropdown';
import TextField from '~ui/components/base/TextField';
import VanillaImage from '~assets/logos/vanilla.png';
import FabricImage from '~assets/logos/fabric.png';
import ForgeImage from '~assets/logos/forge.png';
import QuiltImage from '~assets/logos/quilt.png';
import useCommand from '~ui/hooks/useCommand';
import { bridge } from '~imports';
import SelectList from '~ui/components/base/SelectList';
import Checkbox from '~ui/components/base/Checkbox';
import type { Loader, VersionType } from '~bindings';
import { formatVersionRelease } from '~utils';
import { formatVersionRelease, getLoaders } from '~utils';
import LoaderIcon from '~ui/components/game/LoaderIcon';
const loaders: {
name: string;
icon: () => JSX.Element;
}[] = [
{
name: 'Vanilla',
icon: () => <img src={VanillaImage} />,
},
{
name: 'Fabric',
icon: () => <img src={FabricImage} />,
},
{
name: 'Forge',
icon: () => <img src={ForgeImage} />,
},
{
name: 'Quilt',
icon: () => <img src={QuiltImage} />,
},
];
export default createClusterStep({
message: 'Game Setup',
buttonType: 'create',
Component: ClusterGameSetup,
});
export function ClusterStepTwo(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;
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;
props.setCanGoForward(hasName && hasVersion && hasLoader);
};
// props.setCanGoForward(hasName && hasVersion && hasLoader);
// };
createEffect(check);
createEffect(on(() => props.isVisible(), (curr: boolean) => {
if (curr)
check();
}));
// createEffect(check);
// createEffect(on(() => props.isVisible(), (curr: boolean) => {
// if (curr)
// check();
// }));
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 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);
onMount(() => {
setLoader('vanilla');
@ -74,15 +55,15 @@ export function ClusterStepTwo(props: ClusterStepProps) {
</Option>
<Option header="Loader">
<Dropdown onChange={index => setLoader(loaders[index]?.name || 'vanilla')}>
<For each={loaders}>
<Dropdown onChange={index => setLoader(getLoaders()[index] || 'vanilla')}>
<For each={getLoaders()}>
{loader => (
<Dropdown.Row>
<div class="flex flex-row gap-x-2">
<div class="w-4 h-4">
<loader.icon />
<LoaderIcon loader={loader} />
</div>
{loader.name}
{loader}
</div>
</Dropdown.Row>
)}

View file

@ -1,6 +1,6 @@
import { Upload01Icon, User03Icon } from '@untitled-theme/icons-solid';
import { Index, type JSX, createEffect, createSignal, on } from 'solid-js';
import type { ClusterStepProps } from './ClusterCreationModal';
import { Index, type JSX, createSignal } from 'solid-js';
import { type ClusterStepProps, createClusterStep } from './ClusterCreationModal';
import ModrinthIcon from '~assets/logos/modrinth.svg?component-solid';
import CurseforgeIcon from '~assets/logos/curseforge.svg?component-solid';
@ -23,18 +23,24 @@ const providers: Omit<ProviderCardProps, 'selected' | 'setSelected'>[] = [
},
];
export default function ClusterStepOne(props: ClusterStepProps) {
export default createClusterStep({
message: 'Select Provider',
buttonType: 'next',
Component: ClusterProviderSelection,
});
function ClusterProviderSelection(_props: ClusterStepProps) {
const [selected, setSelected] = createSignal<number>();
const check = () => {
props.setCanGoForward(selected() !== undefined);
};
// const check = () => {
// props.setCanGoForward(selected() !== undefined);
// };
createEffect(check);
createEffect(on(() => props.isVisible(), (curr: boolean) => {
if (curr)
check();
}));
// createEffect(check);
// createEffect(on(() => props.isVisible(), (curr: boolean) => {
// if (curr)
// check();
// }));
return (
<div class="grid grid-cols-3 gap-2">

View file

@ -17,13 +17,13 @@ import type { Cluster } from '~bindings';
import useCommand from '~ui/hooks/useCommand';
import { bridge } from '~imports';
import { secondsToWords, upperFirst } from '~utils';
import { useClusterModalController } from '~ui/components/overlay/cluster/ClusterCreationModal';
import { useClusterCreator } from '~ui/components/overlay/cluster/ClusterCreationModal';
type GroupedClusters = Record<string, Cluster[]>;
function HomePage() {
const [clusters, { refetch }] = useCommand<GroupedClusters>(bridge.commands.getClustersGrouped);
const controller = useClusterModalController();
const controller = useClusterCreator();
const containerIds = (list: GroupedClusters | undefined) => Object.keys(list || []);

View file

@ -96,3 +96,7 @@ export function asEnvVariables(str: string): [string, string][] {
})
.filter(pair => pair !== null);
}
export function getLoaders(): Loader[] {
return ['vanilla', 'fabric', 'forge', 'neoforge', 'quilt'];
}

View file

@ -111,33 +111,33 @@ pub async fn import_instances(import: ImportType, path: PathBuf) -> crate::Resul
pub async fn import_instance(
cluster_path: ClusterPath,
import: ImportType,
path: PathBuf,
base_path: PathBuf,
instance_path: String,
) -> crate::Result<()> {
tracing::debug!("importing instance from {instance_path}");
let result = match import {
ImportType::MultiMC | ImportType::PrismLauncher => {
multibased::import_mmc(path, instance_path, cluster_path.clone()).await
multibased::import_mmc(base_path, instance_path, cluster_path.clone()).await
}
ImportType::ATLauncher => {
atlauncher::import_atlauncher(path, instance_path, cluster_path.clone()).await
atlauncher::import_atlauncher(base_path, instance_path, cluster_path.clone()).await
}
ImportType::GDLauncher => {
gdlauncher::import_gdlauncher(
path.join("instances").join(instance_path),
base_path.join("instances").join(instance_path),
cluster_path.clone(),
)
.await
}
ImportType::Curseforge => {
curseforge::import_curseforge(
path.join("Instances").join(instance_path),
base_path.join("Instances").join(instance_path),
cluster_path.clone(),
)
.await
}
// ImportType::Modrinth => modrinth::import_modrinth(path.join("profiles").join(instance_path), cluster_path.clone()).await,
ImportType::Modrinth => modrinth::import_modrinth(base_path.join("profiles").join(instance_path), cluster_path.clone()).await,
// ImportType::TLauncher => tlauncher::import_tlauncher(path.join("instances").join(instance_path), cluster_path.clone()).await,
// ImportType::Technic => technic::import_technic(path.join("instances").join(instance_path), cluster_path.clone()).await,
// ImportType::FTBLauncher => ftb::import_ftb(path.join("instances").join(instance_path), cluster_path.clone()).await,