feat: add cluster view and improve scroll behavior
mid commit again
This commit is contained in:
parent
eebd4e90c2
commit
be48b6042b
14 changed files with 162 additions and 57 deletions
|
|
@ -19,6 +19,7 @@ macro_rules! collect_commands {
|
|||
|
||||
cluster::create_cluster,
|
||||
cluster::get_clusters,
|
||||
cluster::get_cluster_by_id,
|
||||
|
||||
settings::get_global_profile,
|
||||
|
||||
|
|
|
|||
|
|
@ -216,7 +216,9 @@
|
|||
// Ok(())
|
||||
// }
|
||||
|
||||
use onelauncher_core::{api::cluster, entity::{clusters::Model, icon::Icon, loader::GameLoader}};
|
||||
use std::ops::Deref;
|
||||
|
||||
use onelauncher_core::{api::cluster, entity::{clusters::Model, icon::Icon, loader::GameLoader}, error::LauncherResult};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
use crate::api::error::SerializableResult;
|
||||
|
|
@ -235,6 +237,8 @@ pub struct CreateCluster {
|
|||
pub async fn create_cluster(options: CreateCluster) -> SerializableResult<Model> {
|
||||
let thing = cluster::create_cluster(&options.name, &options.mc_version, options.mc_loader, options.mc_loader_version.as_deref(), options.icon_url).await?;
|
||||
|
||||
cluster::prepare_cluster(&mut thing.clone(), Some(false));
|
||||
|
||||
Ok(thing)
|
||||
}
|
||||
|
||||
|
|
@ -242,4 +246,10 @@ pub async fn create_cluster(options: CreateCluster) -> SerializableResult<Model>
|
|||
#[tauri::command]
|
||||
pub async fn get_clusters() -> SerializableResult<Vec<Model>> {
|
||||
Ok(cluster::dao::get_all_clusters().await?)
|
||||
}
|
||||
|
||||
#[specta::specta]
|
||||
#[tauri::command]
|
||||
pub async fn get_cluster_by_id(id: i64) -> SerializableResult<Option<Model>> {
|
||||
Ok(cluster::dao::get_cluster_by_id(id).await?)
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
/>
|
||||
<link rel="apple-touch-icon" href="/logo192.png" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<title>OneClient</title>
|
||||
<title>OneLauncher</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
|
|
|||
|
|
@ -16,4 +16,4 @@ function ScrollableContainer(props: ScrollableContainerProps) {
|
|||
);
|
||||
}
|
||||
|
||||
export default ScrollableContainer;
|
||||
export default ScrollableContainer;
|
||||
|
|
@ -8,6 +8,7 @@ import { routeTree } from './routeTree.gen';
|
|||
import './fonts';
|
||||
import './utils/nativeExperience';
|
||||
import './utils/devExperience';
|
||||
import 'overlayscrollbars/overlayscrollbars.css';
|
||||
import './styles/global.css';
|
||||
|
||||
export * as bindings from './bindings.gen';
|
||||
|
|
|
|||
|
|
@ -29,13 +29,13 @@ function RootRoute() {
|
|||
navigate={(to, options) => router.navigate({ to, ...options })}
|
||||
useHref={to => router.buildLocation({ to }).href}
|
||||
>
|
||||
<main className='max-h-screen min-h-screen flex flex-col'>
|
||||
<div className="h-screen flex flex-col overflow-hidden px-0.5">
|
||||
<WindowFrame />
|
||||
|
||||
<Outlet />
|
||||
|
||||
<TanStackRouterDevtools />
|
||||
</main>
|
||||
</div>
|
||||
</RouterProvider>
|
||||
</AnimatedOutletProvider>
|
||||
);
|
||||
|
|
|
|||
23
apps/onelauncher/frontend/src/routes/app/cluster/index.tsx
Normal file
23
apps/onelauncher/frontend/src/routes/app/cluster/index.tsx
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import useCommand from '@/hooks/useCommand'
|
||||
import { bindings } from '@/main'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import Sidebar from '../settings/route'
|
||||
|
||||
export const Route = createFileRoute('/app/cluster/')({
|
||||
component: RouteComponent
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
const { id } = Route.useSearch()
|
||||
|
||||
// dumbass fix ik
|
||||
const cluster = useCommand("getClusterById", () => bindings.commands.getClusterById(Number(id.toString()) as unknown as bigint))
|
||||
|
||||
return (
|
||||
<Sidebar.Page>
|
||||
<div className="h-full">
|
||||
<pre>{JSON.stringify(cluster, null, 2)}</pre>
|
||||
</div>
|
||||
</Sidebar.Page>
|
||||
)
|
||||
}
|
||||
43
apps/onelauncher/frontend/src/routes/app/cluster/route.tsx
Normal file
43
apps/onelauncher/frontend/src/routes/app/cluster/route.tsx
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { createFileRoute, Outlet } from '@tanstack/react-router'
|
||||
import Sidebar from '../settings/route'
|
||||
import { EyeIcon, File06Icon, Globe04Icon, Image03Icon, PackagePlusIcon, Settings04Icon } from '@untitled-theme/icons-react'
|
||||
|
||||
type ClusterSearch = {
|
||||
id: bigint
|
||||
}
|
||||
|
||||
export const Route = createFileRoute('/app/cluster')({
|
||||
component: RouteComponent,
|
||||
validateSearch: (search: Record<string, unknown>): ClusterSearch => {
|
||||
return {
|
||||
id: BigInt(search.id as string),
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
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': [
|
||||
[<EyeIcon />, 'Overview', '/'],
|
||||
[<PackagePlusIcon />, 'Mods', '/mods'],
|
||||
[<Image03Icon />, 'Screenshots', '/screenshots'],
|
||||
[<Globe04Icon />, 'Worlds', '/worlds'],
|
||||
[<File06Icon />, 'Logs', '/logs'],
|
||||
[<Settings04Icon />, 'Game Settings', '/settings'],
|
||||
],
|
||||
}}
|
||||
/>
|
||||
{/* <Info /> */}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 min-w-0 h-full">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import Button from '@/components/base/Button';
|
|||
import useCommand from '@/hooks/useCommand';
|
||||
import { bindings } from '@/main';
|
||||
import { useIsFetching, useQueryClient } from '@tanstack/react-query';
|
||||
import { createFileRoute, useNavigate } from '@tanstack/react-router';
|
||||
import { createFileRoute, Link, useNavigate } from '@tanstack/react-router';
|
||||
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
|
||||
import DefaultInstancePhoto from "@/assets/images/default_instance_cover.jpg"
|
||||
|
||||
|
|
@ -176,33 +176,37 @@ function ClusterGroup(props: ClusterGroupProps) {
|
|||
|
||||
function ClusterCard(props: Model) {
|
||||
return (
|
||||
<div
|
||||
className="group relative h-[152px] flex flex-col rounded-xl border border-component-border/5 bg-component-bg active:bg-component-bg-pressed hover:bg-component-bg-hover"
|
||||
>
|
||||
<div className="relative flex-1 overflow-hidden rounded-t-xl">
|
||||
<div
|
||||
className="absolute h-full w-full transition-transform group-hover:!scale-110"
|
||||
>
|
||||
<img
|
||||
className="h-full w-full object-cover"
|
||||
src={DefaultInstancePhoto}
|
||||
/>
|
||||
<Link to='/app/cluster' search={{
|
||||
id: props.id
|
||||
}}>
|
||||
<div
|
||||
className="group relative h-[152px] flex flex-col rounded-xl border border-component-border/5 bg-component-bg active:bg-component-bg-pressed hover:bg-component-bg-hover"
|
||||
>
|
||||
<div className="relative flex-1 overflow-hidden rounded-t-xl">
|
||||
<div
|
||||
className="absolute h-full w-full transition-transform group-hover:!scale-110"
|
||||
>
|
||||
<img
|
||||
className="h-full w-full object-cover"
|
||||
src={DefaultInstancePhoto}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</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-xs">
|
||||
{props.mc_loader}
|
||||
{' '}
|
||||
{props.mc_version}
|
||||
{/* {' '}
|
||||
<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-xs">
|
||||
{props.mc_loader}
|
||||
{' '}
|
||||
{props.mc_version}
|
||||
{/* {' '}
|
||||
{props.packages.mods && `• ${props.mods} mods`} */}
|
||||
</p>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* <LaunchButton cluster={props} iconOnly /> */}
|
||||
{/* <LaunchButton cluster={props} iconOnly /> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
|
@ -9,33 +9,33 @@ export const Route = createFileRoute('/app')({
|
|||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
<>
|
||||
<div className="flex flex-col px-4">
|
||||
<Navbar />
|
||||
</div>
|
||||
|
||||
<div className="h-full w-full overflow-hidden">
|
||||
<div className="relative h-full w-full flex flex-col overflow-x-hidden pb-8">
|
||||
<OverlayScrollbarsComponent className="h-full w-full flex flex-col overflow-x-hidden overflow-y-auto">
|
||||
<div className="h-full flex-1 px-4">
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<OverlayScrollbarsComponent
|
||||
className="h-full w-full overflow-auto overflow-x-hidden"
|
||||
>
|
||||
<div className="h-full pb-4 px-4">
|
||||
|
||||
<AnimatedOutlet
|
||||
enter={{
|
||||
initial: { opacity: 0, transform: 'translateX(-20%)' },
|
||||
animate: { opacity: 1, transform: 'translateX(0)' },
|
||||
}}
|
||||
exit={{
|
||||
initial: { opacity: 1, transform: 'translateX(0)' },
|
||||
animate: { opacity: 0, transform: 'translateX(-20%)' },
|
||||
}}
|
||||
from={Route.id}
|
||||
transition={{ duration: 0.3, bounce: 0.1, power: 0.2, type: 'spring' }}
|
||||
/>
|
||||
<AnimatedOutlet
|
||||
enter={{
|
||||
initial: { opacity: 0, transform: 'translateX(-20%)' },
|
||||
animate: { opacity: 1, transform: 'translateX(0)' },
|
||||
}}
|
||||
exit={{
|
||||
initial: { opacity: 1, transform: 'translateX(0)' },
|
||||
animate: { opacity: 0, transform: 'translateX(-20%)' },
|
||||
}}
|
||||
from={Route.id}
|
||||
transition={{ duration: 0.3, bounce: 0.1, power: 0.2, type: 'spring' }}
|
||||
/>
|
||||
|
||||
</div>
|
||||
</OverlayScrollbarsComponent>
|
||||
</div>
|
||||
</div>
|
||||
</OverlayScrollbarsComponent>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ function RouteComponent() {
|
|||
|
||||
return (
|
||||
<Sidebar.Page>
|
||||
<div>
|
||||
<div className="h-full">
|
||||
<h1>General Settings</h1>
|
||||
|
||||
<SettingsRow
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export const Route = createFileRoute('/app/settings/minecraft')({
|
|||
function RouteComponent() {
|
||||
return (
|
||||
<Sidebar.Page>
|
||||
<div>
|
||||
<div className="h-full">
|
||||
<h1>Minecraft Settings</h1>
|
||||
|
||||
<SettingsRow.Header>Game</SettingsRow.Header>
|
||||
|
|
|
|||
|
|
@ -75,11 +75,11 @@ function Sidebar(props: SidebarProps) {
|
|||
return (
|
||||
<div className="w-56 flex flex-col pr-2">
|
||||
{Object.keys(props.links).map((section, i) => (
|
||||
<div className="flex flex-col gap-y-2">
|
||||
<div key={i}>
|
||||
<div className="flex flex-col gap-y-2" key={i}>
|
||||
<div>
|
||||
<h3 className="m-1.5 mt-5 text-xs text-fg-secondary font-medium">{section.toUpperCase()}</h3>
|
||||
<div className="flex flex-col gap-y-1 fill-fg-primary text-fg-primary font-medium">
|
||||
{props.links[section].map((link) => {
|
||||
{props.links[section].map((link, i) => {
|
||||
if (!link)
|
||||
return;
|
||||
|
||||
|
|
@ -88,6 +88,7 @@ function Sidebar(props: SidebarProps) {
|
|||
className={
|
||||
`px-3 py-1 rounded-md text-md border border-component-bg hover:bg-component-bg-hover active:bg-component-bg-pressed ${isActive(link[2], link[3]) ? 'bg-component-bg border-border/05' : 'border-transparent'}`
|
||||
}
|
||||
key={i}
|
||||
onClick={() => goto(link[2], link[3])}
|
||||
>
|
||||
<span className="flex flex-row items-center gap-x-3 *:w-5">
|
||||
|
|
@ -113,7 +114,7 @@ interface SidebarPageProps {
|
|||
|
||||
Sidebar.Page = function (props: SidebarPageProps) {
|
||||
return (
|
||||
<div {...props} className={`flex flex-col flex-1 gap-y-1 ${props.className || ''}`}>
|
||||
<div {...props} className={`flex flex-col flex-1 gap-y-1 overflow-y-auto h-full ${props.className || ''}`}>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -60,6 +60,10 @@
|
|||
--color-ghost-overlay: rgba(255, 255, 255, 0.05);
|
||||
--color-ghost-overlay-hover: rgba(255, 255, 255, 0.10);
|
||||
|
||||
--color-scrollbar-bg: rgba(255, 255, 255, 0.05);
|
||||
--color-scrollbar-bg-hover: rgba(255, 255, 255, 0.10);
|
||||
--color-scrollbar-bg-pressed: rgba(255, 255, 255, 0.15);
|
||||
|
||||
/* other */
|
||||
--font-sans: "Poppins", sans-serif;
|
||||
--text-xs: 0.688rem;
|
||||
|
|
@ -83,3 +87,21 @@ body {
|
|||
@apply bg-page text-fg-primary text-base;
|
||||
}
|
||||
|
||||
.os-scrollbar {
|
||||
--os-size: 10px;
|
||||
--os-track-border-radius: var(--border-radius-lg);
|
||||
--os-handle-bg: var(--color-scrollbar-bg);
|
||||
--os-handle-bg-hover: var(--color-scrollbar-bg-hover);
|
||||
--os-handle-bg-active: var(--color-scrollbar-bg-pressed);
|
||||
--os-handle-perpendicular-size-hover: 140%;
|
||||
--os-handle-perpendicular-size-active: 140%;
|
||||
--os-handle-interactive-area-offset: 4px;
|
||||
}
|
||||
|
||||
.os-scrollbar-offset-right {
|
||||
--os-padding-axis: 10;
|
||||
}
|
||||
|
||||
.os-hide-horizontal-scrollbar > .os-scrollbar-horizontal {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue