@dxos/shell 0.4.10-main.1f36d57 → 0.4.10-main.2b2626f
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/index-Ba36DtoM.mjs +94 -0
- package/dist/bundle/index-Ba36DtoM.mjs.map +1 -0
- package/dist/bundle/index-CnChDHv2.mjs +96553 -0
- package/dist/bundle/index-CnChDHv2.mjs.map +1 -0
- package/dist/bundle/shell.mjs +10 -81099
- package/dist/bundle/shell.mjs.map +1 -1
- package/dist/bundle/shell.umd.js +156 -88
- 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 +835 -795
- package/dist/lib/browser/react.mjs.map +4 -4
- package/dist/lib/browser/testing/index.mjs +8 -8
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/types/src/components/AgentConfig.d.ts +3 -0
- package/dist/types/src/components/AgentConfig.d.ts.map +1 -0
- package/dist/types/src/components/AgentConfig.js +37 -0
- package/dist/types/src/components/AgentConfig.js.map +1 -0
- package/dist/types/src/components/DeviceList/DeviceList.d.ts +3 -0
- package/dist/types/src/components/DeviceList/DeviceList.d.ts.map +1 -0
- package/dist/types/src/components/DeviceList/DeviceList.js +29 -0
- package/dist/types/src/components/DeviceList/DeviceList.js.map +1 -0
- package/dist/types/src/components/DeviceList/DeviceListItem.d.ts +6 -0
- package/dist/types/src/components/DeviceList/DeviceListItem.d.ts.map +1 -0
- package/dist/types/src/components/DeviceList/DeviceListItem.js +72 -0
- package/dist/types/src/components/DeviceList/DeviceListItem.js.map +1 -0
- package/dist/types/src/components/DeviceList/DeviceListProps.d.ts +27 -0
- package/dist/types/src/components/DeviceList/DeviceListProps.d.ts.map +1 -0
- package/dist/types/src/components/DeviceList/DeviceListProps.js +6 -0
- package/dist/types/src/components/DeviceList/DeviceListProps.js.map +1 -0
- package/dist/types/src/components/DeviceList/index.d.ts +3 -0
- package/dist/types/src/components/DeviceList/index.d.ts.map +1 -0
- package/dist/types/src/components/DeviceList/index.js +22 -0
- package/dist/types/src/components/DeviceList/index.js.map +1 -0
- package/dist/types/src/components/EmojiPicker.d.ts +9 -2
- package/dist/types/src/components/EmojiPicker.d.ts.map +1 -1
- package/dist/types/src/components/EmojiPicker.js +57 -3
- package/dist/types/src/components/EmojiPicker.js.map +1 -1
- package/dist/types/src/components/HuePicker.d.ts +9 -2
- package/dist/types/src/components/HuePicker.d.ts.map +1 -1
- package/dist/types/src/components/HuePicker.js +75 -6
- package/dist/types/src/components/HuePicker.js.map +1 -1
- package/dist/types/src/components/IdentityList/index.d.ts +0 -1
- package/dist/types/src/components/IdentityList/index.d.ts.map +1 -1
- package/dist/types/src/components/IdentityList/index.js +0 -1
- 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 +2 -0
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/index.js +2 -0
- 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 +1 -5
- 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 +80 -40
- package/dist/types/src/panels/IdentityPanel/IdentityPanel.js.map +1 -1
- package/dist/types/src/panels/IdentityPanel/IdentityPanel.stories.d.ts +0 -3
- package/dist/types/src/panels/IdentityPanel/IdentityPanel.stories.d.ts.map +1 -1
- package/dist/types/src/panels/IdentityPanel/IdentityPanel.stories.js +1 -43
- package/dist/types/src/panels/IdentityPanel/IdentityPanel.stories.js.map +1 -1
- package/dist/types/src/panels/IdentityPanel/IdentityPanelProps.d.ts +8 -11
- 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 +0 -24
- package/dist/types/src/panels/IdentityPanel/identityMachine.js.map +1 -1
- 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 +9 -43
- 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 +0 -2
- package/dist/types/src/panels/IdentityPanel/steps/index.d.ts.map +1 -1
- package/dist/types/src/panels/IdentityPanel/steps/index.js +0 -2
- package/dist/types/src/panels/IdentityPanel/steps/index.js.map +1 -1
- package/dist/types/src/steps/ConfirmReset.d.ts.map +1 -1
- package/dist/types/src/steps/ConfirmReset.js +4 -3
- package/dist/types/src/steps/ConfirmReset.js.map +1 -1
- package/dist/types/src/testing/shell-manager.d.ts.map +1 -1
- package/dist/types/src/testing/shell-manager.js +8 -8
- package/dist/types/src/testing/shell-manager.js.map +1 -1
- package/dist/types/src/translations/locales/en-US.d.ts +12 -2
- package/dist/types/src/translations/locales/en-US.d.ts.map +1 -1
- package/dist/types/src/translations/locales/en-US.js +12 -2
- package/dist/types/src/translations/locales/en-US.js.map +1 -1
- package/dist/types/src/types/index.js +1 -0
- package/dist/types/src/types/index.js.map +1 -1
- package/package.json +20 -20
- package/src/components/AgentConfig.tsx +66 -0
- package/src/components/DeviceList/DeviceList.tsx +54 -0
- package/src/components/DeviceList/DeviceListItem.tsx +120 -0
- package/src/components/DeviceList/DeviceListProps.ts +33 -0
- package/src/components/DeviceList/index.ts +6 -0
- package/src/components/EmojiPicker.tsx +101 -4
- package/src/components/HuePicker.tsx +99 -5
- package/src/components/IdentityList/index.ts +0 -1
- package/src/components/index.ts +2 -0
- package/src/composites/Shell/Shell.tsx +1 -7
- package/src/panels/IdentityPanel/IdentityPanel.stories.tsx +0 -63
- package/src/panels/IdentityPanel/IdentityPanel.tsx +123 -34
- package/src/panels/IdentityPanel/IdentityPanelProps.ts +10 -14
- package/src/panels/IdentityPanel/identityMachine.ts +1 -31
- package/src/panels/IdentityPanel/steps/IdentityActionChooser.tsx +23 -76
- package/src/panels/IdentityPanel/steps/ProfileForm.tsx +12 -4
- package/src/panels/IdentityPanel/steps/index.ts +0 -2
- package/src/steps/ConfirmReset.tsx +4 -3
- package/src/testing/shell-manager.ts +8 -8
- package/src/translations/locales/en-US.ts +13 -2
- package/src/types/index.ts +2 -0
- package/dist/types/src/components/IdentityList/DeviceList.d.ts +0 -7
- package/dist/types/src/components/IdentityList/DeviceList.d.ts.map +0 -1
- package/dist/types/src/components/IdentityList/DeviceList.js +0 -34
- package/dist/types/src/components/IdentityList/DeviceList.js.map +0 -1
- package/dist/types/src/panels/IdentityPanel/steps/AgentForm.d.ts +0 -3
- package/dist/types/src/panels/IdentityPanel/steps/AgentForm.d.ts.map +0 -1
- package/dist/types/src/panels/IdentityPanel/steps/AgentForm.js +0 -26
- package/dist/types/src/panels/IdentityPanel/steps/AgentForm.js.map +0 -1
- package/dist/types/src/panels/IdentityPanel/steps/DeviceManager.d.ts +0 -10
- package/dist/types/src/panels/IdentityPanel/steps/DeviceManager.d.ts.map +0 -1
- package/dist/types/src/panels/IdentityPanel/steps/DeviceManager.js +0 -23
- package/dist/types/src/panels/IdentityPanel/steps/DeviceManager.js.map +0 -1
- package/src/components/IdentityList/DeviceList.tsx +0 -49
- package/src/panels/IdentityPanel/steps/AgentForm.tsx +0 -61
- package/src/panels/IdentityPanel/steps/DeviceManager.tsx +0 -41
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
//
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
|
-
import { ArrowCounterClockwise, CaretDown, Check } from '@phosphor-icons/react';
|
|
4
|
+
import { ArrowCounterClockwise, CaretDown, Check, Palette } from '@phosphor-icons/react';
|
|
5
5
|
import { useControllableState } from '@radix-ui/react-use-controllable-state';
|
|
6
|
-
import React, { type Dispatch, type SetStateAction } from 'react';
|
|
6
|
+
import React, { type Dispatch, type SetStateAction, useRef, useState } from 'react';
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
|
|
8
|
+
import {
|
|
9
|
+
Button,
|
|
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';
|
|
10
19
|
|
|
11
20
|
const HuePreview = ({ hue }: { hue: string }) => {
|
|
12
21
|
const { tx } = useThemeContext();
|
|
@@ -25,7 +34,92 @@ export type HuePickerProps = {
|
|
|
25
34
|
onClickClear?: ButtonProps['onClick'];
|
|
26
35
|
};
|
|
27
36
|
|
|
28
|
-
|
|
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) => {
|
|
29
123
|
const { t } = useTranslation('os');
|
|
30
124
|
|
|
31
125
|
const [hueValue, setHueValue] = useControllableState<string>({
|
package/src/components/index.ts
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
export * from './AgentConfig';
|
|
5
6
|
export * from './Clipboard';
|
|
6
7
|
export * from './CompoundButton';
|
|
7
8
|
export * from './EmojiPicker';
|
|
8
9
|
export * from './HuePicker';
|
|
9
10
|
export * from './IdentityList';
|
|
11
|
+
export * from './DeviceList';
|
|
10
12
|
export * from './InvitationList';
|
|
11
13
|
export * from './Panel';
|
|
12
14
|
export * from './Viewport';
|
|
@@ -81,13 +81,7 @@ 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={
|
|
85
|
-
layout === ShellLayout.SHARE_IDENTITY
|
|
86
|
-
? 'manage-device-invitation'
|
|
87
|
-
: layout === ShellLayout.EDIT_PROFILE
|
|
88
|
-
? 'manage-profile'
|
|
89
|
-
: 'default'
|
|
90
|
-
}
|
|
84
|
+
initialDisposition={layout === ShellLayout.SHARE_IDENTITY ? 'manage-device-invitation' : 'default'}
|
|
91
85
|
/>
|
|
92
86
|
);
|
|
93
87
|
|
|
@@ -8,7 +8,6 @@ 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';
|
|
12
11
|
import { Invitation } from '@dxos/react-client/invitations';
|
|
13
12
|
import { withTheme } from '@dxos/storybook-utils';
|
|
14
13
|
|
|
@@ -53,58 +52,6 @@ export const IdentityActionChooser = () => {
|
|
|
53
52
|
);
|
|
54
53
|
};
|
|
55
54
|
|
|
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
|
-
|
|
108
55
|
const DeviceInvitationManagerWithState = (extraProps: InvitationManagerProps) => (
|
|
109
56
|
<StorybookDialog inOverlayLayout>
|
|
110
57
|
<IdentityPanelImpl
|
|
@@ -156,13 +103,3 @@ export const DeviceInvitationManagerCancelled = () =>
|
|
|
156
103
|
DeviceInvitationManagerWithState({
|
|
157
104
|
status: Invitation.State.CANCELLED,
|
|
158
105
|
});
|
|
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,7 +1,8 @@
|
|
|
1
1
|
//
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
|
-
import
|
|
4
|
+
import { CopySimple, Plugs, PlugsConnected } from '@phosphor-icons/react';
|
|
5
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
5
6
|
|
|
6
7
|
import { generateName } from '@dxos/display-name';
|
|
7
8
|
import { log } from '@dxos/log';
|
|
@@ -9,8 +10,10 @@ import { useClient } from '@dxos/react-client';
|
|
|
9
10
|
import { type Identity, useIdentity, useDevices, useHaloInvitations } from '@dxos/react-client/halo';
|
|
10
11
|
import { useInvitationStatus } from '@dxos/react-client/invitations';
|
|
11
12
|
import type { CancellableInvitationObservable } from '@dxos/react-client/invitations';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
13
|
+
import { useNetworkStatus, ConnectionState } from '@dxos/react-client/mesh';
|
|
14
|
+
import { Avatar, DensityProvider, Input, Toolbar, Tooltip, useId, useTranslation } from '@dxos/react-ui';
|
|
15
|
+
import { getSize } from '@dxos/react-ui-theme';
|
|
16
|
+
import { hexToEmoji, hexToHue, keyToFallback } from '@dxos/util';
|
|
14
17
|
|
|
15
18
|
import {
|
|
16
19
|
type IdentityPanelHeadingProps,
|
|
@@ -18,24 +21,122 @@ import {
|
|
|
18
21
|
type IdentityPanelProps,
|
|
19
22
|
} from './IdentityPanelProps';
|
|
20
23
|
import { useIdentityMachine } from './identityMachine';
|
|
21
|
-
import {
|
|
24
|
+
import { IdentityActionChooser } from './steps';
|
|
22
25
|
import { useAgentHandlers } from './useAgentHandlers';
|
|
23
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
Viewport,
|
|
28
|
+
Heading,
|
|
29
|
+
CloseButton,
|
|
30
|
+
EmojiPickerToolbarButton,
|
|
31
|
+
useClipboardContext,
|
|
32
|
+
HuePickerToolbarButton,
|
|
33
|
+
} from '../../components';
|
|
24
34
|
import { ConfirmReset, InvitationManager } from '../../steps';
|
|
25
35
|
|
|
26
36
|
const viewStyles = 'pbs-1 pbe-3 pli-3';
|
|
27
37
|
|
|
28
|
-
|
|
38
|
+
// TODO(thure): Factor out?
|
|
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) => {
|
|
29
53
|
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
|
+
|
|
30
76
|
return (
|
|
31
77
|
<Heading titleId={titleId} title={title} corner={<CloseButton onDone={onDone} />}>
|
|
32
|
-
<Avatar.Root size={
|
|
33
|
-
<
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
|
|
78
|
+
<Avatar.Root size={16} variant='circle' status={isConnected ? 'active' : 'error'} hue={hue || fallbackValue.hue}>
|
|
79
|
+
<Toolbar.Root classNames='grid grid-cols-[1fr_var(--rail-action)_min-content_var(--rail-action)_1fr] items-center gap-2'>
|
|
80
|
+
<Tooltip.Root>
|
|
81
|
+
<Tooltip.Trigger asChild>
|
|
82
|
+
<Toolbar.Button
|
|
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'>
|
|
37
124
|
{identity.profile?.displayName ?? generateName(identity.identityKey.toHex())}
|
|
38
125
|
</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>
|
|
39
140
|
</Avatar.Root>
|
|
40
141
|
</Heading>
|
|
41
142
|
);
|
|
@@ -51,14 +152,14 @@ export const IdentityPanelImpl = (props: IdentityPanelImplProps) => {
|
|
|
51
152
|
onJoinNewIdentity,
|
|
52
153
|
IdentityActionChooser: IdentityActionChooserComponent = IdentityActionChooser,
|
|
53
154
|
InvitationManager: InvitationManagerComponent = InvitationManager,
|
|
155
|
+
connectionState,
|
|
156
|
+
onChangeConnectionState,
|
|
54
157
|
onDone,
|
|
55
158
|
...rest
|
|
56
159
|
} = props;
|
|
57
160
|
const { t } = useTranslation('os');
|
|
58
161
|
const title = useMemo(() => {
|
|
59
162
|
switch (activeView) {
|
|
60
|
-
case 'agent manager':
|
|
61
|
-
return 'Manage Agent';
|
|
62
163
|
case 'device invitation manager':
|
|
63
164
|
return t('choose add device label');
|
|
64
165
|
default:
|
|
@@ -70,7 +171,9 @@ export const IdentityPanelImpl = (props: IdentityPanelImplProps) => {
|
|
|
70
171
|
|
|
71
172
|
return (
|
|
72
173
|
<DensityProvider density='fine'>
|
|
73
|
-
<IdentityHeading
|
|
174
|
+
<IdentityHeading
|
|
175
|
+
{...{ identity, titleId, title, onDone, onUpdateProfile, connectionState, onChangeConnectionState }}
|
|
176
|
+
/>
|
|
74
177
|
<Viewport.Root activeView={activeView}>
|
|
75
178
|
<Viewport.Views>
|
|
76
179
|
<Viewport.View id='identity action chooser' classNames={viewStyles}>
|
|
@@ -83,20 +186,6 @@ export const IdentityPanelImpl = (props: IdentityPanelImplProps) => {
|
|
|
83
186
|
invitationUrl={rest.createInvitationUrl(rest.invitationCode!)}
|
|
84
187
|
/>
|
|
85
188
|
</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>
|
|
100
189
|
<Viewport.View classNames={viewStyles} id='confirm join new identity'>
|
|
101
190
|
<ConfirmReset
|
|
102
191
|
active={activeView === 'confirm join new identity'}
|
|
@@ -149,6 +238,10 @@ export const IdentityPanel = ({
|
|
|
149
238
|
context: { initialDisposition },
|
|
150
239
|
});
|
|
151
240
|
|
|
241
|
+
const { swarm: connectionState } = useNetworkStatus();
|
|
242
|
+
|
|
243
|
+
const handleChangeConnectionState = async (nextState: ConnectionState) => client?.mesh.updateConfig(nextState);
|
|
244
|
+
|
|
152
245
|
useEffect(() => {
|
|
153
246
|
const subscription = identityService.subscribe((state) => {
|
|
154
247
|
log('[state]', state);
|
|
@@ -163,16 +256,10 @@ export const IdentityPanel = ({
|
|
|
163
256
|
return 'identity action chooser';
|
|
164
257
|
case identityState.matches('managingDeviceInvitation'):
|
|
165
258
|
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';
|
|
170
259
|
case identityState.matches('confirmingResetStorage'):
|
|
171
260
|
return 'confirm reset storage';
|
|
172
261
|
case identityState.matches('confirmingJoinNewIdentity'):
|
|
173
262
|
return 'confirm join new identity';
|
|
174
|
-
case [{ managingAgent: 'idle' }, { managingAgent: 'pending' }].some(identityState.matches):
|
|
175
|
-
return 'agent manager';
|
|
176
263
|
default:
|
|
177
264
|
return 'identity action chooser';
|
|
178
265
|
}
|
|
@@ -192,6 +279,8 @@ export const IdentityPanel = ({
|
|
|
192
279
|
titleId,
|
|
193
280
|
createInvitationUrl,
|
|
194
281
|
onUpdateProfile,
|
|
282
|
+
connectionState,
|
|
283
|
+
onChangeConnectionState: handleChangeConnectionState,
|
|
195
284
|
...agentProps,
|
|
196
285
|
} satisfies IdentityPanelImplProps;
|
|
197
286
|
|
|
@@ -7,27 +7,17 @@ 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';
|
|
10
11
|
|
|
11
12
|
import type { IdentityEvent } from './identityMachine';
|
|
13
|
+
import { type AgentFormProps } from '../../components';
|
|
12
14
|
import type { InvitationManagerProps } from '../../steps';
|
|
13
15
|
|
|
14
|
-
export type IdentityPanelInitialDisposition = 'default' | 'manage-device-invitation'
|
|
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
|
-
};
|
|
16
|
+
export type IdentityPanelInitialDisposition = 'default' | 'manage-device-invitation';
|
|
24
17
|
|
|
25
18
|
export type IdentityPanelImplProps = {
|
|
26
19
|
titleId: string;
|
|
27
20
|
activeView:
|
|
28
|
-
| 'device manager'
|
|
29
|
-
| 'agent manager'
|
|
30
|
-
| 'update profile form'
|
|
31
21
|
| 'device invitation manager'
|
|
32
22
|
| 'identity action chooser'
|
|
33
23
|
| 'confirm join new identity'
|
|
@@ -35,6 +25,8 @@ export type IdentityPanelImplProps = {
|
|
|
35
25
|
initialDisposition?: IdentityPanelInitialDisposition;
|
|
36
26
|
identity: Identity;
|
|
37
27
|
devices: Device[];
|
|
28
|
+
connectionState?: ConnectionState;
|
|
29
|
+
onChangeConnectionState?: (nextState: ConnectionState) => Promise<void>;
|
|
38
30
|
onUpdateProfile?: (profile: NonNullable<Identity['profile']>) => Promise<void>;
|
|
39
31
|
onResetStorage?: () => Promise<void>;
|
|
40
32
|
onJoinNewIdentity?: () => Promise<void>;
|
|
@@ -50,11 +42,15 @@ export type IdentityPanelImplProps = {
|
|
|
50
42
|
|
|
51
43
|
export type IdentityPanelProps = Partial<Omit<IdentityPanelImplProps, 'send' | 'activeView' | 'identity'>>;
|
|
52
44
|
|
|
53
|
-
export type IdentityPanelHeadingProps = Pick<
|
|
45
|
+
export type IdentityPanelHeadingProps = Pick<
|
|
46
|
+
IdentityPanelImplProps,
|
|
47
|
+
'titleId' | 'identity' | 'onDone' | 'onUpdateProfile' | 'connectionState' | 'onChangeConnectionState'
|
|
48
|
+
> & {
|
|
54
49
|
title: string;
|
|
55
50
|
};
|
|
56
51
|
|
|
57
52
|
export type IdentityPanelStepProps = Pick<IdentityPanelImplProps, 'send' | 'onDone' | 'doneActionParent'> & {
|
|
58
53
|
active?: boolean;
|
|
59
54
|
agentHostingEnabled?: boolean;
|
|
55
|
+
devices: Device[];
|
|
60
56
|
};
|
|
@@ -21,13 +21,7 @@ type IdentityMachineContext = {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
type IdentityChooseActionEvent = {
|
|
24
|
-
type:
|
|
25
|
-
| 'chooseDevices'
|
|
26
|
-
| 'chooseAgent'
|
|
27
|
-
| 'chooseProfile'
|
|
28
|
-
| 'chooseResetStorage'
|
|
29
|
-
| 'chooseJoinNewIdentity'
|
|
30
|
-
| 'unchooseAction';
|
|
24
|
+
type: 'chooseResetStorage' | 'chooseJoinNewIdentity' | 'unchooseAction';
|
|
31
25
|
};
|
|
32
26
|
|
|
33
27
|
type IdentitySelectDeviceInvitationEvent = {
|
|
@@ -67,31 +61,11 @@ const identityMachine = createMachine<IdentityMachineContext, IdentityEvent>(
|
|
|
67
61
|
unknown: {
|
|
68
62
|
always: [
|
|
69
63
|
{ cond: 'initiallyManagingDeviceInvitation', target: 'managingDeviceInvitation', actions: 'log' },
|
|
70
|
-
{ cond: 'initiallyManagingProfile', target: 'managingProfile', actions: 'log' },
|
|
71
64
|
{ target: 'choosingAction', actions: 'log' },
|
|
72
65
|
],
|
|
73
66
|
},
|
|
74
67
|
choosingAction: {},
|
|
75
68
|
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
|
-
},
|
|
95
69
|
confirmingResetStorage: {},
|
|
96
70
|
confirmingJoinNewIdentity: {},
|
|
97
71
|
},
|
|
@@ -99,18 +73,14 @@ const identityMachine = createMachine<IdentityMachineContext, IdentityEvent>(
|
|
|
99
73
|
unchooseAction: { target: '.choosingAction', actions: ['unsetInvitation', 'log'] },
|
|
100
74
|
deselectInvitation: { target: '.choosingAction', actions: ['unsetInvitation', 'log'] },
|
|
101
75
|
selectInvitation: { target: '.managingDeviceInvitation', actions: ['setInvitation', 'log'] },
|
|
102
|
-
chooseDevices: { target: '.managingDevices', actions: 'log' },
|
|
103
|
-
chooseProfile: { target: '.managingProfile', actions: 'log' },
|
|
104
76
|
chooseJoinNewIdentity: { target: '.confirmingJoinNewIdentity', actions: 'log' },
|
|
105
77
|
chooseResetStorage: { target: '.confirmingResetStorage', actions: 'log' },
|
|
106
|
-
chooseAgent: { target: '.managingAgent', actions: 'log' },
|
|
107
78
|
},
|
|
108
79
|
},
|
|
109
80
|
{
|
|
110
81
|
guards: {
|
|
111
82
|
initiallyManagingDeviceInvitation: ({ initialDisposition }, _event) =>
|
|
112
83
|
initialDisposition === 'manage-device-invitation',
|
|
113
|
-
initiallyManagingProfile: ({ initialDisposition }, _event) => initialDisposition === 'manage-profile',
|
|
114
84
|
},
|
|
115
85
|
actions: {
|
|
116
86
|
setIdentity: assign<IdentityMachineContext, IdentityEvent>({
|