Skip to content

Commit 6371911

Browse files
authored
Merge pull request #70 from Volumetrics-io/mobile-sync
Mobile sync
2 parents ff25bf2 + 81a296b commit 6371911

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+1154
-671
lines changed

admin/src/services/fetch.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { AlefError } from '@alef/common';
33

44
export const fetch = createFetch({
55
refreshSessionEndpoint: `${import.meta.env.VITE_PUBLIC_API_ORIGIN}/auth/refresh`,
6+
logoutEndpoint: `${import.meta.env.VITE_PUBLIC_API_ORIGIN}/auth/logout`,
67
isSessionExpired: (res) => {
78
const asAlefError = AlefError.fromResponse(res);
89
if (asAlefError) {

app/src/components/desktop/DesktopUI.module.css

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,21 @@
1010
width: 100%;
1111
background-color: var(--track);
1212

13-
--main-width: 400px;
14-
--secondary-width: 300px;
13+
--main-size: 400px;
14+
--secondary-size: 300px;
1515
}
1616

1717
.main {
1818
grid-area: main;
1919
max-height: 100%;
2020
min-height: 0;
21-
width: var(--main-width);
21+
width: var(--main-size);
2222
padding-top: 0.5rem;
2323
}
2424

2525
.content {
2626
grid-area: content;
27-
min-width: var(--secondary-width);
27+
min-width: var(--secondary-size);
2828
border-left: var(--border);
2929
border-right: var(--border);
3030
}
@@ -35,24 +35,36 @@
3535

3636
.secondaryContent {
3737
width: 0px;
38-
transition: width var(--animation-duration) var(--bounce);
38+
transition:
39+
width var(--animation-duration) var(--bounce),
40+
height var(--animation-duration) var(--bounce);
3941
overflow: hidden;
4042
position: relative;
4143
}
4244

4345
.secondaryContentInner {
44-
width: var(--secondary-width);
46+
width: var(--secondary-size);
4547
position: absolute;
4648
top: 3rem;
4749
left: 0;
4850
height: calc(100% - 3rem);
4951
}
5052

5153
.secondaryContentOpen {
52-
width: var(--secondary-width);
54+
width: var(--secondary-size);
5355
}
5456

5557
.secondaryToggle {
58+
left: -38px;
59+
padding: 0.25rem;
60+
position: absolute;
61+
top: 0.75rem;
62+
}
63+
.secondaryContentOpen > .secondaryToggle {
64+
left: auto;
65+
}
66+
.secondaryContentToggleLabel {
67+
display: none;
5668
}
5769

5870
.tabs {
@@ -64,23 +76,62 @@
6476
.root {
6577
grid-template-areas: 'main content' 'secondary content';
6678
grid-template-columns: auto 1fr;
67-
grid-template-rows: 1fr 1fr;
79+
grid-template-rows: 1fr auto;
80+
align-items: stretch;
81+
--main-size: 100%;
82+
}
83+
84+
.main {
85+
padding-bottom: calc(46px + env(safe-area-inset-bottom, 0px));
6886
}
6987

7088
.secondary {
7189
border-top: var(--border);
7290
}
7391

7492
.secondaryContent {
75-
width: var(--secondary-width);
93+
height: 0;
94+
width: 100%;
95+
}
96+
97+
.secondaryContentOpen {
98+
height: var(--secondary-size);
7699
}
77100

78101
.secondaryToggle {
79-
display: none;
102+
left: 0;
103+
right: 0;
104+
top: calc(-34px - env(safe-area-inset-bottom, 0px));
105+
z-index: 1;
106+
display: flex;
107+
align-items: center;
108+
justify-content: center;
109+
gap: 0.5rem;
110+
border-radius: 0;
111+
background-color: var(--paper);
112+
}
113+
.secondaryContentToggleLabel {
114+
display: block;
115+
}
116+
.secondaryContentToggleIcon {
117+
transform: rotate(90deg);
80118
}
81119

82120
.secondaryContentInner {
83-
height: 100%;
121+
width: 100%;
122+
height: var(--secondary-size);
84123
top: 0;
85124
}
86125
}
126+
127+
@media (max-width: 768px) {
128+
.root {
129+
grid-template-areas: 'main' 'secondary';
130+
grid-template-columns: 1fr;
131+
grid-template-rows: 1fr auto;
132+
}
133+
134+
.content {
135+
display: none;
136+
}
137+
}

app/src/components/desktop/DesktopUI.tsx

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,43 @@
1-
import { StageMode, useDetailsOpen, useEditorStageMode } from '@/stores/editorStore';
1+
import { useMedia } from '@/hooks/useMedia';
2+
import { useDetailsOpen } from '@/stores/editorStore';
3+
import { useUndo } from '@/stores/roomStore';
4+
import { useEditorMode, useOnSelectionChanged } from '@/stores/roomStore/hooks/editing';
5+
import { EditorMode } from '@alef/common';
26
import { Box, Frame, Icon, Tabs } from '@alef/sys';
37
import clsx from 'clsx';
48
import { ReactNode, Suspense } from 'react';
9+
import { useHotkeys } from 'react-hotkeys-hook';
510
import cls from './DesktopUI.module.css';
611
import { DesktopAddFurniture } from './furniture/DesktopAddFurniture';
712
import { DesktopFurnitureEditor } from './furniture/DesktopFurnitureEditor';
13+
import { DesktopFurnitureMobileInstructions } from './furniture/DesktopFurnitureMobileInstructions';
814
import { DesktopPlacedFurnitureList } from './furniture/DesktopPlacedFurnitureList';
915
import { DesktopAddLayout } from './layouts/DesktopAddLayout';
1016
import { DesktopLayoutEditor } from './layouts/DesktopLayoutEditor';
1117
import { DesktopLayoutsPicker } from './layouts/DesktopLayoutsPicker';
1218
import { DesktopLightEditor } from './lighting/DesktopLightEditor';
1319
import { DesktopLightsMainEditor } from './lighting/DesktopLightsMainEditor';
20+
import { HeadsetConnectedIndicator } from './presence/HeadsetConnectedIndicator';
1421

1522
export interface DesktopUIProps {
1623
children?: ReactNode;
1724
}
1825

1926
export function DesktopUI({ children }: DesktopUIProps) {
20-
const [mode, setMode] = useEditorStageMode();
27+
const [mode, setMode] = useEditorMode();
28+
// don't bother rendering content, it won't be visible.
29+
const isMobile = useMedia('(max-width: 768px)');
30+
31+
// bind ctrl+z to undo, ctrl+shift+z to redo
32+
const { undo, redo } = useUndo();
33+
useHotkeys('mod+z', undo);
34+
useHotkeys('mod+shift+z, mod+y', redo);
2135

2236
return (
2337
<Box asChild className={cls.root}>
24-
<Tabs value={mode || 'layouts'} onValueChange={(m) => setMode(m as StageMode)}>
38+
<Tabs value={mode || 'layouts'} onValueChange={(m) => setMode(m as EditorMode)}>
2539
<DesktopUIMain />
26-
<Box className={cls.content}>{children}</Box>
40+
{!isMobile && <Box className={cls.content}>{children}</Box>}
2741
<DesktopUISecondary />
2842
</Tabs>
2943
</Box>
@@ -33,6 +47,9 @@ export function DesktopUI({ children }: DesktopUIProps) {
3347
function DesktopUIMain() {
3448
return (
3549
<Box className={cls.main} stacked>
50+
<Box p="small" layout="center center">
51+
<HeadsetConnectedIndicator />
52+
</Box>
3653
<Tabs.List className={cls.tabs}>
3754
<Tabs.Trigger value="layouts">
3855
<Icon name="house" />
@@ -55,7 +72,10 @@ function DesktopUIMain() {
5572
<Tabs.Content value="furniture">
5673
<Suspense>
5774
<Box p="small" full stacked justify="between">
58-
<DesktopPlacedFurnitureList />
75+
<Box stacked gapped>
76+
<DesktopFurnitureMobileInstructions />
77+
<DesktopPlacedFurnitureList />
78+
</Box>
5979
<DesktopAddFurniture />
6080
</Box>
6181
</Suspense>
@@ -69,10 +89,13 @@ function DesktopUIMain() {
6989

7090
function DesktopUISecondary() {
7191
const [open, setOpen] = useDetailsOpen();
92+
// open details panel when selection changes
93+
useOnSelectionChanged(() => setOpen(true));
7294
return (
7395
<Box className={cls.secondary}>
74-
<Frame float="top-left" style={{ left: open ? undefined : -38, padding: '0.25rem' }} className={cls.secondaryToggle}>
75-
<Icon name={open ? 'panel-right-close' : 'panel-right-open'} onClick={() => setOpen(!open)} />
96+
<Frame float="top-left" className={cls.secondaryToggle} onClick={() => setOpen(!open)} aria-label="Toggle details panel" tabIndex={0} role="button">
97+
<Icon name={open ? 'panel-right-close' : 'panel-right-open'} className={cls.secondaryContentToggleIcon} />
98+
<span className={cls.secondaryContentToggleLabel}>Details</span>
7699
</Frame>
77100
<Box className={clsx(cls.secondaryContent, open && cls.secondaryContentOpen)}>
78101
<Box className={cls.secondaryContentInner}>

app/src/components/desktop/furniture/DesktopAddFurniture.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1+
import { useSetPlacingFurniture } from '@/stores/roomStore/hooks/editing';
12
import { Button, Dialog, Icon } from '@alef/sys';
23
import { useState } from 'react';
4+
import { useHotkeys } from 'react-hotkeys-hook';
35
import { DesktopOnlineFurniturePicker } from './DesktopOnlineFurniturePicker';
46

57
export function DesktopAddFurniture() {
68
const [open, setOpen] = useState(false);
9+
const setSelectedModelId = useSetPlacingFurniture();
10+
useHotkeys('esc', () => {
11+
setSelectedModelId(null);
12+
});
713

814
return (
915
<>

app/src/components/desktop/furniture/DesktopFurnitureCollection.tsx

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { FurnitureItem } from '@/services/publicApi/furnitureHooks';
2-
import { useSetSelectedModelId } from '@/stores/editorStore';
2+
import { useSetPlacingFurniture } from '@/stores/roomStore/hooks/editing';
33
import { PrefixedId } from '@alef/common';
44
import { Button, Card, CardGrid, Icon, ScrollArea } from '@alef/sys';
5-
import { useHotkeys } from 'react-hotkeys-hook';
65
import cls from './DesktopFurnitureCollection.module.css';
76

87
export interface DesktopFurnitureCollectionProps {
@@ -13,11 +12,6 @@ export interface DesktopFurnitureCollectionProps {
1312
}
1413

1514
export function DesktopFurnitureCollection({ furniture, hasMore, onLoadMore, onSelect }: DesktopFurnitureCollectionProps) {
16-
const setSelectedModelId = useSetSelectedModelId();
17-
useHotkeys('esc', () => {
18-
setSelectedModelId(null);
19-
});
20-
2115
return (
2216
<ScrollArea>
2317
<CardGrid p="small">
@@ -36,7 +30,7 @@ export function DesktopFurnitureCollection({ furniture, hasMore, onLoadMore, onS
3630
}
3731

3832
function DesktopFurnitureCard({ item, onSelect }: { item: FurnitureItem; onSelect?: (id: PrefixedId<'f'>) => void }) {
39-
const setSelectedModelId = useSetSelectedModelId();
33+
const setSelectedModelId = useSetPlacingFurniture();
4034

4135
const add = () => {
4236
setSelectedModelId(item.id);

app/src/components/desktop/furniture/DesktopFurnitureEditor.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { useEditorStore } from '@/stores/editorStore';
21
import { useDeleteFurniturePlacement, useFurniturePlacement, useFurnitureQuickSwap } from '@/stores/roomStore';
3-
import { isPrefixedId, RoomFurniturePlacement } from '@alef/common';
2+
import { useSelectedFurniturePlacementId } from '@/stores/roomStore/hooks/editing';
3+
import { RoomFurniturePlacement } from '@alef/common';
44
import { Box, Button, Heading, Icon, Text } from '@alef/sys';
55
import { Suspense } from 'react';
66
import { FurnitureName } from './FurnitureName';
77

88
export function DesktopFurnitureEditor() {
9-
const selectedId = useEditorStore((s) => (s.selectedId && isPrefixedId(s.selectedId, 'fp') ? s.selectedId : null));
9+
const selectedId = useSelectedFurniturePlacementId();
1010
const placement = useFurniturePlacement(selectedId || 'fp-none');
1111

1212
if (!placement) {
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.root {
2+
flex-shrink: 0;
3+
}
4+
@media (min-width: 768px) {
5+
.root {
6+
display: none;
7+
}
8+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { usePlacingFurnitureId } from '@/stores/roomStore/hooks/editing';
2+
import { Box, Frame, Heading, Icon, Text } from '@alef/sys';
3+
import { useIsHeadsetConnected } from '../presence/hooks';
4+
import cls from './DesktopFurnitureMobileInstructions.module.css';
5+
6+
export interface DesktopFurnitureMobileInstructionsProps {}
7+
8+
export function DesktopFurnitureMobileInstructions({}: DesktopFurnitureMobileInstructionsProps) {
9+
const isHeadsetConnected = useIsHeadsetConnected();
10+
const placingFurniture = !!usePlacingFurnitureId();
11+
12+
if (!isHeadsetConnected || !placingFurniture) {
13+
// advice is not relevant...
14+
return null;
15+
}
16+
17+
return (
18+
<Frame p="small" stacked gapped color="primary" className={cls.root}>
19+
<Box gapped>
20+
<Icon name="glasses" />
21+
<Heading level={4}>Continue in headset</Heading>
22+
</Box>
23+
<Text>Use your controller or hand to point and place this furniture</Text>
24+
</Frame>
25+
);
26+
}

app/src/components/desktop/furniture/DesktopPlacedFurnitureList.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useFurnitureDetails } from '@/services/publicApi/furnitureHooks';
2-
import { useIsSelected, useSelect } from '@/stores/editorStore';
32
import { useFurniturePlacement, useFurniturePlacementIds } from '@/stores/roomStore';
3+
import { useIsSelected, useSelect } from '@/stores/roomStore/hooks/editing';
44
import { PrefixedId } from '@alef/common';
55
import { Box, BoxProps, Button, Icon, ScrollArea } from '@alef/sys';
66
import { Suspense } from 'react';

app/src/components/desktop/lighting/DesktopLightEditor.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { useEditorStore } from '@/stores/editorStore';
21
import { useDeleteLightPlacement, useLightPlacement } from '@/stores/roomStore';
3-
import { isPrefixedId, RoomLightPlacement } from '@alef/common';
2+
import { useSelectedLightPlacementId } from '@/stores/roomStore/hooks/editing';
3+
import { RoomLightPlacement } from '@alef/common';
44
import { Box, Button, Heading, Icon } from '@alef/sys';
55

66
export function DesktopLightEditor() {
7-
const selectedId = useEditorStore((s) => (s.selectedId && isPrefixedId(s.selectedId, 'lp') ? s.selectedId : null));
7+
const selectedId = useSelectedLightPlacementId();
88
const placement = useLightPlacement(selectedId || 'lp-none');
99

1010
if (!placement) {

0 commit comments

Comments
 (0)