@dxos/react-ui 0.1.26 → 0.1.27-next.6f5e1d5

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.
Files changed (142) hide show
  1. package/dist/lib/browser/index.mjs +1025 -473
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/components/IdentityList/DeviceList.d.ts +7 -0
  5. package/dist/types/src/components/IdentityList/DeviceList.d.ts.map +1 -0
  6. package/dist/types/src/components/IdentityList/IdentityListItem.d.ts +4 -3
  7. package/dist/types/src/components/IdentityList/IdentityListItem.d.ts.map +1 -1
  8. package/dist/types/src/components/IdentityList/IdentityListItem.stories.d.ts +3 -2
  9. package/dist/types/src/components/IdentityList/IdentityListItem.stories.d.ts.map +1 -1
  10. package/dist/types/src/components/IdentityList/SpaceMemberList.d.ts +3 -2
  11. package/dist/types/src/components/IdentityList/SpaceMemberList.d.ts.map +1 -1
  12. package/dist/types/src/components/IdentityList/SpaceMemberListContainer.d.ts +3 -1
  13. package/dist/types/src/components/IdentityList/SpaceMemberListContainer.d.ts.map +1 -1
  14. package/dist/types/src/components/IdentityList/index.d.ts +1 -0
  15. package/dist/types/src/components/IdentityList/index.d.ts.map +1 -1
  16. package/dist/types/src/components/InvitationList/InvitationList.d.ts.map +1 -1
  17. package/dist/types/src/components/InvitationList/InvitationListItem.d.ts.map +1 -1
  18. package/dist/types/src/components/InvitationList/InvitationStatusAvatar.d.ts +6 -1
  19. package/dist/types/src/components/InvitationList/InvitationStatusAvatar.d.ts.map +1 -1
  20. package/dist/types/src/components/InvitationList/InvitationStatusAvatar.stories.d.ts +1 -1
  21. package/dist/types/src/components/SpaceList/SpaceListItem.d.ts +7 -0
  22. package/dist/types/src/components/SpaceList/SpaceListItem.d.ts.map +1 -0
  23. package/dist/types/src/components/SpaceList/index.d.ts +2 -0
  24. package/dist/types/src/components/SpaceList/index.d.ts.map +1 -0
  25. package/dist/types/src/components/index.d.ts +1 -0
  26. package/dist/types/src/components/index.d.ts.map +1 -1
  27. package/dist/types/src/composites/DevicesDialog/DevicesDialog.d.ts +6 -0
  28. package/dist/types/src/composites/DevicesDialog/DevicesDialog.d.ts.map +1 -0
  29. package/dist/types/src/composites/DevicesDialog/index.d.ts +2 -0
  30. package/dist/types/src/composites/DevicesDialog/index.d.ts.map +1 -0
  31. package/dist/types/src/composites/IdentityPopover/IdentityPopover.d.ts +2 -2
  32. package/dist/types/src/composites/IdentityPopover/IdentityPopover.d.ts.map +1 -1
  33. package/dist/types/src/composites/JoinDialog/JoinDialog.d.ts +1 -1
  34. package/dist/types/src/composites/JoinDialog/JoinDialog.d.ts.map +1 -1
  35. package/dist/types/src/composites/JoinDialog/JoinDialog.stories.d.ts +2 -2
  36. package/dist/types/src/composites/JoinDialog/JoinDialog.stories.d.ts.map +1 -1
  37. package/dist/types/src/composites/Shell/Shell.d.ts +6 -0
  38. package/dist/types/src/composites/Shell/Shell.d.ts.map +1 -0
  39. package/dist/types/src/composites/Shell/index.d.ts +2 -0
  40. package/dist/types/src/composites/Shell/index.d.ts.map +1 -0
  41. package/dist/types/src/composites/SpaceDialog/SpaceDialog.d.ts +6 -0
  42. package/dist/types/src/composites/SpaceDialog/SpaceDialog.d.ts.map +1 -0
  43. package/dist/types/src/composites/SpaceDialog/index.d.ts +2 -0
  44. package/dist/types/src/composites/SpaceDialog/index.d.ts.map +1 -0
  45. package/dist/types/src/composites/index.d.ts +2 -0
  46. package/dist/types/src/composites/index.d.ts.map +1 -1
  47. package/dist/types/src/layouts/PanelAlertDialog/PanelAlertDialog.d.ts.map +1 -1
  48. package/dist/types/src/layouts/PanelDialog/PanelDialog.d.ts +14 -0
  49. package/dist/types/src/layouts/PanelDialog/PanelDialog.d.ts.map +1 -0
  50. package/dist/types/src/layouts/PanelDialog/index.d.ts +2 -0
  51. package/dist/types/src/layouts/PanelDialog/index.d.ts.map +1 -0
  52. package/dist/types/src/layouts/index.d.ts +1 -0
  53. package/dist/types/src/layouts/index.d.ts.map +1 -1
  54. package/dist/types/src/panels/DevicesPanel/DevicesPanel.d.ts +10 -0
  55. package/dist/types/src/panels/DevicesPanel/DevicesPanel.d.ts.map +1 -0
  56. package/dist/types/src/panels/DevicesPanel/index.d.ts +2 -0
  57. package/dist/types/src/panels/DevicesPanel/index.d.ts.map +1 -0
  58. package/dist/types/src/panels/IdentityPanel/IdentityPanel.d.ts +2 -2
  59. package/dist/types/src/panels/IdentityPanel/IdentityPanel.d.ts.map +1 -1
  60. package/dist/types/src/panels/JoinPanel/JoinHeading.d.ts +3 -0
  61. package/dist/types/src/panels/JoinPanel/JoinHeading.d.ts.map +1 -1
  62. package/dist/types/src/panels/JoinPanel/JoinPanel.d.ts +1 -1
  63. package/dist/types/src/panels/JoinPanel/JoinPanel.d.ts.map +1 -1
  64. package/dist/types/src/panels/JoinPanel/JoinPanelProps.d.ts +6 -5
  65. package/dist/types/src/panels/JoinPanel/JoinPanelProps.d.ts.map +1 -1
  66. package/dist/types/src/panels/JoinPanel/view-states/AdditionMethodSelector.d.ts +2 -2
  67. package/dist/types/src/panels/JoinPanel/view-states/AdditionMethodSelector.d.ts.map +1 -1
  68. package/dist/types/src/panels/JoinPanel/view-states/IdentityAdded.d.ts +12 -4
  69. package/dist/types/src/panels/JoinPanel/view-states/IdentityAdded.d.ts.map +1 -1
  70. package/dist/types/src/panels/JoinPanel/view-states/IdentityInput.d.ts.map +1 -1
  71. package/dist/types/src/panels/JoinPanel/view-states/IdentitySelector.d.ts +2 -2
  72. package/dist/types/src/panels/JoinPanel/view-states/IdentitySelector.d.ts.map +1 -1
  73. package/dist/types/src/panels/JoinPanel/view-states/InvitationAccepted.d.ts.map +1 -1
  74. package/dist/types/src/panels/JoinPanel/view-states/InvitationAuthenticator.d.ts.map +1 -1
  75. package/dist/types/src/panels/JoinPanel/view-states/InvitationInput.d.ts.map +1 -1
  76. package/dist/types/src/panels/JoinPanel/view-states/InvitationRescuer.d.ts.map +1 -1
  77. package/dist/types/src/panels/JoinPanel/view-states/ViewState.d.ts +2 -2
  78. package/dist/types/src/panels/JoinPanel/view-states/ViewState.d.ts.map +1 -1
  79. package/dist/types/src/panels/SpacePanel/SpacePanel.d.ts +12 -0
  80. package/dist/types/src/panels/SpacePanel/SpacePanel.d.ts.map +1 -0
  81. package/dist/types/src/panels/SpacePanel/SpacePanel.stories.d.ts +9 -0
  82. package/dist/types/src/panels/SpacePanel/SpacePanel.stories.d.ts.map +1 -0
  83. package/dist/types/src/panels/SpacePanel/index.d.ts +2 -0
  84. package/dist/types/src/panels/SpacePanel/index.d.ts.map +1 -0
  85. package/dist/types/src/panels/index.d.ts +2 -0
  86. package/dist/types/src/panels/index.d.ts.map +1 -1
  87. package/dist/types/src/styles/dialogStyles.d.ts +3 -0
  88. package/dist/types/src/styles/dialogStyles.d.ts.map +1 -0
  89. package/dist/types/src/styles/index.d.ts +1 -0
  90. package/dist/types/src/styles/index.d.ts.map +1 -1
  91. package/dist/types/src/translations/locales/en-US.d.ts +12 -1
  92. package/dist/types/src/translations/locales/en-US.d.ts.map +1 -1
  93. package/package.json +12 -11
  94. package/src/components/IdentityList/DeviceList.tsx +31 -0
  95. package/src/components/IdentityList/IdentityListItem.tsx +17 -5
  96. package/src/components/IdentityList/SpaceMemberList.tsx +10 -8
  97. package/src/components/IdentityList/SpaceMemberListContainer.tsx +7 -6
  98. package/src/components/IdentityList/index.ts +1 -0
  99. package/src/components/InvitationList/InvitationList.stories.tsx +1 -1
  100. package/src/components/InvitationList/InvitationList.tsx +12 -6
  101. package/src/components/InvitationList/InvitationListItem.tsx +30 -25
  102. package/src/components/InvitationList/InvitationStatusAvatar.tsx +10 -5
  103. package/src/components/SpaceList/SpaceListItem.tsx +32 -0
  104. package/src/components/SpaceList/index.ts +5 -0
  105. package/src/components/index.ts +1 -0
  106. package/src/composites/DevicesDialog/DevicesDialog.tsx +38 -0
  107. package/src/composites/DevicesDialog/index.ts +5 -0
  108. package/src/composites/IdentityPopover/IdentityPopover.tsx +3 -3
  109. package/src/composites/JoinDialog/JoinDialog.stories.tsx +59 -58
  110. package/src/composites/JoinDialog/JoinDialog.tsx +3 -1
  111. package/src/composites/Shell/Shell.tsx +86 -0
  112. package/src/composites/Shell/index.ts +5 -0
  113. package/src/composites/SpaceDialog/SpaceDialog.tsx +38 -0
  114. package/src/composites/SpaceDialog/index.ts +5 -0
  115. package/src/composites/index.ts +3 -0
  116. package/src/layouts/PanelAlertDialog/PanelAlertDialog.tsx +4 -8
  117. package/src/layouts/PanelDialog/PanelDialog.tsx +41 -0
  118. package/src/layouts/PanelDialog/index.ts +5 -0
  119. package/src/layouts/PanelSidebar/PanelSidebar.tsx +1 -1
  120. package/src/layouts/index.ts +1 -0
  121. package/src/panels/DevicesPanel/DevicesPanel.tsx +101 -0
  122. package/src/panels/DevicesPanel/index.ts +5 -0
  123. package/src/panels/IdentityPanel/IdentityPanel.tsx +5 -5
  124. package/src/panels/JoinPanel/JoinHeading.tsx +41 -19
  125. package/src/panels/JoinPanel/JoinPanel.tsx +35 -14
  126. package/src/panels/JoinPanel/JoinPanelProps.ts +6 -10
  127. package/src/panels/JoinPanel/view-states/AdditionMethodSelector.tsx +8 -2
  128. package/src/panels/JoinPanel/view-states/IdentityAdded.tsx +67 -28
  129. package/src/panels/JoinPanel/view-states/IdentityInput.tsx +9 -2
  130. package/src/panels/JoinPanel/view-states/IdentitySelector.tsx +8 -6
  131. package/src/panels/JoinPanel/view-states/InvitationAccepted.tsx +1 -0
  132. package/src/panels/JoinPanel/view-states/InvitationAuthenticator.tsx +4 -1
  133. package/src/panels/JoinPanel/view-states/InvitationInput.tsx +8 -1
  134. package/src/panels/JoinPanel/view-states/InvitationRescuer.tsx +4 -1
  135. package/src/panels/JoinPanel/view-states/ViewState.tsx +3 -3
  136. package/src/panels/SpacePanel/SpacePanel.stories.tsx +53 -0
  137. package/src/panels/SpacePanel/SpacePanel.tsx +90 -0
  138. package/src/panels/SpacePanel/index.ts +5 -0
  139. package/src/panels/index.ts +2 -0
  140. package/src/styles/dialogStyles.ts +8 -0
  141. package/src/styles/index.ts +1 -0
  142. package/src/translations/locales/en-US.ts +14 -3
@@ -23,7 +23,7 @@ export const IdentityInput = ({ method, ...viewStateProps }: IdentityCreatorProp
23
23
  const [validationMessage, setValidationMessage] = useState('');
24
24
  const isRecover = method === 'recover identity';
25
25
  const handleNext = () => {
26
- void client.halo.createProfile({ [isRecover ? 'seedphrase' : 'displayName']: inputValue }).then(
26
+ void client.halo.createIdentity({ [isRecover ? 'seedphrase' : 'displayName']: inputValue }).then(
27
27
  (identity) => {
28
28
  dispatch({ type: 'added identity', identity });
29
29
  },
@@ -50,10 +50,16 @@ export const IdentityInput = ({ method, ...viewStateProps }: IdentityCreatorProp
50
50
  } as ComponentPropsWithoutRef<'input'>
51
51
  }}
52
52
  {...(validationMessage.length && { validationValence: 'error', validationMessage })}
53
+ data-testid='identity-input'
53
54
  />
54
55
  <div role='none' className='grow' />
55
56
  <div className='flex gap-2'>
56
- <Button disabled={disabled} className='grow flex items-center gap-2 pli-2 order-2' onClick={handleNext}>
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
+ >
57
63
  <CaretLeft weight='bold' className={mx(getSize(2), 'invisible')} />
58
64
  <span className='grow'>{t('continue label')}</span>
59
65
  <CaretRight weight='bold' className={getSize(4)} />
@@ -62,6 +68,7 @@ export const IdentityInput = ({ method, ...viewStateProps }: IdentityCreatorProp
62
68
  disabled={disabled}
63
69
  onClick={() => dispatch({ type: 'add identity' })}
64
70
  className='flex items-center gap-2 pis-2 pie-4'
71
+ data-testid={`${method === 'recover identity' ? 'recover' : 'create'}-identity-input-back`}
65
72
  >
66
73
  <CaretLeft weight='bold' className={getSize(4)} />
67
74
  <span>{t('back label')}</span>
@@ -6,13 +6,13 @@ import * as RadioGroup from '@radix-ui/react-radio-group';
6
6
  import { CaretLeft, CaretRight, UserPlus } from 'phosphor-react';
7
7
  import React, { useCallback, useState } from 'react';
8
8
 
9
+ import type { Identity } from '@dxos/client';
9
10
  import { Avatar, Button, themeVariantFocus, getSize, mx, useTranslation } from '@dxos/react-components';
10
11
 
11
- import { Profile } from '../JoinPanelProps';
12
12
  import { ViewState, ViewStateHeading, ViewStateProps } from './ViewState';
13
13
 
14
14
  export interface IdentitySelectorProps extends ViewStateProps {
15
- availableIdentities: Profile[];
15
+ availableIdentities: Identity[];
16
16
  }
17
17
 
18
18
  export const IdentitySelector = ({ availableIdentities, ...viewStateProps }: IdentitySelectorProps) => {
@@ -40,7 +40,7 @@ export const IdentitySelector = ({ availableIdentities, ...viewStateProps }: Ide
40
40
  aria-label={t('identity radio group title')}
41
41
  data-autofocus='identity selector'
42
42
  >
43
- {availableIdentities.map(({ displayName, identityKey }) => {
43
+ {availableIdentities.map(({ profile, identityKey }) => {
44
44
  const hex = identityKey.toHex();
45
45
  const inputId = `identitySelector__item--${hex}`;
46
46
  const labelId = `identitySelector__itemLabel--${hex}`;
@@ -57,14 +57,15 @@ export const IdentitySelector = ({ availableIdentities, ...viewStateProps }: Ide
57
57
  'radix-state-unchecked:bg-neutral-100 dark:radix-state-unchecked:bg-neutral-900',
58
58
  themeVariantFocus('os')
59
59
  )}
60
+ data-testid='identity-selector-item'
60
61
  >
61
62
  <RadioGroup.Indicator className='absolute inset-0 flex items-center justify-center leading-0'>
62
63
  <div className='w-1.5 h-1.5 rounded-full bg-white' />
63
64
  </RadioGroup.Indicator>
64
65
  </RadioGroup.Item>
65
66
  <Avatar fallbackValue={hex} labelId={labelId} variant='circle' />
66
- <span id={labelId} className={mx(!displayName && 'font-mono')}>
67
- {displayName ?? identityKey.truncate() ?? ''}
67
+ <span id={labelId} className={mx(!profile?.displayName && 'font-mono')}>
68
+ {profile?.displayName ?? identityKey.truncate() ?? ''}
68
69
  </span>
69
70
  </label>
70
71
  );
@@ -72,9 +73,9 @@ export const IdentitySelector = ({ availableIdentities, ...viewStateProps }: Ide
72
73
  </RadioGroup.Root>
73
74
  <Button
74
75
  disabled={disabled}
75
- compact
76
76
  onClick={() => dispatch({ type: 'add identity' })}
77
77
  className='flex items-center gap-2 pli-2'
78
+ data-testid='add-identity'
78
79
  >
79
80
  <span>{t('add identity label')}</span>
80
81
  <UserPlus weight='bold' className={getSize(3.5)} />
@@ -88,6 +89,7 @@ export const IdentitySelector = ({ availableIdentities, ...viewStateProps }: Ide
88
89
  identity: activeIdentity
89
90
  })
90
91
  }
92
+ data-testid='select-identity'
91
93
  >
92
94
  <CaretLeft weight='bold' className={mx(getSize(4), 'invisible')} />
93
95
  <span className='grow'>{t('continue label')}</span>
@@ -33,6 +33,7 @@ const PureInvitationAcceptedContent = ({
33
33
  disabled={disabled}
34
34
  className='grow flex items-center gap-2 pli-2'
35
35
  data-autofocus={`${invitationType} invitation acceptor; invitation accepted`}
36
+ data-testid={`${invitationType}-invitation-accepted-done`}
36
37
  >
37
38
  <CaretLeft weight='bold' className={mx(getSize(2), 'invisible')} />
38
39
  <span className='grow'>{t('done label')}</span>
@@ -37,7 +37,7 @@ const PureInvitationAuthenticatorContent = ({
37
37
  return (
38
38
  <>
39
39
  <Input
40
- label={t('pin input label')}
40
+ label={t('auth code input label')}
41
41
  size='pin'
42
42
  length={pinLength}
43
43
  onChange={onChange}
@@ -57,6 +57,7 @@ const PureInvitationAuthenticatorContent = ({
57
57
  validationValence: 'error',
58
58
  validationMessage: t('failed to authenticate message')
59
59
  })}
60
+ data-testid='auth-code-input'
60
61
  />
61
62
  <div role='none' className='grow' />
62
63
  <div className='flex gap-2'>
@@ -65,6 +66,7 @@ const PureInvitationAuthenticatorContent = ({
65
66
  className='grow flex items-center gap-2 pli-2 order-2'
66
67
  onClick={onAuthenticate}
67
68
  data-autofocus-pinlength={invitationType}
69
+ data-testid={`${invitationType}-invitation-authenticator-next`}
68
70
  >
69
71
  <CaretLeft weight='bold' className={mx(getSize(2), 'invisible')} />
70
72
  <span className='grow'>{t('next label')}</span>
@@ -74,6 +76,7 @@ const PureInvitationAuthenticatorContent = ({
74
76
  disabled={disabled}
75
77
  className='flex items-center gap-2 pis-2 pie-4'
76
78
  onClick={() => dispatch({ type: 'cancel invitation', from: invitationType })}
79
+ data-testid={`${invitationType}-invitation-authenticator-cancel`}
77
80
  >
78
81
  <CaretLeft weight='bold' className={getSize(4)} />
79
82
  <span>{t('cancel label')}</span>
@@ -46,10 +46,16 @@ export const InvitationInput = ({ invitationType, ...viewStateProps }: Invitatio
46
46
  onKeyUp: ({ key }) => key === 'Enter' && handleNext()
47
47
  } as ComponentPropsWithoutRef<'input'>
48
48
  }}
49
+ data-testid='invitation-input'
49
50
  />
50
51
  <div role='none' className='grow' />
51
52
  <div className='flex gap-2'>
52
- <Button disabled={disabled} className='grow flex items-center gap-2 pli-2 order-2' onClick={handleNext}>
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
+ >
53
59
  <CaretLeft weight='bold' className={mx(getSize(2), 'invisible')} />
54
60
  <span className='grow'>{t('continue label')}</span>
55
61
  <CaretRight weight='bold' className={getSize(4)} />
@@ -58,6 +64,7 @@ export const InvitationInput = ({ invitationType, ...viewStateProps }: Invitatio
58
64
  disabled={disabled}
59
65
  onClick={() => dispatch({ type: 'add identity' })}
60
66
  className='flex items-center gap-2 pis-2 pie-4'
67
+ data-testid={`${invitationType}-invitation-input-back`}
61
68
  >
62
69
  <CaretLeft weight='bold' className={getSize(4)} />
63
70
  <span>{t('back label')}</span>
@@ -35,7 +35,7 @@ const InvitationActions = ({
35
35
  <>
36
36
  <div role='none' className='grow' />
37
37
  <div className='flex gap-2'>
38
- <Button disabled className='grow flex items-center gap-2 pli-2 order-2'>
38
+ <Button disabled className='grow flex items-center gap-2 pli-2 order-2' data-testid='next'>
39
39
  <CaretLeft weight='bold' className={mx(getSize(2), 'invisible')} />
40
40
  <span className='grow'>{t('next label')}</span>
41
41
  <CaretRight weight='bold' className={getSize(4)} />
@@ -45,6 +45,7 @@ const InvitationActions = ({
45
45
  className='flex items-center gap-2 pis-2 pie-4'
46
46
  onClick={cancel}
47
47
  data-autofocus={`${invitationType} invitation acceptor; invitation rescuer`}
48
+ data-testid='invitation-rescuer-cancel'
48
49
  >
49
50
  <CaretLeft weight='bold' className={getSize(4)} />
50
51
  <span>{t('cancel label')}</span>
@@ -62,6 +63,7 @@ const InvitationActions = ({
62
63
  className='grow flex items-center gap-2 pli-2'
63
64
  onClick={() => dispatch({ type: 'reset invitation', from: invitationType })}
64
65
  data-autofocus={`${invitationType} invitation acceptor; invitation rescuer`}
66
+ data-testid='invitation-rescuer-reset'
65
67
  >
66
68
  <CaretLeft weight='bold' className={mx(getSize(5), 'invisible')} />
67
69
  <span className='grow'>{t('reset label')}</span>
@@ -82,6 +84,7 @@ export const InvitationRescuer = ({ invitationType, ...viewStateProps }: Invitat
82
84
  disabled={disabled}
83
85
  className='grow flex items-center gap-2 pli-2'
84
86
  data-autofocus='space invitation acceptor; invitation rescuer'
87
+ data-testid='invitation-rescuer-connect'
85
88
  >
86
89
  <CaretLeft weight='bold' className={mx(getSize(5), 'invisible')} />
87
90
  <span className='grow'>{t('connect label')}</span>
@@ -5,7 +5,7 @@
5
5
  import { CheckCircle, HourglassSimple, X } from 'phosphor-react';
6
6
  import React, { ComponentProps, ComponentPropsWithoutRef, ReactNode, useMemo } from 'react';
7
7
 
8
- import type { AuthenticatingInvitationObservable, Profile } from '@dxos/client';
8
+ import type { AuthenticatingInvitationObservable, Identity } from '@dxos/client';
9
9
  import { Invitation } from '@dxos/client';
10
10
  import { useInvitationStatus } from '@dxos/react-client';
11
11
  import { mx, useTranslation, Trans, Avatar, useId, getSize, strongShimmer } from '@dxos/react-components';
@@ -17,7 +17,7 @@ import { JoinDispatch } from '../JoinPanelProps';
17
17
  export interface ViewStateProps extends ComponentProps<'div'> {
18
18
  active: boolean;
19
19
  dispatch: JoinDispatch;
20
- selectedIdentity?: true | Profile;
20
+ selectedIdentity?: true | Identity;
21
21
  activeInvitation?: true | AuthenticatingInvitationObservable;
22
22
  }
23
23
 
@@ -176,7 +176,7 @@ export const ViewState = ({
176
176
  labelValue:
177
177
  selectedIdentity === true
178
178
  ? ' '
179
- : selectedIdentity.displayName ?? selectedIdentity.identityKey.truncate()
179
+ : selectedIdentity.profile?.displayName ?? selectedIdentity.identityKey.truncate()
180
180
  }
181
181
  }}
182
182
  />
@@ -0,0 +1,53 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { StoryContext, StoryFn } from '@storybook/react';
6
+ import React, { useEffect, useState } from 'react';
7
+
8
+ import { Client, Space } from '@dxos/client';
9
+ import { TestBuilder } from '@dxos/client/testing';
10
+ import { ClientContext } from '@dxos/react-client';
11
+ import { Loading } from '@dxos/react-components';
12
+
13
+ import { SpacePanel } from './SpacePanel';
14
+
15
+ export default {
16
+ component: SpacePanel
17
+ };
18
+
19
+ const SpaceDecorator = (Story: StoryFn, { args }: StoryContext) => {
20
+ const testBuilder = new TestBuilder();
21
+ const [client, setClient] = useState<Client>();
22
+ const [space, setSpace] = useState<Space>();
23
+
24
+ useEffect(() => {
25
+ const timeout = setTimeout(async () => {
26
+ const client = new Client({ services: testBuilder.createClientServicesHost() });
27
+ await client.initialize();
28
+ setClient(client);
29
+
30
+ await client.halo.createIdentity();
31
+ const space = await client.echo.createSpace();
32
+ space.properties.name = 'Q3 2022 Planning';
33
+
34
+ setSpace(space);
35
+ });
36
+
37
+ return () => clearTimeout(timeout);
38
+ }, []);
39
+
40
+ if (!client || !space) {
41
+ return <Loading label='Setting things up…' />;
42
+ }
43
+
44
+ return (
45
+ <ClientContext.Provider value={{ client }}>
46
+ <Story args={{ space, ...args }} />
47
+ </ClientContext.Provider>
48
+ );
49
+ };
50
+
51
+ export const Default = {
52
+ decorators: [SpaceDecorator]
53
+ };
@@ -0,0 +1,90 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { UserPlus } from 'phosphor-react';
6
+ import React, { cloneElement, useReducer } from 'react';
7
+
8
+ import { Space } from '@dxos/client';
9
+ import { useSpaceInvitations } from '@dxos/react-client';
10
+ import { Button, DensityProvider, getSize, mx, useTranslation } from '@dxos/react-components';
11
+
12
+ import { InvitationList, PanelSeparator, SpaceMemberListContainer } from '../../components';
13
+ import { defaultSurface, subduedSurface } from '../../styles';
14
+
15
+ export type SpacePanelProps = {
16
+ titleId?: string;
17
+ space?: Space;
18
+ createInvitationUrl: (invitationCode: string) => string;
19
+ doneActionParent?: Parameters<typeof cloneElement>[0];
20
+ onDone?: () => void;
21
+ };
22
+
23
+ export type SpaceView = 'current space';
24
+
25
+ const CurrentSpaceView = ({ space, createInvitationUrl, titleId }: SpacePanelProps) => {
26
+ const { t } = useTranslation('os');
27
+ const invitations = useSpaceInvitations(space?.key);
28
+ const name = space?.properties.name;
29
+
30
+ if (!space) {
31
+ return null;
32
+ }
33
+
34
+ return (
35
+ <div role='none' className='flex flex-col'>
36
+ <div role='none' className={mx(subduedSurface, 'rounded-bs-md flex items-center p-2 gap-2')}>
37
+ {/* TODO(wittjosiah): Label this as the space panel. */}
38
+ <h2 id={titleId} className={mx('grow font-system-medium', !name && 'font-mono')}>
39
+ {name ?? space.key.truncate()}
40
+ </h2>
41
+ </div>
42
+ <div role='region' className={mx(defaultSurface, 'rounded-be-md p-2')}>
43
+ <InvitationList
44
+ invitations={invitations}
45
+ onClickRemove={({ invitation }) => invitation && space?.removeInvitation(invitation.invitationId!)}
46
+ createInvitationUrl={createInvitationUrl}
47
+ />
48
+ <Button
49
+ className='is-full flex gap-2 mbs-2'
50
+ onClick={() => space?.createInvitation()}
51
+ data-testid='create-space-invitation'
52
+ >
53
+ <span>{t('create space invitation label')}</span>
54
+ <UserPlus className={getSize(4)} weight='bold' />
55
+ </Button>
56
+ <PanelSeparator />
57
+ <SpaceMemberListContainer spaceKey={space.key} includeSelf />
58
+ </div>
59
+ </div>
60
+ );
61
+ };
62
+
63
+ interface SpacePanelState {
64
+ activeView: SpaceView;
65
+ }
66
+
67
+ interface SpacePanelAction {
68
+ type: null;
69
+ }
70
+
71
+ export const SpacePanel = (props: SpacePanelProps) => {
72
+ const reducer = (state: SpacePanelState, action: SpacePanelAction) => {
73
+ const nextState = { ...state };
74
+ switch (action.type) {
75
+ case null:
76
+ }
77
+ return nextState;
78
+ };
79
+
80
+ const [panelState] = useReducer(reducer, {
81
+ activeView: 'current space'
82
+ });
83
+
84
+ // TODO(wittjosiah): Use ViewState or similar.
85
+ return (
86
+ <DensityProvider density='fine'>
87
+ {panelState.activeView === 'current space' ? <CurrentSpaceView {...props} /> : null}
88
+ </DensityProvider>
89
+ );
90
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export * from './SpacePanel';
@@ -2,5 +2,7 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ export * from './DevicesPanel';
5
6
  export * from './IdentityPanel';
6
7
  export * from './JoinPanel';
8
+ export * from './SpacePanel';
@@ -0,0 +1,8 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export const defaultOverlay =
6
+ 'fixed inset-0 backdrop-blur z-50 overflow-auto grid place-items-center p-2 md:p-4 lg:p-8';
7
+
8
+ export const defaultDialogContent = 'is-full min-is-[260px] max-is-[320px] rounded-md shadow-md backdrop-blur-md';
@@ -2,5 +2,6 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ export * from './dialogStyles';
5
6
  export * from './panelStyles';
6
7
  export * from './invitationStatusStyles';
@@ -8,7 +8,9 @@ export const os = {
8
8
  'sidebar label': 'DXOS sidebar',
9
9
  'copy invitation code label': 'Copy',
10
10
  'open share panel label': 'Share',
11
- 'join space heading': '<small>Joining</small><large>{{spaceTitle}}</large>',
11
+ 'joining heading': '<small>Joining</small><large>{{name}}</large>',
12
+ 'join space heading': 'Join a space',
13
+ 'halo heading': 'Initialize device identity',
12
14
  'exit label': 'Exit',
13
15
  'identity selector title': 'Join as',
14
16
  'identity radio group title': 'Select an identity you’ve associated with this device already',
@@ -16,6 +18,7 @@ export const os = {
16
18
  'continue label': 'Continue',
17
19
  'back label': 'Back',
18
20
  'next label': 'Next',
21
+ 'close label': 'Close',
19
22
  'auth choices label': 'Choose an identity',
20
23
  'create identity label': 'Create an identity',
21
24
  'create identity description': 'Create a new identity.',
@@ -45,6 +48,14 @@ export const os = {
45
48
  'cancel label': 'Cancel',
46
49
  'done label': 'Done',
47
50
  'reset label': 'Start over',
48
- 'pin input label': 'The PIN provided by the invitation sender',
49
- 'invitation input label': 'Paste an invitation code or URL'
51
+ 'auth code input label': 'Auth code provided by the invitation sender',
52
+ 'invitation input label': 'Paste an invitation code or URL',
53
+ 'create space invitation label': 'Invite',
54
+ 'qr label': 'Scan to accept invitation',
55
+ 'empty invitations message': 'No pending invitations',
56
+ 'show all spaces label': 'All spaces',
57
+ 'all spaces label': 'All spaces',
58
+ 'create space label': 'Create a new space',
59
+ 'join space label': 'Join a space',
60
+ 'show current space label': 'Current space'
50
61
  };