mirror of
https://github.com/SlimeVR/SlimeVR-Server.git
synced 2026-04-05 18:01:56 +02:00
118 lines
4.2 KiB
TypeScript
118 lines
4.2 KiB
TypeScript
import Quaternion from 'quaternion';
|
|
import { useMemo, useState } from 'react';
|
|
import { AssignTrackerRequestT, BodyPart, RpcMessage } from 'solarxr-protocol';
|
|
import { FlatDeviceTracker } from '../../../../hooks/app';
|
|
import { useOnboarding } from '../../../../hooks/onboarding';
|
|
import { useTrackers } from '../../../../hooks/tracker';
|
|
import { useWebsocketAPI } from '../../../../hooks/websocket-api';
|
|
import { QuaternionToQuatT } from '../../../../maths/quaternion';
|
|
import { ArrowLink } from '../../../commons/ArrowLink';
|
|
import { Button } from '../../../commons/Button';
|
|
import { TipBox } from '../../../commons/TipBox';
|
|
import { Typography } from '../../../commons/Typography';
|
|
import { BodyAssignment } from '../../BodyAssignment';
|
|
import { MountingSelectionMenu } from './MountingSelectionMenu';
|
|
|
|
export function ManualMountingPage() {
|
|
const { applyProgress, skipSetup, state } = useOnboarding();
|
|
const { sendRPCPacket } = useWebsocketAPI();
|
|
|
|
const [selectedRole, setSelectRole] = useState<BodyPart>(BodyPart.NONE);
|
|
|
|
applyProgress(0.7);
|
|
|
|
const { useAssignedTrackers } = useTrackers();
|
|
const assignedTrackers = useAssignedTrackers();
|
|
|
|
const trackerPartGrouped = useMemo(
|
|
() =>
|
|
assignedTrackers.reduce<{ [key: number]: FlatDeviceTracker[] }>(
|
|
(curr, td) => {
|
|
const key = td.tracker.info?.bodyPart || BodyPart.NONE;
|
|
return {
|
|
...curr,
|
|
[key]: [...(curr[key] || []), td],
|
|
};
|
|
},
|
|
{}
|
|
),
|
|
[assignedTrackers]
|
|
);
|
|
|
|
const onDirectionSelected = (mountingOrientation: number) => {
|
|
(trackerPartGrouped[selectedRole] || []).forEach((td) => {
|
|
const assignreq = new AssignTrackerRequestT();
|
|
|
|
assignreq.bodyPosition = td.tracker.info?.bodyPart || BodyPart.NONE;
|
|
assignreq.mountingRotation = QuaternionToQuatT(
|
|
Quaternion.fromEuler(0, +mountingOrientation, 0)
|
|
);
|
|
assignreq.trackerId = td.tracker.trackerId;
|
|
sendRPCPacket(RpcMessage.AssignTrackerRequest, assignreq);
|
|
});
|
|
|
|
setSelectRole(BodyPart.NONE);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<MountingSelectionMenu
|
|
isOpen={selectedRole !== BodyPart.NONE}
|
|
onClose={() => setSelectRole(BodyPart.NONE)}
|
|
onDirectionSelected={onDirectionSelected}
|
|
></MountingSelectionMenu>
|
|
<div className="flex flex-col gap-5 h-full items-center w-full justify-center">
|
|
<div className="flex flex-col w-full h-full justify-center items-center">
|
|
<div className="flex md:gap-8">
|
|
<div className="flex flex-col w-full max-w-md gap-3">
|
|
{!state.alonePage && (
|
|
<ArrowLink to="/onboarding/enter-vr" direction="left">
|
|
Go Back to Enter VR
|
|
</ArrowLink>
|
|
)}
|
|
<Typography variant="main-title">Manual Mounting</Typography>
|
|
<Typography color="secondary">
|
|
Click on every tracker and select which way they are mounted
|
|
</Typography>
|
|
<TipBox>
|
|
Not sure which tracker is which? Shake a tracker and it will
|
|
highlight the corresponding item.
|
|
</TipBox>
|
|
</div>
|
|
<div className="flex flex-col flex-grow gap-3 rounded-xl fill-background-50">
|
|
<BodyAssignment
|
|
onlyAssigned={true}
|
|
advanced={true}
|
|
onRoleSelected={setSelectRole}
|
|
></BodyAssignment>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="w-full pb-4 flex flex-row">
|
|
<div className="flex flex-grow">
|
|
{!state.alonePage && (
|
|
<Button variant="secondary" to="/" onClick={skipSetup}>
|
|
Skip setup
|
|
</Button>
|
|
)}
|
|
</div>
|
|
<div className="flex gap-3">
|
|
<Button
|
|
variant="secondary"
|
|
state={{ alonePage: state.alonePage }}
|
|
to="/onboarding/mounting/auto"
|
|
>
|
|
Automatic mounting
|
|
</Button>
|
|
{!state.alonePage && (
|
|
<Button variant="primary" to="/onboarding/reset-tutorial">
|
|
Next step
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|