refactor: ClusterCreationModal (still awaits refactoring, Contexts shouldn't be used like this)
This commit is contained in:
parent
91aa2e03a7
commit
676c16f14a
2 changed files with 136 additions and 146 deletions
|
|
@ -1,7 +1,7 @@
|
|||
import { type Accessor, type Context, Match, type ParentProps, type Setter, Show, Switch, createContext, createEffect, createSignal, untrack, useContext } from 'solid-js';
|
||||
import { type Accessor, type Context, Match, type ParentProps, type Setter, Switch, createContext, createEffect, createSignal, onCleanup, untrack, useContext } from 'solid-js';
|
||||
import { ArrowRightIcon, Server01Icon } from '@untitled-theme/icons-solid';
|
||||
import HeaderImage from '../../../../assets/images/header.png';
|
||||
import FullscreenOverlay from '../FullscreenOverlay';
|
||||
import { createModal } from '../Modal';
|
||||
import ClusterStepOne from './ClusterStepOne';
|
||||
import { ClusterStepTwo } from './ClusterStepTwo';
|
||||
import Button from '~ui/components/base/Button';
|
||||
|
|
@ -22,8 +22,6 @@ interface ClusterModalContextFunc {
|
|||
updatePartialCluster: PartialClusterUpdateFunc;
|
||||
start: () => Promise<void>; // Returns true if its finished (created) successfully
|
||||
finish: () => void;
|
||||
visible: Accessor<boolean>;
|
||||
setVisible: Setter<boolean>;
|
||||
}
|
||||
|
||||
const ClusterModalContext = createContext<ClusterModalContextFunc>() as Context<ClusterModalContextFunc>;
|
||||
|
|
@ -37,7 +35,6 @@ type ClusterModalStagesLength = UnionToArray<keyof typeof ClusterModalStages>['l
|
|||
|
||||
export function ClusterModalController(props: ParentProps) {
|
||||
const [step, setStep] = createSignal<number>(ClusterModalStages.STAGE_1_PROVIDER);
|
||||
const [visible, setVisible] = createSignal(false);
|
||||
const [partialCluster, setPartialCluster] = createSignal<PartialCluster>({});
|
||||
const [inProgress, setInProgress] = createSignal(false);
|
||||
const [error, setError] = createSignal<string | undefined>(undefined);
|
||||
|
|
@ -51,91 +48,92 @@ export function ClusterModalController(props: ParentProps) {
|
|||
});
|
||||
};
|
||||
|
||||
const start = () => {
|
||||
setPartialCluster({});
|
||||
setStep(ClusterModalStages.STAGE_1_PROVIDER);
|
||||
setError(undefined);
|
||||
setInProgress(true);
|
||||
setVisible(true);
|
||||
const modal = createModal(() => (
|
||||
// eslint-disable-next-line ts/no-use-before-define -- circular dependency should be refactored later
|
||||
<ClusterCreationModal controller={context} />
|
||||
));
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
createEffect(() => {
|
||||
const result = error();
|
||||
if (inProgress() === true)
|
||||
return;
|
||||
|
||||
if (result === undefined || result.length === 0)
|
||||
resolve();
|
||||
else
|
||||
reject(result);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const finish = async () => {
|
||||
const untracked = untrack(partialCluster);
|
||||
|
||||
if (untracked.name === undefined || untracked.mc_version === undefined || untracked.mod_loader === undefined)
|
||||
throw new Error('Cluster is missing required fields');
|
||||
|
||||
setVisible(false);
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
setInProgress(false);
|
||||
};
|
||||
|
||||
const stepper: ClusterModalContextFunc = {
|
||||
const context: ClusterModalContextFunc = {
|
||||
step,
|
||||
setStep,
|
||||
partialCluster,
|
||||
setPartialCluster,
|
||||
updatePartialCluster,
|
||||
start,
|
||||
finish,
|
||||
visible,
|
||||
setVisible,
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
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();
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
setInProgress(false);
|
||||
},
|
||||
};
|
||||
|
||||
createEffect(() => {
|
||||
if (visible() === false)
|
||||
setStep(ClusterModalStages.STAGE_1_PROVIDER);
|
||||
onCleanup(() => {
|
||||
setStep(ClusterModalStages.STAGE_1_PROVIDER);
|
||||
});
|
||||
|
||||
return (
|
||||
<ClusterModalContext.Provider value={stepper}>
|
||||
<ClusterModalContext.Provider value={context}>
|
||||
{props.children}
|
||||
{/* Makes sure theres a new instance of the modal */}
|
||||
<Show when={visible()}>
|
||||
<ClusterCreationModal />
|
||||
</Show>
|
||||
</ClusterModalContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useClusterModalController() {
|
||||
return useContext(ClusterModalContext);
|
||||
const ctx = useContext(ClusterModalContext);
|
||||
|
||||
if (!ctx)
|
||||
throw new Error('useClusterModalController must be used within a ClusterModalController');
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
export interface ClusterStepProps {
|
||||
isVisible: Accessor<boolean>;
|
||||
setCanGoForward: Setter<boolean>;
|
||||
controller: Accessor<ClusterModalContextFunc>;
|
||||
};
|
||||
|
||||
function ClusterCreationModal() {
|
||||
const controller = useClusterModalController();
|
||||
const buttonIsNext = () => controller.step() !== ((Object.keys(ClusterModalStages).length / 2) - 1);
|
||||
function ClusterCreationModal(props: { controller: ClusterModalContextFunc }) {
|
||||
const buttonIsNext = () => props.controller.step() !== ((Object.keys(ClusterModalStages).length / 2) - 1);
|
||||
|
||||
const [canGoForward, setCanGoForward] = createSignal(false);
|
||||
// Indexed by the current step
|
||||
|
|
@ -150,97 +148,91 @@ function ClusterCreationModal() {
|
|||
].map((Component, index) => (
|
||||
Component({
|
||||
setCanGoForward,
|
||||
isVisible: () => controller.visible() && controller.step() === index,
|
||||
isVisible: () => props.controller.step() === index,
|
||||
controller: () => props.controller,
|
||||
})
|
||||
));
|
||||
|
||||
const Step = (props: { step: number }) => <>{stepComponents[props.step]}</>;
|
||||
|
||||
function cancel() {
|
||||
controller.setVisible(false);
|
||||
// controller.setVisible(false);
|
||||
}
|
||||
|
||||
function prev() {
|
||||
controller.setStep(prev => prev - 1);
|
||||
props.controller.setStep(prev => prev - 1);
|
||||
}
|
||||
|
||||
function next() {
|
||||
controller.setStep(prev => prev + 1);
|
||||
props.controller.setStep(prev => prev + 1);
|
||||
}
|
||||
|
||||
function finish() {
|
||||
controller.finish();
|
||||
props.controller.finish();
|
||||
}
|
||||
|
||||
return (
|
||||
<FullscreenOverlay
|
||||
visible={controller.visible}
|
||||
setVisible={controller.setVisible}
|
||||
>
|
||||
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
|
||||
<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">
|
||||
{/** weird positioning?? */}
|
||||
<h1 class="h-10 -mt-2">New Cluster</h1>
|
||||
<span>{messages[controller.step()]}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col border border-white/5 rounded-b-lg">
|
||||
<div class="p-3">
|
||||
<Step step={controller.step()} />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-2 justify-end pt-0 p-3">
|
||||
<Switch>
|
||||
<Match when={controller.step() === 0}>
|
||||
<Button
|
||||
children="Cancel"
|
||||
buttonStyle="ghost"
|
||||
onClick={cancel}
|
||||
/>
|
||||
</Match>
|
||||
<Match when={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 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">
|
||||
{/** weird positioning?? */}
|
||||
<h1 class="h-10 -mt-2">New Cluster</h1>
|
||||
<span>{messages[props.controller.step()]}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FullscreenOverlay>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
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, useClusterModalController } from './ClusterCreationModal';
|
||||
import type { ClusterStepProps } from './ClusterCreationModal';
|
||||
import Dropdown from '~ui/components/base/Dropdown';
|
||||
import TextField from '~ui/components/base/TextField';
|
||||
import VanillaImage from '~assets/logos/vanilla.png';
|
||||
|
|
@ -37,12 +37,10 @@ const loaders: {
|
|||
];
|
||||
|
||||
export function ClusterStepTwo(props: ClusterStepProps) {
|
||||
const { partialCluster, updatePartialCluster } = useClusterModalController();
|
||||
|
||||
const check = () => {
|
||||
const hasName = (partialCluster().name?.length ?? 0) > 0;
|
||||
const hasVersion = (partialCluster().mc_version?.length ?? 0) > 0;
|
||||
const hasLoader = (partialCluster().mod_loader?.length ?? 0) > 0;
|
||||
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);
|
||||
};
|
||||
|
|
@ -53,9 +51,9 @@ export function ClusterStepTwo(props: ClusterStepProps) {
|
|||
check();
|
||||
}));
|
||||
|
||||
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);
|
||||
|
||||
onMount(() => {
|
||||
setLoader('vanilla');
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue