@dxos/react-ui 0.1.23 → 0.1.24-next.6b1f434
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/lib/browser/index.mjs +2428 -72
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +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/IdentityListItem.d.ts +7 -0
- package/dist/types/src/components/IdentityList/IdentityListItem.d.ts.map +1 -0
- package/dist/types/src/components/{IdentityListItem → IdentityList}/IdentityListItem.stories.d.ts +2 -1
- package/dist/types/src/components/IdentityList/IdentityListItem.stories.d.ts.map +1 -0
- package/dist/types/src/components/IdentityList/SpaceMemberList.d.ts +7 -0
- package/dist/types/src/components/IdentityList/SpaceMemberList.d.ts.map +1 -0
- package/dist/types/src/components/IdentityList/SpaceMemberListContainer.d.ts +9 -0
- package/dist/types/src/components/IdentityList/SpaceMemberListContainer.d.ts.map +1 -0
- package/dist/types/src/components/IdentityList/index.d.ts +5 -0
- package/dist/types/src/components/IdentityList/index.d.ts.map +1 -0
- package/dist/types/src/components/InvitationList/InvitationList.d.ts +7 -0
- package/dist/types/src/components/InvitationList/InvitationList.d.ts.map +1 -0
- package/dist/types/src/components/InvitationList/InvitationList.stories.d.ts +10 -0
- package/dist/types/src/components/InvitationList/InvitationList.stories.d.ts.map +1 -0
- package/dist/types/src/components/InvitationList/InvitationListContainer.d.ts +7 -0
- package/dist/types/src/components/InvitationList/InvitationListContainer.d.ts.map +1 -0
- package/dist/types/src/components/InvitationList/InvitationListItem.d.ts +9 -0
- package/dist/types/src/components/InvitationList/InvitationListItem.d.ts.map +1 -0
- package/dist/types/src/components/InvitationList/InvitationListProps.d.ts +4 -0
- package/dist/types/src/components/InvitationList/InvitationListProps.d.ts.map +1 -0
- package/dist/types/src/components/InvitationList/InvitationStatusAvatar.d.ts +14 -0
- package/dist/types/src/components/InvitationList/InvitationStatusAvatar.d.ts.map +1 -0
- package/dist/types/src/components/InvitationList/InvitationStatusAvatar.stories.d.ts +9 -0
- package/dist/types/src/components/InvitationList/InvitationStatusAvatar.stories.d.ts.map +1 -0
- package/dist/types/src/components/InvitationList/index.d.ts +5 -0
- package/dist/types/src/components/InvitationList/index.d.ts.map +1 -0
- package/dist/types/src/components/PanelSeparator/PanelSeparator.d.ts +3 -0
- package/dist/types/src/components/PanelSeparator/PanelSeparator.d.ts.map +1 -0
- package/dist/types/src/components/PanelSeparator/index.d.ts +2 -0
- package/dist/types/src/components/PanelSeparator/index.d.ts.map +1 -0
- package/dist/types/src/components/SpaceList/SpaceListItem.d.ts +7 -0
- package/dist/types/src/components/SpaceList/SpaceListItem.d.ts.map +1 -0
- package/dist/types/src/components/SpaceList/index.d.ts +2 -0
- package/dist/types/src/components/SpaceList/index.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +5 -0
- package/dist/types/src/components/index.d.ts.map +1 -0
- package/dist/types/src/composites/DevicesDialog/DevicesDialog.d.ts +6 -0
- package/dist/types/src/composites/DevicesDialog/DevicesDialog.d.ts.map +1 -0
- package/dist/types/src/composites/DevicesDialog/index.d.ts +2 -0
- package/dist/types/src/composites/DevicesDialog/index.d.ts.map +1 -0
- package/dist/types/src/composites/JoinDialog/JoinDialog.d.ts +6 -0
- package/dist/types/src/composites/JoinDialog/JoinDialog.d.ts.map +1 -0
- package/dist/types/src/composites/JoinDialog/JoinDialog.stories.d.ts +10 -0
- package/dist/types/src/composites/JoinDialog/JoinDialog.stories.d.ts.map +1 -0
- package/dist/types/src/composites/JoinDialog/index.d.ts +2 -0
- package/dist/types/src/composites/JoinDialog/index.d.ts.map +1 -0
- package/dist/types/src/composites/Shell/Shell.d.ts +6 -0
- package/dist/types/src/composites/Shell/Shell.d.ts.map +1 -0
- package/dist/types/src/composites/Shell/index.d.ts +2 -0
- package/dist/types/src/composites/Shell/index.d.ts.map +1 -0
- package/dist/types/src/composites/SpaceDialog/SpaceDialog.d.ts +6 -0
- package/dist/types/src/composites/SpaceDialog/SpaceDialog.d.ts.map +1 -0
- package/dist/types/src/composites/SpaceDialog/index.d.ts +2 -0
- package/dist/types/src/composites/SpaceDialog/index.d.ts.map +1 -0
- package/dist/types/src/composites/index.d.ts +3 -0
- package/dist/types/src/composites/index.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/layouts/PanelAlertDialog/PanelAlertDialog.d.ts +14 -0
- package/dist/types/src/layouts/PanelAlertDialog/PanelAlertDialog.d.ts.map +1 -0
- package/dist/types/src/layouts/PanelAlertDialog/index.d.ts +2 -0
- package/dist/types/src/layouts/PanelAlertDialog/index.d.ts.map +1 -0
- package/dist/types/src/layouts/PanelDialog/PanelDialog.d.ts +14 -0
- package/dist/types/src/layouts/PanelDialog/PanelDialog.d.ts.map +1 -0
- package/dist/types/src/layouts/PanelDialog/index.d.ts +2 -0
- package/dist/types/src/layouts/PanelDialog/index.d.ts.map +1 -0
- package/dist/types/src/layouts/PanelSidebar/PanelSidebar.d.ts +2 -3
- package/dist/types/src/layouts/PanelSidebar/PanelSidebar.d.ts.map +1 -1
- package/dist/types/src/layouts/index.d.ts +2 -0
- package/dist/types/src/layouts/index.d.ts.map +1 -1
- package/dist/types/src/panels/DevicesPanel/DevicesPanel.d.ts +10 -0
- package/dist/types/src/panels/DevicesPanel/DevicesPanel.d.ts.map +1 -0
- package/dist/types/src/panels/DevicesPanel/index.d.ts +2 -0
- package/dist/types/src/panels/DevicesPanel/index.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/JoinHeading.d.ts +13 -0
- package/dist/types/src/panels/JoinPanel/JoinHeading.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/JoinPanel.d.ts +3 -0
- package/dist/types/src/panels/JoinPanel/JoinPanel.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/JoinPanelProps.d.ts +54 -0
- package/dist/types/src/panels/JoinPanel/JoinPanelProps.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/index.d.ts +3 -0
- package/dist/types/src/panels/JoinPanel/index.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/view-states/AdditionMethodSelector.d.ts +7 -0
- package/dist/types/src/panels/JoinPanel/view-states/AdditionMethodSelector.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/view-states/IdentityAdded.d.ts +15 -0
- package/dist/types/src/panels/JoinPanel/view-states/IdentityAdded.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/view-states/IdentityInput.d.ts +6 -0
- package/dist/types/src/panels/JoinPanel/view-states/IdentityInput.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/view-states/IdentitySelector.d.ts +7 -0
- package/dist/types/src/panels/JoinPanel/view-states/IdentitySelector.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/view-states/InvitationAccepted.d.ts +10 -0
- package/dist/types/src/panels/JoinPanel/view-states/InvitationAccepted.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/view-states/InvitationAuthenticator.d.ts +7 -0
- package/dist/types/src/panels/JoinPanel/view-states/InvitationAuthenticator.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/view-states/InvitationInput.d.ts +6 -0
- package/dist/types/src/panels/JoinPanel/view-states/InvitationInput.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/view-states/InvitationRescuer.d.ts +6 -0
- package/dist/types/src/panels/JoinPanel/view-states/InvitationRescuer.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/view-states/ViewState.d.ts +12 -0
- package/dist/types/src/panels/JoinPanel/view-states/ViewState.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/view-states/index.d.ts +9 -0
- package/dist/types/src/panels/JoinPanel/view-states/index.d.ts.map +1 -0
- package/dist/types/src/panels/SpacePanel/SpacePanel.d.ts +18 -0
- package/dist/types/src/panels/SpacePanel/SpacePanel.d.ts.map +1 -0
- package/dist/types/src/panels/SpacePanel/SpacePanel.stories.d.ts +9 -0
- package/dist/types/src/panels/SpacePanel/SpacePanel.stories.d.ts.map +1 -0
- package/dist/types/src/panels/SpacePanel/index.d.ts +2 -0
- package/dist/types/src/panels/SpacePanel/index.d.ts.map +1 -0
- package/dist/types/src/panels/index.d.ts +3 -0
- package/dist/types/src/panels/index.d.ts.map +1 -1
- package/dist/types/src/styles/dialogStyles.d.ts +3 -0
- package/dist/types/src/styles/dialogStyles.d.ts.map +1 -0
- package/dist/types/src/styles/index.d.ts +2 -0
- package/dist/types/src/styles/index.d.ts.map +1 -1
- package/dist/types/src/styles/invitationStatusStyles.d.ts +20 -0
- package/dist/types/src/styles/invitationStatusStyles.d.ts.map +1 -0
- package/dist/types/src/styles/panelStyles.d.ts +2 -1
- package/dist/types/src/styles/panelStyles.d.ts.map +1 -1
- package/dist/types/src/translations/locales/en-US.d.ts +52 -0
- package/dist/types/src/translations/locales/en-US.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +2 -0
- package/dist/types/src/util/index.d.ts.map +1 -0
- package/dist/types/src/util/invitationStatusValue.d.ts +8 -0
- package/dist/types/src/util/invitationStatusValue.d.ts.map +1 -0
- package/package.json +19 -11
- package/src/components/IdentityList/DeviceList.tsx +31 -0
- package/src/components/{IdentityListItem → IdentityList}/IdentityListItem.stories.tsx +0 -0
- package/src/components/{IdentityListItem → IdentityList}/IdentityListItem.tsx +18 -5
- package/src/components/IdentityList/SpaceMemberList.tsx +33 -0
- package/src/components/IdentityList/SpaceMemberListContainer.tsx +28 -0
- package/src/components/IdentityList/index.ts +8 -0
- package/src/components/InvitationList/InvitationList.stories.tsx +65 -0
- package/src/components/InvitationList/InvitationList.tsx +28 -0
- package/src/components/InvitationList/InvitationListContainer.tsx +27 -0
- package/src/components/InvitationList/InvitationListItem.tsx +106 -0
- package/src/components/InvitationList/InvitationListProps.ts +7 -0
- package/src/components/InvitationList/InvitationStatusAvatar.stories.tsx +52 -0
- package/src/components/InvitationList/InvitationStatusAvatar.tsx +80 -0
- package/src/components/InvitationList/index.ts +8 -0
- package/src/components/PanelSeparator/PanelSeparator.tsx +17 -0
- package/src/components/PanelSeparator/index.ts +5 -0
- package/src/components/SpaceList/SpaceListItem.tsx +32 -0
- package/src/components/SpaceList/index.ts +5 -0
- package/src/components/index.ts +8 -0
- package/src/composites/DevicesDialog/DevicesDialog.tsx +38 -0
- package/src/composites/DevicesDialog/index.ts +5 -0
- package/src/composites/JoinDialog/JoinDialog.stories.tsx +81 -0
- package/src/composites/JoinDialog/JoinDialog.tsx +33 -0
- package/src/composites/JoinDialog/index.ts +5 -0
- package/src/composites/Shell/Shell.tsx +92 -0
- package/src/composites/Shell/index.ts +5 -0
- package/src/composites/SpaceDialog/SpaceDialog.tsx +38 -0
- package/src/composites/SpaceDialog/index.ts +5 -0
- package/src/composites/index.ts +4 -0
- package/src/index.ts +1 -0
- package/src/layouts/PanelAlertDialog/PanelAlertDialog.tsx +40 -0
- package/src/layouts/PanelAlertDialog/index.ts +5 -0
- package/src/layouts/PanelDialog/PanelDialog.tsx +39 -0
- package/src/layouts/PanelDialog/index.ts +5 -0
- package/src/layouts/PanelPopover/PanelPopover.tsx +2 -2
- package/src/layouts/PanelSidebar/PanelSidebar.tsx +17 -25
- package/src/layouts/index.ts +2 -0
- package/src/panels/DevicesPanel/DevicesPanel.tsx +98 -0
- package/src/panels/DevicesPanel/index.ts +5 -0
- package/src/panels/JoinPanel/JoinHeading.tsx +85 -0
- package/src/panels/JoinPanel/JoinPanel.tsx +319 -0
- package/src/panels/JoinPanel/JoinPanelProps.ts +90 -0
- package/src/panels/JoinPanel/index.ts +6 -0
- package/src/panels/JoinPanel/view-states/AdditionMethodSelector.tsx +73 -0
- package/src/panels/JoinPanel/view-states/IdentityAdded.tsx +99 -0
- package/src/panels/JoinPanel/view-states/IdentityInput.tsx +79 -0
- package/src/panels/JoinPanel/view-states/IdentitySelector.tsx +101 -0
- package/src/panels/JoinPanel/view-states/InvitationAccepted.tsx +70 -0
- package/src/panels/JoinPanel/view-states/InvitationAuthenticator.tsx +142 -0
- package/src/panels/JoinPanel/view-states/InvitationInput.tsx +75 -0
- package/src/panels/JoinPanel/view-states/InvitationRescuer.tsx +98 -0
- package/src/panels/JoinPanel/view-states/ViewState.tsx +204 -0
- package/src/panels/JoinPanel/view-states/index.ts +12 -0
- package/src/panels/SpacePanel/SpacePanel.stories.tsx +53 -0
- package/src/panels/SpacePanel/SpacePanel.tsx +174 -0
- package/src/panels/SpacePanel/index.ts +5 -0
- package/src/panels/index.ts +3 -0
- package/src/styles/dialogStyles.ts +8 -0
- package/src/styles/index.ts +2 -0
- package/src/styles/invitationStatusStyles.ts +42 -0
- package/src/styles/panelStyles.ts +2 -1
- package/src/testing/ClientProvider.tsx +1 -1
- package/src/translations/locales/en-US.ts +53 -1
- package/src/util/index.ts +5 -0
- package/src/util/invitationStatusValue.ts +21 -0
- package/theme-extensions.js +11 -0
- package/dist/types/src/components/IdentityListItem/IdentityListItem.d.ts +0 -6
- package/dist/types/src/components/IdentityListItem/IdentityListItem.d.ts.map +0 -1
- package/dist/types/src/components/IdentityListItem/IdentityListItem.stories.d.ts.map +0 -1
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { CaretLeft, CaretRight } from 'phosphor-react';
|
|
6
|
+
import React, { ComponentPropsWithoutRef, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
import { useClient } from '@dxos/react-client';
|
|
9
|
+
import { Button, getSize, Input, mx, useTranslation } from '@dxos/react-components';
|
|
10
|
+
|
|
11
|
+
import { ViewState, ViewStateHeading, ViewStateProps } from './ViewState';
|
|
12
|
+
|
|
13
|
+
export interface IdentityCreatorProps extends ViewStateProps {
|
|
14
|
+
method: 'recover identity' | 'create identity';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const IdentityInput = ({ method, ...viewStateProps }: IdentityCreatorProps) => {
|
|
18
|
+
const disabled = !viewStateProps.active;
|
|
19
|
+
const { dispatch } = viewStateProps;
|
|
20
|
+
const { t } = useTranslation('os');
|
|
21
|
+
const [inputValue, setInputValue] = useState('');
|
|
22
|
+
const client = useClient();
|
|
23
|
+
const [validationMessage, setValidationMessage] = useState('');
|
|
24
|
+
const isRecover = method === 'recover identity';
|
|
25
|
+
const handleNext = () => {
|
|
26
|
+
void client.halo.createProfile({ [isRecover ? 'seedphrase' : 'displayName']: inputValue }).then(
|
|
27
|
+
(identity) => {
|
|
28
|
+
dispatch({ type: 'added identity', identity });
|
|
29
|
+
},
|
|
30
|
+
(_error) => {
|
|
31
|
+
setValidationMessage(t(isRecover ? 'failed to recover identity message' : 'failed to create identity message'));
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
return (
|
|
36
|
+
<ViewState {...viewStateProps}>
|
|
37
|
+
<Input
|
|
38
|
+
disabled={disabled}
|
|
39
|
+
label={
|
|
40
|
+
<ViewStateHeading>
|
|
41
|
+
{t(isRecover ? 'recover identity input label' : 'new identity input label')}
|
|
42
|
+
</ViewStateHeading>
|
|
43
|
+
}
|
|
44
|
+
onChange={({ target: { value } }) => setInputValue(value)}
|
|
45
|
+
slots={{
|
|
46
|
+
root: { className: 'm-0' },
|
|
47
|
+
input: {
|
|
48
|
+
'data-autofocus': isRecover ? 'identity input; recover identity' : 'identity input; create identity',
|
|
49
|
+
onKeyUp: ({ key }) => key === 'Enter' && handleNext()
|
|
50
|
+
} as ComponentPropsWithoutRef<'input'>
|
|
51
|
+
}}
|
|
52
|
+
{...(validationMessage.length && { validationValence: 'error', validationMessage })}
|
|
53
|
+
data-testid='identity-input'
|
|
54
|
+
/>
|
|
55
|
+
<div role='none' className='grow' />
|
|
56
|
+
<div className='flex gap-2'>
|
|
57
|
+
<Button
|
|
58
|
+
disabled={disabled}
|
|
59
|
+
className='grow flex items-center gap-2 pli-2 order-2'
|
|
60
|
+
onClick={handleNext}
|
|
61
|
+
data-testid={`${method === 'recover identity' ? 'recover' : 'create'}-identity-input-continue`}
|
|
62
|
+
>
|
|
63
|
+
<CaretLeft weight='bold' className={mx(getSize(2), 'invisible')} />
|
|
64
|
+
<span className='grow'>{t('continue label')}</span>
|
|
65
|
+
<CaretRight weight='bold' className={getSize(4)} />
|
|
66
|
+
</Button>
|
|
67
|
+
<Button
|
|
68
|
+
disabled={disabled}
|
|
69
|
+
onClick={() => dispatch({ type: 'add identity' })}
|
|
70
|
+
className='flex items-center gap-2 pis-2 pie-4'
|
|
71
|
+
data-testid={`${method === 'recover identity' ? 'recover' : 'create'}-identity-input-back`}
|
|
72
|
+
>
|
|
73
|
+
<CaretLeft weight='bold' className={getSize(4)} />
|
|
74
|
+
<span>{t('back label')}</span>
|
|
75
|
+
</Button>
|
|
76
|
+
</div>
|
|
77
|
+
</ViewState>
|
|
78
|
+
);
|
|
79
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as RadioGroup from '@radix-ui/react-radio-group';
|
|
6
|
+
import { CaretLeft, CaretRight, UserPlus } from 'phosphor-react';
|
|
7
|
+
import React, { useCallback, useState } from 'react';
|
|
8
|
+
|
|
9
|
+
import type { Profile } from '@dxos/client';
|
|
10
|
+
import { Avatar, Button, themeVariantFocus, getSize, mx, useTranslation } from '@dxos/react-components';
|
|
11
|
+
|
|
12
|
+
import { ViewState, ViewStateHeading, ViewStateProps } from './ViewState';
|
|
13
|
+
|
|
14
|
+
export interface IdentitySelectorProps extends ViewStateProps {
|
|
15
|
+
availableIdentities: Profile[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const IdentitySelector = ({ availableIdentities, ...viewStateProps }: IdentitySelectorProps) => {
|
|
19
|
+
const { dispatch } = viewStateProps;
|
|
20
|
+
const disabled = !viewStateProps.active;
|
|
21
|
+
|
|
22
|
+
const { t } = useTranslation('os');
|
|
23
|
+
const [activeIdentity, setActiveIdentity] = useState(availableIdentities[0]);
|
|
24
|
+
const onValueChange = useCallback(
|
|
25
|
+
(nextHex: string) =>
|
|
26
|
+
setActiveIdentity(
|
|
27
|
+
availableIdentities.find((identity) => identity.identityKey.toHex() === nextHex) || activeIdentity
|
|
28
|
+
),
|
|
29
|
+
[availableIdentities]
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<ViewState {...viewStateProps}>
|
|
34
|
+
<ViewStateHeading>{t('identity selector title')}</ViewStateHeading>
|
|
35
|
+
<RadioGroup.Root
|
|
36
|
+
disabled={disabled}
|
|
37
|
+
value={activeIdentity?.identityKey.toHex()}
|
|
38
|
+
onValueChange={onValueChange}
|
|
39
|
+
className='overflow-y-auto grow shrink min-bs-10 pli-1 -mli-1'
|
|
40
|
+
aria-label={t('identity radio group title')}
|
|
41
|
+
data-autofocus='identity selector'
|
|
42
|
+
>
|
|
43
|
+
{availableIdentities.map(({ displayName, identityKey }) => {
|
|
44
|
+
const hex = identityKey.toHex();
|
|
45
|
+
const inputId = `identitySelector__item--${hex}`;
|
|
46
|
+
const labelId = `identitySelector__itemLabel--${hex}`;
|
|
47
|
+
return (
|
|
48
|
+
<label htmlFor={inputId} key={hex} className='flex items-center mbe-2 gap-2 cursor-pointer'>
|
|
49
|
+
<RadioGroup.Item
|
|
50
|
+
id={inputId}
|
|
51
|
+
aria-labelledby={labelId}
|
|
52
|
+
value={hex}
|
|
53
|
+
className={mx(
|
|
54
|
+
'peer relative w-4 h-4 rounded-full',
|
|
55
|
+
'border border-transparent text-white',
|
|
56
|
+
'radix-state-checked:bg-primary-600',
|
|
57
|
+
'radix-state-unchecked:bg-neutral-100 dark:radix-state-unchecked:bg-neutral-900',
|
|
58
|
+
themeVariantFocus('os')
|
|
59
|
+
)}
|
|
60
|
+
data-testid='identity-selector-item'
|
|
61
|
+
>
|
|
62
|
+
<RadioGroup.Indicator className='absolute inset-0 flex items-center justify-center leading-0'>
|
|
63
|
+
<div className='w-1.5 h-1.5 rounded-full bg-white' />
|
|
64
|
+
</RadioGroup.Indicator>
|
|
65
|
+
</RadioGroup.Item>
|
|
66
|
+
<Avatar fallbackValue={hex} labelId={labelId} variant='circle' />
|
|
67
|
+
<span id={labelId} className={mx(!displayName && 'font-mono')}>
|
|
68
|
+
{displayName ?? identityKey.truncate() ?? ''}
|
|
69
|
+
</span>
|
|
70
|
+
</label>
|
|
71
|
+
);
|
|
72
|
+
})}
|
|
73
|
+
</RadioGroup.Root>
|
|
74
|
+
<Button
|
|
75
|
+
disabled={disabled}
|
|
76
|
+
compact
|
|
77
|
+
onClick={() => dispatch({ type: 'add identity' })}
|
|
78
|
+
className='flex items-center gap-2 pli-2'
|
|
79
|
+
data-testid='add-identity'
|
|
80
|
+
>
|
|
81
|
+
<span>{t('add identity label')}</span>
|
|
82
|
+
<UserPlus weight='bold' className={getSize(3.5)} />
|
|
83
|
+
</Button>
|
|
84
|
+
<Button
|
|
85
|
+
disabled={disabled || !activeIdentity}
|
|
86
|
+
className='flex items-center gap-2 pli-2'
|
|
87
|
+
onClick={() =>
|
|
88
|
+
dispatch({
|
|
89
|
+
type: 'select identity',
|
|
90
|
+
identity: activeIdentity
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
data-testid='select-identity'
|
|
94
|
+
>
|
|
95
|
+
<CaretLeft weight='bold' className={mx(getSize(4), 'invisible')} />
|
|
96
|
+
<span className='grow'>{t('continue label')}</span>
|
|
97
|
+
<CaretRight weight='bold' className={getSize(4)} />
|
|
98
|
+
</Button>
|
|
99
|
+
</ViewState>
|
|
100
|
+
);
|
|
101
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { CaretLeft, Check } from 'phosphor-react';
|
|
6
|
+
import React, { cloneElement } from 'react';
|
|
7
|
+
|
|
8
|
+
import { AuthenticatingInvitationObservable } from '@dxos/client';
|
|
9
|
+
import { InvitationResult, useInvitationStatus } from '@dxos/react-client';
|
|
10
|
+
import { Button, getSize, mx, useTranslation } from '@dxos/react-components';
|
|
11
|
+
|
|
12
|
+
import { ViewState, ViewStateProps } from './ViewState';
|
|
13
|
+
|
|
14
|
+
export interface InvitationAcceptedProps extends ViewStateProps {
|
|
15
|
+
invitationType: 'space' | 'halo';
|
|
16
|
+
doneActionParent?: Parameters<typeof cloneElement>[0];
|
|
17
|
+
onDone?: (result: InvitationResult | null) => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const PureInvitationAcceptedContent = ({
|
|
21
|
+
onDone,
|
|
22
|
+
result,
|
|
23
|
+
invitationType,
|
|
24
|
+
doneActionParent,
|
|
25
|
+
active
|
|
26
|
+
}: InvitationAcceptedProps & { result: InvitationResult | null }) => {
|
|
27
|
+
const disabled = !active;
|
|
28
|
+
const { t } = useTranslation('os');
|
|
29
|
+
|
|
30
|
+
const doneButton = (
|
|
31
|
+
<Button
|
|
32
|
+
{...(onDone && { onClick: () => onDone(result) })}
|
|
33
|
+
disabled={disabled}
|
|
34
|
+
className='grow flex items-center gap-2 pli-2'
|
|
35
|
+
data-autofocus={`${invitationType} invitation acceptor; invitation accepted`}
|
|
36
|
+
data-testid={`${invitationType}-invitation-accepted-done`}
|
|
37
|
+
>
|
|
38
|
+
<CaretLeft weight='bold' className={mx(getSize(2), 'invisible')} />
|
|
39
|
+
<span className='grow'>{t('done label')}</span>
|
|
40
|
+
<Check weight='bold' className={getSize(4)} />
|
|
41
|
+
</Button>
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
return doneActionParent ? cloneElement(doneActionParent, {}, doneButton) : doneButton;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const InvitationAcceptedContent = (props: InvitationAcceptedProps) => {
|
|
48
|
+
const { result } = useInvitationStatus(props.activeInvitation as AuthenticatingInvitationObservable);
|
|
49
|
+
return <PureInvitationAcceptedContent {...props} result={result} />;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const InvitationAccepted = (props: InvitationAcceptedProps) => {
|
|
53
|
+
const {
|
|
54
|
+
invitationType: _invitationType,
|
|
55
|
+
doneActionParent: _doneActionParent,
|
|
56
|
+
onDone: _onDone,
|
|
57
|
+
...viewStateProps
|
|
58
|
+
} = props;
|
|
59
|
+
const { activeInvitation } = viewStateProps;
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<ViewState {...viewStateProps}>
|
|
63
|
+
{!activeInvitation || activeInvitation === true ? (
|
|
64
|
+
<PureInvitationAcceptedContent {...props} result={null} />
|
|
65
|
+
) : (
|
|
66
|
+
<InvitationAcceptedContent {...props} />
|
|
67
|
+
)}
|
|
68
|
+
</ViewState>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { CaretLeft, CaretRight } from 'phosphor-react';
|
|
6
|
+
import React, { ChangeEvent, ComponentProps, ComponentPropsWithoutRef, useCallback, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
import { AuthenticatingInvitationObservable } from '@dxos/client';
|
|
9
|
+
import { useInvitationStatus } from '@dxos/react-client';
|
|
10
|
+
import { Button, getSize, Input, mx, useTranslation } from '@dxos/react-components';
|
|
11
|
+
|
|
12
|
+
import { ViewState, ViewStateProps } from './ViewState';
|
|
13
|
+
|
|
14
|
+
const pinLength = 6;
|
|
15
|
+
|
|
16
|
+
export interface InvitationAuthenticatorProps extends ViewStateProps {
|
|
17
|
+
invitationType: 'space' | 'halo';
|
|
18
|
+
failed?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const PureInvitationAuthenticatorContent = ({
|
|
22
|
+
disabled,
|
|
23
|
+
failed,
|
|
24
|
+
dispatch,
|
|
25
|
+
invitationType,
|
|
26
|
+
onChange,
|
|
27
|
+
onAuthenticate
|
|
28
|
+
}: {
|
|
29
|
+
disabled?: boolean;
|
|
30
|
+
dispatch: ViewStateProps['dispatch'];
|
|
31
|
+
invitationType: InvitationAuthenticatorProps['invitationType'];
|
|
32
|
+
failed: InvitationAuthenticatorProps['failed'];
|
|
33
|
+
onChange: ComponentProps<typeof Input>['onChange'];
|
|
34
|
+
onAuthenticate: ComponentProps<typeof Button>['onClick'];
|
|
35
|
+
}) => {
|
|
36
|
+
const { t } = useTranslation('os');
|
|
37
|
+
return (
|
|
38
|
+
<>
|
|
39
|
+
<Input
|
|
40
|
+
label={t('auth code input label')}
|
|
41
|
+
size='pin'
|
|
42
|
+
length={pinLength}
|
|
43
|
+
onChange={onChange}
|
|
44
|
+
disabled={disabled}
|
|
45
|
+
slots={{
|
|
46
|
+
root: { className: 'm-0' },
|
|
47
|
+
label: { className: 'sr-only' },
|
|
48
|
+
description: { className: 'text-center' },
|
|
49
|
+
input: {
|
|
50
|
+
disabled,
|
|
51
|
+
inputMode: 'numeric',
|
|
52
|
+
pattern: '\\d*',
|
|
53
|
+
'data-autofocus': `${invitationType} invitation acceptor; invitation authenticator`
|
|
54
|
+
} as ComponentPropsWithoutRef<'input'>
|
|
55
|
+
}}
|
|
56
|
+
{...(failed && {
|
|
57
|
+
validationValence: 'error',
|
|
58
|
+
validationMessage: t('failed to authenticate message')
|
|
59
|
+
})}
|
|
60
|
+
data-testid='auth-code-input'
|
|
61
|
+
/>
|
|
62
|
+
<div role='none' className='grow' />
|
|
63
|
+
<div className='flex gap-2'>
|
|
64
|
+
<Button
|
|
65
|
+
disabled={disabled}
|
|
66
|
+
className='grow flex items-center gap-2 pli-2 order-2'
|
|
67
|
+
onClick={onAuthenticate}
|
|
68
|
+
data-autofocus-pinlength={invitationType}
|
|
69
|
+
data-testid={`${invitationType}-invitation-authenticator-next`}
|
|
70
|
+
>
|
|
71
|
+
<CaretLeft weight='bold' className={mx(getSize(2), 'invisible')} />
|
|
72
|
+
<span className='grow'>{t('next label')}</span>
|
|
73
|
+
<CaretRight weight='bold' className={getSize(4)} />
|
|
74
|
+
</Button>
|
|
75
|
+
<Button
|
|
76
|
+
disabled={disabled}
|
|
77
|
+
className='flex items-center gap-2 pis-2 pie-4'
|
|
78
|
+
onClick={() => dispatch({ type: 'cancel invitation', from: invitationType })}
|
|
79
|
+
data-testid={`${invitationType}-invitation-authenticator-cancel`}
|
|
80
|
+
>
|
|
81
|
+
<CaretLeft weight='bold' className={getSize(4)} />
|
|
82
|
+
<span>{t('cancel label')}</span>
|
|
83
|
+
</Button>
|
|
84
|
+
</div>
|
|
85
|
+
</>
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const InvitationAuthenticatorContent = ({
|
|
90
|
+
disabled,
|
|
91
|
+
invitation,
|
|
92
|
+
dispatch,
|
|
93
|
+
invitationType,
|
|
94
|
+
failed
|
|
95
|
+
}: {
|
|
96
|
+
disabled?: boolean;
|
|
97
|
+
invitation: AuthenticatingInvitationObservable;
|
|
98
|
+
dispatch: ViewStateProps['dispatch'];
|
|
99
|
+
invitationType: InvitationAuthenticatorProps['invitationType'];
|
|
100
|
+
failed: InvitationAuthenticatorProps['failed'];
|
|
101
|
+
}) => {
|
|
102
|
+
const [pinValue, setPinValue] = useState('');
|
|
103
|
+
const { authenticate } = useInvitationStatus(invitation);
|
|
104
|
+
const onAuthenticate = useCallback(() => {
|
|
105
|
+
dispatch({ type: 'authenticating invitation', from: invitationType });
|
|
106
|
+
void authenticate(pinValue);
|
|
107
|
+
}, [dispatch, invitationType, authenticate, pinValue]);
|
|
108
|
+
const onChange = useCallback(
|
|
109
|
+
({ target: { value } }: ChangeEvent<HTMLInputElement>) => {
|
|
110
|
+
setPinValue(value);
|
|
111
|
+
if (value.length === pinLength) {
|
|
112
|
+
(document.querySelector(`[data-autofocus-pinlength="${invitationType}"]`) as HTMLElement | null)?.focus();
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
[authenticate, pinValue]
|
|
116
|
+
);
|
|
117
|
+
return (
|
|
118
|
+
<PureInvitationAuthenticatorContent {...{ disabled, failed, dispatch, invitationType, onChange, onAuthenticate }} />
|
|
119
|
+
);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const InvitationAuthenticator = ({
|
|
123
|
+
invitationType,
|
|
124
|
+
failed,
|
|
125
|
+
...viewStateProps
|
|
126
|
+
}: InvitationAuthenticatorProps) => {
|
|
127
|
+
const disabled = !viewStateProps.active;
|
|
128
|
+
const { activeInvitation, dispatch } = viewStateProps;
|
|
129
|
+
return (
|
|
130
|
+
<ViewState {...viewStateProps}>
|
|
131
|
+
{!activeInvitation || activeInvitation === true ? (
|
|
132
|
+
<PureInvitationAuthenticatorContent
|
|
133
|
+
{...{ disabled, failed, dispatch, invitationType, onChange: () => {}, onAuthenticate: () => {} }}
|
|
134
|
+
/>
|
|
135
|
+
) : (
|
|
136
|
+
<InvitationAuthenticatorContent
|
|
137
|
+
{...{ disabled, failed, invitation: activeInvitation, dispatch, invitationType }}
|
|
138
|
+
/>
|
|
139
|
+
)}
|
|
140
|
+
</ViewState>
|
|
141
|
+
);
|
|
142
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { CaretLeft, CaretRight } from 'phosphor-react';
|
|
6
|
+
import React, { ComponentPropsWithoutRef, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
import { InvitationEncoder } from '@dxos/client';
|
|
9
|
+
import { useClient } from '@dxos/react-client';
|
|
10
|
+
import { Button, getSize, Input, mx, useTranslation } from '@dxos/react-components';
|
|
11
|
+
|
|
12
|
+
import { ViewState, ViewStateHeading, ViewStateProps } from './ViewState';
|
|
13
|
+
|
|
14
|
+
export interface InvitationInputProps extends ViewStateProps {
|
|
15
|
+
invitationType: 'space' | 'halo';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const InvitationInput = ({ invitationType, ...viewStateProps }: InvitationInputProps) => {
|
|
19
|
+
const client = useClient();
|
|
20
|
+
const disabled = !viewStateProps.active;
|
|
21
|
+
const { dispatch } = viewStateProps;
|
|
22
|
+
const { t } = useTranslation('os');
|
|
23
|
+
|
|
24
|
+
const [inputValue, setInputValue] = useState('');
|
|
25
|
+
|
|
26
|
+
const handleNext = () =>
|
|
27
|
+
dispatch({
|
|
28
|
+
type: 'connecting invitation',
|
|
29
|
+
from: invitationType,
|
|
30
|
+
invitation: client[invitationType === 'halo' ? 'halo' : 'echo'].acceptInvitation(
|
|
31
|
+
InvitationEncoder.decode(inputValue)
|
|
32
|
+
)
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<ViewState {...viewStateProps}>
|
|
37
|
+
<Input
|
|
38
|
+
disabled={disabled}
|
|
39
|
+
label={<ViewStateHeading>{t('invitation input label')}</ViewStateHeading>}
|
|
40
|
+
onChange={({ target: { value } }) => setInputValue(value)}
|
|
41
|
+
slots={{
|
|
42
|
+
root: { className: 'm-0' },
|
|
43
|
+
label: { className: 'sr-only' },
|
|
44
|
+
input: {
|
|
45
|
+
'data-autofocus': `${invitationType} invitation acceptor; invitation input`,
|
|
46
|
+
onKeyUp: ({ key }) => key === 'Enter' && handleNext()
|
|
47
|
+
} as ComponentPropsWithoutRef<'input'>
|
|
48
|
+
}}
|
|
49
|
+
data-testid='invitation-input'
|
|
50
|
+
/>
|
|
51
|
+
<div role='none' className='grow' />
|
|
52
|
+
<div className='flex gap-2'>
|
|
53
|
+
<Button
|
|
54
|
+
disabled={disabled}
|
|
55
|
+
className='grow flex items-center gap-2 pli-2 order-2'
|
|
56
|
+
onClick={handleNext}
|
|
57
|
+
data-testid={`${invitationType}-invitation-input-continue`}
|
|
58
|
+
>
|
|
59
|
+
<CaretLeft weight='bold' className={mx(getSize(2), 'invisible')} />
|
|
60
|
+
<span className='grow'>{t('continue label')}</span>
|
|
61
|
+
<CaretRight weight='bold' className={getSize(4)} />
|
|
62
|
+
</Button>
|
|
63
|
+
<Button
|
|
64
|
+
disabled={disabled}
|
|
65
|
+
onClick={() => dispatch({ type: 'add identity' })}
|
|
66
|
+
className='flex items-center gap-2 pis-2 pie-4'
|
|
67
|
+
data-testid={`${invitationType}-invitation-input-back`}
|
|
68
|
+
>
|
|
69
|
+
<CaretLeft weight='bold' className={getSize(4)} />
|
|
70
|
+
<span>{t('back label')}</span>
|
|
71
|
+
</Button>
|
|
72
|
+
</div>
|
|
73
|
+
</ViewState>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { ArrowsClockwise, CaretLeft, CaretRight } from 'phosphor-react';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
|
|
8
|
+
import { AuthenticatingInvitationObservable, Invitation } from '@dxos/client';
|
|
9
|
+
import { useInvitationStatus } from '@dxos/react-client';
|
|
10
|
+
import { Button, getSize, mx, useTranslation } from '@dxos/react-components';
|
|
11
|
+
|
|
12
|
+
import { ViewState, ViewStateProps } from './ViewState';
|
|
13
|
+
|
|
14
|
+
export interface InvitationConnectorProps extends ViewStateProps {
|
|
15
|
+
invitationType: 'space' | 'halo';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const InvitationActions = ({
|
|
19
|
+
activeInvitation,
|
|
20
|
+
disabled,
|
|
21
|
+
dispatch,
|
|
22
|
+
invitationType
|
|
23
|
+
}: {
|
|
24
|
+
activeInvitation: AuthenticatingInvitationObservable;
|
|
25
|
+
disabled?: boolean;
|
|
26
|
+
dispatch: ViewStateProps['dispatch'];
|
|
27
|
+
invitationType: InvitationConnectorProps['invitationType'];
|
|
28
|
+
}) => {
|
|
29
|
+
const { status, cancel } = useInvitationStatus(activeInvitation);
|
|
30
|
+
const { t } = useTranslation('os');
|
|
31
|
+
|
|
32
|
+
switch (status) {
|
|
33
|
+
case Invitation.State.CONNECTING:
|
|
34
|
+
return (
|
|
35
|
+
<>
|
|
36
|
+
<div role='none' className='grow' />
|
|
37
|
+
<div className='flex gap-2'>
|
|
38
|
+
<Button disabled className='grow flex items-center gap-2 pli-2 order-2' data-testid='next'>
|
|
39
|
+
<CaretLeft weight='bold' className={mx(getSize(2), 'invisible')} />
|
|
40
|
+
<span className='grow'>{t('next label')}</span>
|
|
41
|
+
<CaretRight weight='bold' className={getSize(4)} />
|
|
42
|
+
</Button>
|
|
43
|
+
<Button
|
|
44
|
+
disabled={disabled}
|
|
45
|
+
className='flex items-center gap-2 pis-2 pie-4'
|
|
46
|
+
onClick={cancel}
|
|
47
|
+
data-autofocus={`${invitationType} invitation acceptor; invitation rescuer`}
|
|
48
|
+
data-testid='invitation-rescuer-cancel'
|
|
49
|
+
>
|
|
50
|
+
<CaretLeft weight='bold' className={getSize(4)} />
|
|
51
|
+
<span>{t('cancel label')}</span>
|
|
52
|
+
</Button>
|
|
53
|
+
</div>
|
|
54
|
+
</>
|
|
55
|
+
);
|
|
56
|
+
case Invitation.State.TIMEOUT:
|
|
57
|
+
case Invitation.State.CANCELLED:
|
|
58
|
+
case Invitation.State.ERROR:
|
|
59
|
+
default:
|
|
60
|
+
return (
|
|
61
|
+
<Button
|
|
62
|
+
disabled={disabled}
|
|
63
|
+
className='grow flex items-center gap-2 pli-2'
|
|
64
|
+
onClick={() => dispatch({ type: 'reset invitation', from: invitationType })}
|
|
65
|
+
data-autofocus={`${invitationType} invitation acceptor; invitation rescuer`}
|
|
66
|
+
data-testid='invitation-rescuer-reset'
|
|
67
|
+
>
|
|
68
|
+
<CaretLeft weight='bold' className={mx(getSize(5), 'invisible')} />
|
|
69
|
+
<span className='grow'>{t('reset label')}</span>
|
|
70
|
+
<ArrowsClockwise weight='bold' className={getSize(5)} />
|
|
71
|
+
</Button>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const InvitationRescuer = ({ invitationType, ...viewStateProps }: InvitationConnectorProps) => {
|
|
77
|
+
const disabled = !viewStateProps.active;
|
|
78
|
+
const { dispatch, activeInvitation } = viewStateProps;
|
|
79
|
+
const { t } = useTranslation('os');
|
|
80
|
+
return (
|
|
81
|
+
<ViewState {...viewStateProps}>
|
|
82
|
+
{activeInvitation === true || !activeInvitation ? (
|
|
83
|
+
<Button
|
|
84
|
+
disabled={disabled}
|
|
85
|
+
className='grow flex items-center gap-2 pli-2'
|
|
86
|
+
data-autofocus='space invitation acceptor; invitation rescuer'
|
|
87
|
+
data-testid='invitation-rescuer-connect'
|
|
88
|
+
>
|
|
89
|
+
<CaretLeft weight='bold' className={mx(getSize(5), 'invisible')} />
|
|
90
|
+
<span className='grow'>{t('connect label')}</span>
|
|
91
|
+
<ArrowsClockwise weight='bold' className={getSize(5)} />
|
|
92
|
+
</Button>
|
|
93
|
+
) : (
|
|
94
|
+
<InvitationActions {...{ activeInvitation, disabled, dispatch, invitationType }} />
|
|
95
|
+
)}
|
|
96
|
+
</ViewState>
|
|
97
|
+
);
|
|
98
|
+
};
|