chore: created a common package for frontend code
This commit is contained in:
parent
7d4acc2468
commit
9eadef559c
36 changed files with 336 additions and 273 deletions
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
|
|
@ -4,8 +4,7 @@
|
|||
},
|
||||
"files.readonlyInclude": {
|
||||
"**/node_modules/**": true,
|
||||
"**/routeTree.gen.ts": true,
|
||||
"**/bindings.gen.ts": true
|
||||
"**/*.gen.ts": true
|
||||
},
|
||||
"files.watcherExclude": {
|
||||
"**/_build": true,
|
||||
|
|
@ -21,7 +20,7 @@
|
|||
"**/.rustup": true,
|
||||
"**/.cargo": true,
|
||||
"**/.crates": true,
|
||||
"**/routeTree.gen.ts": true
|
||||
"**/*.gen.ts": true
|
||||
},
|
||||
"rust-analyzer.procMacro.enable": true,
|
||||
"rust-analyzer.procMacro.attributes.enable": true,
|
||||
|
|
|
|||
2
apps/oneclient/frontend/.gitignore
vendored
2
apps/oneclient/frontend/.gitignore
vendored
|
|
@ -3,3 +3,5 @@ node_modules
|
|||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
*.gen.ts
|
||||
|
|
|
|||
|
|
@ -103,11 +103,11 @@ export interface SettingProfileModel { name: string; java_id: bigint | null; res
|
|||
|
||||
export interface SettingsOsExtra { enable_gamemode: boolean | null }
|
||||
|
||||
const ARGS_MAP = { 'core': '{"get_cluster":["id"],"remove_cluster":["id"],"get_profile_or_default":["name"],"get_clusters":[],"remove_user":["uuid"],"get_global_profile":[],"get_users":[],"get_default_user":["fallback"],"create_cluster":["options"],"open_msa_login":[],"get_user":["uuid"],"set_default_user":["uuid"]}', 'oneclient': '{"open_dev_tools":[],"return_error":[]}', '': '{"send_event":["event"]}' };
|
||||
const ARGS_MAP = { 'core': '{"openMsaLogin":[],"getClusters":[],"getClusterById":["id"],"getUsers":[],"removeUser":["uuid"],"removeCluster":["id"],"getGlobalProfile":[],"getUser":["uuid"],"setDefaultUser":["uuid"],"createCluster":["options"],"getDefaultUser":["fallback"],"getProfileOrDefault":["name"]}', 'oneclient': '{"return_error":[],"open_dev_tools":[]}', '': '{"send_event":["event"]}' };
|
||||
export interface Router {
|
||||
'core': { get_clusters: () => Promise<Array<Model>>; get_cluster: (id: bigint) => Promise<Model | null>; remove_cluster: (id: bigint) => Promise<null>; create_cluster: (options: CreateCluster) => Promise<Model>; get_profile_or_default: (name: string | null) => Promise<SettingProfileModel>; get_global_profile: () => Promise<SettingProfileModel>; get_users: () => Promise<Array<MinecraftCredentials>>; get_user: (uuid: string) => Promise<MinecraftCredentials | null>; remove_user: (uuid: string) => Promise<null>; get_default_user: (fallback: boolean | null) => Promise<MinecraftCredentials | null>; set_default_user: (uuid: string | null) => Promise<null>; open_msa_login: () => Promise<MinecraftCredentials | null> };
|
||||
'oneclient': { return_error: () => Promise<null>; open_dev_tools: () => Promise<void> };
|
||||
'': { send_event: (event: LauncherEvent) => Promise<void> };
|
||||
'core': { getClusters: () => Promise<Array<Model>>; getClusterById: (id: bigint) => Promise<Model | null>; removeCluster: (id: bigint) => Promise<null>; createCluster: (options: CreateCluster) => Promise<Model>; getProfileOrDefault: (name: string | null) => Promise<SettingProfileModel>; getGlobalProfile: () => Promise<SettingProfileModel>; getUsers: () => Promise<Array<MinecraftCredentials>>; getUser: (uuid: string) => Promise<MinecraftCredentials | null>; removeUser: (uuid: string) => Promise<null>; getDefaultUser: (fallback: boolean | null) => Promise<MinecraftCredentials | null>; setDefaultUser: (uuid: string | null) => Promise<null>; openMsaLogin: () => Promise<MinecraftCredentials | null> };
|
||||
'oneclient': { return_error: () => Promise<null>; open_dev_tools: () => Promise<void> };
|
||||
}
|
||||
|
||||
export type { InferCommandOutput };
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Button } from '@onelauncher/common/components';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { Button } from "@onelauncher/common/components";
|
||||
|
||||
export const Route = createFileRoute('/')({
|
||||
component: App,
|
||||
|
|
|
|||
2
apps/onelauncher/frontend/.gitignore
vendored
2
apps/onelauncher/frontend/.gitignore
vendored
|
|
@ -3,3 +3,5 @@ node_modules
|
|||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
*.gen.ts
|
||||
|
|
|
|||
|
|
@ -103,11 +103,11 @@ export interface SettingProfileModel { name: string; java_id: bigint | null; res
|
|||
|
||||
export interface SettingsOsExtra { enable_gamemode: boolean | null }
|
||||
|
||||
const ARGS_MAP = { '': '{"send_event":["event"]}', 'onelauncher': '{"return_error":[],"open_dev_tools":[]}', 'core': '{"get_user":["uuid"],"get_default_user":["fallback"],"set_default_user":["uuid"],"get_global_profile":[],"get_clusters":[],"remove_cluster":["id"],"get_profile_or_default":["name"],"get_users":[],"remove_user":["uuid"],"open_msa_login":[],"create_cluster":["options"],"get_cluster":["id"]}' };
|
||||
const ARGS_MAP = { 'core': '{"getGlobalProfile":[],"getUsers":[],"removeUser":["uuid"],"getDefaultUser":["fallback"],"getUser":["uuid"],"getClusterById":["id"],"setDefaultUser":["uuid"],"openMsaLogin":[],"removeCluster":["id"],"getProfileOrDefault":["name"],"getClusters":[],"createCluster":["options"]}', 'onelauncher': '{"open_dev_tools":[],"return_error":[]}', '': '{"send_event":["event"]}' };
|
||||
export interface Router {
|
||||
'onelauncher': { return_error: () => Promise<null>; open_dev_tools: () => Promise<void> };
|
||||
'core': { get_clusters: () => Promise<Array<Model>>; get_cluster: (id: bigint) => Promise<Model | null>; remove_cluster: (id: bigint) => Promise<null>; create_cluster: (options: CreateCluster) => Promise<Model>; get_profile_or_default: (name: string | null) => Promise<SettingProfileModel>; get_global_profile: () => Promise<SettingProfileModel>; get_users: () => Promise<Array<MinecraftCredentials>>; get_user: (uuid: string) => Promise<MinecraftCredentials | null>; remove_user: (uuid: string) => Promise<null>; get_default_user: (fallback: boolean | null) => Promise<MinecraftCredentials | null>; set_default_user: (uuid: string | null) => Promise<null>; open_msa_login: () => Promise<MinecraftCredentials | null> };
|
||||
'': { send_event: (event: LauncherEvent) => Promise<void> };
|
||||
'core': { getClusters: () => Promise<Array<Model>>; getClusterById: (id: bigint) => Promise<Model | null>; removeCluster: (id: bigint) => Promise<null>; createCluster: (options: CreateCluster) => Promise<Model>; getProfileOrDefault: (name: string | null) => Promise<SettingProfileModel>; getGlobalProfile: () => Promise<SettingProfileModel>; getUsers: () => Promise<Array<MinecraftCredentials>>; getUser: (uuid: string) => Promise<MinecraftCredentials | null>; removeUser: (uuid: string) => Promise<null>; getDefaultUser: (fallback: boolean | null) => Promise<MinecraftCredentials | null>; setDefaultUser: (uuid: string | null) => Promise<null>; openMsaLogin: () => Promise<MinecraftCredentials | null> };
|
||||
}
|
||||
|
||||
export type { InferCommandOutput };
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import type { ToOptions } from '@tanstack/react-router';
|
||||
import {useCommand} from '@onelauncher/common';
|
||||
import { bindings } from '@/main';
|
||||
import { useCommand } from '@onelauncher/common';
|
||||
import { Button, Menu } from '@onelauncher/common/components';
|
||||
import { Link } from '@tanstack/react-router';
|
||||
import { Bell01Icon, Cloud01Icon, TerminalBrowserIcon } from '@untitled-theme/icons-react';
|
||||
import { MenuTrigger } from 'react-aria-components';
|
||||
import { Button, Menu } from '@onelauncher/common/components';
|
||||
import PlayerHead from './content/PlayerHead';
|
||||
import AccountPopup from './overlay/popups/AccountPopup';
|
||||
|
||||
function Navbar() {
|
||||
const defaultUser = useCommand('getDefaultUser', () => bindings.core.get_default_user(false));
|
||||
const defaultUser = useCommand('getDefaultUser', () => bindings.core.getDefaultUser(false));
|
||||
|
||||
return (
|
||||
<div className="h-15 min-h-15 flex flex-row items-center *:flex-1">
|
||||
|
|
|
|||
|
|
@ -1,45 +1,45 @@
|
|||
import type { JSX } from "react";
|
||||
import type { JSX } from 'react';
|
||||
|
||||
export type SettingsRowProps = {
|
||||
title: JSX.Element | string;
|
||||
description: JSX.Element | string;
|
||||
icon: JSX.Element;
|
||||
disabled?: boolean;
|
||||
children?: JSX.Element;
|
||||
};
|
||||
export interface SettingsRowProps {
|
||||
title: JSX.Element | string;
|
||||
description: JSX.Element | string;
|
||||
icon: JSX.Element;
|
||||
disabled?: boolean;
|
||||
children?: JSX.Element;
|
||||
}
|
||||
|
||||
function SettingsRow(props: SettingsRowProps) {
|
||||
return (
|
||||
<div
|
||||
className={`
|
||||
function SettingsRow({ disabled, description, icon, title, children }: SettingsRowProps) {
|
||||
return (
|
||||
<div
|
||||
className={`
|
||||
flex flex-row items-center gap-3.5 rounded-xl p-4 my-2
|
||||
${props.disabled ? 'bg-component-bg-disabled' : 'bg-page-elevated hover:bg-component-bg-hover'}
|
||||
${disabled ? 'bg-component-bg-disabled' : 'bg-page-elevated hover:bg-component-bg-hover'}
|
||||
`}
|
||||
>
|
||||
<div className="flex h-8 w-8 items-center justify-center">
|
||||
{props.icon}
|
||||
</div>
|
||||
>
|
||||
<div className="flex h-8 w-8 items-center justify-center">
|
||||
{icon}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-1 flex-col gap-2">
|
||||
<h3 className="text-lg capitalize leading-tight">{props.title}</h3>
|
||||
<p className="text-sm text-fg-secondary">{props.description}</p>
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col gap-2">
|
||||
<h3 className="text-lg capitalize leading-tight">{title}</h3>
|
||||
<p className="text-sm text-fg-secondary">{description}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex h-9 flex-row items-center gap-2">
|
||||
{props.children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
<div className="flex h-9 flex-row items-center gap-2">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface SettingsRowHeaderProps {
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
SettingsRow.Header = ({ className, children }: SettingsRowHeaderProps) => {
|
||||
const headerClasses = `mt-4 mb-1 ml-2 text-md text-fg-secondary uppercase ${className || ''}`;
|
||||
return <h3 className={headerClasses}>{children}</h3>;
|
||||
const headerClasses = `mt-4 mb-1 ml-2 text-md text-fg-secondary uppercase ${className || ''}`;
|
||||
return <h3 className={headerClasses}>{children}</h3>;
|
||||
};
|
||||
|
||||
export default SettingsRow;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import type { ImgHTMLAttributes, JSX } from 'react';
|
||||
import { Show } from '@onelauncher/common/components';
|
||||
import CurseforgeIcon from '../../assets/logos/curseforge.svg';
|
||||
import AtLauncherIcon from '../../assets/logos/launchers/atlauncher.svg';
|
||||
import FTBIcon from '../../assets/logos/launchers/ftb.svg';
|
||||
|
|
@ -9,7 +10,6 @@ import TechnicImage from '../../assets/logos/launchers/technic.png';
|
|||
import TLauncherImage from '../../assets/logos/launchers/tlauncher.png';
|
||||
import UnknownImage from '../../assets/logos/minecraft.png';
|
||||
import ModrinthIcon from '../../assets/logos/modrinth.svg';
|
||||
import { Show } from '../base/Show';
|
||||
|
||||
type ElementType = (props: any) => JSX.Element;
|
||||
|
||||
|
|
@ -45,8 +45,8 @@ type LauncherIconProps = ImgHTMLAttributes<HTMLImageElement> & {
|
|||
};
|
||||
|
||||
function LauncherIcon({
|
||||
launcher,
|
||||
...props
|
||||
launcher,
|
||||
...props
|
||||
}: LauncherIconProps) {
|
||||
return (
|
||||
<Show
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import type { Model, GameLoader } from '@/bindings.gen';
|
||||
import type { GameLoader, Model } from '@/bindings.gen';
|
||||
import type { ImgHTMLAttributes, RefAttributes } from 'react';
|
||||
import FabricImage from '@/assets/logos/loaders/fabric.png';
|
||||
import ForgeImage from '@/assets/logos/loaders/forge.png';
|
||||
import NeoForgeImage from '@/assets/logos/loaders/neoforge.png';
|
||||
import QuiltImage from '@/assets/logos/loaders/quilt.png';
|
||||
import VanillaImage from '@/assets/logos/minecraft.png';
|
||||
import type { RefAttributes } from 'react';
|
||||
|
||||
export function getLoaderLogoSrc(loader: Model | GameLoader): string {
|
||||
const loaderName = (typeof loader === 'string' ? loader : loader.mc_loader)?.toLowerCase() as GameLoader;
|
||||
const loaderName = (typeof loader === 'string' ? loader : loader.mc_loader).toLowerCase() as GameLoader;
|
||||
|
||||
const mapping: Record<GameLoader, string> = {
|
||||
vanilla: VanillaImage,
|
||||
|
|
@ -21,17 +21,21 @@ export function getLoaderLogoSrc(loader: Model | GameLoader): string {
|
|||
return mapping[loaderName];
|
||||
}
|
||||
|
||||
type LoaderIconProp = RefAttributes<HTMLImageElement> & {
|
||||
type LoaderIconProp = ImgHTMLAttributes<HTMLImageElement> & RefAttributes<HTMLImageElement> & {
|
||||
loader: GameLoader | undefined;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function LoaderIcon(props: LoaderIconProp) {
|
||||
function LoaderIcon({
|
||||
loader,
|
||||
src: _src,
|
||||
alt: _alt,
|
||||
...rest
|
||||
}: LoaderIconProp) {
|
||||
return (
|
||||
<img
|
||||
{...props}
|
||||
alt={`${props.loader}'s logo`}
|
||||
src={props.loader ? getLoaderLogoSrc(props.loader) : VanillaImage}
|
||||
{...rest}
|
||||
alt={`${loader}'s logo`}
|
||||
src={loader ? getLoaderLogoSrc(loader) : VanillaImage}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
import type { ClassNameString } from '@/types/global';
|
||||
import {
|
||||
Dialog as AriaDialog,
|
||||
DialogTrigger,
|
||||
Modal as ModalOverlay
|
||||
} from 'react-aria-components';
|
||||
import type { ClassNameString } from '@onelauncher/common';
|
||||
import type { DialogProps } from 'react-aria-components';
|
||||
import {
|
||||
Dialog as AriaDialog,
|
||||
DialogTrigger,
|
||||
Modal as ModalOverlay,
|
||||
} from 'react-aria-components';
|
||||
|
||||
interface ModalProps extends DialogProps, ClassNameString { }
|
||||
|
||||
function Modal({ className, ...props }: ModalProps) {
|
||||
return (
|
||||
<ModalOverlay isDismissable className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
|
||||
<AriaDialog className="" {...props} />
|
||||
</ModalOverlay>
|
||||
);
|
||||
return (
|
||||
<ModalOverlay className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4" isDismissable>
|
||||
<AriaDialog className="" {...props} />
|
||||
</ModalOverlay>
|
||||
);
|
||||
}
|
||||
|
||||
Modal.Trigger = DialogTrigger;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
import type { MinecraftCredentials } from '@/bindings.gen';
|
||||
import PlayerHead from '@/components/content/PlayerHead';
|
||||
import {useCommand} from '@onelauncher/common';
|
||||
import { Button, Menu } from '@onelauncher/common/components';
|
||||
import { bindings } from '@/main';
|
||||
import { useCommand } from '@onelauncher/common';
|
||||
import { Button, Menu } from '@onelauncher/common/components';
|
||||
import { PlusIcon, Settings01Icon } from '@untitled-theme/icons-react';
|
||||
import { Separator } from 'react-aria-components';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
function AccountPopup() {
|
||||
const users = useCommand('getUsers', bindings.commands.getUsers);
|
||||
const defaultUser = useCommand('getDefaultUser', () => bindings.commands.getDefaultUser(false));
|
||||
const users = useCommand('getUsers', bindings.core.getUsers);
|
||||
const defaultUser = useCommand('getDefaultUser', () => bindings.core.getDefaultUser(false));
|
||||
|
||||
const setDefaultUser = (user: bindings.MinecraftCredentials) => {
|
||||
bindings.commands.setDefaultUser(user.id);
|
||||
const setDefaultUser = (user: MinecraftCredentials) => {
|
||||
bindings.core.setDefaultUser(user.id);
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
@ -64,7 +65,7 @@ function AccountEntry({
|
|||
loggedIn = false,
|
||||
}: {
|
||||
onClick: () => void;
|
||||
user: bindings.MinecraftCredentials;
|
||||
user: MinecraftCredentials;
|
||||
loggedIn?: boolean;
|
||||
}) {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import type { Model } from '@/bindings.gen';
|
||||
import { bindings } from '@/main';
|
||||
import { useCommand } from '@onelauncher/common';
|
||||
import { useEffect, useState } from 'react';
|
||||
import useCommand from './useCommand';
|
||||
|
||||
function useRecentCluster() {
|
||||
const result = useCommand('getClusters', bindings.core.get_clusters);
|
||||
const result = useCommand('getClusters', bindings.core.getClusters);
|
||||
const [cluster, setCluster] = useState<Model | undefined>();
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Button, TextField } from '@onelauncher/common/components';
|
||||
import Modal from '@/components/overlay/Modal';
|
||||
import { Button, TextField } from '@onelauncher/common/components';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/app/browser/')({
|
||||
|
|
|
|||
|
|
@ -3,12 +3,13 @@ import type { Dispatch, SetStateAction } from 'react';
|
|||
import LoaderIcon from '@/components/launcher/LoaderIcon';
|
||||
import ScrollableContainer from '@/components/ScrollableContainer';
|
||||
import SettingsRow from '@/components/SettingsRow';
|
||||
import useCommand from '@/hooks/useCommand';
|
||||
import { bindings } from '@/main';
|
||||
import { useCommand } from '@onelauncher/common';
|
||||
import { Button, Show, TextField } from '@onelauncher/common/components';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { Edit02Icon, FolderIcon, ImagePlusIcon, Share07Icon, Tool02Icon, Trash01Icon } from '@untitled-theme/icons-react';
|
||||
import { useState } from 'react';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import Sidebar from '../settings/route';
|
||||
|
||||
export const Route = createFileRoute('/app/cluster/')({
|
||||
|
|
@ -21,7 +22,7 @@ function RouteComponent() {
|
|||
const [newName, setNewName] = useState<string>('');
|
||||
|
||||
// dumbass fix ik
|
||||
const cluster = useCommand('getClusterById', () => bindings.commands.getClusterById(Number(id.toString()) as unknown as bigint));
|
||||
const cluster = useCommand('getClusterById', () => bindings.core.getClusterById(Number(id.toString()) as unknown as bigint));
|
||||
|
||||
return (
|
||||
<Sidebar.Page>
|
||||
|
|
@ -102,7 +103,11 @@ interface BannerProps {
|
|||
refetch: () => void;
|
||||
}
|
||||
|
||||
function Banner(props: BannerProps) {
|
||||
function Banner({
|
||||
cluster,
|
||||
editMode,
|
||||
setNewName,
|
||||
}: BannerProps) {
|
||||
// async function launchFilePicker() {
|
||||
// const selected = await dialog.open({
|
||||
// multiple: false,
|
||||
|
|
@ -123,13 +128,13 @@ function Banner(props: BannerProps) {
|
|||
if (name.length > 30 || name.length <= 0)
|
||||
return;
|
||||
|
||||
props.setNewName(name);
|
||||
setNewName(name);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-37 flex flex-row gap-x-2.5 rounded-xl bg-page-elevated p-2.5">
|
||||
<div className="relative aspect-ratio-video h-full min-w-57 w-57 overflow-hidden border border-component-bg/10 rounded-lg">
|
||||
<Show when={props.editMode}>
|
||||
<Show when={editMode}>
|
||||
<div
|
||||
className="absolute z-1 h-full w-full flex items-center justify-center bg-black/50 opacity-50 hover:opacity-100"
|
||||
// onClick={launchFilePicker}
|
||||
|
|
@ -146,37 +151,37 @@ function Banner(props: BannerProps) {
|
|||
<div>
|
||||
<Show
|
||||
fallback={
|
||||
<h2 className="break-words text-wrap text-2xl">{props.cluster?.name}</h2>
|
||||
<h2 className="break-words text-wrap text-2xl">{cluster?.name}</h2>
|
||||
}
|
||||
when={props.editMode}
|
||||
when={editMode}
|
||||
>
|
||||
<TextField
|
||||
className="text-xl font-bold"
|
||||
onChange={e => updateName(e.target.value)}
|
||||
placeholder={props.cluster?.name}
|
||||
placeholder={cluster?.name}
|
||||
/>
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-1 flex-row">
|
||||
<div
|
||||
className={`flex flex-1 flex-col items-start justify-between ${props.editMode}` ? 'text-fg-primary-disabled' : ''}
|
||||
className={twMerge(`flex flex-1 flex-col items-start justify-between`, editMode && 'text-fg-primary-disabled')}
|
||||
>
|
||||
<span className="flex flex-row items-center gap-x-1">
|
||||
<LoaderIcon
|
||||
className="w-5"
|
||||
loader={props.cluster?.mc_loader}
|
||||
loader={cluster?.mc_loader}
|
||||
/>
|
||||
<span>{props.cluster?.mc_version}</span>
|
||||
<span>{props.cluster?.mc_loader || 'unknown'}</span>
|
||||
{props.cluster?.mc_loader_version && <span>{props.cluster.mc_loader_version}</span>}
|
||||
<span>{cluster?.mc_version}</span>
|
||||
<span>{cluster?.mc_loader || 'unknown'}</span>
|
||||
{cluster?.mc_loader_version && <span>{cluster.mc_loader_version}</span>}
|
||||
</span>
|
||||
<span
|
||||
className={`text-xs text-fg-secondary ${props.editMode}` ? 'text-fg-secondary-disabled' : ''}
|
||||
className={twMerge(`text-xs text-fg-secondary`, editMode && 'text-fg-secondary-disabled')}
|
||||
>
|
||||
Played for
|
||||
{' '}
|
||||
<b>{props.cluster?.overall_played || 0}</b>
|
||||
<b>{cluster?.overall_played || 0}</b>
|
||||
.
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,52 +1,52 @@
|
|||
import { createFileRoute, Outlet } from '@tanstack/react-router'
|
||||
import Sidebar from '../settings/route'
|
||||
import { EyeIcon, File06Icon, Globe04Icon, Image03Icon, PackagePlusIcon, Settings04Icon } from '@untitled-theme/icons-react'
|
||||
import useCommand from '@/hooks/useCommand'
|
||||
import { bindings } from '@/main'
|
||||
import { bindings } from '@/main';
|
||||
import { useCommand } from '@onelauncher/common';
|
||||
import { createFileRoute, Outlet } from '@tanstack/react-router';
|
||||
import { EyeIcon, File06Icon, Globe04Icon, Image03Icon, PackagePlusIcon, Settings04Icon } from '@untitled-theme/icons-react';
|
||||
import Sidebar from '../settings/route';
|
||||
|
||||
type ClusterSearch = {
|
||||
id: bigint
|
||||
interface ClusterSearch {
|
||||
id: bigint;
|
||||
}
|
||||
|
||||
export const Route = createFileRoute('/app/cluster')({
|
||||
component: RouteComponent,
|
||||
validateSearch: (search: Record<string, unknown>): ClusterSearch => {
|
||||
return {
|
||||
id: BigInt(search.id as string),
|
||||
}
|
||||
},
|
||||
})
|
||||
component: RouteComponent,
|
||||
validateSearch: (search: Record<string, unknown>): ClusterSearch => {
|
||||
return {
|
||||
id: BigInt(search.id as string),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const { id } = Route.useSearch()
|
||||
const { id } = Route.useSearch();
|
||||
|
||||
const cluster = useCommand("getClusterById", () => bindings.commands.getClusterById(Number(id.toString()) as unknown as bigint))
|
||||
const cluster = useCommand('getClusterById', () => bindings.core.getClusterById(Number(id.toString()) as unknown as bigint));
|
||||
|
||||
const isModded = cluster.data?.mc_loader != "vanilla"
|
||||
const isModded = cluster.data?.mc_loader != 'vanilla';
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-row overflow-hidden">
|
||||
<div className="flex-shrink-0 w-72 flex flex-col pt-8 pr-7 pb-8">
|
||||
<Sidebar
|
||||
base="/app/cluster"
|
||||
links={{
|
||||
'Cluster Settings': [
|
||||
// if someone has a better way to do this, please let me know
|
||||
[<EyeIcon />, 'Overview', '/?id=' + id],
|
||||
(isModded ? [<PackagePlusIcon />, 'Mods', '/mods?id=' + id] : undefined),
|
||||
[<Image03Icon />, 'Screenshots', '/screenshots?id=' + id],
|
||||
[<Globe04Icon />, 'Worlds', '/worlds?id=' + id],
|
||||
[<File06Icon />, 'Logs', '/logs?id=' + id],
|
||||
[<Settings04Icon />, 'Game Settings', '/settings?id=' + id],
|
||||
],
|
||||
}}
|
||||
/>
|
||||
{/* <Info /> */}
|
||||
</div>
|
||||
return (
|
||||
<div className="h-full flex flex-row overflow-hidden">
|
||||
<div className="flex-shrink-0 w-72 flex flex-col pt-8 pr-7 pb-8">
|
||||
<Sidebar
|
||||
base="/app/cluster"
|
||||
links={{
|
||||
'Cluster Settings': [
|
||||
// if someone has a better way to do this, please let me know
|
||||
[<EyeIcon />, 'Overview', `/?id=${id}`],
|
||||
(isModded ? [<PackagePlusIcon />, 'Mods', `/mods?id=${id}`] : undefined),
|
||||
[<Image03Icon />, 'Screenshots', `/screenshots?id=${id}`],
|
||||
[<Globe04Icon />, 'Worlds', `/worlds?id=${id}`],
|
||||
[<File06Icon />, 'Logs', `/logs?id=${id}`],
|
||||
[<Settings04Icon />, 'Game Settings', `/settings?id=${id}`],
|
||||
],
|
||||
}}
|
||||
/>
|
||||
{/* <Info /> */}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 min-w-0 h-full">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
<div className="flex-1 min-w-0 h-full">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,46 +1,51 @@
|
|||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import Sidebar from '../settings/route';
|
||||
import ScrollableContainer from '@/components/ScrollableContainer';
|
||||
import { Show } from '@/components/base/Show';
|
||||
import { Show } from '@onelauncher/common/components';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import Sidebar from '../settings/route';
|
||||
|
||||
export const Route = createFileRoute('/app/cluster/screenshots')({
|
||||
component: RouteComponent,
|
||||
})
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const arr = [1, 2, 3, 4, 5, 6]
|
||||
return (
|
||||
<Sidebar.Page>
|
||||
<h1>Screenshots</h1>
|
||||
<ScrollableContainer>
|
||||
<div className="h-full">
|
||||
<Show
|
||||
fallback={<div className="text-border/400">No screenshots found. Press F2 in game to take a screenshot!</div>}
|
||||
when={true}
|
||||
>
|
||||
<div className="grid grid-cols-[repeat(auto-fill,minmax(350px,1fr))] w-full transform-gpu gap-2">
|
||||
{arr.map((data) => (
|
||||
<ScreenshotEntry key={data} path={`screenshot-${data}.png`} cluster_path="cluster" />
|
||||
))}
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</ScrollableContainer>
|
||||
</Sidebar.Page>
|
||||
)
|
||||
const arr = [1, 2, 3, 4, 5, 6];
|
||||
return (
|
||||
<Sidebar.Page>
|
||||
<h1>Screenshots</h1>
|
||||
<ScrollableContainer>
|
||||
<div className="h-full">
|
||||
<Show
|
||||
fallback={<div className="text-border/400">No screenshots found. Press F2 in game to take a screenshot!</div>}
|
||||
when
|
||||
>
|
||||
<div className="grid grid-cols-[repeat(auto-fill,minmax(350px,1fr))] w-full transform-gpu gap-2">
|
||||
{arr.map(data => (
|
||||
<ScreenshotEntry cluster_path="cluster" key={data} path={`screenshot-${data}.png`} />
|
||||
))}
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</ScrollableContainer>
|
||||
</Sidebar.Page>
|
||||
);
|
||||
}
|
||||
|
||||
function ScreenshotEntry(props: { path: string; cluster_path: string }) {
|
||||
function onClick() {
|
||||
//screen shot viewer overlay
|
||||
}
|
||||
function ScreenshotEntry({
|
||||
path,
|
||||
}: {
|
||||
path: string;
|
||||
cluster_path: string;
|
||||
}) {
|
||||
function onClick() {
|
||||
// screen shot viewer overlay
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex flex-col items-center gap-3 rounded-xl bg-component-bg p-3 active:bg-component-bg-pressed hover:bg-component-bg-hover hover:opacity-80"
|
||||
onClick={onClick}
|
||||
>
|
||||
<img alt={props.path} className="aspect-video w-full rounded-lg" src="https://github.com/emirsassan.png" />
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div
|
||||
className="flex flex-col items-center gap-3 rounded-xl bg-component-bg p-3 active:bg-component-bg-pressed hover:bg-component-bg-hover hover:opacity-80"
|
||||
onClick={onClick}
|
||||
>
|
||||
<img alt={path} className="aspect-video w-full rounded-lg" src="https://github.com/emirsassan.png" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +1,33 @@
|
|||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import Sidebar from '../settings/route'
|
||||
import ScrollableContainer from '@/components/ScrollableContainer'
|
||||
import useCommand from '@/hooks/useCommand'
|
||||
import { bindings } from '@/main'
|
||||
import { GameSettings, ProcessSettings } from '../settings/minecraft'
|
||||
import ScrollableContainer from '@/components/ScrollableContainer';
|
||||
import { bindings } from '@/main';
|
||||
import { useCommand } from '@onelauncher/common';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { GameSettings, ProcessSettings } from '../settings/minecraft';
|
||||
import Sidebar from '../settings/route';
|
||||
|
||||
export const Route = createFileRoute('/app/cluster/settings')({
|
||||
component: RouteComponent,
|
||||
})
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const { id } = Route.useSearch()
|
||||
const { id } = Route.useSearch();
|
||||
|
||||
const cluster = useCommand("getClusterById", () => bindings.commands.getClusterById(Number(id.toString()) as unknown as bigint))
|
||||
const result = useCommand("getProfileOrDefault", () => bindings.commands.getProfileOrDefault(cluster.data?.name as string), {
|
||||
enabled: !!cluster.data?.name
|
||||
})
|
||||
const cluster = useCommand('getClusterById', () => bindings.core.getClusterById(Number(id.toString()) as unknown as bigint));
|
||||
const result = useCommand('getProfileOrDefault', () => bindings.core.getProfileOrDefault(cluster.data?.name as string), {
|
||||
enabled: !!cluster.data?.name,
|
||||
});
|
||||
|
||||
console.log(result.data)
|
||||
return (
|
||||
<Sidebar.Page>
|
||||
<ScrollableContainer>
|
||||
<div className="h-full">
|
||||
<h1>Minecraft Settings</h1>
|
||||
|
||||
return (
|
||||
<Sidebar.Page>
|
||||
<ScrollableContainer>
|
||||
<div className="h-full">
|
||||
<h1>Minecraft Settings</h1>
|
||||
<GameSettings />
|
||||
|
||||
<GameSettings />
|
||||
|
||||
<ProcessSettings />
|
||||
</div>
|
||||
</ScrollableContainer>
|
||||
</Sidebar.Page>
|
||||
)
|
||||
<ProcessSettings />
|
||||
</div>
|
||||
</ScrollableContainer>
|
||||
</Sidebar.Page>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import Sidebar from '../settings/route'
|
||||
import ScrollableContainer from '@/components/ScrollableContainer'
|
||||
import ScrollableContainer from '@/components/ScrollableContainer';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import Sidebar from '../settings/route';
|
||||
|
||||
export const Route = createFileRoute('/app/cluster/worlds')({
|
||||
component: RouteComponent,
|
||||
})
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<Sidebar.Page>
|
||||
<h1>Worlds</h1>
|
||||
<ScrollableContainer>
|
||||
<div className="h-full">
|
||||
|
||||
</div>
|
||||
</ScrollableContainer>
|
||||
</Sidebar.Page>
|
||||
)
|
||||
return (
|
||||
<Sidebar.Page>
|
||||
<h1>Worlds</h1>
|
||||
<ScrollableContainer>
|
||||
<div className="h-full">
|
||||
|
||||
</div>
|
||||
</ScrollableContainer>
|
||||
</Sidebar.Page>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import Modal from '@/components/overlay/Modal';
|
|||
import { bindings } from '@/main';
|
||||
import { useCommand } from '@onelauncher/common';
|
||||
import { Button, TextField } from '@onelauncher/common/components';
|
||||
import { createFileRoute, Link, useNavigate } from '@tanstack/react-router';
|
||||
import { createFileRoute, Link } from '@tanstack/react-router';
|
||||
import { Server01Icon } from '@untitled-theme/icons-react';
|
||||
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ Please note this route has a very big issue related to scrolling
|
|||
and i am very angry rn so i will not be fixing it rn
|
||||
*/
|
||||
function RouteComponent() {
|
||||
const result = useCommand('getClusters', bindings.core.get_clusters);
|
||||
const result = useCommand('getClusters', bindings.core.getClusters);
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col gap-y-4 text-fg-primary">
|
||||
|
|
@ -49,7 +49,7 @@ function Banner() {
|
|||
*/
|
||||
// const cluster = useRecentCluster();
|
||||
// const launch = useLaunchCluster(() => cluster()?.uuid);
|
||||
const navigate = useNavigate();
|
||||
// const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div className="relative h-52 min-h-52 w-full overflow-hidden rounded-xl border border-component-border">
|
||||
|
|
@ -110,8 +110,8 @@ function Banner() {
|
|||
}
|
||||
|
||||
function ClusterCreate() {
|
||||
const result = useCommand('createCluster', () => bindings.commands.createCluster({
|
||||
icon_url: 'asd',
|
||||
const result = useCommand('createCluster', () => bindings.core.createCluster({
|
||||
icon: 'asd',
|
||||
mc_loader: 'vanilla',
|
||||
mc_version: '1.20.1',
|
||||
name: 'Test Cluster',
|
||||
|
|
@ -177,8 +177,11 @@ interface ClusterGroupProps {
|
|||
isFetching?: boolean;
|
||||
}
|
||||
|
||||
function ClusterGroup(props: ClusterGroupProps) {
|
||||
if (props.isFetching)
|
||||
function ClusterGroup({
|
||||
isFetching,
|
||||
clusters,
|
||||
}: ClusterGroupProps) {
|
||||
if (isFetching)
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<div className="w-8 h-8 border-4 border-brand rounded-full border-t-transparent animate-spin" />
|
||||
|
|
@ -191,7 +194,7 @@ function ClusterGroup(props: ClusterGroupProps) {
|
|||
className="h-full w-full"
|
||||
>
|
||||
<div className="grid grid-cols-4 gap-4 max-h-96 2xl:grid-cols-6 pb-4">
|
||||
{props.clusters?.map(data => (
|
||||
{clusters?.map(data => (
|
||||
<ClusterCard key={data.id} {...data} />
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -200,11 +203,16 @@ function ClusterGroup(props: ClusterGroupProps) {
|
|||
);
|
||||
}
|
||||
|
||||
function ClusterCard(props: Model) {
|
||||
function ClusterCard({
|
||||
id,
|
||||
name,
|
||||
mc_loader,
|
||||
mc_version,
|
||||
}: Model) {
|
||||
return (
|
||||
<Link
|
||||
search={{
|
||||
id: props.id,
|
||||
id,
|
||||
}} to="/app/cluster"
|
||||
>
|
||||
<div
|
||||
|
|
@ -222,11 +230,11 @@ function ClusterCard(props: Model) {
|
|||
</div>
|
||||
<div className="z-10 flex flex-row items-center justify-between gap-x-3 p-3">
|
||||
<div className="h-full flex flex-col gap-1.5 overflow-hidden">
|
||||
<p className="h-4 text-ellipsis whitespace-nowrap font-medium">{props.name}</p>
|
||||
<p className="h-4 text-ellipsis whitespace-nowrap font-medium">{name}</p>
|
||||
<p className="h-4 text-xs">
|
||||
{props.mc_loader}
|
||||
{mc_loader}
|
||||
{' '}
|
||||
{props.mc_version}
|
||||
{mc_version}
|
||||
{/* {' '}
|
||||
{props.packages.mods && `• ${props.mods} mods`} */}
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import PlayerHead from '@/components/content/PlayerHead';
|
||||
import ScrollableContainer from '@/components/ScrollableContainer';
|
||||
import useCommand from '@/hooks/useCommand';
|
||||
import { bindings } from '@/main';
|
||||
import { useCommand } from '@onelauncher/common';
|
||||
import { Button, Show, Tooltip } from '@onelauncher/common/components';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { InfoCircleIcon, Trash01Icon, UserPlus02Icon } from '@untitled-theme/icons-react';
|
||||
|
|
@ -18,8 +18,8 @@ interface Account {
|
|||
}
|
||||
|
||||
function RouteComponent() {
|
||||
const { data: usersData, isLoading: usersLoading } = useCommand('getUsers', bindings.commands.getUsers);
|
||||
const { data: defaultUserData, isLoading: defaultUserLoading } = useCommand('getDefaultUser', () => bindings.commands.getDefaultUser(false));
|
||||
const { data: usersData, isLoading: usersLoading } = useCommand('getUsers', bindings.core.getUsers);
|
||||
const { data: defaultUserData, isLoading: defaultUserLoading } = useCommand('getDefaultUser', () => bindings.core.getDefaultUser(false));
|
||||
|
||||
if (usersLoading || defaultUserLoading)
|
||||
return (
|
||||
|
|
@ -67,7 +67,7 @@ function AccountRow({ account, isCurrent }: AccountRowProps) {
|
|||
|
||||
const setDefaultUserCommand = useCommand(
|
||||
'setDefaultUser',
|
||||
() => bindings.commands.setDefaultUser(account.id),
|
||||
() => bindings.core.setDefaultUser(account.id),
|
||||
{
|
||||
enabled: false,
|
||||
subscribed: false,
|
||||
|
|
@ -76,7 +76,7 @@ function AccountRow({ account, isCurrent }: AccountRowProps) {
|
|||
|
||||
const removeUserCommand = useCommand(
|
||||
'removeUser',
|
||||
() => bindings.commands.removeUser(account.id),
|
||||
() => bindings.core.removeUser(account.id),
|
||||
{
|
||||
enabled: false,
|
||||
subscribed: false,
|
||||
|
|
|
|||
|
|
@ -77,9 +77,12 @@ interface ThemeCardProps {
|
|||
setTheme: (merged: string) => void;
|
||||
};
|
||||
|
||||
function ThemeCard(props: ThemeCardProps) {
|
||||
function ThemeCard({
|
||||
themeSelector,
|
||||
setTheme,
|
||||
}: ThemeCardProps) {
|
||||
return (
|
||||
<div className={`h-min theme-${props.themeSelector}`} onClick={() => props.setTheme(props.themeSelector)}>
|
||||
<div className={`h-min theme-${themeSelector}`} onClick={() => setTheme(themeSelector)}>
|
||||
<svg
|
||||
fill="none"
|
||||
height="78"
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export const Route = createFileRoute('/app/settings/')({
|
|||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const result = useCommand('getGlobalProfile', bindings.commands.getGlobalProfile);
|
||||
const result = useCommand('getGlobalProfile', bindings.core.getGlobalProfile);
|
||||
|
||||
return (
|
||||
<Sidebar.Page>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import ScrollableContainer from '@/components/ScrollableContainer';
|
||||
import SettingsRow from '@/components/SettingsRow';
|
||||
import { Switch, TextField, ToggleButton } from '@onelauncher/common/components';
|
||||
import { Switch, TextField } from '@onelauncher/common/components';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { Database01Icon, EyeIcon, FilePlus02Icon, FileX02Icon, LayoutTopIcon, Maximize01Icon, ParagraphWrapIcon, XIcon } from '@untitled-theme/icons-react';
|
||||
import { Database01Icon, FilePlus02Icon, FileX02Icon, LayoutTopIcon, Maximize01Icon, ParagraphWrapIcon, XIcon } from '@untitled-theme/icons-react';
|
||||
import Sidebar from './route';
|
||||
|
||||
export const Route = createFileRoute('/app/settings/minecraft')({
|
||||
|
|
|
|||
|
|
@ -100,8 +100,7 @@ function Sidebar(props: SidebarProps) {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
)}
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/onboarding/complete')({
|
||||
component: RouteComponent,
|
||||
})
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/onboarding/complete"!</div>
|
||||
return <div>Hello "/onboarding/complete"!</div>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/onboarding/')({
|
||||
component: RouteComponent,
|
||||
})
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div className="grid grid-cols-2 h-full w-full items-center gap-x-16">
|
||||
<h1 className="text-6xl">
|
||||
Welcome to
|
||||
{' '}
|
||||
<span className="underline underline-8 underline-brand">OneLauncher</span>
|
||||
</h1>
|
||||
return (
|
||||
<div className="grid grid-cols-2 h-full w-full items-center gap-x-16">
|
||||
<h1 className="text-6xl">
|
||||
Welcome to
|
||||
{' '}
|
||||
<span className="underline underline-8 underline-brand">OneLauncher</span>
|
||||
</h1>
|
||||
|
||||
<h3>A powerful yet easy to use launcher for Minecraft.</h3>
|
||||
</div>
|
||||
)
|
||||
<h3>A powerful yet easy to use launcher for Minecraft.</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export const Route = createFileRoute('/onboarding/login')({
|
|||
function RouteComponent() {
|
||||
const [profile, setProfile] = useState<MinecraftCredentials>();
|
||||
const [errorMessage, setErrorMessage] = useState<string>('');
|
||||
const result = useCommand('beginMsFlow', bindings.commands.beginMsFlow, {
|
||||
const result = useCommand('openMsaLogin', bindings.core.openMsaLogin, {
|
||||
enabled: false,
|
||||
subscribed: false,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -120,21 +120,26 @@ export interface OnboardingStepProps {
|
|||
children: JSX.Element;
|
||||
}
|
||||
|
||||
export function OnboardingStep(props: OnboardingStepProps) {
|
||||
export function OnboardingStep({
|
||||
title,
|
||||
paragraph,
|
||||
illustration,
|
||||
children,
|
||||
}: OnboardingStepProps) {
|
||||
return (
|
||||
<div className="grid grid-cols-2 h-full w-full gap-x-16">
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
{props.illustration}
|
||||
{illustration}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col justify-center gap-y-4">
|
||||
<div className="w-full flex flex-col gap-y-2">
|
||||
<h1 className="text-2xl">{props.title}</h1>
|
||||
<p className="text-lg text-fg-secondary line-height-normal">{props.paragraph}</p>
|
||||
<h1 className="text-2xl">{title}</h1>
|
||||
<p className="text-lg text-fg-secondary line-height-normal">{paragraph}</p>
|
||||
</div>
|
||||
|
||||
<div className="max-h-96 w-full flex flex-1 flex-col gap-y-2">
|
||||
{props.children}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
10
apps/onelauncher/frontend/src/types/global.d.ts
vendored
10
apps/onelauncher/frontend/src/types/global.d.ts
vendored
|
|
@ -1,3 +1,9 @@
|
|||
export interface ClassNameString {
|
||||
className?: string;
|
||||
import type { bindings } from '@/main';
|
||||
|
||||
export type BindingCommands = (keyof typeof bindings.core) | (keyof typeof bindings.onelauncher);
|
||||
|
||||
declare module '@onelauncher/common' {
|
||||
interface Register {
|
||||
commands: Array<BindingCommands>;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,5 +27,5 @@
|
|||
"skipLibCheck": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["**/*.ts", "**/*.tsx", "prettier.config.js", "vite.config.js"]
|
||||
"include": ["**/*.ts", "**/*.tsx", "vite.config.js"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,6 @@ export default defineConfig(
|
|||
react: true,
|
||||
query: true,
|
||||
ignores: ['*.rs', '**/migrations/**'],
|
||||
rules: {
|
||||
'ts/no-use-before-define': 'off',
|
||||
},
|
||||
toml: {
|
||||
overrides: {
|
||||
'toml/array-element-newline': ['error', 'always'],
|
||||
|
|
@ -30,7 +27,9 @@ export default defineConfig(
|
|||
rules: {
|
||||
'sort-imports': 'off', // Replaced by perfectionist/sort-named-imports',
|
||||
'import/order': 'off', // Replaced by perfectionist/sort-named-imports',
|
||||
'style/jsx-max-props-per-line': ['error', { maximum: 3}]
|
||||
'style/jsx-max-props-per-line': ['error', { maximum: 3}],
|
||||
'ts/no-use-before-define': 'off',
|
||||
'style/function-paren-newline': ['error', 'consistent']
|
||||
},
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -9,21 +9,43 @@ use onelauncher_entity::prelude::model::*;
|
|||
pub trait TauriLauncherApi {
|
||||
|
||||
// Clusters
|
||||
#[taurpc(alias = "getClusters")]
|
||||
async fn get_clusters() -> LauncherResult<Vec<Cluster>>;
|
||||
async fn get_cluster(id: ClusterId) -> LauncherResult<Option<Cluster>>;
|
||||
|
||||
#[taurpc(alias = "getClusterById")]
|
||||
async fn get_cluster_by_id(id: ClusterId) -> LauncherResult<Option<Cluster>>;
|
||||
|
||||
#[taurpc(alias = "removeCluster")]
|
||||
async fn remove_cluster(id: ClusterId) -> LauncherResult<()>;
|
||||
|
||||
#[taurpc(alias = "createCluster")]
|
||||
async fn create_cluster(options: CreateCluster) -> LauncherResult<Cluster>;
|
||||
|
||||
// Setting Profiles
|
||||
#[taurpc(alias = "getProfileOrDefault")]
|
||||
async fn get_profile_or_default(name: Option<String>) -> LauncherResult<SettingsProfile>;
|
||||
|
||||
#[taurpc(alias = "getGlobalProfile")]
|
||||
async fn get_global_profile() -> SettingsProfile;
|
||||
|
||||
|
||||
// Users
|
||||
#[taurpc(alias = "getUsers")]
|
||||
async fn get_users() -> LauncherResult<Vec<MinecraftCredentials>>;
|
||||
|
||||
#[taurpc(alias = "getUser")]
|
||||
async fn get_user(uuid: uuid::Uuid) -> LauncherResult<Option<MinecraftCredentials>>;
|
||||
|
||||
#[taurpc(alias = "removeUser")]
|
||||
async fn remove_user(uuid: uuid::Uuid) -> LauncherResult<()>;
|
||||
|
||||
#[taurpc(alias = "getDefaultUser")]
|
||||
async fn get_default_user(fallback: Option<bool>) -> LauncherResult<Option<MinecraftCredentials>>;
|
||||
|
||||
#[taurpc(alias = "setDefaultUser")]
|
||||
async fn set_default_user(uuid: Option<uuid::Uuid>) -> LauncherResult<()>;
|
||||
|
||||
#[taurpc(alias = "openMsaLogin")]
|
||||
async fn open_msa_login<R: Runtime>(app_handle: AppHandle<R>) -> LauncherResult<Option<MinecraftCredentials>>;
|
||||
|
||||
}
|
||||
|
|
@ -48,7 +70,7 @@ impl TauriLauncherApi for TauriLauncherApiImpl {
|
|||
api::cluster::dao::get_all_clusters().await
|
||||
}
|
||||
|
||||
async fn get_cluster(self, id: ClusterId) -> LauncherResult<Option<Cluster>> {
|
||||
async fn get_cluster_by_id(self, id: ClusterId) -> LauncherResult<Option<Cluster>> {
|
||||
api::cluster::dao::get_cluster_by_id(id).await
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -116,8 +116,7 @@ export function AnimatedOutlet({
|
|||
|
||||
const handleAnimationComplete = (id: number) => {
|
||||
setSnapshots(prevSnapshots =>
|
||||
prevSnapshots.filter(snapshot => snapshot.id !== id),
|
||||
);
|
||||
prevSnapshots.filter(snapshot => snapshot.id !== id));
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,11 +1,18 @@
|
|||
import type { UndefinedInitialDataOptions, UseQueryResult } from '@tanstack/react-query';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
type CacheKey = string;
|
||||
// this gets overwritten by the consumer project
|
||||
export interface Register {
|
||||
// commands
|
||||
}
|
||||
|
||||
export function useCommand<T>(cacheKey: CacheKey | (string & {}), command: () => Promise<T>, options?: Omit<UndefinedInitialDataOptions<T>, 'queryKey' | 'queryFn'>): UseQueryResult<T, Error> {
|
||||
type RegisterConfig = Register extends {
|
||||
commands: infer Keys;
|
||||
} ? Keys : never;
|
||||
|
||||
export function useCommand<T>(cacheKey: RegisterConfig[number] | (string & {}) | false, command: () => Promise<T>, options?: Omit<UndefinedInitialDataOptions<T>, 'queryKey' | 'queryFn'>): UseQueryResult<T, Error> {
|
||||
return useQuery({
|
||||
queryKey: [cacheKey],
|
||||
queryKey: cacheKey === false ? [] : [cacheKey],
|
||||
queryFn: async () => {
|
||||
try {
|
||||
const result = await command();
|
||||
|
|
@ -24,5 +31,3 @@ export function useCommand<T>(cacheKey: CacheKey | (string & {}), command: () =>
|
|||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
// export useCommand;
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
export * from './hooks';
|
||||
export type * from './types/global';
|
||||
export * from './utils';
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue