feat: Less empty home page + work on integrating backend with frontend
This commit is contained in:
parent
0dcc7083a9
commit
5e6c628f55
11 changed files with 253 additions and 180 deletions
|
|
@ -1,13 +1,9 @@
|
|||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use chrono::DateTime;
|
||||
use interpulse::api::minecraft::Version;
|
||||
use onelauncher::constants::{NATIVE_ARCH, TARGET_OS, VERSION};
|
||||
use onelauncher::data::{ClusterMeta, Loader, MinecraftCredentials, PackageData, Settings};
|
||||
use onelauncher::store::{Cluster, ClusterPath, MinecraftLogin};
|
||||
use onelauncher::data::{Loader, MinecraftCredentials, PackageData, Settings};
|
||||
use onelauncher::store::{Cluster, MinecraftLogin};
|
||||
use onelauncher::{cluster, minecraft, settings};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
|
|
@ -81,58 +77,56 @@ pub async fn create_cluster(props: CreateCluster) -> Result<Uuid, String> {
|
|||
|
||||
// }
|
||||
|
||||
fn placeholder_cluster() -> Cluster {
|
||||
let path = ClusterPath("test".into());
|
||||
Cluster {
|
||||
uuid: Uuid::from_str("56d1cbcf-1961-4477-b263-80e3b1c7a9d1").unwrap(),
|
||||
stage: onelauncher::store::ClusterStage::Installed,
|
||||
path: path.0,
|
||||
meta: ClusterMeta {
|
||||
created_at: DateTime::from_timestamp_millis(1718297861712).unwrap(),
|
||||
modified_at: DateTime::from_timestamp_millis(1718297861712).unwrap(),
|
||||
group: vec![],
|
||||
icon: None,
|
||||
icon_url: None,
|
||||
loader: Loader::Vanilla,
|
||||
loader_version: None,
|
||||
mc_version: "1.8.9".into(),
|
||||
name: "Test Cluster".into(),
|
||||
overall_played: 58195,
|
||||
recently_played: 0,
|
||||
package_data: None,
|
||||
played_at: None,
|
||||
},
|
||||
memory: None,
|
||||
java: None,
|
||||
resolution: None,
|
||||
force_fullscreen: None,
|
||||
init_hooks: None,
|
||||
packages: HashMap::new(),
|
||||
update: None,
|
||||
// fn placeholder_cluster() -> Cluster {
|
||||
// let path = ClusterPath("test".into());
|
||||
// Cluster {
|
||||
// uuid: Uuid::from_str("56d1cbcf-1961-4477-b263-80e3b1c7a9d1").unwrap(),
|
||||
// stage: onelauncher::store::ClusterStage::Installed,
|
||||
// path: path.0,
|
||||
// meta: ClusterMeta {
|
||||
// created_at: DateTime::from_timestamp_millis(1718297861712).unwrap(),
|
||||
// modified_at: DateTime::from_timestamp_millis(1718297861712).unwrap(),
|
||||
// group: vec![],
|
||||
// icon: None,
|
||||
// icon_url: None,
|
||||
// loader: Loader::Vanilla,
|
||||
// loader_version: None,
|
||||
// mc_version: "1.8.9".into(),
|
||||
// name: "Test Cluster".into(),
|
||||
// overall_played: 58195,
|
||||
// recently_played: 0,
|
||||
// package_data: None,
|
||||
// played_at: None,
|
||||
// },
|
||||
// memory: None,
|
||||
// java: None,
|
||||
// resolution: None,
|
||||
// force_fullscreen: None,
|
||||
// init_hooks: None,
|
||||
// packages: HashMap::new(),
|
||||
// update: None,
|
||||
// }
|
||||
// }
|
||||
|
||||
#[specta::specta]
|
||||
#[tauri::command]
|
||||
pub async fn get_cluster(uuid: Uuid) -> Result<Cluster, String> {
|
||||
match cluster::get_by_uuid(uuid, None).await? {
|
||||
Some(cluster) => Ok(cluster),
|
||||
None => Err("Cluster does not exist".into())
|
||||
}
|
||||
}
|
||||
|
||||
#[specta::specta]
|
||||
#[tauri::command]
|
||||
pub async fn get_cluster(uuid: Uuid) -> Result<Cluster, String> {
|
||||
Ok(placeholder_cluster())
|
||||
pub async fn get_clusters() -> Result<Vec<Cluster>, String> {
|
||||
// let mut map = HashMap::<ClusterPath, Cluster>::new();
|
||||
// let cluster = placeholder_cluster();
|
||||
// map.insert(cluster.cluster_path(), cluster);
|
||||
|
||||
// match cluster::get_by_uuid(uuid, None).await? {
|
||||
// Some(cluster) => Ok(cluster),
|
||||
// None => Err("Cluster does not exist".into())
|
||||
// }
|
||||
}
|
||||
// Ok(map)
|
||||
|
||||
#[specta::specta]
|
||||
#[tauri::command]
|
||||
pub async fn get_clusters() -> Result<HashMap<ClusterPath, Cluster>, String> {
|
||||
let mut map = HashMap::<ClusterPath, Cluster>::new();
|
||||
let cluster = placeholder_cluster();
|
||||
map.insert(cluster.cluster_path(), cluster);
|
||||
|
||||
Ok(map)
|
||||
|
||||
// cluster::list(None).await.map_err(|op| op.into())
|
||||
Ok(cluster::list(None).await?)
|
||||
}
|
||||
|
||||
#[specta::specta]
|
||||
|
|
@ -212,3 +206,13 @@ pub async fn finish_msa(
|
|||
pub async fn remove_user(uuid: Uuid) -> Result<(), String> {
|
||||
Ok(minecraft::remove_user(uuid).await?)
|
||||
}
|
||||
|
||||
// #[specta::specta]
|
||||
// #[tauri::command]
|
||||
// pub async fn import_instance() -> Result<(), String> {
|
||||
// onelauncher::api::package::import::import_instance()
|
||||
// }
|
||||
|
||||
// #[specta::specta]
|
||||
// #[tauri::command]
|
||||
// pub async fn
|
||||
|
|
|
|||
|
|
@ -16,31 +16,40 @@ function App(props: ParentProps) {
|
|||
});
|
||||
|
||||
return (
|
||||
<ClusterModalController>
|
||||
<SettingsProvider>
|
||||
<main class="flex flex-col bg-primary w-full min-h-screen overflow-hidden h-screen max-h-screen text-fg-primary">
|
||||
<WindowFrame />
|
||||
<div class="flex flex-col px-8">
|
||||
<Navbar />
|
||||
</div>
|
||||
<GlobalContexts>
|
||||
<main class="flex flex-col bg-primary w-full min-h-screen overflow-hidden h-screen max-h-screen text-fg-primary">
|
||||
<WindowFrame />
|
||||
<div class="flex flex-col px-8">
|
||||
<Navbar />
|
||||
</div>
|
||||
|
||||
<div class="relative h-full w-full overflow-hidden">
|
||||
<div class="absolute top-0 left-0 flex flex-col h-full w-full overflow-x-hidden">
|
||||
<ErrorBoundary>
|
||||
<OverlayScrollbarsComponent class="os-hide-horizontal-scrollbar absolute top-0 left-0 flex flex-col h-full w-full overflow-x-hidden overflow-y-auto px-8 pb-8">
|
||||
<AnimatedRoutes>
|
||||
{props.children}
|
||||
</AnimatedRoutes>
|
||||
</OverlayScrollbarsComponent>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
<div class="relative h-full w-full overflow-hidden">
|
||||
<div class="absolute top-0 left-0 flex flex-col h-full w-full overflow-x-hidden">
|
||||
<ErrorBoundary>
|
||||
<OverlayScrollbarsComponent class="os-hide-horizontal-scrollbar absolute top-0 left-0 flex flex-col h-full w-full overflow-x-hidden overflow-y-auto px-8 pb-8">
|
||||
<AnimatedRoutes>
|
||||
{props.children}
|
||||
</AnimatedRoutes>
|
||||
</OverlayScrollbarsComponent>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<NotificationOverlay />
|
||||
</main>
|
||||
</SettingsProvider>
|
||||
</ClusterModalController>
|
||||
<NotificationOverlay />
|
||||
</main>
|
||||
</GlobalContexts>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
function GlobalContexts(props: ParentProps) {
|
||||
// TODO(refactor): Something something lots of nesting will look awful soon
|
||||
return (
|
||||
<SettingsProvider>
|
||||
<ClusterModalController>
|
||||
{props.children}
|
||||
</ClusterModalController>
|
||||
</SettingsProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
import { type Accessor, type Context, Match, type ParentProps, type Setter, Show, Switch, createContext, createEffect, createSignal, useContext } from 'solid-js';
|
||||
import { type Accessor, type Context, Match, type ParentProps, type Setter, Show, Switch, createContext, createEffect, createSignal, untrack, useContext } from 'solid-js';
|
||||
import { ArrowRightIcon, Server01Icon } from '@untitled-theme/icons-solid';
|
||||
import HeaderImage from '../../../../assets/images/header.png';
|
||||
import FullscreenOverlay, { type FullscreenOverlayProps } from '../FullscreenOverlay';
|
||||
import FullscreenOverlay from '../FullscreenOverlay';
|
||||
import ClusterStepOne from './ClusterStepOne';
|
||||
import { ClusterStepTwo } from './ClusterStepTwo';
|
||||
import Button from '~ui/components/base/Button';
|
||||
import type { CreateCluster } from '~bindings';
|
||||
import { bridge } from '~imports';
|
||||
|
||||
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
|
||||
|
|
@ -16,7 +18,9 @@ interface ClusterModalContextFunc {
|
|||
setStep: Setter<number>;
|
||||
partialCluster: Accessor<PartialCluster>;
|
||||
setPartialCluster: Setter<PartialCluster>;
|
||||
updatePartialCluster: <K extends keyof PartialCluster>(key: K, value: PartialCluster[K]) => any;
|
||||
updatePartialCluster: PartialClusterUpdateFunc;
|
||||
finish: () => void;
|
||||
visible: Accessor<boolean>;
|
||||
setVisible: Setter<boolean>;
|
||||
}
|
||||
|
||||
|
|
@ -34,19 +38,43 @@ export function ClusterModalController(props: ParentProps) {
|
|||
const [visible, setVisible] = createSignal(false);
|
||||
const [partialCluster, setPartialCluster] = createSignal<PartialCluster>({});
|
||||
|
||||
const updatePartialCluster: PartialClusterUpdateFunc = (key, value) => {
|
||||
setPartialCluster((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
[key]: value,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const 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');
|
||||
|
||||
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,
|
||||
});
|
||||
};
|
||||
|
||||
const stepper: ClusterModalContextFunc = {
|
||||
step,
|
||||
setStep,
|
||||
partialCluster,
|
||||
setPartialCluster,
|
||||
updatePartialCluster(key, value) {
|
||||
setPartialCluster((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
[key]: value,
|
||||
};
|
||||
});
|
||||
},
|
||||
updatePartialCluster,
|
||||
finish,
|
||||
visible,
|
||||
setVisible,
|
||||
};
|
||||
|
||||
|
|
@ -60,13 +88,7 @@ export function ClusterModalController(props: ParentProps) {
|
|||
{props.children}
|
||||
{/* Makes sure theres a new instance of the modal */}
|
||||
<Show when={visible()}>
|
||||
<ClusterCreationModal
|
||||
visible={visible}
|
||||
setVisible={setVisible}
|
||||
step={step}
|
||||
setStep={setStep}
|
||||
buttonIsNext={step() !== ((Object.keys(ClusterModalStages).length / 2) - 1)}
|
||||
/>
|
||||
<ClusterCreationModal />
|
||||
</Show>
|
||||
</ClusterModalContext.Provider>
|
||||
);
|
||||
|
|
@ -81,13 +103,10 @@ export interface ClusterStepProps {
|
|||
setCanGoForward: Setter<boolean>;
|
||||
};
|
||||
|
||||
type ClusterCreationModalProps = FullscreenOverlayProps & {
|
||||
step: Accessor<number>;
|
||||
setStep: Setter<number>;
|
||||
buttonIsNext: boolean;
|
||||
};
|
||||
function ClusterCreationModal() {
|
||||
const controller = useClusterModalController();
|
||||
const buttonIsNext = () => controller.step() !== ((Object.keys(ClusterModalStages).length / 2) - 1);
|
||||
|
||||
function ClusterCreationModal(props: ClusterCreationModalProps) {
|
||||
const [canGoForward, setCanGoForward] = createSignal(false);
|
||||
// Indexed by the current step
|
||||
const messages: FixedArray<string, ClusterModalStagesLength> = [
|
||||
|
|
@ -101,18 +120,32 @@ function ClusterCreationModal(props: ClusterCreationModalProps) {
|
|||
].map((Component, index) => (
|
||||
Component({
|
||||
setCanGoForward,
|
||||
isVisible: () => props.visible() && props.step() === index,
|
||||
isVisible: () => controller.visible() && controller.step() === index,
|
||||
})
|
||||
));
|
||||
|
||||
const Step = (props: { step: number }) => <>{stepComponents[props.step]}</>;
|
||||
|
||||
function cancel() {
|
||||
controller.setVisible(false);
|
||||
}
|
||||
|
||||
function prev() {
|
||||
controller.setStep(prev => prev - 1);
|
||||
}
|
||||
|
||||
function next() {
|
||||
controller.setStep(prev => prev + 1);
|
||||
}
|
||||
|
||||
function finish() {
|
||||
controller.finish();
|
||||
}
|
||||
|
||||
return (
|
||||
<FullscreenOverlay
|
||||
visible={props.visible}
|
||||
setVisible={props.setVisible}
|
||||
mount={props.mount}
|
||||
zIndex={props.zIndex || 1000}
|
||||
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">
|
||||
|
|
@ -127,49 +160,50 @@ function ClusterCreationModal(props: ClusterCreationModalProps) {
|
|||
<div class="flex flex-col items-start justify-center">
|
||||
{/** weird positioning?? */}
|
||||
<h1 class="h-10 -mt-2">New Cluster</h1>
|
||||
<span>{messages[props.step()]}</span>
|
||||
<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={props.step()} />
|
||||
<Step step={controller.step()} />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-x-2 justify-end pt-0 p-3">
|
||||
<Switch>
|
||||
<Match when={props.step() === 0}>
|
||||
<Match when={controller.step() === 0}>
|
||||
<Button
|
||||
children="Cancel"
|
||||
buttonStyle="ghost"
|
||||
onClick={() => props.setVisible(false)}
|
||||
onClick={cancel}
|
||||
/>
|
||||
</Match>
|
||||
<Match when={props.step() >= 1}>
|
||||
<Match when={controller.step() >= 1}>
|
||||
<Button
|
||||
children="Previous"
|
||||
buttonStyle="ghost"
|
||||
onClick={() => props.setStep(prev => prev - 1)}
|
||||
onClick={prev}
|
||||
/>
|
||||
</Match>
|
||||
</Switch>
|
||||
|
||||
<Switch>
|
||||
<Match when={props.buttonIsNext === true}>
|
||||
<Match when={buttonIsNext() === true}>
|
||||
<Button
|
||||
children="Next"
|
||||
buttonStyle="primary"
|
||||
disabled={!canGoForward()}
|
||||
iconRight={<ArrowRightIcon />}
|
||||
onClick={() => props.setStep(prev => prev + 1)}
|
||||
onClick={next}
|
||||
/>
|
||||
</Match>
|
||||
<Match when={props.buttonIsNext === false}>
|
||||
<Match when={buttonIsNext() === false}>
|
||||
<Button
|
||||
children="Create"
|
||||
buttonStyle="primary"
|
||||
disabled={!canGoForward()}
|
||||
iconRight={<ArrowRightIcon />}
|
||||
onClick={finish}
|
||||
/>
|
||||
</Match>
|
||||
</Switch>
|
||||
|
|
|
|||
|
|
@ -18,17 +18,9 @@ const providers: Omit<ProviderCardProps, 'selected' | 'setSelected'>[] = [
|
|||
icon: <CurseforgeIcon color="#F16436" />,
|
||||
},
|
||||
{
|
||||
name: 'New',
|
||||
name: 'Import',
|
||||
icon: <User03Icon />,
|
||||
},
|
||||
{
|
||||
name: 'Modrinth',
|
||||
icon: <ModrinthIcon color="#1bd96a" />,
|
||||
},
|
||||
{
|
||||
name: 'Curseforge',
|
||||
icon: <CurseforgeIcon color="#F16436" />,
|
||||
},
|
||||
];
|
||||
|
||||
export default function ClusterStepOne(props: ClusterStepProps) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { TextInputIcon } from '@untitled-theme/icons-solid';
|
||||
import { For, Index, type JSX, type Setter, Show, createEffect, createSignal, on, splitProps, untrack } from 'solid-js';
|
||||
import { For, Index, type JSX, Show, createEffect, createSignal, on, onMount, splitProps, untrack } from 'solid-js';
|
||||
import { type ClusterStepProps, useClusterModalController } from './ClusterCreationModal';
|
||||
import Dropdown from '~ui/components/base/Dropdown';
|
||||
import TextField from '~ui/components/base/TextField';
|
||||
|
|
@ -55,7 +55,11 @@ export function ClusterStepTwo(props: ClusterStepProps) {
|
|||
|
||||
const setName = (name: string) => updatePartialCluster('name', name);
|
||||
const setVersion = (version: string) => updatePartialCluster('mc_version', version);
|
||||
const setLoader = (loader: string) => updatePartialCluster('mod_loader', loader.toLowerCase() as Loader);
|
||||
const setLoader = (loader: Loader | string) => updatePartialCluster('mod_loader', loader.toLowerCase() as Loader);
|
||||
|
||||
onMount(() => {
|
||||
setLoader('vanilla');
|
||||
});
|
||||
|
||||
return (
|
||||
<div class="flex flex-col gap-y-4">
|
||||
|
|
@ -101,7 +105,7 @@ function VersionSelector(props: { setVersion: (version: string) => any }) {
|
|||
const [filters, setFilters] = createSignal<VersionReleaseFilters>({
|
||||
old_alpha: false,
|
||||
old_beta: false,
|
||||
release: false,
|
||||
release: true,
|
||||
snapshot: false,
|
||||
});
|
||||
|
||||
|
|
@ -142,7 +146,7 @@ function VersionSelector(props: { setVersion: (version: string) => any }) {
|
|||
refresh(filters(), list);
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
onMount(() => {
|
||||
setFilteredVersions(versions()!);
|
||||
});
|
||||
|
||||
|
|
@ -164,7 +168,12 @@ function VersionSelector(props: { setVersion: (version: string) => any }) {
|
|||
<div class="bg-component-bg rounded-lg overflow-hidden border border-gray-05 flex-1 p-2 flex flex-col gap-y-2">
|
||||
<For each={Object.keys(filters())}>
|
||||
{name => (
|
||||
<Checkbox onChecked={() => toggleFilter(name)}>{formatVersionRelease(name as VersionType)}</Checkbox>
|
||||
<Checkbox
|
||||
defaultChecked={filters()[name as VersionType]!}
|
||||
onChecked={() => toggleFilter(name)}
|
||||
>
|
||||
{formatVersionRelease(name as VersionType)}
|
||||
</Checkbox>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
SearchMdIcon,
|
||||
} from '@untitled-theme/icons-solid';
|
||||
import type { Accessor, Setter } from 'solid-js';
|
||||
import { For, Show, createSignal } from 'solid-js';
|
||||
import { For, Match, Switch, createSignal } from 'solid-js';
|
||||
import { useNavigate } from '@solidjs/router';
|
||||
import image from '../../assets/images/header.png';
|
||||
import ContextMenu from '../components/overlay/ContextMenu';
|
||||
|
|
@ -21,6 +21,49 @@ import { bridge } from '~imports';
|
|||
import { upperFirst } from '~utils/primitives';
|
||||
import { useClusterModalController } from '~ui/components/overlay/cluster/ClusterCreationModal';
|
||||
|
||||
function HomePage() {
|
||||
const [clusters, { refetch }] = useCommand(bridge.commands.getClusters);
|
||||
const controller = useClusterModalController();
|
||||
|
||||
async function newCluster() {
|
||||
controller.setVisible(true);
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="flex flex-col h-full gap-y-4 text-fg-primary">
|
||||
<Banner />
|
||||
|
||||
<div class="flex flex-row justify-between items-center">
|
||||
<div>
|
||||
<TextField iconLeft={<SearchMdIcon />} placeholder="Search for clusters..." />
|
||||
</div>
|
||||
<div class="flex flex-row gap-x-4">
|
||||
<Button
|
||||
buttonStyle="primary"
|
||||
iconLeft={<PlusIcon class="!w-5 stroke-[2.2]" />}
|
||||
children="New Cluster"
|
||||
onClick={newCluster}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Switch>
|
||||
<Match when={clusters()?.length === 0}>
|
||||
<div class="flex flex-col flex-1 gap-y-4 max-h-64 justify-center items-center">
|
||||
<span class="text-lg font-bold uppercase text-fg-secondary">No clusters were found.</span>
|
||||
<span class="text-xl font-bold">Create one now with the New Cluster button.</span>
|
||||
</div>
|
||||
</Match>
|
||||
<Match when={clusters() !== undefined}>
|
||||
<ClusterGroup title="Clusters" clusters={clusters()!} />
|
||||
</Match>
|
||||
</Switch>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HomePage;
|
||||
|
||||
// TODO: Replace this into it's own component
|
||||
// possibly with the core ui lirbary with autogenerated and optimized svgs pls
|
||||
function OneConfigLogo() {
|
||||
|
|
@ -40,6 +83,15 @@ function OneConfigLogo() {
|
|||
}
|
||||
|
||||
function Banner() {
|
||||
// TODO: Banner information
|
||||
/**
|
||||
* If there are any clusters, display the most recent cluster name + some statistics as the "description".
|
||||
* The background would prioritise
|
||||
* any screenshots taken from the cluster, if there are none, it would display the cluster cover if it has been set.
|
||||
* The button would launch the cluster.
|
||||
*
|
||||
* If there are no clusters, display a generic background with the button action creating a new cluster.
|
||||
*/
|
||||
return (
|
||||
<div class="relative w-full h-52">
|
||||
<img src={image} class="absolute rounded-xl w-full h-52 object-cover" />
|
||||
|
|
@ -50,10 +102,15 @@ function Banner() {
|
|||
</div>
|
||||
<div class="flex w-full flex-row justify-between items-end">
|
||||
<div class="flex flex-row items-center gap-x-4">
|
||||
<Button iconLeft={<PlayIcon />}>Launch 1.19.2</Button>
|
||||
<Button buttonStyle="icon" class="w-8 h-8">
|
||||
<DotsVerticalIcon />
|
||||
</Button>
|
||||
<Button
|
||||
buttonStyle="primary"
|
||||
iconLeft={<PlayIcon />}
|
||||
children="Launch 1.19.2"
|
||||
/>
|
||||
<Button
|
||||
buttonStyle="icon"
|
||||
children={<DotsVerticalIcon />}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-row gap-x-2">
|
||||
<Tag iconLeft={<OneConfigLogo />} />
|
||||
|
|
@ -184,44 +241,3 @@ function ClusterGroup(props: ClusterGroupProps) {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function HomePage() {
|
||||
const [clusters, { refetch }] = useCommand(bridge.commands.getClusters);
|
||||
const { setVisible } = useClusterModalController();
|
||||
|
||||
async function newInstance() {
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="flex flex-col gap-y-4 text-fg-primary">
|
||||
<Banner />
|
||||
|
||||
<div class="flex flex-row justify-between items-center">
|
||||
<div>
|
||||
<TextField iconLeft={<SearchMdIcon />} placeholder="Search for something..." />
|
||||
</div>
|
||||
<div class="flex flex-row gap-x-4">
|
||||
<Button
|
||||
buttonStyle="primary"
|
||||
iconLeft={<PlusIcon class="!w-5 stroke-[2.2]" />}
|
||||
children="New Cluster"
|
||||
onClick={() => newInstance()}
|
||||
/>
|
||||
|
||||
{/* <Button
|
||||
styleType="secondary"
|
||||
iconLeft={<Download01Icon />}
|
||||
children="From URL"
|
||||
/> */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Show when={clusters() !== undefined}>
|
||||
<ClusterGroup title="Placeholder" clusters={Object.values(clusters()!)} />
|
||||
</Show>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HomePage;
|
||||
|
|
|
|||
|
|
@ -107,9 +107,9 @@ pub async fn create_cluster(
|
|||
.await?;
|
||||
}
|
||||
|
||||
if !skip.unwrap_or(false) {
|
||||
crate::game::install_minecraft(&cluster, None, false).await?;
|
||||
}
|
||||
// if !skip.unwrap_or(false) {
|
||||
// crate::game::install_minecraft(&cluster, None, false).await?;
|
||||
// }
|
||||
|
||||
State::sync().await?;
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ pub async fn get(path: &ClusterPath, clear: Option<bool>) -> crate::Result<Optio
|
|||
|
||||
/// get a list of all [`Cluster`]s
|
||||
#[tracing::instrument]
|
||||
pub async fn list(clear: Option<bool>) -> crate::Result<HashMap<ClusterPath, Cluster>> {
|
||||
pub async fn list(clear: Option<bool>) -> crate::Result<Vec<Cluster>> {
|
||||
let state = State::get().await?;
|
||||
let clusters = state.clusters.read().await;
|
||||
Ok(clusters
|
||||
|
|
@ -56,11 +56,18 @@ pub async fn list(clear: Option<bool>) -> crate::Result<HashMap<ClusterPath, Clu
|
|||
if clear.unwrap_or(false) {
|
||||
it.1.packages = HashMap::new();
|
||||
}
|
||||
it
|
||||
it.1
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
// #[tracing::instrument]
|
||||
// pub async fn list_grouped(clear: Option<bool>) -> crate::Result<HashMap<String, Cluster>> {
|
||||
// let clusters = list(clear).await?;
|
||||
// let mut map = HashMap::new();
|
||||
|
||||
// }
|
||||
|
||||
/// run a Minecraft [`Cluster`] using the default credentials.
|
||||
#[tracing::instrument]
|
||||
pub async fn run(path: &ClusterPath) -> crate::Result<Arc<RwLock<ProcessorChild>>> {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ pub mod technic;
|
|||
pub mod tlauncher;
|
||||
|
||||
/// List of launcher types we support importing from.
|
||||
#[cfg_attr(feature = "tauri", derive(specta::Type))]
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub enum ImportType {
|
||||
/// MultiMC based launchers
|
||||
|
|
|
|||
|
|
@ -15,4 +15,4 @@ onelauncher = { path = "../core", features = [ "cli" ] }
|
|||
|
||||
tokio = { workspace = true }
|
||||
webbrowser = { version = "0.8" }
|
||||
url = { version = "2.5" }
|
||||
url = { version = "2.5.2" }
|
||||
|
|
|
|||
|
|
@ -23,11 +23,12 @@ pub async fn authenticate_mc() -> onelauncher::Result<MinecraftCredentials> {
|
|||
|
||||
let parsed = url::Url::parse(input.trim()).expect("idk");
|
||||
let code = if let Some((_, code)) = parsed.query_pairs().find(|x| x.0 == "code") {
|
||||
&code.clone()
|
||||
let code = code.clone();
|
||||
code.to_string()
|
||||
} else {
|
||||
panic!()
|
||||
};
|
||||
let creds = minecraft::finish(code, login).await?;
|
||||
let creds = minecraft::finish(code.as_str(), login).await?;
|
||||
|
||||
|
||||
println!("logged in {}", creds.username);
|
||||
|
|
@ -53,8 +54,8 @@ async fn main() -> onelauncher::Result<()> {
|
|||
println!("clearing clusters");
|
||||
{
|
||||
let c = cluster::list(None).await?;
|
||||
for (id, _) in c.into_iter() {
|
||||
cluster::remove(&id).await?;
|
||||
for cluster in c.into_iter() {
|
||||
cluster::remove(&cluster.cluster_path()).await?;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue