@dxos/react-ui 0.1.31 → 0.1.32-next.86f5578
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 +1018 -870
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/IdentityList/SpaceMemberListContainer.d.ts.map +1 -1
- package/dist/types/src/composites/JoinDialog/JoinDialog.stories.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/JoinHeading.d.ts +2 -2
- package/dist/types/src/panels/JoinPanel/JoinHeading.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/JoinPanel.d.ts +1 -1
- package/dist/types/src/panels/JoinPanel/JoinPanel.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/JoinPanelProps.d.ts +1 -1
- package/dist/types/src/panels/JoinPanel/JoinPanelProps.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/joinMachine.d.ts +64 -0
- package/dist/types/src/panels/JoinPanel/joinMachine.d.ts.map +1 -0
- package/dist/types/src/panels/JoinPanel/view-states/AdditionMethodSelector.d.ts +2 -5
- package/dist/types/src/panels/JoinPanel/view-states/AdditionMethodSelector.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/view-states/InvitationAccepted.d.ts +1 -1
- package/dist/types/src/panels/JoinPanel/view-states/InvitationAccepted.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/view-states/InvitationAuthenticator.d.ts +2 -2
- package/dist/types/src/panels/JoinPanel/view-states/InvitationAuthenticator.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/view-states/InvitationInput.d.ts +2 -2
- package/dist/types/src/panels/JoinPanel/view-states/InvitationInput.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/view-states/InvitationRescuer.d.ts +2 -2
- package/dist/types/src/panels/JoinPanel/view-states/InvitationRescuer.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/view-states/ViewState.d.ts +4 -6
- package/dist/types/src/panels/JoinPanel/view-states/ViewState.d.ts.map +1 -1
- package/dist/types/src/panels/JoinPanel/view-states/index.d.ts +0 -1
- package/dist/types/src/panels/JoinPanel/view-states/index.d.ts.map +1 -1
- package/dist/types/src/playwright/invitations-manager.d.ts.map +1 -1
- package/dist/types/src/translations/locales/en-US.d.ts +3 -1
- package/dist/types/src/translations/locales/en-US.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +1 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/toEmoji.d.ts +2 -0
- package/dist/types/src/util/toEmoji.d.ts.map +1 -0
- package/package.json +18 -14
- package/src/components/IdentityList/SpaceMemberListContainer.tsx +6 -2
- package/src/components/InvitationList/InvitationList.stories.tsx +1 -1
- package/src/composites/JoinDialog/JoinDialog.stories.tsx +4 -2
- package/src/composites/Shell/Shell.stories.tsx +1 -1
- package/src/composites/Shell/Shell.tsx +1 -1
- package/src/panels/JoinPanel/JoinHeading.tsx +42 -40
- package/src/panels/JoinPanel/JoinPanel.tsx +140 -218
- package/src/panels/JoinPanel/JoinPanelProps.ts +1 -1
- package/src/panels/JoinPanel/joinMachine.ts +418 -0
- package/src/panels/JoinPanel/view-states/AdditionMethodSelector.tsx +9 -21
- package/src/panels/JoinPanel/view-states/IdentityAdded.tsx +6 -6
- package/src/panels/JoinPanel/view-states/IdentityInput.tsx +4 -4
- package/src/panels/JoinPanel/view-states/InvitationAccepted.tsx +22 -18
- package/src/panels/JoinPanel/view-states/InvitationAuthenticator.tsx +37 -28
- package/src/panels/JoinPanel/view-states/InvitationInput.tsx +21 -21
- package/src/panels/JoinPanel/view-states/InvitationRescuer.tsx +58 -41
- package/src/panels/JoinPanel/view-states/ViewState.tsx +11 -62
- package/src/panels/JoinPanel/view-states/index.ts +0 -1
- package/src/playwright/invitations-manager.ts +6 -2
- package/src/playwright/invitations.spec.ts +14 -5
- package/src/stories/Invitations.stories.tsx +2 -2
- package/src/translations/locales/en-US.ts +6 -4
- package/src/util/index.ts +1 -0
- package/src/util/toEmoji.ts +22 -0
- package/dist/types/src/panels/JoinPanel/view-states/IdentitySelector.d.ts +0 -7
- package/dist/types/src/panels/JoinPanel/view-states/IdentitySelector.d.ts.map +0 -1
- package/src/panels/JoinPanel/view-states/IdentitySelector.tsx +0 -100
|
@@ -9,49 +9,54 @@ import { AuthenticatingInvitationObservable } from '@dxos/client';
|
|
|
9
9
|
import { useInvitationStatus } from '@dxos/react-client';
|
|
10
10
|
import { Button, getSize, Input, mx, useTranslation } from '@dxos/react-components';
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import { JoinSend, JoinState } from '../joinMachine';
|
|
13
|
+
import { ViewState, ViewStateHeading, ViewStateProps } from './ViewState';
|
|
13
14
|
|
|
14
15
|
const pinLength = 6;
|
|
15
16
|
|
|
16
17
|
export interface InvitationAuthenticatorProps extends ViewStateProps {
|
|
17
|
-
|
|
18
|
+
Domain: 'Space' | 'Halo';
|
|
18
19
|
failed?: boolean;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
const PureInvitationAuthenticatorContent = ({
|
|
22
23
|
disabled,
|
|
23
24
|
failed,
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
joinSend,
|
|
26
|
+
joinState,
|
|
27
|
+
Domain,
|
|
26
28
|
onChange,
|
|
27
29
|
onAuthenticate
|
|
28
30
|
}: {
|
|
29
31
|
disabled?: boolean;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
joinSend: JoinSend;
|
|
33
|
+
joinState?: JoinState;
|
|
34
|
+
Domain: InvitationAuthenticatorProps['Domain'];
|
|
32
35
|
failed: InvitationAuthenticatorProps['failed'];
|
|
33
36
|
onChange: ComponentProps<typeof Input>['onChange'];
|
|
34
37
|
onAuthenticate: ComponentProps<typeof Button>['onClick'];
|
|
35
38
|
}) => {
|
|
36
39
|
const { t } = useTranslation('os');
|
|
40
|
+
const invitationType = Domain.toLowerCase() as 'space' | 'halo';
|
|
37
41
|
return (
|
|
38
42
|
<>
|
|
39
43
|
<Input
|
|
40
|
-
label={t('auth code input label')}
|
|
44
|
+
label={<ViewStateHeading>{t('auth code input label')}</ViewStateHeading>}
|
|
41
45
|
size='pin'
|
|
42
46
|
length={pinLength}
|
|
43
47
|
onChange={onChange}
|
|
44
48
|
disabled={disabled}
|
|
45
49
|
slots={{
|
|
46
50
|
root: { className: 'm-0' },
|
|
47
|
-
label: { className: 'sr-only' },
|
|
48
51
|
description: { className: 'text-center' },
|
|
49
52
|
input: {
|
|
50
53
|
disabled,
|
|
51
54
|
inputMode: 'numeric',
|
|
55
|
+
autoComplete: 'off',
|
|
52
56
|
pattern: '\\d*',
|
|
53
|
-
'data-autofocus':
|
|
54
|
-
'data-testid': `${invitationType}-auth-code-input
|
|
57
|
+
'data-autofocus': `connecting${Domain}Invitation inputting${Domain}VerificationCode authenticationFailing${Domain}VerificationCode authenticating${Domain}VerificationCode`,
|
|
58
|
+
'data-testid': `${invitationType}-auth-code-input`,
|
|
59
|
+
'data-1p-ignore': true
|
|
55
60
|
} as ComponentPropsWithoutRef<'input'>
|
|
56
61
|
}}
|
|
57
62
|
{...(failed && {
|
|
@@ -75,7 +80,7 @@ const PureInvitationAuthenticatorContent = ({
|
|
|
75
80
|
<Button
|
|
76
81
|
disabled={disabled}
|
|
77
82
|
className='flex items-center gap-2 pis-2 pie-4'
|
|
78
|
-
onClick={() =>
|
|
83
|
+
onClick={() => joinState?.context[invitationType].invitationObservable?.cancel()}
|
|
79
84
|
data-testid={`${invitationType}-invitation-authenticator-cancel`}
|
|
80
85
|
>
|
|
81
86
|
<CaretLeft weight='bold' className={getSize(4)} />
|
|
@@ -87,24 +92,27 @@ const PureInvitationAuthenticatorContent = ({
|
|
|
87
92
|
};
|
|
88
93
|
|
|
89
94
|
const InvitationAuthenticatorContent = ({
|
|
95
|
+
joinSend,
|
|
96
|
+
joinState,
|
|
90
97
|
disabled,
|
|
91
98
|
invitation,
|
|
92
|
-
|
|
93
|
-
invitationType,
|
|
99
|
+
Domain,
|
|
94
100
|
failed
|
|
95
101
|
}: {
|
|
102
|
+
joinSend: JoinSend;
|
|
103
|
+
joinState?: JoinState;
|
|
96
104
|
disabled?: boolean;
|
|
97
105
|
invitation: AuthenticatingInvitationObservable;
|
|
98
|
-
|
|
99
|
-
invitationType: InvitationAuthenticatorProps['invitationType'];
|
|
106
|
+
Domain: InvitationAuthenticatorProps['Domain'];
|
|
100
107
|
failed: InvitationAuthenticatorProps['failed'];
|
|
101
108
|
}) => {
|
|
109
|
+
const invitationType = Domain.toLowerCase();
|
|
102
110
|
const [pinValue, setPinValue] = useState('');
|
|
103
111
|
const { authenticate } = useInvitationStatus(invitation);
|
|
104
112
|
const onAuthenticate = useCallback(() => {
|
|
105
|
-
|
|
113
|
+
joinSend({ type: `authenticate${Domain}VerificationCode` });
|
|
106
114
|
void authenticate(pinValue);
|
|
107
|
-
}, [
|
|
115
|
+
}, [joinSend, authenticate, pinValue]);
|
|
108
116
|
const onChange = useCallback(
|
|
109
117
|
({ target: { value } }: ChangeEvent<HTMLInputElement>) => {
|
|
110
118
|
setPinValue(value);
|
|
@@ -115,26 +123,27 @@ const InvitationAuthenticatorContent = ({
|
|
|
115
123
|
[authenticate, pinValue]
|
|
116
124
|
);
|
|
117
125
|
return (
|
|
118
|
-
<PureInvitationAuthenticatorContent
|
|
126
|
+
<PureInvitationAuthenticatorContent
|
|
127
|
+
{...{ disabled, failed, joinSend, joinState, Domain, onChange, onAuthenticate }}
|
|
128
|
+
/>
|
|
119
129
|
);
|
|
120
130
|
};
|
|
121
131
|
|
|
122
|
-
export const InvitationAuthenticator = ({
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
const { activeInvitation, dispatch } = viewStateProps;
|
|
132
|
+
export const InvitationAuthenticator = ({ failed, Domain, ...viewStateProps }: InvitationAuthenticatorProps) => {
|
|
133
|
+
const { joinSend, joinState } = viewStateProps;
|
|
134
|
+
const disabled =
|
|
135
|
+
!viewStateProps.active ||
|
|
136
|
+
['connecting', 'authenticating'].some((str) => joinState?.configuration[0].id.includes(str));
|
|
137
|
+
const activeInvitation = joinState?.context[Domain.toLowerCase() as 'space' | 'halo'].invitationObservable;
|
|
129
138
|
return (
|
|
130
139
|
<ViewState {...viewStateProps}>
|
|
131
|
-
{!activeInvitation
|
|
140
|
+
{!activeInvitation ? (
|
|
132
141
|
<PureInvitationAuthenticatorContent
|
|
133
|
-
{...{ disabled, failed,
|
|
142
|
+
{...{ disabled, failed, joinSend, joinState, Domain, onChange: () => {}, onAuthenticate: () => {} }}
|
|
134
143
|
/>
|
|
135
144
|
) : (
|
|
136
145
|
<InvitationAuthenticatorContent
|
|
137
|
-
{...{ disabled, failed, invitation: activeInvitation,
|
|
146
|
+
{...{ disabled, failed, invitation: activeInvitation, joinSend, joinState, Domain }}
|
|
138
147
|
/>
|
|
139
148
|
)}
|
|
140
149
|
</ViewState>
|
|
@@ -3,33 +3,33 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { CaretLeft, CaretRight } from '@phosphor-icons/react';
|
|
6
|
-
import React, { ComponentPropsWithoutRef, useState } from 'react';
|
|
6
|
+
import React, { ComponentPropsWithoutRef, useEffect, useState } from 'react';
|
|
7
7
|
|
|
8
|
-
import { InvitationEncoder } from '@dxos/client';
|
|
9
|
-
import { useClient } from '@dxos/react-client';
|
|
10
8
|
import { Button, getSize, Input, mx, useTranslation } from '@dxos/react-components';
|
|
11
9
|
|
|
12
10
|
import { ViewState, ViewStateHeading, ViewStateProps } from './ViewState';
|
|
13
11
|
|
|
14
12
|
export interface InvitationInputProps extends ViewStateProps {
|
|
15
|
-
|
|
13
|
+
Domain: 'Space' | 'Halo';
|
|
16
14
|
}
|
|
17
15
|
|
|
18
|
-
export const InvitationInput = ({
|
|
19
|
-
const client = useClient();
|
|
16
|
+
export const InvitationInput = ({ Domain, ...viewStateProps }: InvitationInputProps) => {
|
|
20
17
|
const disabled = !viewStateProps.active;
|
|
21
|
-
const {
|
|
18
|
+
const { joinSend, joinState } = viewStateProps;
|
|
22
19
|
const { t } = useTranslation('os');
|
|
23
20
|
|
|
24
|
-
const
|
|
21
|
+
const contextUnredeemedCode = joinState?.context[Domain.toLowerCase() as 'space' | 'halo'].unredeemedCode;
|
|
22
|
+
|
|
23
|
+
const [inputValue, setInputValue] = useState(contextUnredeemedCode ?? '');
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
contextUnredeemedCode && setInputValue(contextUnredeemedCode ?? '');
|
|
27
|
+
}, [contextUnredeemedCode]);
|
|
25
28
|
|
|
26
29
|
const handleNext = () =>
|
|
27
|
-
|
|
28
|
-
type:
|
|
29
|
-
|
|
30
|
-
invitation: client[invitationType === 'halo' ? 'halo' : 'echo'].acceptInvitation(
|
|
31
|
-
InvitationEncoder.decode(inputValue)
|
|
32
|
-
)
|
|
30
|
+
joinSend({
|
|
31
|
+
type: `set${Domain}InvitationCode`,
|
|
32
|
+
code: inputValue
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
return (
|
|
@@ -37,13 +37,13 @@ export const InvitationInput = ({ invitationType, ...viewStateProps }: Invitatio
|
|
|
37
37
|
<Input
|
|
38
38
|
disabled={disabled}
|
|
39
39
|
label={<ViewStateHeading>{t('invitation input label')}</ViewStateHeading>}
|
|
40
|
+
value={inputValue}
|
|
40
41
|
onChange={({ target: { value } }) => setInputValue(value)}
|
|
41
42
|
slots={{
|
|
42
43
|
root: { className: 'm-0' },
|
|
43
|
-
label: { className: 'sr-only' },
|
|
44
44
|
input: {
|
|
45
|
-
'data-autofocus':
|
|
46
|
-
'data-testid': `${
|
|
45
|
+
'data-autofocus': `inputting${Domain}InvitationCode`,
|
|
46
|
+
'data-testid': `${Domain.toLowerCase()}-invitation-input`,
|
|
47
47
|
onKeyUp: ({ key }) => key === 'Enter' && handleNext()
|
|
48
48
|
} as ComponentPropsWithoutRef<'input'>
|
|
49
49
|
}}
|
|
@@ -54,17 +54,17 @@ export const InvitationInput = ({ invitationType, ...viewStateProps }: Invitatio
|
|
|
54
54
|
disabled={disabled}
|
|
55
55
|
className='grow flex items-center gap-2 pli-2 order-2'
|
|
56
56
|
onClick={handleNext}
|
|
57
|
-
data-testid={`${
|
|
57
|
+
data-testid={`${Domain.toLowerCase()}-invitation-input-continue`}
|
|
58
58
|
>
|
|
59
59
|
<CaretLeft weight='bold' className={mx(getSize(2), 'invisible')} />
|
|
60
60
|
<span className='grow'>{t('continue label')}</span>
|
|
61
61
|
<CaretRight weight='bold' className={getSize(4)} />
|
|
62
62
|
</Button>
|
|
63
63
|
<Button
|
|
64
|
-
disabled={
|
|
65
|
-
onClick={() =>
|
|
64
|
+
disabled={Domain === 'Space'}
|
|
65
|
+
onClick={() => joinSend({ type: 'deselectAuthMethod' })}
|
|
66
66
|
className='flex items-center gap-2 pis-2 pie-4'
|
|
67
|
-
data-testid={`${
|
|
67
|
+
data-testid={`${Domain.toLowerCase()}-invitation-input-back`}
|
|
68
68
|
>
|
|
69
69
|
<CaretLeft weight='bold' className={getSize(4)} />
|
|
70
70
|
<span>{t('back label')}</span>
|
|
@@ -5,34 +5,36 @@
|
|
|
5
5
|
import { ArrowsClockwise, CaretLeft, CaretRight } from '@phosphor-icons/react';
|
|
6
6
|
import React from 'react';
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { Button, getSize, mx, useTranslation } from '@dxos/react-components';
|
|
8
|
+
import { Invitation } from '@dxos/client';
|
|
9
|
+
import { Button, defaultDescription, getSize, mx, useTranslation } from '@dxos/react-components';
|
|
11
10
|
|
|
12
|
-
import {
|
|
11
|
+
import { JoinSend, JoinState } from '../joinMachine';
|
|
12
|
+
import { ViewState, ViewStateHeading, ViewStateProps } from './ViewState';
|
|
13
13
|
|
|
14
14
|
export interface InvitationConnectorProps extends ViewStateProps {
|
|
15
|
-
|
|
15
|
+
Domain: 'Space' | 'Halo';
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
const InvitationActions = ({
|
|
19
|
-
|
|
19
|
+
invitationState,
|
|
20
20
|
disabled,
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
joinSend,
|
|
22
|
+
joinState,
|
|
23
|
+
Domain
|
|
23
24
|
}: {
|
|
24
|
-
|
|
25
|
+
invitationState?: Invitation.State;
|
|
25
26
|
disabled?: boolean;
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
joinSend: JoinSend;
|
|
28
|
+
joinState?: JoinState;
|
|
29
|
+
Domain: InvitationConnectorProps['Domain'];
|
|
28
30
|
}) => {
|
|
29
|
-
const { status, cancel } = useInvitationStatus(activeInvitation);
|
|
30
31
|
const { t } = useTranslation('os');
|
|
31
|
-
|
|
32
|
-
switch (
|
|
32
|
+
const invitationType = Domain.toLowerCase() as 'space' | 'halo';
|
|
33
|
+
switch (invitationState) {
|
|
33
34
|
case Invitation.State.CONNECTING:
|
|
34
35
|
return (
|
|
35
36
|
<>
|
|
37
|
+
<ViewStateHeading className={defaultDescription}>{t('connecting status label')}</ViewStateHeading>
|
|
36
38
|
<div role='none' className='grow' />
|
|
37
39
|
<div className='flex gap-2'>
|
|
38
40
|
<Button disabled className='grow flex items-center gap-2 pli-2 order-2' data-testid='next'>
|
|
@@ -43,8 +45,7 @@ const InvitationActions = ({
|
|
|
43
45
|
<Button
|
|
44
46
|
disabled={disabled}
|
|
45
47
|
className='flex items-center gap-2 pis-2 pie-4'
|
|
46
|
-
onClick={cancel}
|
|
47
|
-
data-autofocus={`${invitationType} invitation acceptor; invitation rescuer`}
|
|
48
|
+
onClick={() => joinState?.context[invitationType].invitationObservable?.cancel()}
|
|
48
49
|
data-testid='invitation-rescuer-cancel'
|
|
49
50
|
>
|
|
50
51
|
<CaretLeft weight='bold' className={getSize(4)} />
|
|
@@ -58,40 +59,56 @@ const InvitationActions = ({
|
|
|
58
59
|
case Invitation.State.ERROR:
|
|
59
60
|
default:
|
|
60
61
|
return (
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
62
|
+
<>
|
|
63
|
+
<ViewStateHeading className={defaultDescription}>
|
|
64
|
+
{t(
|
|
65
|
+
invitationState === Invitation.State.TIMEOUT
|
|
66
|
+
? 'timeout status label'
|
|
67
|
+
: invitationState === Invitation.State.CANCELLED
|
|
68
|
+
? 'cancelled status label'
|
|
69
|
+
: 'error status label'
|
|
70
|
+
)}
|
|
71
|
+
</ViewStateHeading>
|
|
72
|
+
<div role='none' className='grow' />
|
|
73
|
+
<Button
|
|
74
|
+
disabled={disabled}
|
|
75
|
+
className='flex items-center gap-2 pli-2'
|
|
76
|
+
onClick={() => joinSend({ type: `reset${Domain}Invitation` })}
|
|
77
|
+
data-testid='invitation-rescuer-reset'
|
|
78
|
+
>
|
|
79
|
+
<CaretLeft weight='bold' className={mx(getSize(5), 'invisible')} />
|
|
80
|
+
<span className='grow'>{t('reset label')}</span>
|
|
81
|
+
<ArrowsClockwise className={getSize(4)} />
|
|
82
|
+
</Button>
|
|
83
|
+
</>
|
|
72
84
|
);
|
|
73
85
|
}
|
|
74
86
|
};
|
|
75
87
|
|
|
76
|
-
export const InvitationRescuer = ({
|
|
88
|
+
export const InvitationRescuer = ({ Domain, ...viewStateProps }: InvitationConnectorProps) => {
|
|
77
89
|
const disabled = !viewStateProps.active;
|
|
78
|
-
const {
|
|
90
|
+
const { joinSend, joinState } = viewStateProps;
|
|
91
|
+
const invitationState = joinState?.context[Domain.toLowerCase() as 'space' | 'halo'].invitation?.state;
|
|
79
92
|
const { t } = useTranslation('os');
|
|
80
93
|
return (
|
|
81
94
|
<ViewState {...viewStateProps}>
|
|
82
|
-
{
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
95
|
+
{typeof invitationState === 'undefined' ? (
|
|
96
|
+
<>
|
|
97
|
+
<div role='none' className='grow' />
|
|
98
|
+
<Button
|
|
99
|
+
disabled={disabled}
|
|
100
|
+
className='flex items-center gap-2 pli-2'
|
|
101
|
+
data-autofocus={`inputting${Domain}InvitationCode`}
|
|
102
|
+
data-testid='invitation-rescuer-blank-reset'
|
|
103
|
+
onClick={() => joinSend({ type: `reset${Domain}Invitation` })}
|
|
104
|
+
>
|
|
105
|
+
<CaretLeft weight='bold' className={mx(getSize(5), 'invisible')} />
|
|
106
|
+
<span className='grow'>{t('reset label')}</span>
|
|
107
|
+
<ArrowsClockwise className={getSize(5)} />
|
|
108
|
+
</Button>
|
|
109
|
+
</>
|
|
93
110
|
) : (
|
|
94
|
-
<InvitationActions {...{
|
|
111
|
+
<InvitationActions {...{ invitationState, disabled, joinSend, joinState, Domain }} />
|
|
95
112
|
)}
|
|
96
113
|
</ViewState>
|
|
97
114
|
);
|
|
@@ -5,27 +5,26 @@
|
|
|
5
5
|
import { CheckCircle, HourglassSimple, X } from '@phosphor-icons/react';
|
|
6
6
|
import React, { ComponentProps, ComponentPropsWithoutRef, ReactNode, useMemo } from 'react';
|
|
7
7
|
|
|
8
|
-
import type { AuthenticatingInvitationObservable
|
|
8
|
+
import type { AuthenticatingInvitationObservable } from '@dxos/client';
|
|
9
9
|
import { Invitation } from '@dxos/client';
|
|
10
10
|
import { useInvitationStatus } from '@dxos/react-client';
|
|
11
|
-
import { mx, useTranslation,
|
|
11
|
+
import { mx, useTranslation, useId, getSize, strongShimmer } from '@dxos/react-components';
|
|
12
12
|
|
|
13
|
-
import { defaultSurface,
|
|
13
|
+
import { defaultSurface, resolvedBgColor, activeBgColor, inactiveBgColor } from '../../../styles';
|
|
14
14
|
import { invitationStatusValue } from '../../../util';
|
|
15
|
-
import {
|
|
15
|
+
import { JoinSend, JoinState } from '../joinMachine';
|
|
16
16
|
|
|
17
17
|
export interface ViewStateProps extends ComponentProps<'div'> {
|
|
18
18
|
active: boolean;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
activeInvitation?: true | AuthenticatingInvitationObservable;
|
|
19
|
+
joinSend: JoinSend;
|
|
20
|
+
joinState?: JoinState;
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
const stripe = mx('rounded-full grow', getSize(3));
|
|
25
24
|
|
|
26
25
|
export const ViewStateHeading = ({ children, className, ...props }: ComponentPropsWithoutRef<'h2'>) => {
|
|
27
26
|
return (
|
|
28
|
-
<h2 {...props} className={mx('font-system-
|
|
27
|
+
<h2 {...props} className={mx('font-system-normal text-sm mbe-1 mli-1 text-center', className)}>
|
|
29
28
|
{children}
|
|
30
29
|
</h2>
|
|
31
30
|
);
|
|
@@ -74,7 +73,8 @@ const PureViewStateInvitation = ({
|
|
|
74
73
|
);
|
|
75
74
|
};
|
|
76
75
|
|
|
77
|
-
|
|
76
|
+
// todo (thure): Restore this?
|
|
77
|
+
const _ViewStateInvitationStatus = ({ activeInvitation }: { activeInvitation: AuthenticatingInvitationObservable }) => {
|
|
78
78
|
const { t } = useTranslation('os');
|
|
79
79
|
const { status, haltedAt } = useInvitationStatus(activeInvitation);
|
|
80
80
|
|
|
@@ -136,19 +136,8 @@ const ViewStateInvitationStatus = ({ activeInvitation }: { activeInvitation: Aut
|
|
|
136
136
|
);
|
|
137
137
|
};
|
|
138
138
|
|
|
139
|
-
export const ViewState = ({
|
|
140
|
-
active,
|
|
141
|
-
children,
|
|
142
|
-
className,
|
|
143
|
-
dispatch,
|
|
144
|
-
selectedIdentity,
|
|
145
|
-
activeInvitation,
|
|
146
|
-
...props
|
|
147
|
-
}: ViewStateProps) => {
|
|
139
|
+
export const ViewState = ({ active, children, className, joinSend, joinState, ...props }: ViewStateProps) => {
|
|
148
140
|
// note (thure): reserve `order-1` and `order-3` for outgoing steps in different directions
|
|
149
|
-
const { t } = useTranslation('os');
|
|
150
|
-
const identityLabel = useId('selectedIdentityLabel');
|
|
151
|
-
|
|
152
141
|
return (
|
|
153
142
|
<div
|
|
154
143
|
role='none'
|
|
@@ -156,47 +145,7 @@ export const ViewState = ({
|
|
|
156
145
|
{...(!active && { 'aria-hidden': true })}
|
|
157
146
|
className={mx('is-[50%] flex flex-col', active ? 'order-2' : 'order-4', className)}
|
|
158
147
|
>
|
|
159
|
-
{
|
|
160
|
-
<div role='none' className={mx(subduedSurface, 'flex-none flex items-center gap-1 pli-2 pbe-1.5')}>
|
|
161
|
-
<Trans
|
|
162
|
-
{...{
|
|
163
|
-
defaults: t('join space as identity heading'),
|
|
164
|
-
components: {
|
|
165
|
-
icon: (
|
|
166
|
-
<Avatar
|
|
167
|
-
size={4}
|
|
168
|
-
fallbackValue={selectedIdentity === true ? '' : selectedIdentity.identityKey.toHex()}
|
|
169
|
-
labelId={identityLabel}
|
|
170
|
-
/>
|
|
171
|
-
),
|
|
172
|
-
label: <span id={identityLabel} />,
|
|
173
|
-
part: <span role='none' className='flex items-center gap-1 leading-none' />
|
|
174
|
-
},
|
|
175
|
-
values: {
|
|
176
|
-
labelValue:
|
|
177
|
-
selectedIdentity === true
|
|
178
|
-
? ' '
|
|
179
|
-
: selectedIdentity.profile?.displayName ?? selectedIdentity.identityKey.truncate()
|
|
180
|
-
}
|
|
181
|
-
}}
|
|
182
|
-
/>
|
|
183
|
-
</div>
|
|
184
|
-
)}
|
|
185
|
-
{activeInvitation &&
|
|
186
|
-
(activeInvitation === true ? (
|
|
187
|
-
<PureViewStateInvitation
|
|
188
|
-
{...{
|
|
189
|
-
halted: false,
|
|
190
|
-
status: Invitation.State.INIT,
|
|
191
|
-
label: t('invitation input label'),
|
|
192
|
-
cursor: 0,
|
|
193
|
-
resolvedColor: inactiveBgColor
|
|
194
|
-
}}
|
|
195
|
-
/>
|
|
196
|
-
) : (
|
|
197
|
-
<ViewStateInvitationStatus {...{ activeInvitation }} />
|
|
198
|
-
))}
|
|
199
|
-
<div role='region' className={mx(defaultSurface, 'rounded-be-md grow shrink-0 flex flex-col gap-1 p-2')}>
|
|
148
|
+
<div role='region' className={mx(defaultSurface, 'rounded-be-md grow shrink-0 flex flex-col gap-1 p-4 pbs-1')}>
|
|
200
149
|
{children}
|
|
201
150
|
</div>
|
|
202
151
|
</div>
|
|
@@ -141,7 +141,9 @@ export class InvitationsManager {
|
|
|
141
141
|
async acceptInvitation(id: number, type: 'device' | 'space', invitation: string) {
|
|
142
142
|
const peer = this._peer(id);
|
|
143
143
|
// TODO(wittjosiah): Update ids.
|
|
144
|
-
|
|
144
|
+
if (type === 'device') {
|
|
145
|
+
await peer.getByTestId('join-identity').click();
|
|
146
|
+
}
|
|
145
147
|
await peer.getByTestId(`${type === 'device' ? 'halo' : 'space'}-invitation-input`).type(invitation);
|
|
146
148
|
await this.page.keyboard.press('Enter');
|
|
147
149
|
}
|
|
@@ -189,7 +191,9 @@ export class InvitationsManager {
|
|
|
189
191
|
async doneInvitation(id: number, type: 'device' | 'space') {
|
|
190
192
|
const peer = this._peer(id);
|
|
191
193
|
// TODO(wittjosiah): Update ids.
|
|
192
|
-
await peer
|
|
194
|
+
await peer
|
|
195
|
+
.getByTestId(type === 'device' ? 'halo-invitation-accepted-done' : 'space-invitation-accepted-done')
|
|
196
|
+
.click();
|
|
193
197
|
}
|
|
194
198
|
|
|
195
199
|
// TODO(wittjosiah): Peer manager?
|
|
@@ -104,9 +104,10 @@ test.describe('Invitations', () => {
|
|
|
104
104
|
await manager.acceptInvitation(1, 'device', invitation);
|
|
105
105
|
await manager.cancelInvitation(1, 'device', 'guest');
|
|
106
106
|
await manager.resetInvitation(1);
|
|
107
|
+
await manager.invitationInputContinue(1, 'device');
|
|
107
108
|
const [authenticationCode] = await Promise.all([
|
|
108
109
|
manager.getAuthenticationCode(),
|
|
109
|
-
manager.
|
|
110
|
+
manager.clearAuthenticationCode(1, 'device')
|
|
110
111
|
]);
|
|
111
112
|
await manager.authenticateInvitation(1, 'device', authenticationCode);
|
|
112
113
|
await manager.doneInvitation(1, 'device');
|
|
@@ -126,9 +127,10 @@ test.describe('Invitations', () => {
|
|
|
126
127
|
await sleep(100);
|
|
127
128
|
await manager.setConnectionState(0, ConnectionState.ONLINE);
|
|
128
129
|
await manager.resetInvitation(1);
|
|
130
|
+
await manager.invitationInputContinue(1, 'device');
|
|
129
131
|
const [authenticationCode] = await Promise.all([
|
|
130
132
|
manager.getAuthenticationCode(),
|
|
131
|
-
manager.
|
|
133
|
+
manager.clearAuthenticationCode(1, 'device')
|
|
132
134
|
]);
|
|
133
135
|
await manager.authenticateInvitation(1, 'device', authenticationCode);
|
|
134
136
|
await manager.doneInvitation(1, 'device');
|
|
@@ -243,10 +245,13 @@ test.describe('Invitations', () => {
|
|
|
243
245
|
expect(await manager.invitationFailed(1)).to.be.true;
|
|
244
246
|
|
|
245
247
|
await manager.resetInvitation(1);
|
|
248
|
+
await manager.invitationInputContinue(1, 'space');
|
|
249
|
+
|
|
246
250
|
const [authenticationCode] = await Promise.all([
|
|
247
251
|
manager.getAuthenticationCode(),
|
|
248
|
-
manager.
|
|
252
|
+
manager.clearAuthenticationCode(1, 'space')
|
|
249
253
|
]);
|
|
254
|
+
|
|
250
255
|
await manager.authenticateInvitation(1, 'space', authenticationCode);
|
|
251
256
|
await manager.doneInvitation(1, 'space');
|
|
252
257
|
|
|
@@ -292,10 +297,13 @@ test.describe('Invitations', () => {
|
|
|
292
297
|
await manager.acceptInvitation(1, 'space', invitation);
|
|
293
298
|
await manager.cancelInvitation(1, 'space', 'guest');
|
|
294
299
|
await manager.resetInvitation(1);
|
|
300
|
+
await manager.invitationInputContinue(1, 'space');
|
|
301
|
+
|
|
295
302
|
const [authenticationCode] = await Promise.all([
|
|
296
303
|
manager.getAuthenticationCode(),
|
|
297
|
-
manager.
|
|
304
|
+
manager.clearAuthenticationCode(1, 'space')
|
|
298
305
|
]);
|
|
306
|
+
|
|
299
307
|
await manager.authenticateInvitation(1, 'space', authenticationCode);
|
|
300
308
|
await manager.doneInvitation(1, 'space');
|
|
301
309
|
|
|
@@ -317,9 +325,10 @@ test.describe('Invitations', () => {
|
|
|
317
325
|
await sleep(100);
|
|
318
326
|
await manager.setConnectionState(0, ConnectionState.ONLINE);
|
|
319
327
|
await manager.resetInvitation(1);
|
|
328
|
+
await manager.invitationInputContinue(1, 'space');
|
|
320
329
|
const [authenticationCode] = await Promise.all([
|
|
321
330
|
manager.getAuthenticationCode(),
|
|
322
|
-
manager.
|
|
331
|
+
manager.clearAuthenticationCode(1, 'space')
|
|
323
332
|
]);
|
|
324
333
|
await manager.authenticateInvitation(1, 'space', authenticationCode);
|
|
325
334
|
await manager.doneInvitation(1, 'space');
|
|
@@ -55,7 +55,7 @@ const Panel = ({ id, panel, setPanel }: { id: number; panel?: PanelType; setPane
|
|
|
55
55
|
<ButtonGroup className='mbe-4'>
|
|
56
56
|
{/* <Tooltip content='Create Space'> */}
|
|
57
57
|
<Button
|
|
58
|
-
onClick={() => client.
|
|
58
|
+
onClick={() => client.createSpace({ name: faker.animal.bird() })}
|
|
59
59
|
data-testid='invitations.create-space'
|
|
60
60
|
>
|
|
61
61
|
<PlusCircle className={getSize(6)} />
|
|
@@ -118,7 +118,7 @@ const render = ({ id }: { id: number }) => {
|
|
|
118
118
|
<Tooltip content='Join Existing Identity'> */}
|
|
119
119
|
<Button
|
|
120
120
|
onClick={() => setPanel('identity')}
|
|
121
|
-
disabled={panel === 'identity'}
|
|
121
|
+
disabled={Boolean(identity) || panel === 'identity'}
|
|
122
122
|
data-testid='invitations.open-join-identity'
|
|
123
123
|
>
|
|
124
124
|
<QrCode weight='fill' className={getSize(6)} />
|
|
@@ -8,7 +8,7 @@ export const os = {
|
|
|
8
8
|
'sidebar label': 'DXOS sidebar',
|
|
9
9
|
'copy invitation code label': 'Copy',
|
|
10
10
|
'open share panel label': 'Share',
|
|
11
|
-
'joining heading': '
|
|
11
|
+
'joining space heading': 'Joining space',
|
|
12
12
|
'join space heading': 'Join a space',
|
|
13
13
|
'halo heading': 'Initialize device identity',
|
|
14
14
|
'exit label': 'Exit',
|
|
@@ -27,7 +27,7 @@ export const os = {
|
|
|
27
27
|
'join identity label': 'Use an authed device',
|
|
28
28
|
'join identity description': 'Add this device to an identity you’re already logged into on another device.',
|
|
29
29
|
'deselect identity label': 'Back to identities',
|
|
30
|
-
'addition method selector title': '
|
|
30
|
+
'addition method selector title': 'An identity is required to continue',
|
|
31
31
|
'new identity input label': 'Add a display name',
|
|
32
32
|
'recover identity input label': 'Type or paste your seed phrase',
|
|
33
33
|
'failed to create identity message': 'Failed to create an identity.',
|
|
@@ -48,7 +48,7 @@ export const os = {
|
|
|
48
48
|
'cancel label': 'Cancel',
|
|
49
49
|
'done label': 'Done',
|
|
50
50
|
'reset label': 'Start over',
|
|
51
|
-
'auth code input label': '
|
|
51
|
+
'auth code input label': 'Enter the verification code',
|
|
52
52
|
'invitation input label': 'Paste an invitation code or URL',
|
|
53
53
|
'create device invitation label': 'Invite',
|
|
54
54
|
'create space invitation label': 'Invite',
|
|
@@ -62,5 +62,7 @@ export const os = {
|
|
|
62
62
|
'view space invitations label': 'View space invitations',
|
|
63
63
|
'toggle sidebar label': 'Open/close sidebar',
|
|
64
64
|
'open sidebar label': 'Open sidebar',
|
|
65
|
-
'close sidebar label': 'Close sidebar'
|
|
65
|
+
'close sidebar label': 'Close sidebar',
|
|
66
|
+
'welcome message': 'Welcome',
|
|
67
|
+
'selecting identity heading': 'Selecting identity'
|
|
66
68
|
};
|
package/src/util/index.ts
CHANGED