@dxos/shell 0.4.10-main.b9b4602 → 0.4.10-main.c2d5db5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle/shell.mjs +81827 -10
- package/dist/bundle/shell.mjs.map +1 -1
- package/dist/bundle/shell.umd.js +89 -152
- package/dist/bundle/shell.umd.js.map +1 -1
- package/dist/bundle/style.css +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/react.mjs +792 -831
- package/dist/lib/browser/react.mjs.map +4 -4
- package/dist/lib/browser/testing/index.mjs +2 -4
- package/dist/lib/browser/testing/index.mjs.map +2 -2
- package/dist/types/src/components/EmojiPicker.d.ts +2 -9
- package/dist/types/src/components/EmojiPicker.d.ts.map +1 -1
- package/dist/types/src/components/EmojiPicker.js +3 -57
- package/dist/types/src/components/EmojiPicker.js.map +1 -1
- package/dist/types/src/components/HuePicker.d.ts +2 -9
- package/dist/types/src/components/HuePicker.d.ts.map +1 -1
- package/dist/types/src/components/HuePicker.js +6 -75
- package/dist/types/src/components/HuePicker.js.map +1 -1
- package/dist/types/src/components/IdentityList/DeviceList.d.ts +7 -0
- package/dist/types/src/components/IdentityList/DeviceList.d.ts.map +1 -0
- package/dist/types/src/components/IdentityList/DeviceList.js +34 -0
- package/dist/types/src/components/IdentityList/DeviceList.js.map +1 -0
- package/dist/types/src/components/IdentityList/index.d.ts +1 -0
- package/dist/types/src/components/IdentityList/index.d.ts.map +1 -1
- package/dist/types/src/components/IdentityList/index.js +1 -0
- package/dist/types/src/components/IdentityList/index.js.map +1 -1
- package/dist/types/src/components/Panel/Action.d.ts +2 -2
- package/dist/types/src/components/Viewport/Viewport.d.ts +1 -1
- package/dist/types/src/components/index.d.ts +0 -2
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/index.js +0 -2
- package/dist/types/src/components/index.js.map +1 -1
- package/dist/types/src/composites/Shell/Shell.d.ts.map +1 -1
- package/dist/types/src/composites/Shell/Shell.js +5 -1
- package/dist/types/src/composites/Shell/Shell.js.map +1 -1
- package/dist/types/src/panels/IdentityPanel/IdentityPanel.d.ts.map +1 -1
- package/dist/types/src/panels/IdentityPanel/IdentityPanel.js +40 -80
- package/dist/types/src/panels/IdentityPanel/IdentityPanel.js.map +1 -1
- package/dist/types/src/panels/IdentityPanel/IdentityPanel.stories.d.ts +3 -0
- package/dist/types/src/panels/IdentityPanel/IdentityPanel.stories.d.ts.map +1 -1
- package/dist/types/src/panels/IdentityPanel/IdentityPanel.stories.js +43 -1
- package/dist/types/src/panels/IdentityPanel/IdentityPanel.stories.js.map +1 -1
- package/dist/types/src/panels/IdentityPanel/IdentityPanelProps.d.ts +11 -8
- package/dist/types/src/panels/IdentityPanel/IdentityPanelProps.d.ts.map +1 -1
- package/dist/types/src/panels/IdentityPanel/identityMachine.d.ts +1 -1
- package/dist/types/src/panels/IdentityPanel/identityMachine.d.ts.map +1 -1
- package/dist/types/src/panels/IdentityPanel/identityMachine.js +24 -0
- package/dist/types/src/panels/IdentityPanel/identityMachine.js.map +1 -1
- package/dist/types/src/panels/IdentityPanel/steps/AgentForm.d.ts +3 -0
- package/dist/types/src/panels/IdentityPanel/steps/AgentForm.d.ts.map +1 -0
- package/dist/types/src/panels/IdentityPanel/steps/AgentForm.js +26 -0
- package/dist/types/src/panels/IdentityPanel/steps/AgentForm.js.map +1 -0
- package/dist/types/src/panels/IdentityPanel/steps/DeviceManager.d.ts +10 -0
- package/dist/types/src/panels/IdentityPanel/steps/DeviceManager.d.ts.map +1 -0
- package/dist/types/src/panels/IdentityPanel/steps/DeviceManager.js +23 -0
- package/dist/types/src/panels/IdentityPanel/steps/DeviceManager.js.map +1 -0
- package/dist/types/src/panels/IdentityPanel/steps/IdentityActionChooser.d.ts +4 -4
- package/dist/types/src/panels/IdentityPanel/steps/IdentityActionChooser.d.ts.map +1 -1
- package/dist/types/src/panels/IdentityPanel/steps/IdentityActionChooser.js +43 -9
- package/dist/types/src/panels/IdentityPanel/steps/IdentityActionChooser.js.map +1 -1
- package/dist/types/src/panels/IdentityPanel/steps/ProfileForm.d.ts +1 -1
- package/dist/types/src/panels/IdentityPanel/steps/ProfileForm.d.ts.map +1 -1
- package/dist/types/src/panels/IdentityPanel/steps/ProfileForm.js +2 -2
- package/dist/types/src/panels/IdentityPanel/steps/ProfileForm.js.map +1 -1
- package/dist/types/src/panels/IdentityPanel/steps/index.d.ts +2 -0
- package/dist/types/src/panels/IdentityPanel/steps/index.d.ts.map +1 -1
- package/dist/types/src/panels/IdentityPanel/steps/index.js +2 -0
- package/dist/types/src/panels/IdentityPanel/steps/index.js.map +1 -1
- package/dist/types/src/testing/shell-manager.d.ts.map +1 -1
- package/dist/types/src/testing/shell-manager.js +2 -4
- package/dist/types/src/testing/shell-manager.js.map +1 -1
- package/dist/types/src/translations/locales/en-US.d.ts +2 -12
- package/dist/types/src/translations/locales/en-US.d.ts.map +1 -1
- package/dist/types/src/translations/locales/en-US.js +2 -12
- package/dist/types/src/translations/locales/en-US.js.map +1 -1
- package/package.json +19 -19
- package/src/components/EmojiPicker.tsx +4 -101
- package/src/components/HuePicker.tsx +5 -99
- package/src/components/IdentityList/DeviceList.tsx +49 -0
- package/src/components/IdentityList/index.ts +1 -0
- package/src/components/index.ts +0 -2
- package/src/composites/Shell/Shell.tsx +7 -1
- package/src/panels/IdentityPanel/IdentityPanel.stories.tsx +63 -0
- package/src/panels/IdentityPanel/IdentityPanel.tsx +34 -123
- package/src/panels/IdentityPanel/IdentityPanelProps.ts +14 -10
- package/src/panels/IdentityPanel/identityMachine.ts +31 -1
- package/src/panels/IdentityPanel/steps/AgentForm.tsx +61 -0
- package/src/panels/IdentityPanel/steps/DeviceManager.tsx +41 -0
- package/src/panels/IdentityPanel/steps/IdentityActionChooser.tsx +76 -23
- package/src/panels/IdentityPanel/steps/ProfileForm.tsx +4 -12
- package/src/panels/IdentityPanel/steps/index.ts +2 -0
- package/src/testing/shell-manager.ts +2 -4
- package/src/translations/locales/en-US.ts +2 -13
- package/dist/bundle/index-D-K0uxrm.mjs +0 -94
- package/dist/bundle/index-D-K0uxrm.mjs.map +0 -1
- package/dist/bundle/index-DRtLjStG.mjs +0 -96550
- package/dist/bundle/index-DRtLjStG.mjs.map +0 -1
- package/dist/types/src/components/AgentConfig.d.ts +0 -3
- package/dist/types/src/components/AgentConfig.d.ts.map +0 -1
- package/dist/types/src/components/AgentConfig.js +0 -37
- package/dist/types/src/components/AgentConfig.js.map +0 -1
- package/dist/types/src/components/DeviceList/DeviceList.d.ts +0 -3
- package/dist/types/src/components/DeviceList/DeviceList.d.ts.map +0 -1
- package/dist/types/src/components/DeviceList/DeviceList.js +0 -29
- package/dist/types/src/components/DeviceList/DeviceList.js.map +0 -1
- package/dist/types/src/components/DeviceList/DeviceListItem.d.ts +0 -6
- package/dist/types/src/components/DeviceList/DeviceListItem.d.ts.map +0 -1
- package/dist/types/src/components/DeviceList/DeviceListItem.js +0 -72
- package/dist/types/src/components/DeviceList/DeviceListItem.js.map +0 -1
- package/dist/types/src/components/DeviceList/DeviceListProps.d.ts +0 -27
- package/dist/types/src/components/DeviceList/DeviceListProps.d.ts.map +0 -1
- package/dist/types/src/components/DeviceList/DeviceListProps.js +0 -6
- package/dist/types/src/components/DeviceList/DeviceListProps.js.map +0 -1
- package/dist/types/src/components/DeviceList/index.d.ts +0 -3
- package/dist/types/src/components/DeviceList/index.d.ts.map +0 -1
- package/dist/types/src/components/DeviceList/index.js +0 -22
- package/dist/types/src/components/DeviceList/index.js.map +0 -1
- package/src/components/AgentConfig.tsx +0 -66
- package/src/components/DeviceList/DeviceList.tsx +0 -54
- package/src/components/DeviceList/DeviceListItem.tsx +0 -120
- package/src/components/DeviceList/DeviceListProps.ts +0 -33
- package/src/components/DeviceList/index.ts +0 -6
|
@@ -1,21 +1,12 @@
|
|
|
1
1
|
//
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
|
-
import { ArrowCounterClockwise, CaretDown, Check
|
|
4
|
+
import { ArrowCounterClockwise, CaretDown, Check } from '@phosphor-icons/react';
|
|
5
5
|
import { useControllableState } from '@radix-ui/react-use-controllable-state';
|
|
6
|
-
import React, { type Dispatch, type SetStateAction
|
|
6
|
+
import React, { type Dispatch, type SetStateAction } from 'react';
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
useTranslation,
|
|
11
|
-
Tooltip,
|
|
12
|
-
DropdownMenu,
|
|
13
|
-
useThemeContext,
|
|
14
|
-
type ButtonProps,
|
|
15
|
-
Toolbar,
|
|
16
|
-
type ThemedClassName,
|
|
17
|
-
} from '@dxos/react-ui';
|
|
18
|
-
import { getSize, hueTokenThemes, mx } from '@dxos/react-ui-theme';
|
|
8
|
+
import { Button, useTranslation, Tooltip, DropdownMenu, useThemeContext, type ButtonProps } from '@dxos/react-ui';
|
|
9
|
+
import { getSize, hueTokenThemes } from '@dxos/react-ui-theme';
|
|
19
10
|
|
|
20
11
|
const HuePreview = ({ hue }: { hue: string }) => {
|
|
21
12
|
const { tx } = useThemeContext();
|
|
@@ -34,92 +25,7 @@ export type HuePickerProps = {
|
|
|
34
25
|
onClickClear?: ButtonProps['onClick'];
|
|
35
26
|
};
|
|
36
27
|
|
|
37
|
-
|
|
38
|
-
* A toolbar button for picking hue. Use only in `role=toolbar` elements. Unable to unset the value.
|
|
39
|
-
*/
|
|
40
|
-
export const HuePickerToolbarButton = ({
|
|
41
|
-
disabled,
|
|
42
|
-
hue,
|
|
43
|
-
onChangeHue,
|
|
44
|
-
classNames,
|
|
45
|
-
defaultHue,
|
|
46
|
-
}: ThemedClassName<Omit<HuePickerProps, 'onClickClear'>>) => {
|
|
47
|
-
const { t } = useTranslation('os');
|
|
48
|
-
|
|
49
|
-
const [hueValue, setHueValue] = useControllableState<string>({
|
|
50
|
-
prop: hue,
|
|
51
|
-
onChange: onChangeHue,
|
|
52
|
-
defaultProp: defaultHue,
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
const [huePickerOpen, setHuePickerOpen] = useState<boolean>(false);
|
|
56
|
-
|
|
57
|
-
const suppressNextTooltip = useRef<boolean>(false);
|
|
58
|
-
const [triggerTooltipOpen, setTriggerTooltipOpen] = useState(false);
|
|
59
|
-
|
|
60
|
-
return (
|
|
61
|
-
<Tooltip.Root
|
|
62
|
-
open={triggerTooltipOpen}
|
|
63
|
-
onOpenChange={(nextOpen) => {
|
|
64
|
-
if (suppressNextTooltip.current) {
|
|
65
|
-
setTriggerTooltipOpen(false);
|
|
66
|
-
suppressNextTooltip.current = false;
|
|
67
|
-
} else {
|
|
68
|
-
setTriggerTooltipOpen(nextOpen);
|
|
69
|
-
}
|
|
70
|
-
}}
|
|
71
|
-
>
|
|
72
|
-
<DropdownMenu.Root
|
|
73
|
-
modal={false}
|
|
74
|
-
open={huePickerOpen}
|
|
75
|
-
onOpenChange={(nextOpen) => {
|
|
76
|
-
setHuePickerOpen(nextOpen);
|
|
77
|
-
suppressNextTooltip.current = true;
|
|
78
|
-
}}
|
|
79
|
-
>
|
|
80
|
-
<Tooltip.Trigger asChild>
|
|
81
|
-
<DropdownMenu.Trigger asChild>
|
|
82
|
-
<Toolbar.Button classNames={mx('gap-2 plb-1', classNames)} disabled={disabled}>
|
|
83
|
-
<span className='sr-only'>{t('select hue label')}</span>
|
|
84
|
-
<Palette className={getSize(5)} />
|
|
85
|
-
</Toolbar.Button>
|
|
86
|
-
</DropdownMenu.Trigger>
|
|
87
|
-
</Tooltip.Trigger>
|
|
88
|
-
<Tooltip.Portal>
|
|
89
|
-
<Tooltip.Content side='bottom' classNames='z-50'>
|
|
90
|
-
{t('select hue label')}
|
|
91
|
-
<Tooltip.Arrow />
|
|
92
|
-
</Tooltip.Content>
|
|
93
|
-
</Tooltip.Portal>
|
|
94
|
-
<DropdownMenu.Content side='bottom'>
|
|
95
|
-
<DropdownMenu.Viewport>
|
|
96
|
-
{Object.keys(hueTokenThemes).map((hue) => {
|
|
97
|
-
return (
|
|
98
|
-
<DropdownMenu.CheckboxItem
|
|
99
|
-
key={hue}
|
|
100
|
-
checked={hue === hueValue}
|
|
101
|
-
onCheckedChange={() => setHueValue(hue)}
|
|
102
|
-
>
|
|
103
|
-
<HuePreview hue={hue} />
|
|
104
|
-
<span className='grow'>{t(`${hue} label`)}</span>
|
|
105
|
-
<DropdownMenu.ItemIndicator>
|
|
106
|
-
<Check />
|
|
107
|
-
</DropdownMenu.ItemIndicator>
|
|
108
|
-
</DropdownMenu.CheckboxItem>
|
|
109
|
-
);
|
|
110
|
-
})}
|
|
111
|
-
</DropdownMenu.Viewport>
|
|
112
|
-
<DropdownMenu.Arrow />
|
|
113
|
-
</DropdownMenu.Content>
|
|
114
|
-
</DropdownMenu.Root>
|
|
115
|
-
</Tooltip.Root>
|
|
116
|
-
);
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* A button for picking hue alongside a button for unsetting it.
|
|
121
|
-
*/
|
|
122
|
-
export const HuePickerBlock = ({ disabled, hue, onChangeHue, defaultHue, onClickClear }: HuePickerProps) => {
|
|
28
|
+
export const HuePicker = ({ disabled, hue, onChangeHue, defaultHue, onClickClear }: HuePickerProps) => {
|
|
123
29
|
const { t } = useTranslation('os');
|
|
124
30
|
|
|
125
31
|
const [hueValue, setHueValue] = useControllableState<string>({
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { generateName } from '@dxos/display-name';
|
|
8
|
+
import { type Device, DeviceType } from '@dxos/react-client/halo';
|
|
9
|
+
import { List, useTranslation } from '@dxos/react-ui';
|
|
10
|
+
import { descriptionText, mx } from '@dxos/react-ui-theme';
|
|
11
|
+
|
|
12
|
+
import { IdentityListItem } from './IdentityListItem';
|
|
13
|
+
|
|
14
|
+
export interface DeviceListProps {
|
|
15
|
+
devices: Device[];
|
|
16
|
+
onSelect?: (device: Device) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const DeviceList = ({ devices, onSelect }: DeviceListProps) => {
|
|
20
|
+
const { t } = useTranslation('os');
|
|
21
|
+
return devices.length > 0 ? (
|
|
22
|
+
<List classNames='flex flex-col gap-2'>
|
|
23
|
+
{devices.map((device) => {
|
|
24
|
+
// TODO(wittjosiah): Render this better. Consider communicating these as apps rather than devices.
|
|
25
|
+
const deviceType = device.profile?.type || DeviceType.UNKNOWN;
|
|
26
|
+
const displayName = device.profile?.label
|
|
27
|
+
? `${device.profile.label} (${DeviceType[deviceType]})`
|
|
28
|
+
: generateName(device.deviceKey.toHex());
|
|
29
|
+
|
|
30
|
+
const identity = {
|
|
31
|
+
identityKey: device.deviceKey,
|
|
32
|
+
profile: { displayName },
|
|
33
|
+
};
|
|
34
|
+
return (
|
|
35
|
+
<IdentityListItem
|
|
36
|
+
key={device.deviceKey.toHex()}
|
|
37
|
+
identity={identity}
|
|
38
|
+
presence={device.presence}
|
|
39
|
+
onClick={onSelect && (() => onSelect(device))}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
})}
|
|
43
|
+
</List>
|
|
44
|
+
) : (
|
|
45
|
+
<div role='none' className='grow flex items-center p-2'>
|
|
46
|
+
<p className={mx(descriptionText, 'text-center is-full')}>{t('empty device list message')}</p>
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
};
|
package/src/components/index.ts
CHANGED
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
export * from './AgentConfig';
|
|
6
5
|
export * from './Clipboard';
|
|
7
6
|
export * from './CompoundButton';
|
|
8
7
|
export * from './EmojiPicker';
|
|
9
8
|
export * from './HuePicker';
|
|
10
9
|
export * from './IdentityList';
|
|
11
|
-
export * from './DeviceList';
|
|
12
10
|
export * from './InvitationList';
|
|
13
11
|
export * from './Panel';
|
|
14
12
|
export * from './Viewport';
|
|
@@ -81,7 +81,13 @@ export const Shell = ({ runtime, origin }: { runtime: ShellRuntime; origin: stri
|
|
|
81
81
|
await runtime.setAppContext({ display: ShellDisplay.NONE });
|
|
82
82
|
runtime.setLayout({ layout: ShellLayout.DEFAULT });
|
|
83
83
|
}}
|
|
84
|
-
initialDisposition={
|
|
84
|
+
initialDisposition={
|
|
85
|
+
layout === ShellLayout.SHARE_IDENTITY
|
|
86
|
+
? 'manage-device-invitation'
|
|
87
|
+
: layout === ShellLayout.EDIT_PROFILE
|
|
88
|
+
? 'manage-profile'
|
|
89
|
+
: 'default'
|
|
90
|
+
}
|
|
85
91
|
/>
|
|
86
92
|
);
|
|
87
93
|
|
|
@@ -8,6 +8,7 @@ import React from 'react';
|
|
|
8
8
|
|
|
9
9
|
import { faker } from '@dxos/random';
|
|
10
10
|
import { PublicKey } from '@dxos/react-client';
|
|
11
|
+
import { Device, DeviceKind } from '@dxos/react-client/halo';
|
|
11
12
|
import { Invitation } from '@dxos/react-client/invitations';
|
|
12
13
|
import { withTheme } from '@dxos/storybook-utils';
|
|
13
14
|
|
|
@@ -52,6 +53,58 @@ export const IdentityActionChooser = () => {
|
|
|
52
53
|
);
|
|
53
54
|
};
|
|
54
55
|
|
|
56
|
+
export const UpdateProfileForm = () => {
|
|
57
|
+
return (
|
|
58
|
+
<StorybookDialog inOverlayLayout>
|
|
59
|
+
<IdentityPanelImpl
|
|
60
|
+
{...noOpProps}
|
|
61
|
+
activeView='update profile form'
|
|
62
|
+
IdentityActionChooser={IdentityActionChooserImpl}
|
|
63
|
+
/>
|
|
64
|
+
</StorybookDialog>
|
|
65
|
+
);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const DeviceManager = () => {
|
|
69
|
+
return (
|
|
70
|
+
<StorybookDialog inOverlayLayout>
|
|
71
|
+
<IdentityPanelImpl
|
|
72
|
+
{...{
|
|
73
|
+
...noOpProps,
|
|
74
|
+
// TODO(nf): update
|
|
75
|
+
devices: [
|
|
76
|
+
{
|
|
77
|
+
deviceKey: PublicKey.random(),
|
|
78
|
+
kind: DeviceKind.CURRENT,
|
|
79
|
+
presence: Device.PresenceState.ONLINE,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
deviceKey: PublicKey.random(),
|
|
83
|
+
kind: DeviceKind.TRUSTED,
|
|
84
|
+
presence: Device.PresenceState.ONLINE,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
deviceKey: PublicKey.random(),
|
|
88
|
+
kind: DeviceKind.TRUSTED,
|
|
89
|
+
presence: Device.PresenceState.OFFLINE,
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
}}
|
|
93
|
+
activeView='device manager'
|
|
94
|
+
IdentityActionChooser={IdentityActionChooserImpl}
|
|
95
|
+
/>
|
|
96
|
+
</StorybookDialog>
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const DeviceManagerEmpty = () => {
|
|
101
|
+
return (
|
|
102
|
+
<StorybookDialog inOverlayLayout>
|
|
103
|
+
<IdentityPanelImpl {...noOpProps} activeView='device manager' IdentityActionChooser={IdentityActionChooserImpl} />
|
|
104
|
+
</StorybookDialog>
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
|
|
55
108
|
const DeviceInvitationManagerWithState = (extraProps: InvitationManagerProps) => (
|
|
56
109
|
<StorybookDialog inOverlayLayout>
|
|
57
110
|
<IdentityPanelImpl
|
|
@@ -103,3 +156,13 @@ export const DeviceInvitationManagerCancelled = () =>
|
|
|
103
156
|
DeviceInvitationManagerWithState({
|
|
104
157
|
status: Invitation.State.CANCELLED,
|
|
105
158
|
});
|
|
159
|
+
|
|
160
|
+
// export const IdentityActionChooser = {
|
|
161
|
+
// decorators: [ClientDecorator()],
|
|
162
|
+
// args: { activeView: 'identity action chooser' },
|
|
163
|
+
// };
|
|
164
|
+
|
|
165
|
+
// export const DeviceManager = {
|
|
166
|
+
// decorators: [ClientDecorator()],
|
|
167
|
+
// args: { activeView: 'device manager' },
|
|
168
|
+
// };
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
//
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
|
-
import {
|
|
5
|
-
import React, { useEffect, useMemo, useState } from 'react';
|
|
4
|
+
import React, { useEffect, useMemo } from 'react';
|
|
6
5
|
|
|
7
6
|
import { generateName } from '@dxos/display-name';
|
|
8
7
|
import { log } from '@dxos/log';
|
|
@@ -10,10 +9,8 @@ import { useClient } from '@dxos/react-client';
|
|
|
10
9
|
import { type Identity, useIdentity, useDevices, useHaloInvitations } from '@dxos/react-client/halo';
|
|
11
10
|
import { useInvitationStatus } from '@dxos/react-client/invitations';
|
|
12
11
|
import type { CancellableInvitationObservable } from '@dxos/react-client/invitations';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { getSize } from '@dxos/react-ui-theme';
|
|
16
|
-
import { hexToEmoji, hexToHue, keyToFallback } from '@dxos/util';
|
|
12
|
+
import { Avatar, DensityProvider, useId, useTranslation } from '@dxos/react-ui';
|
|
13
|
+
import { keyToFallback } from '@dxos/util';
|
|
17
14
|
|
|
18
15
|
import {
|
|
19
16
|
type IdentityPanelHeadingProps,
|
|
@@ -21,122 +18,24 @@ import {
|
|
|
21
18
|
type IdentityPanelProps,
|
|
22
19
|
} from './IdentityPanelProps';
|
|
23
20
|
import { useIdentityMachine } from './identityMachine';
|
|
24
|
-
import { IdentityActionChooser } from './steps';
|
|
21
|
+
import { AgentForm, DeviceManager, IdentityActionChooser, ProfileForm } from './steps';
|
|
25
22
|
import { useAgentHandlers } from './useAgentHandlers';
|
|
26
|
-
import {
|
|
27
|
-
Viewport,
|
|
28
|
-
Heading,
|
|
29
|
-
CloseButton,
|
|
30
|
-
EmojiPickerToolbarButton,
|
|
31
|
-
useClipboardContext,
|
|
32
|
-
HuePickerToolbarButton,
|
|
33
|
-
} from '../../components';
|
|
23
|
+
import { Viewport, Heading, CloseButton } from '../../components';
|
|
34
24
|
import { ConfirmReset, InvitationManager } from '../../steps';
|
|
35
25
|
|
|
36
26
|
const viewStyles = 'pbs-1 pbe-3 pli-3';
|
|
37
27
|
|
|
38
|
-
|
|
39
|
-
const getHueValue = (identity?: Identity) =>
|
|
40
|
-
identity?.profile?.data?.hue || hexToHue(identity?.identityKey.toHex() ?? '0');
|
|
41
|
-
const getEmojiValue = (identity?: Identity) =>
|
|
42
|
-
identity?.profile?.data?.emoji || hexToEmoji(identity?.identityKey.toHex() ?? '0');
|
|
43
|
-
|
|
44
|
-
const IdentityHeading = ({
|
|
45
|
-
titleId,
|
|
46
|
-
title,
|
|
47
|
-
identity,
|
|
48
|
-
onDone,
|
|
49
|
-
onUpdateProfile,
|
|
50
|
-
connectionState,
|
|
51
|
-
onChangeConnectionState,
|
|
52
|
-
}: IdentityPanelHeadingProps) => {
|
|
28
|
+
const IdentityHeading = ({ titleId, title, identity, onDone }: IdentityPanelHeadingProps) => {
|
|
53
29
|
const fallbackValue = keyToFallback(identity.identityKey);
|
|
54
|
-
const { t } = useTranslation('os');
|
|
55
|
-
const [displayName, setDisplayName] = useState(identity.profile?.displayName ?? '');
|
|
56
|
-
const [emoji, setEmoji] = useState<string>(getEmojiValue(identity));
|
|
57
|
-
const [hue, setHue] = useState<string>(getHueValue(identity));
|
|
58
|
-
const { textValue, setTextValue } = useClipboardContext();
|
|
59
|
-
const identityHex = identity.identityKey.toHex();
|
|
60
|
-
const copied = textValue === identityHex;
|
|
61
|
-
|
|
62
|
-
const handleUpdateProfile = async () => {
|
|
63
|
-
const nextProfile = {
|
|
64
|
-
...(displayName && { displayName }),
|
|
65
|
-
...((emoji || hue) && { data: { ...(emoji && { emoji }), ...(hue && { hue }) } }),
|
|
66
|
-
};
|
|
67
|
-
return onUpdateProfile?.(nextProfile);
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
useEffect(() => {
|
|
71
|
-
void handleUpdateProfile();
|
|
72
|
-
}, [emoji, hue]);
|
|
73
|
-
|
|
74
|
-
const isConnected = connectionState === ConnectionState.ONLINE;
|
|
75
|
-
|
|
76
30
|
return (
|
|
77
31
|
<Heading titleId={titleId} title={title} corner={<CloseButton onDone={onDone} />}>
|
|
78
|
-
<Avatar.Root size={
|
|
79
|
-
<
|
|
80
|
-
<
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
classNames='bs-[--rail-action] is-[--rail-action] justify-self-end'
|
|
84
|
-
onClick={() =>
|
|
85
|
-
onChangeConnectionState?.(isConnected ? ConnectionState.OFFLINE : ConnectionState.ONLINE)
|
|
86
|
-
}
|
|
87
|
-
>
|
|
88
|
-
<span className='sr-only'>{t(isConnected ? 'disconnect label' : 'connect label')}</span>
|
|
89
|
-
{isConnected ? <PlugsConnected className={getSize(5)} /> : <Plugs className={getSize(5)} />}
|
|
90
|
-
</Toolbar.Button>
|
|
91
|
-
</Tooltip.Trigger>
|
|
92
|
-
<Tooltip.Portal>
|
|
93
|
-
<Tooltip.Content side='bottom' classNames='z-50'>
|
|
94
|
-
{t(isConnected ? 'disconnect label' : 'connect label')}
|
|
95
|
-
<Tooltip.Arrow />
|
|
96
|
-
</Tooltip.Content>
|
|
97
|
-
</Tooltip.Portal>
|
|
98
|
-
</Tooltip.Root>
|
|
99
|
-
<EmojiPickerToolbarButton emoji={emoji} onChangeEmoji={setEmoji} classNames='bs-[--rail-action]' />
|
|
100
|
-
<Avatar.Frame classNames='relative z-[2] -mli-4 chromatic-ignore'>
|
|
101
|
-
<Avatar.Fallback text={emoji || fallbackValue.emoji} />
|
|
102
|
-
</Avatar.Frame>
|
|
103
|
-
<HuePickerToolbarButton hue={hue} onChangeHue={setHue} classNames='bs-[--rail-action]' />
|
|
104
|
-
<Tooltip.Root>
|
|
105
|
-
<Tooltip.Trigger asChild>
|
|
106
|
-
<Toolbar.Button
|
|
107
|
-
classNames='bs-[--rail-action] justify-self-start'
|
|
108
|
-
data-testid='update-profile-form-copy-key'
|
|
109
|
-
onClick={() => setTextValue(identityHex)}
|
|
110
|
-
>
|
|
111
|
-
<span className='sr-only'>{t(copied ? 'copy success label' : 'copy self public key label')}</span>
|
|
112
|
-
<CopySimple className={getSize(5)} />
|
|
113
|
-
</Toolbar.Button>
|
|
114
|
-
</Tooltip.Trigger>
|
|
115
|
-
<Tooltip.Portal>
|
|
116
|
-
<Tooltip.Content side='bottom' classNames='z-50'>
|
|
117
|
-
{t(copied ? 'copy success label' : 'copy self public key label')}
|
|
118
|
-
<Tooltip.Arrow />
|
|
119
|
-
</Tooltip.Content>
|
|
120
|
-
</Tooltip.Portal>
|
|
121
|
-
</Tooltip.Root>
|
|
122
|
-
</Toolbar.Root>
|
|
123
|
-
<Avatar.Label classNames='sr-only' data-testid='identityHeading.displayName'>
|
|
32
|
+
<Avatar.Root size={12} variant='circle' status='active' hue={identity.profile?.data?.hue || fallbackValue.hue}>
|
|
33
|
+
<Avatar.Frame classNames='block mbs-4 mbe-2 mli-auto chromatic-ignore'>
|
|
34
|
+
<Avatar.Fallback text={identity.profile?.data?.emoji || fallbackValue.emoji} />
|
|
35
|
+
</Avatar.Frame>
|
|
36
|
+
<Avatar.Label classNames='block text-center font-light text-xl' data-testid='identityHeading.displayName'>
|
|
124
37
|
{identity.profile?.displayName ?? generateName(identity.identityKey.toHex())}
|
|
125
38
|
</Avatar.Label>
|
|
126
|
-
<Input.Root>
|
|
127
|
-
<Input.Label srOnly>{t('display name input label')}</Input.Label>
|
|
128
|
-
<Input.TextInput
|
|
129
|
-
variant='subdued'
|
|
130
|
-
data-testid='display-name-input'
|
|
131
|
-
placeholder={t('display name input placeholder')}
|
|
132
|
-
classNames='mbs-2 text-center font-light text-xl'
|
|
133
|
-
value={displayName}
|
|
134
|
-
onChange={({ target: { value } }) => {
|
|
135
|
-
setDisplayName(value);
|
|
136
|
-
}}
|
|
137
|
-
onBlur={handleUpdateProfile}
|
|
138
|
-
/>
|
|
139
|
-
</Input.Root>
|
|
140
39
|
</Avatar.Root>
|
|
141
40
|
</Heading>
|
|
142
41
|
);
|
|
@@ -152,14 +51,14 @@ export const IdentityPanelImpl = (props: IdentityPanelImplProps) => {
|
|
|
152
51
|
onJoinNewIdentity,
|
|
153
52
|
IdentityActionChooser: IdentityActionChooserComponent = IdentityActionChooser,
|
|
154
53
|
InvitationManager: InvitationManagerComponent = InvitationManager,
|
|
155
|
-
connectionState,
|
|
156
|
-
onChangeConnectionState,
|
|
157
54
|
onDone,
|
|
158
55
|
...rest
|
|
159
56
|
} = props;
|
|
160
57
|
const { t } = useTranslation('os');
|
|
161
58
|
const title = useMemo(() => {
|
|
162
59
|
switch (activeView) {
|
|
60
|
+
case 'agent manager':
|
|
61
|
+
return 'Manage Agent';
|
|
163
62
|
case 'device invitation manager':
|
|
164
63
|
return t('choose add device label');
|
|
165
64
|
default:
|
|
@@ -171,9 +70,7 @@ export const IdentityPanelImpl = (props: IdentityPanelImplProps) => {
|
|
|
171
70
|
|
|
172
71
|
return (
|
|
173
72
|
<DensityProvider density='fine'>
|
|
174
|
-
<IdentityHeading
|
|
175
|
-
{...{ identity, titleId, title, onDone, onUpdateProfile, connectionState, onChangeConnectionState }}
|
|
176
|
-
/>
|
|
73
|
+
<IdentityHeading {...{ identity, titleId, title, onDone }} />
|
|
177
74
|
<Viewport.Root activeView={activeView}>
|
|
178
75
|
<Viewport.Views>
|
|
179
76
|
<Viewport.View id='identity action chooser' classNames={viewStyles}>
|
|
@@ -186,6 +83,20 @@ export const IdentityPanelImpl = (props: IdentityPanelImplProps) => {
|
|
|
186
83
|
invitationUrl={rest.createInvitationUrl(rest.invitationCode!)}
|
|
187
84
|
/>
|
|
188
85
|
</Viewport.View>
|
|
86
|
+
<Viewport.View id='device manager' classNames={viewStyles}>
|
|
87
|
+
<DeviceManager active={activeView === 'device manager'} {...rest} />
|
|
88
|
+
</Viewport.View>
|
|
89
|
+
<Viewport.View classNames={viewStyles} id='update profile form'>
|
|
90
|
+
<ProfileForm
|
|
91
|
+
send={rest.send}
|
|
92
|
+
active={activeView === 'update profile form'}
|
|
93
|
+
identity={identity}
|
|
94
|
+
onUpdateProfile={onUpdateProfile}
|
|
95
|
+
/>
|
|
96
|
+
</Viewport.View>
|
|
97
|
+
<Viewport.View classNames={viewStyles} id='agent manager'>
|
|
98
|
+
<AgentForm send={rest.send} active={activeView === 'agent manager'} {...rest} />
|
|
99
|
+
</Viewport.View>
|
|
189
100
|
<Viewport.View classNames={viewStyles} id='confirm join new identity'>
|
|
190
101
|
<ConfirmReset
|
|
191
102
|
active={activeView === 'confirm join new identity'}
|
|
@@ -238,10 +149,6 @@ export const IdentityPanel = ({
|
|
|
238
149
|
context: { initialDisposition },
|
|
239
150
|
});
|
|
240
151
|
|
|
241
|
-
const { swarm: connectionState } = useNetworkStatus();
|
|
242
|
-
|
|
243
|
-
const handleChangeConnectionState = async (nextState: ConnectionState) => client?.mesh.updateConfig(nextState);
|
|
244
|
-
|
|
245
152
|
useEffect(() => {
|
|
246
153
|
const subscription = identityService.subscribe((state) => {
|
|
247
154
|
log('[state]', state);
|
|
@@ -256,10 +163,16 @@ export const IdentityPanel = ({
|
|
|
256
163
|
return 'identity action chooser';
|
|
257
164
|
case identityState.matches('managingDeviceInvitation'):
|
|
258
165
|
return 'device invitation manager';
|
|
166
|
+
case identityState.matches('managingDevices'):
|
|
167
|
+
return 'device manager';
|
|
168
|
+
case [{ managingProfile: 'idle' }, { managingProfile: 'pending' }].some(identityState.matches):
|
|
169
|
+
return 'update profile form';
|
|
259
170
|
case identityState.matches('confirmingResetStorage'):
|
|
260
171
|
return 'confirm reset storage';
|
|
261
172
|
case identityState.matches('confirmingJoinNewIdentity'):
|
|
262
173
|
return 'confirm join new identity';
|
|
174
|
+
case [{ managingAgent: 'idle' }, { managingAgent: 'pending' }].some(identityState.matches):
|
|
175
|
+
return 'agent manager';
|
|
263
176
|
default:
|
|
264
177
|
return 'identity action chooser';
|
|
265
178
|
}
|
|
@@ -279,8 +192,6 @@ export const IdentityPanel = ({
|
|
|
279
192
|
titleId,
|
|
280
193
|
createInvitationUrl,
|
|
281
194
|
onUpdateProfile,
|
|
282
|
-
connectionState,
|
|
283
|
-
onChangeConnectionState: handleChangeConnectionState,
|
|
284
195
|
...agentProps,
|
|
285
196
|
} satisfies IdentityPanelImplProps;
|
|
286
197
|
|
|
@@ -7,17 +7,27 @@ import type { Event, SingleOrArray } from 'xstate';
|
|
|
7
7
|
|
|
8
8
|
import type { Device, Identity } from '@dxos/react-client/halo';
|
|
9
9
|
import type { InvitationStatus } from '@dxos/react-client/invitations';
|
|
10
|
-
import type { ConnectionState } from '@dxos/react-client/mesh';
|
|
11
10
|
|
|
12
11
|
import type { IdentityEvent } from './identityMachine';
|
|
13
|
-
import { type AgentFormProps } from '../../components';
|
|
14
12
|
import type { InvitationManagerProps } from '../../steps';
|
|
15
13
|
|
|
16
|
-
export type IdentityPanelInitialDisposition = 'default' | 'manage-device-invitation';
|
|
14
|
+
export type IdentityPanelInitialDisposition = 'default' | 'manage-device-invitation' | 'manage-profile';
|
|
15
|
+
|
|
16
|
+
export type AgentFormProps = {
|
|
17
|
+
onAgentCreate?: () => Promise<void>;
|
|
18
|
+
onAgentDestroy?: () => Promise<void>;
|
|
19
|
+
agentStatus?: string;
|
|
20
|
+
agentActive?: boolean;
|
|
21
|
+
agentProviderDisabled?: boolean;
|
|
22
|
+
validationMessage?: string;
|
|
23
|
+
};
|
|
17
24
|
|
|
18
25
|
export type IdentityPanelImplProps = {
|
|
19
26
|
titleId: string;
|
|
20
27
|
activeView:
|
|
28
|
+
| 'device manager'
|
|
29
|
+
| 'agent manager'
|
|
30
|
+
| 'update profile form'
|
|
21
31
|
| 'device invitation manager'
|
|
22
32
|
| 'identity action chooser'
|
|
23
33
|
| 'confirm join new identity'
|
|
@@ -25,8 +35,6 @@ export type IdentityPanelImplProps = {
|
|
|
25
35
|
initialDisposition?: IdentityPanelInitialDisposition;
|
|
26
36
|
identity: Identity;
|
|
27
37
|
devices: Device[];
|
|
28
|
-
connectionState?: ConnectionState;
|
|
29
|
-
onChangeConnectionState?: (nextState: ConnectionState) => Promise<void>;
|
|
30
38
|
onUpdateProfile?: (profile: NonNullable<Identity['profile']>) => Promise<void>;
|
|
31
39
|
onResetStorage?: () => Promise<void>;
|
|
32
40
|
onJoinNewIdentity?: () => Promise<void>;
|
|
@@ -42,15 +50,11 @@ export type IdentityPanelImplProps = {
|
|
|
42
50
|
|
|
43
51
|
export type IdentityPanelProps = Partial<Omit<IdentityPanelImplProps, 'send' | 'activeView' | 'identity'>>;
|
|
44
52
|
|
|
45
|
-
export type IdentityPanelHeadingProps = Pick<
|
|
46
|
-
IdentityPanelImplProps,
|
|
47
|
-
'titleId' | 'identity' | 'onDone' | 'onUpdateProfile' | 'connectionState' | 'onChangeConnectionState'
|
|
48
|
-
> & {
|
|
53
|
+
export type IdentityPanelHeadingProps = Pick<IdentityPanelImplProps, 'titleId' | 'identity' | 'onDone'> & {
|
|
49
54
|
title: string;
|
|
50
55
|
};
|
|
51
56
|
|
|
52
57
|
export type IdentityPanelStepProps = Pick<IdentityPanelImplProps, 'send' | 'onDone' | 'doneActionParent'> & {
|
|
53
58
|
active?: boolean;
|
|
54
59
|
agentHostingEnabled?: boolean;
|
|
55
|
-
devices: Device[];
|
|
56
60
|
};
|
|
@@ -21,7 +21,13 @@ type IdentityMachineContext = {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
type IdentityChooseActionEvent = {
|
|
24
|
-
type:
|
|
24
|
+
type:
|
|
25
|
+
| 'chooseDevices'
|
|
26
|
+
| 'chooseAgent'
|
|
27
|
+
| 'chooseProfile'
|
|
28
|
+
| 'chooseResetStorage'
|
|
29
|
+
| 'chooseJoinNewIdentity'
|
|
30
|
+
| 'unchooseAction';
|
|
25
31
|
};
|
|
26
32
|
|
|
27
33
|
type IdentitySelectDeviceInvitationEvent = {
|
|
@@ -61,11 +67,31 @@ const identityMachine = createMachine<IdentityMachineContext, IdentityEvent>(
|
|
|
61
67
|
unknown: {
|
|
62
68
|
always: [
|
|
63
69
|
{ cond: 'initiallyManagingDeviceInvitation', target: 'managingDeviceInvitation', actions: 'log' },
|
|
70
|
+
{ cond: 'initiallyManagingProfile', target: 'managingProfile', actions: 'log' },
|
|
64
71
|
{ target: 'choosingAction', actions: 'log' },
|
|
65
72
|
],
|
|
66
73
|
},
|
|
67
74
|
choosingAction: {},
|
|
68
75
|
managingDeviceInvitation: {},
|
|
76
|
+
managingAgent: {
|
|
77
|
+
initial: 'idle',
|
|
78
|
+
states: {
|
|
79
|
+
idle: {},
|
|
80
|
+
pending: {},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
managingDevices: {},
|
|
84
|
+
managingProfile: {
|
|
85
|
+
initial: 'idle',
|
|
86
|
+
states: {
|
|
87
|
+
idle: {},
|
|
88
|
+
pending: {},
|
|
89
|
+
},
|
|
90
|
+
on: {
|
|
91
|
+
setIdentity: { target: 'choosingAction', actions: ['setIdentity', 'log'] },
|
|
92
|
+
updateProfile: { target: '.pending', actions: 'log' },
|
|
93
|
+
},
|
|
94
|
+
},
|
|
69
95
|
confirmingResetStorage: {},
|
|
70
96
|
confirmingJoinNewIdentity: {},
|
|
71
97
|
},
|
|
@@ -73,14 +99,18 @@ const identityMachine = createMachine<IdentityMachineContext, IdentityEvent>(
|
|
|
73
99
|
unchooseAction: { target: '.choosingAction', actions: ['unsetInvitation', 'log'] },
|
|
74
100
|
deselectInvitation: { target: '.choosingAction', actions: ['unsetInvitation', 'log'] },
|
|
75
101
|
selectInvitation: { target: '.managingDeviceInvitation', actions: ['setInvitation', 'log'] },
|
|
102
|
+
chooseDevices: { target: '.managingDevices', actions: 'log' },
|
|
103
|
+
chooseProfile: { target: '.managingProfile', actions: 'log' },
|
|
76
104
|
chooseJoinNewIdentity: { target: '.confirmingJoinNewIdentity', actions: 'log' },
|
|
77
105
|
chooseResetStorage: { target: '.confirmingResetStorage', actions: 'log' },
|
|
106
|
+
chooseAgent: { target: '.managingAgent', actions: 'log' },
|
|
78
107
|
},
|
|
79
108
|
},
|
|
80
109
|
{
|
|
81
110
|
guards: {
|
|
82
111
|
initiallyManagingDeviceInvitation: ({ initialDisposition }, _event) =>
|
|
83
112
|
initialDisposition === 'manage-device-invitation',
|
|
113
|
+
initiallyManagingProfile: ({ initialDisposition }, _event) => initialDisposition === 'manage-profile',
|
|
84
114
|
},
|
|
85
115
|
actions: {
|
|
86
116
|
setIdentity: assign<IdentityMachineContext, IdentityEvent>({
|