Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ tauri-build = { version = "=2.2.0" }
tauri-plugin-clipboard-manager = { version = "=2.2.2" }
tauri-plugin-deep-link = { version = "=2.2.1" }
tauri-plugin-dialog = { version = "=2.2.1" }
tauri-plugin-fs = { version = "=2.2.1" }
tauri-plugin-single-instance = { version = "=2.2.3" }
tauri-plugin-updater = { version = "=2.7.1" }
tauri-plugin-window-state = { version = "=2.2.2" }
Expand Down
1 change: 1 addition & 0 deletions apps/oneclient/desktop/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ tauri-plugin-single-instance = { workspace = true }
tauri-plugin-updater = { workspace = true }
tauri-plugin-clipboard-manager = { workspace = true }
tauri-plugin-dialog = { workspace = true }
tauri-plugin-fs = { workspace = true }
tauri-plugin-deep-link = { workspace = true }

# code gen
Expand Down
7 changes: 6 additions & 1 deletion apps/oneclient/desktop/capabilities/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@
"dialog:allow-save",
"dialog:allow-confirm",
"deep-link:default",
"updater:default"
"updater:default",

"fs:allow-download-read",
"fs:allow-download-write",
"fs:allow-data-read-recursive",
"fs:allow-data-write-recursive"
]
}
1 change: 1 addition & 0 deletions apps/oneclient/desktop/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ async fn initialize_tauri(builder: tauri::Builder<tauri::Wry>) -> LauncherResult
// .plugin(tauri_plugin_updater::Builder::new().build())
.plugin(tauri_plugin_clipboard_manager::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_deep_link::init())
.menu(tauri::menu::Menu::new)
.invoke_handler(router.into_handler())
Expand Down
1 change: 1 addition & 0 deletions apps/oneclient/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@tauri-apps/api": "catalog:",
"@tauri-apps/plugin-clipboard-manager": "catalog:",
"@tauri-apps/plugin-dialog": "catalog:",
"@tauri-apps/plugin-fs": "catalog:",
"@untitled-theme/icons-react": "catalog:",
"motion": "catalog:",
"overlayscrollbars": "catalog:",
Expand Down
15 changes: 10 additions & 5 deletions apps/oneclient/frontend/src/bindings.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,14 @@ export type ModpackManifest = { name: string; version: string; loader: GameLoade

export type MojangCape = { id: string; state: string; url: string; alias: string }

export type MojangFullPlayerProfile = { uuid: string; username: string; skins: MojangSkin[]; capes: MojangCape[] }
export type MojangFullPlayerProfile = { id: string; username: string; skins: MojangSkin[]; capes: MojangCape[] }

export type MojangPlayerProfile = { uuid: string; username: string; is_slim: boolean; skin_url: string | null; cape_url: string | null }

export type MojangSkin = { id: string; state: string; url: string; variant: SkinVariant }

export type MowojangProfile = { id: string; username: string }

export type PackageAuthor = { Team: { team_id: string; org_id: string | null } } | { Users: ManagedUser[] }

export type PackageCategories = { Mod: PackageModCategory[] } | { ResourcePack: PackageResourcePackCategory[] } | { Shader: PackageShaderCategory[] } | { DataPack: PackageModCategory[] } | { ModPack: PackageModPackCategory[] }
Expand Down Expand Up @@ -252,15 +254,15 @@ export type VersionType =
*/
"old_beta"

const ARGS_MAP = { 'core':'{"getScreenshots":["id"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"fetchLoggedInProfile":["access_token"],"getPackageBody":["provider","body"],"getLogs":["id"],"installModpack":["modpack","cluster_id"],"fetchMinecraftProfile":["uuid"],"updateClusterById":["id","request"],"getMultiplePackages":["provider","slugs"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"getUsersFromAuthor":["provider","author"],"updateClusterProfile":["name","profile"],"getLogByName":["id","name"],"removeUser":["uuid"],"getUser":["uuid"],"launchCluster":["id","uuid"],"readSettings":[],"getProfileOrDefault":["name"],"getGameVersions":[],"removeCluster":["id"],"getRunningProcessesByClusterId":["cluster_id"],"open":["input"],"createSettingsProfile":["name"],"getClusterById":["id"],"openMsaLogin":[],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"getRunningProcesses":[],"getClusters":[],"getWorlds":["id"],"changeSkin":["access_token","skin_url","skin_variant"],"killProcess":["pid"],"getLoadersForVersion":["mc_version"],"createCluster":["options"],"getUsers":[],"getGlobalProfile":[],"searchPackages":["provider","query"],"writeSettings":["setting"],"isClusterRunning":["cluster_id"],"getPackage":["provider","slug"],"getDefaultUser":["fallback"],"setDefaultUser":["uuid"]}', 'folders':'{"fromCluster":["folder_name"],"openCluster":["folder_name"]}', 'events':'{"message":["event"],"ingress":["event"],"process":["event"]}', 'oneclient':'{"openDevTools":[],"getClustersGroupedByMajor":[],"getBundlesFor":["cluster_id"]}' }
export type Router = { 'folders': { fromCluster: (folderName: string) => Promise<string>,
openCluster: (folderName: string) => Promise<null> },
'oneclient': { openDevTools: () => Promise<void>,
const ARGS_MAP = { 'events':'{"ingress":["event"],"message":["event"],"process":["event"]}', 'oneclient':'{"getClustersGroupedByMajor":[],"openDevTools":[],"getBundlesFor":["cluster_id"]}', 'core':'{"updateClusterProfile":["name","profile"],"updateClusterById":["id","request"],"getLogByName":["id","name"],"killProcess":["pid"],"getUsers":[],"getPackage":["provider","slug"],"removeCluster":["id"],"launchCluster":["id","uuid"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"getProfileOrDefault":["name"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"changeCape":["access_token","cape_uuid"],"changeSkin":["access_token","skin_url","skin_variant"],"getPackageBody":["provider","body"],"installModpack":["modpack","cluster_id"],"getWorlds":["id"],"removeUser":["uuid"],"convertUsernameUUID":["username_uuid"],"writeSettings":["setting"],"getGameVersions":[],"getClusterById":["id"],"getLoadersForVersion":["mc_version"],"getScreenshots":["id"],"getMultiplePackages":["provider","slugs"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"getUsersFromAuthor":["provider","author"],"openMsaLogin":[],"getDefaultUser":["fallback"],"getUser":["uuid"],"getRunningProcessesByClusterId":["cluster_id"],"getGlobalProfile":[],"createSettingsProfile":["name"],"setDefaultUser":["uuid"],"getClusters":[],"removeCape":["access_token"],"searchPackages":["provider","query"],"isClusterRunning":["cluster_id"],"getLogs":["id"],"fetchLoggedInProfile":["access_token"],"open":["input"],"fetchMinecraftProfile":["uuid"],"readSettings":[],"getRunningProcesses":[],"createCluster":["options"]}', 'folders':'{"openCluster":["folder_name"],"fromCluster":["folder_name"]}' }
export type Router = { 'oneclient': { openDevTools: () => Promise<void>,
getClustersGroupedByMajor: () => Promise<Partial<{ [key in number]: ClusterModel[] }>>,
getBundlesFor: (clusterId: number) => Promise<ModpackArchive[]> },
'events': { ingress: (event: IngressPayload) => Promise<void>,
message: (event: MessagePayload) => Promise<void>,
process: (event: ProcessPayload) => Promise<void> },
'folders': { fromCluster: (folderName: string) => Promise<string>,
openCluster: (folderName: string) => Promise<null> },
'core': { getClusters: () => Promise<ClusterModel[]>,
getClusterById: (id: number) => Promise<ClusterModel | null>,
removeCluster: (id: number) => Promise<null>,
Expand Down Expand Up @@ -301,6 +303,9 @@ fetchMinecraftProfile: (uuid: string) => Promise<MojangPlayerProfile>,
fetchLoggedInProfile: (accessToken: string) => Promise<MojangFullPlayerProfile>,
uploadSkinBytes: (accessToken: string, skinData: number[], imageFormat: string, skinVariant: SkinVariant) => Promise<MojangSkin>,
changeSkin: (accessToken: string, skinUrl: string, skinVariant: SkinVariant) => Promise<MojangSkin>,
changeCape: (accessToken: string, capeUuid: string) => Promise<MojangFullPlayerProfile>,
removeCape: (accessToken: string) => Promise<MojangFullPlayerProfile>,
convertUsernameUUID: (usernameUuid: string) => Promise<MowojangProfile>,
open: (input: string) => Promise<null> } };


Expand Down
86 changes: 77 additions & 9 deletions apps/oneclient/frontend/src/components/SkinViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,48 @@
width?: number;
height?: number;
className?: string | undefined;
autoRotate?: boolean;
autoRotateSpeed?: number;
showText?: boolean;
playerRotatePhi?: number;
playerRotateTheta?: number;
translateRotateX?: number;
translateRotateY?: number;
translateRotateZ?: number;
zoom?: number;
animate?: boolean;
animation?: skinviewer.PlayerAnimation;
enableDamping?: boolean;
enableZoom?: boolean;
enableRotate?: boolean;
enablePan?: boolean;
elytra?: boolean;
}

const defaultIdleAnimation = new skinviewer.IdleAnimation();

export function SkinViewer({
skinUrl,
capeUrl,
width = 260,
height = 300,
className,
autoRotate = true,
autoRotateSpeed = 0.25,
showText = true,
playerRotatePhi = Math.PI / 3,
playerRotateTheta = -Math.PI / 6,
translateRotateX = 0,
translateRotateY = 0,
translateRotateZ = 0,
zoom = 0.9,
animate = false,
animation = defaultIdleAnimation,
enableDamping = true,
enableZoom = true,
enableRotate = true,
enablePan = true,
elytra = false

Check failure on line 53 in apps/oneclient/frontend/src/components/SkinViewer.tsx

View workflow job for this annotation

GitHub Actions / ES Checks

Missing trailing comma
}: SkinViewerProps) {
const canvasRef = useRef<HTMLCanvasElement>(null);
const viewerRef = useRef<skinviewer.SkinViewer | null>(null);
Expand All @@ -29,15 +63,39 @@
canvas: canvasRef.current,
});

viewer.autoRotate = true;
viewer.autoRotateSpeed = 0.25;
viewer.controls.enableDamping = enableDamping;
viewer.controls.enableZoom = enableZoom;
viewer.controls.enableRotate = enableRotate;
viewer.controls.enablePan = enablePan;

viewer.zoom = zoom;

const setAngle = (phi: number, theta: number) => {
const r = viewer.controls.object.position.distanceTo(viewer.controls.target);
const x = r * Math.cos(phi - Math.PI / 2) * Math.sin(theta) + viewer.controls.target.x;
const y = r * Math.sin(phi + Math.PI / 2) + viewer.controls.target.y;
const z = r * Math.cos(phi - Math.PI / 2) * Math.cos(theta) + viewer.controls.target.z;
viewer.controls.object.position.set(x, y, z);
viewer.controls.object.lookAt(viewer.controls.target);
};
setAngle(playerRotatePhi, playerRotateTheta)

Check failure on line 81 in apps/oneclient/frontend/src/components/SkinViewer.tsx

View workflow job for this annotation

GitHub Actions / ES Checks

Missing semicolon

viewer.playerWrapper.translateX(translateRotateX);
viewer.playerWrapper.translateY(translateRotateY);
viewer.playerWrapper.translateZ(translateRotateZ);


Check failure on line 87 in apps/oneclient/frontend/src/components/SkinViewer.tsx

View workflow job for this annotation

GitHub Actions / ES Checks

More than 1 blank line not allowed
viewer.animation = animation;

viewer.autoRotate = autoRotate;
viewer.autoRotateSpeed = autoRotateSpeed;

viewerRef.current = viewer;

return () => {
viewer.dispose();
};
}, []);

Check warning on line 98 in apps/oneclient/frontend/src/components/SkinViewer.tsx

View workflow job for this annotation

GitHub Actions / ES Checks

React Hook useEffect has missing dependencies: 'animation', 'autoRotate', 'autoRotateSpeed', 'enableDamping', 'enablePan', 'enableRotate', 'enableZoom', 'playerRotatePhi', 'playerRotateTheta', 'translateRotateX', 'translateRotateY', 'translateRotateZ', and 'zoom'. Either include them or remove the dependency array

useEffect(() => {
if (!viewerRef.current)
Expand All @@ -46,19 +104,15 @@
viewerRef.current.loadSkin(getSkinUrl(skinUrl));
}, [skinUrl]);

useEffect(() => {

}, [capeUrl]);

useEffect(() => {
if (!viewerRef.current)
return;

if (capeUrl)
viewerRef.current.loadCape(capeUrl);
viewerRef.current.loadCape(capeUrl, { backEquipment: elytra ? 'elytra' : 'cape' });
else
viewerRef.current.resetCape();
}, [capeUrl]);
}, [capeUrl, elytra]);

useEffect(() => {
if (!viewerRef.current)
Expand All @@ -67,6 +121,20 @@
viewerRef.current.setSize(width, height);
}, [width, height]);

useEffect(() => {
if (!viewerRef.current)
return;

viewerRef.current.animation = animation;
}, [animation]);

useEffect(() => {
if (!viewerRef.current || !viewerRef.current.animation)
return;

viewerRef.current.animation.paused = !animate;
}, [animate]);

return (
<div className={twMerge('flex flex-col justify-center items-center', className)} style={{ minWidth: `${width}px`, minHeight: `${height}px` }}>
<canvas
Expand All @@ -75,7 +143,7 @@
width={width}
/>

<span className="text-fg-secondary text-xs">Hold to drag. Scroll to zoom in/out.</span>
{showText ? <span className="text-fg-secondary text-xs">Hold to drag. Scroll to zoom in/out.</span> : <></>}
</div>
);
}
43 changes: 29 additions & 14 deletions apps/oneclient/frontend/src/components/overlay/AccountPopup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { bindings } from '@/main';
import { useCommand, useCommandMut } from '@onelauncher/common';
import { Button } from '@onelauncher/common/components';
import { Link } from '@tanstack/react-router';
import { PlusIcon, Settings01Icon, Trash01Icon } from '@untitled-theme/icons-react';
import { Pencil01Icon, PlusIcon, Settings01Icon, Trash01Icon } from '@untitled-theme/icons-react';
import { DialogTrigger } from 'react-aria-components';
import { twMerge } from 'tailwind-merge';
import { AccountAvatar } from '../AccountAvatar';
Expand All @@ -28,8 +28,9 @@ export function AccountPopup() {
defaultUser.refetch();

if (defaultUser.data && defaultUser.data.id === user.id && users.data && users.data.length > 1) {
const filtered = users.data.filter((userData) => userData.id !== user.id)
if (filtered.length > 0) setDefaultUser(filtered[0]);
const filtered = users.data.filter(userData => userData.id !== user.id);
if (filtered.length > 0)
setDefaultUser(filtered[0]);
}
};

Expand Down Expand Up @@ -99,8 +100,9 @@ function AccountEntry({
loggedIn?: boolean;
}) {
return (
<div
className={twMerge('flex flex-row justify-between p-2 rounded-lg', !loggedIn && 'hover:bg-component-bg-hover active:bg-component-bg-pressed hover:text-fg-primary-hover')}
<Button
className={twMerge('w-full flex flex-row justify-between p-2 rounded-lg', !loggedIn && 'hover:bg-component-bg-hover active:bg-component-bg-pressed hover:text-fg-primary-hover')}
color="ghost"
onClick={onClick}
>
<div className="flex flex-1 flex-row justify-start gap-x-3">
Expand All @@ -114,16 +116,29 @@ function AccountEntry({
</div>
</div>

<DialogTrigger>
<Button className="group w-8 h-8" color="ghost" size="icon">
<Trash01Icon className="group-hover:stroke-danger" />
</Button>
<div className="flex flex-row items-center">
<Link to="/app/accountSkin">
<Button
className="group w-8 h-8"
color="ghost"
onClick={onClick}
size="icon"
>
<Pencil01Icon className="group-hover:stroke-brand-hover" />
</Button>
</Link>

<Overlay>
<RemoveAccountModal onPress={onDelete} profile={user} />
</Overlay>
</DialogTrigger>
<DialogTrigger>
<Button className="group w-8 h-8" color="ghost" size="icon">
<Trash01Icon className="group-hover:stroke-danger" />
</Button>

<Overlay>
<RemoveAccountModal onPress={onDelete} profile={user} />
</Overlay>
</DialogTrigger>
</div>
</div>
</div>
</Button>
);
}
34 changes: 34 additions & 0 deletions apps/oneclient/frontend/src/components/overlay/ImportSkinModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Button, TextField } from '@onelauncher/common/components';
import { useState } from 'react';
import { Overlay } from './Overlay';

export function ImportSkinModal({ importFromURL, importFromUsername }: { importFromURL: (url: string) => void; importFromUsername: (username: string) => void }) {
const [input, setInput] = useState<string>('');
return (
<Overlay.Dialog>
<Overlay.Title>Import</Overlay.Title>
<TextField className="w-full" onChange={e => setInput(e.target.value)} />

<div className="flex flex-row gap-4 h-1/2 w-full">
<Button
className="w-1/2"
color="primary"
onClick={() => importFromUsername(input)}
size="normal"
slot="close"
>
From Username
</Button>
<Button
className="w-1/2"
color="primary"
onClick={() => importFromURL(input)}
size="normal"
slot="close"
>
From URL
</Button>
</div>
</Overlay.Dialog>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Button } from '@onelauncher/common/components';
import { Overlay } from './Overlay';

export function RemoveSkinCapeModal({ onPress }: { onPress: () => void; }) {

Check failure on line 4 in apps/oneclient/frontend/src/components/overlay/RemoveSkinCapeModal.tsx

View workflow job for this annotation

GitHub Actions / ES Checks

Unexpected separator (;)
return (
<Overlay.Dialog>
<Overlay.Title>Are you sure?</Overlay.Title>

<p>This cannot be undone</p>

<Button
className="w-full"
color="danger"
onPress={onPress}
size="large"
slot="close"
>
Remove
</Button>
</Overlay.Dialog>
);
}
2 changes: 2 additions & 0 deletions apps/oneclient/frontend/src/components/overlay/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export * from './AccountPopup';
export * from './AddAccountModal';
export * from './ImportSkinModal';
export * from './Overlay';
export * from './Popup';
export * from './RemoveAccountModal';
export * from './RemoveSkinCapeModal';
export * from './Toasts';
Loading
Loading