@dxos/plugin-space 0.7.4 → 0.7.5-main.9cb18ac

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 (120) hide show
  1. package/dist/lib/browser/chunk-54VE4GTA.mjs +315 -0
  2. package/dist/lib/browser/chunk-54VE4GTA.mjs.map +7 -0
  3. package/dist/lib/browser/chunk-73BCBSLP.mjs +15 -0
  4. package/dist/lib/browser/chunk-73BCBSLP.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +928 -962
  6. package/dist/lib/browser/index.mjs.map +4 -4
  7. package/dist/lib/browser/meta.json +1 -1
  8. package/dist/lib/browser/meta.mjs +1 -5
  9. package/dist/lib/browser/types/index.mjs +8 -1
  10. package/dist/lib/node/chunk-46S3JOES.cjs +39 -0
  11. package/dist/lib/node/chunk-46S3JOES.cjs.map +7 -0
  12. package/dist/lib/node/chunk-YF2AQ7KP.cjs +343 -0
  13. package/dist/lib/node/chunk-YF2AQ7KP.cjs.map +7 -0
  14. package/dist/lib/node/index.cjs +1053 -1086
  15. package/dist/lib/node/index.cjs.map +4 -4
  16. package/dist/lib/node/meta.cjs +5 -9
  17. package/dist/lib/node/meta.cjs.map +2 -2
  18. package/dist/lib/node/meta.json +1 -1
  19. package/dist/lib/node/types/index.cjs +19 -12
  20. package/dist/lib/node/types/index.cjs.map +2 -2
  21. package/dist/lib/node-esm/chunk-2MNFEB23.mjs +17 -0
  22. package/dist/lib/node-esm/chunk-2MNFEB23.mjs.map +7 -0
  23. package/dist/lib/node-esm/chunk-CDZETPO7.mjs +316 -0
  24. package/dist/lib/node-esm/chunk-CDZETPO7.mjs.map +7 -0
  25. package/dist/lib/node-esm/index.mjs +928 -962
  26. package/dist/lib/node-esm/index.mjs.map +4 -4
  27. package/dist/lib/node-esm/meta.json +1 -1
  28. package/dist/lib/node-esm/meta.mjs +1 -5
  29. package/dist/lib/node-esm/types/index.mjs +8 -1
  30. package/dist/types/src/SpacePlugin.d.ts +2 -2
  31. package/dist/types/src/SpacePlugin.d.ts.map +1 -1
  32. package/dist/types/src/components/AwaitingObject.d.ts.map +1 -1
  33. package/dist/types/src/components/CreateDialog/CreateObjectDialog.d.ts +4 -2
  34. package/dist/types/src/components/CreateDialog/CreateObjectDialog.d.ts.map +1 -1
  35. package/dist/types/src/components/CreateDialog/CreateObjectPanel.d.ts +3 -3
  36. package/dist/types/src/components/CreateDialog/CreateObjectPanel.d.ts.map +1 -1
  37. package/dist/types/src/components/CreateDialog/CreateSpaceDialog.d.ts +1 -0
  38. package/dist/types/src/components/CreateDialog/CreateSpaceDialog.d.ts.map +1 -1
  39. package/dist/types/src/components/JoinDialog.d.ts +1 -0
  40. package/dist/types/src/components/JoinDialog.d.ts.map +1 -1
  41. package/dist/types/src/components/PopoverRenameObject.d.ts +1 -0
  42. package/dist/types/src/components/PopoverRenameObject.d.ts.map +1 -1
  43. package/dist/types/src/components/PopoverRenameSpace.d.ts +1 -0
  44. package/dist/types/src/components/PopoverRenameSpace.d.ts.map +1 -1
  45. package/dist/types/src/components/ShareSpaceButton.d.ts.map +1 -1
  46. package/dist/types/src/components/SpacePluginSettings.d.ts.map +1 -1
  47. package/dist/types/src/components/SpacePresence.d.ts +9 -6
  48. package/dist/types/src/components/SpacePresence.d.ts.map +1 -1
  49. package/dist/types/src/components/SpacePresence.stories.d.ts +1 -1
  50. package/dist/types/src/components/SpacePresence.stories.d.ts.map +1 -1
  51. package/dist/types/src/components/SpaceSettings/SpaceSettingsDialog.d.ts +1 -0
  52. package/dist/types/src/components/SpaceSettings/SpaceSettingsDialog.d.ts.map +1 -1
  53. package/dist/types/src/components/SpaceSettings/SpaceSettingsDialog.stories.d.ts.map +1 -1
  54. package/dist/types/src/components/SpaceSettings/SpaceSettingsPanel.d.ts +4 -3
  55. package/dist/types/src/components/SpaceSettings/SpaceSettingsPanel.d.ts.map +1 -1
  56. package/dist/types/src/components/SpaceSettings/SpaceSettingsPanel.stories.d.ts.map +1 -1
  57. package/dist/types/src/components/SyncStatus/InlineSyncStatus.d.ts +3 -3
  58. package/dist/types/src/components/SyncStatus/InlineSyncStatus.d.ts.map +1 -1
  59. package/dist/types/src/components/SyncStatus/SyncStatusDetail.stories.d.ts +2 -2
  60. package/dist/types/src/components/SyncStatus/SyncStatusDetail.stories.d.ts.map +1 -1
  61. package/dist/types/src/hooks/index.d.ts +2 -0
  62. package/dist/types/src/hooks/index.d.ts.map +1 -0
  63. package/dist/types/src/hooks/usePath.d.ts +11 -0
  64. package/dist/types/src/hooks/usePath.d.ts.map +1 -0
  65. package/dist/types/src/meta.d.ts +0 -23
  66. package/dist/types/src/meta.d.ts.map +1 -1
  67. package/dist/types/src/translations.d.ts +6 -3
  68. package/dist/types/src/translations.d.ts.map +1 -1
  69. package/dist/types/src/types/collection.d.ts +8 -12
  70. package/dist/types/src/types/collection.d.ts.map +1 -1
  71. package/dist/types/src/types/thread.d.ts +180 -186
  72. package/dist/types/src/types/thread.d.ts.map +1 -1
  73. package/dist/types/src/types/types.d.ts +234 -4
  74. package/dist/types/src/types/types.d.ts.map +1 -1
  75. package/dist/types/src/util.d.ts +3 -3
  76. package/dist/types/src/util.d.ts.map +1 -1
  77. package/dist/types/tsconfig.tsbuildinfo +1 -0
  78. package/package.json +39 -38
  79. package/src/SpacePlugin.tsx +477 -602
  80. package/src/components/AwaitingObject.tsx +19 -17
  81. package/src/components/CreateDialog/CreateObjectDialog.tsx +33 -22
  82. package/src/components/CreateDialog/CreateObjectPanel.tsx +7 -7
  83. package/src/components/CreateDialog/CreateSpaceDialog.tsx +10 -14
  84. package/src/components/JoinDialog.tsx +18 -34
  85. package/src/components/PersistenceStatus.tsx +1 -1
  86. package/src/components/PopoverRenameObject.tsx +2 -0
  87. package/src/components/PopoverRenameSpace.tsx +2 -0
  88. package/src/components/ShareSpaceButton.tsx +5 -4
  89. package/src/components/SpacePluginSettings.tsx +5 -11
  90. package/src/components/SpacePresence.stories.tsx +25 -17
  91. package/src/components/SpacePresence.tsx +36 -16
  92. package/src/components/SpaceSettings/SpaceSettingsDialog.stories.tsx +2 -3
  93. package/src/components/SpaceSettings/SpaceSettingsDialog.tsx +3 -1
  94. package/src/components/SpaceSettings/SpaceSettingsPanel.stories.tsx +7 -5
  95. package/src/components/SpaceSettings/SpaceSettingsPanel.tsx +6 -5
  96. package/src/components/SyncStatus/InlineSyncStatus.tsx +37 -27
  97. package/src/components/SyncStatus/SyncStatusDetail.stories.tsx +55 -51
  98. package/src/hooks/index.ts +5 -0
  99. package/src/hooks/usePath.ts +44 -0
  100. package/src/meta.ts +0 -26
  101. package/src/translations.ts +3 -2
  102. package/src/types/collection.ts +3 -3
  103. package/src/types/thread.ts +6 -6
  104. package/src/types/types.ts +182 -13
  105. package/src/util.tsx +59 -52
  106. package/dist/lib/browser/chunk-FTKV32QZ.mjs +0 -43
  107. package/dist/lib/browser/chunk-FTKV32QZ.mjs.map +0 -7
  108. package/dist/lib/browser/chunk-MWKXNS5S.mjs +0 -124
  109. package/dist/lib/browser/chunk-MWKXNS5S.mjs.map +0 -7
  110. package/dist/lib/node/chunk-6SNOZF7Y.cjs +0 -152
  111. package/dist/lib/node/chunk-6SNOZF7Y.cjs.map +0 -7
  112. package/dist/lib/node/chunk-QNVEU2UD.cjs +0 -69
  113. package/dist/lib/node/chunk-QNVEU2UD.cjs.map +0 -7
  114. package/dist/lib/node-esm/chunk-OHEAWSCA.mjs +0 -126
  115. package/dist/lib/node-esm/chunk-OHEAWSCA.mjs.map +0 -7
  116. package/dist/lib/node-esm/chunk-UMV7XREB.mjs +0 -45
  117. package/dist/lib/node-esm/chunk-UMV7XREB.mjs.map +0 -7
  118. package/dist/types/src/components/SyncStatus/InlineSyncStatus.stories.d.ts +0 -6
  119. package/dist/types/src/components/SyncStatus/InlineSyncStatus.stories.d.ts.map +0 -1
  120. package/src/components/SyncStatus/InlineSyncStatus.stories.tsx +0 -57
@@ -3,15 +3,22 @@
3
3
  //
4
4
 
5
5
  import { CheckCircle, CircleDashed, CircleNotch } from '@phosphor-icons/react';
6
- import React, { useEffect, useState } from 'react';
6
+ import React, { useCallback, useEffect, useState } from 'react';
7
7
 
8
- import { parseIntentPlugin, useResolvePlugin, parseNavigationPlugin, NavigationAction } from '@dxos/app-framework';
8
+ import {
9
+ useResolvePlugin,
10
+ parseNavigationPlugin,
11
+ NavigationAction,
12
+ useIntentDispatcher,
13
+ createIntent,
14
+ } from '@dxos/app-framework';
9
15
  import { useClient } from '@dxos/react-client';
10
16
  import { Filter, fullyQualifiedId, useQuery } from '@dxos/react-client/echo';
11
17
  import { Button, Toast, useTranslation } from '@dxos/react-ui';
12
18
  import { getSize, mx } from '@dxos/react-ui-theme';
13
19
 
14
- import { SpaceAction, SPACE_PLUGIN } from '../meta';
20
+ import { SPACE_PLUGIN } from '../meta';
21
+ import { SpaceAction } from '../types';
15
22
 
16
23
  const WAIT_FOR_OBJECT_TIMEOUT = 180e3; // 3 minutes
17
24
  const TOAST_TIMEOUT = 240e3; // 4 minutes
@@ -21,7 +28,7 @@ export const AwaitingObject = ({ id }: { id: string }) => {
21
28
  const [waiting, setWaiting] = useState(true);
22
29
  const [found, setFound] = useState(false);
23
30
  const { t } = useTranslation(SPACE_PLUGIN);
24
- const intentPlugin = useResolvePlugin(parseIntentPlugin);
31
+ const { dispatchPromise: dispatch } = useIntentDispatcher();
25
32
  const navigationPlugin = useResolvePlugin(parseNavigationPlugin);
26
33
 
27
34
  const client = useClient();
@@ -47,22 +54,17 @@ export const AwaitingObject = ({ id }: { id: string }) => {
47
54
  setOpen(false);
48
55
  }
49
56
  }
50
- }, [id, objects, intentPlugin]);
57
+ }, [id, objects, navigationPlugin]);
51
58
 
52
- const handleClose = async () =>
53
- intentPlugin?.provides.intent.dispatch({
54
- plugin: SPACE_PLUGIN,
55
- action: SpaceAction.WAIT_FOR_OBJECT,
56
- data: { id: undefined },
57
- });
59
+ const handleClose = useCallback(
60
+ async () => dispatch(createIntent(SpaceAction.WaitForObject, { id: undefined })),
61
+ [dispatch],
62
+ );
58
63
 
59
- const handleNavigate = () => {
60
- void intentPlugin?.provides.intent.dispatch({
61
- action: NavigationAction.OPEN,
62
- data: { activeParts: { main: [id] } },
63
- });
64
+ const handleNavigate = useCallback(() => {
65
+ void dispatch(createIntent(NavigationAction.Open, { activeParts: { main: [id] } }));
64
66
  void handleClose();
65
- };
67
+ }, [id, handleClose, dispatch]);
66
68
 
67
69
  return (
68
70
  <Toast.Root open={open} duration={TOAST_TIMEOUT} onOpenChange={setOpen}>
@@ -2,20 +2,30 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ import { pipe } from 'effect';
5
6
  import React, { useCallback, useRef } from 'react';
6
7
 
7
- import { type MetadataResolver, NavigationAction, useIntentDispatcher } from '@dxos/app-framework';
8
+ import { chain, createIntent, type MetadataResolver, NavigationAction, useIntentDispatcher } from '@dxos/app-framework';
8
9
  import { useClient } from '@dxos/react-client';
9
- import { type AbstractTypedObject, getSpace, isReactiveObject, isSpace, useSpaces } from '@dxos/react-client/echo';
10
+ import {
11
+ getSpace,
12
+ isReactiveObject,
13
+ isSpace,
14
+ type ReactiveObject,
15
+ type TypedObject,
16
+ useSpaces,
17
+ } from '@dxos/react-client/echo';
10
18
  import { Button, Dialog, Icon, useTranslation } from '@dxos/react-ui';
11
19
 
12
20
  import { CreateObjectPanel, type CreateObjectPanelProps } from './CreateObjectPanel';
13
- import { SPACE_PLUGIN, SpaceAction } from '../../meta';
14
- import { CollectionType } from '../../types';
21
+ import { SPACE_PLUGIN } from '../../meta';
22
+ import { CollectionType, SpaceAction } from '../../types';
23
+
24
+ export const CREATE_OBJECT_DIALOG = `${SPACE_PLUGIN}/CreateObjectDialog`;
15
25
 
16
26
  export type CreateObjectDialogProps = Pick<CreateObjectPanelProps, 'schemas' | 'target' | 'typename' | 'name'> & {
17
27
  resolve?: MetadataResolver;
18
- navigableCollections?: boolean;
28
+ shouldNavigate?: (object: ReactiveObject<any>) => boolean;
19
29
  };
20
30
 
21
31
  export const CreateObjectDialog = ({
@@ -23,14 +33,14 @@ export const CreateObjectDialog = ({
23
33
  target,
24
34
  typename,
25
35
  name,
26
- navigableCollections,
36
+ shouldNavigate: _shouldNavigate,
27
37
  resolve,
28
38
  }: CreateObjectDialogProps) => {
29
39
  const closeRef = useRef<HTMLButtonElement | null>(null);
30
40
  const { t } = useTranslation(SPACE_PLUGIN);
31
41
  const client = useClient();
32
42
  const spaces = useSpaces();
33
- const dispatch = useIntentDispatcher();
43
+ const { dispatchPromise: dispatch } = useIntentDispatcher();
34
44
 
35
45
  const handleCreateObject = useCallback(
36
46
  async ({
@@ -38,13 +48,15 @@ export const CreateObjectDialog = ({
38
48
  target: _target,
39
49
  name,
40
50
  }: {
41
- schema: AbstractTypedObject;
51
+ schema: TypedObject;
42
52
  target: CreateObjectPanelProps['target'];
43
53
  name?: string;
44
54
  }) => {
45
- const target = isSpace(_target) ? (_target.properties[CollectionType.typename] as CollectionType) : _target;
46
- const createObjectAction = resolve?.(schema.typename)?.createObject;
47
- if (!createObjectAction || !target) {
55
+ const target = isSpace(_target)
56
+ ? (_target.properties[CollectionType.typename]?.target as CollectionType | undefined)
57
+ : _target;
58
+ const createObjectIntent = resolve?.(schema.typename)?.createObject;
59
+ if (!createObjectIntent || !target) {
48
60
  // TODO(wittjosiah): UI feedback.
49
61
  return;
50
62
  }
@@ -53,17 +65,16 @@ export const CreateObjectDialog = ({
53
65
  closeRef.current?.click();
54
66
 
55
67
  const space = isSpace(target) ? target : getSpace(target);
56
- const result = await dispatch({ action: createObjectAction, data: { name, space } });
57
- const object = result?.data;
68
+ const result = await dispatch(createObjectIntent({ name, space }));
69
+ const object = result.data?.object;
58
70
  if (isReactiveObject(object)) {
59
- await dispatch([
60
- {
61
- plugin: SPACE_PLUGIN,
62
- action: SpaceAction.ADD_OBJECT,
63
- data: { target, object },
64
- },
65
- ...(!(object instanceof CollectionType) || navigableCollections ? [{ action: NavigationAction.OPEN }] : []),
66
- ]);
71
+ const addObjectIntent = createIntent(SpaceAction.AddObject, { target, object });
72
+ const shouldNavigate = _shouldNavigate ?? (() => true);
73
+ if (shouldNavigate(object)) {
74
+ await dispatch(pipe(addObjectIntent, chain(NavigationAction.Open, {})));
75
+ } else {
76
+ await dispatch(addObjectIntent);
77
+ }
67
78
  }
68
79
  },
69
80
  [dispatch, resolve],
@@ -73,7 +84,7 @@ export const CreateObjectDialog = ({
73
84
  // TODO(wittjosiah): The tablist dialog pattern is copied from @dxos/plugin-manager.
74
85
  // Consider factoring it out to the tabs package.
75
86
  <Dialog.Content classNames='p-0 bs-content min-bs-[15rem] max-bs-full md:max-is-[40rem] overflow-hidden'>
76
- <div role='none' className='flex justify-between pbs-3 pis-2 pie-3 @md:pbs-4 @md:pis-4 @md:pie-5'>
87
+ <div role='none' className='flex justify-between pbs-2 pis-2 pie-2 @md:pbs-4 @md:pis-4 @md:pie-4'>
77
88
  <Dialog.Title>{t('create object dialog title')}</Dialog.Title>
78
89
  <Dialog.Close asChild>
79
90
  <Button ref={closeRef} density='fine' variant='ghost' autoFocus>
@@ -5,7 +5,7 @@
5
5
  import React, { useCallback, useState } from 'react';
6
6
 
7
7
  import { type MetadataResolver } from '@dxos/app-framework';
8
- import { type AbstractTypedObject, getObjectAnnotation, S } from '@dxos/echo-schema';
8
+ import { type TypedObject, getObjectAnnotation, S } from '@dxos/echo-schema';
9
9
  import { type SpaceId, type Space, isSpace } from '@dxos/react-client/echo';
10
10
  import { Icon, IconButton, Input, toLocalizedString, useTranslation } from '@dxos/react-ui';
11
11
  import { Form, InputHeader } from '@dxos/react-ui-form';
@@ -17,7 +17,7 @@ import { type CollectionType } from '../../types';
17
17
  import { getSpaceDisplayName } from '../../util';
18
18
 
19
19
  export type CreateObjectPanelProps = {
20
- schemas: AbstractTypedObject[];
20
+ schemas: TypedObject[];
21
21
  spaces: Space[];
22
22
  typename?: string;
23
23
  target?: Space | CollectionType;
@@ -25,7 +25,7 @@ export type CreateObjectPanelProps = {
25
25
  defaultSpaceId?: SpaceId;
26
26
  resolve?: MetadataResolver;
27
27
  onCreateObject?: (params: {
28
- schema: AbstractTypedObject;
28
+ schema: TypedObject;
29
29
  target: Space | CollectionType;
30
30
  name?: string;
31
31
  }) => MaybePromise<void>;
@@ -63,7 +63,7 @@ export const CreateObjectPanel = ({
63
63
 
64
64
  // TODO(wittjosiah): All of these inputs should be rolled into a `Form` once it supports the necessary variants.
65
65
  const schemaInput = (
66
- <SearchList.Root label={t('schema input label')} classNames='flex flex-col grow overflow-hidden my-2 px-2'>
66
+ <SearchList.Root label={t('schema input label')} classNames='flex flex-col grow overflow-hidden'>
67
67
  <SearchList.Input
68
68
  autoFocus
69
69
  data-testid='create-object-form.schema-input'
@@ -89,7 +89,7 @@ export const CreateObjectPanel = ({
89
89
  );
90
90
 
91
91
  const spaceInput = (
92
- <SearchList.Root label={t('space input label')} classNames='flex flex-col grow overflow-hidden my-2 px-2'>
92
+ <SearchList.Root label={t('space input label')} classNames='flex flex-col grow overflow-hidden'>
93
93
  <SearchList.Input
94
94
  autoFocus
95
95
  data-testid='create-object-form.space-input'
@@ -126,7 +126,7 @@ export const CreateObjectPanel = ({
126
126
  return (
127
127
  <div role='form' className='flex flex-col gap-2'>
128
128
  {target && (
129
- <div role='none' className='px-2'>
129
+ <div role='none'>
130
130
  <Input.Root>
131
131
  <InputHeader>
132
132
  <Input.Label>
@@ -148,7 +148,7 @@ export const CreateObjectPanel = ({
148
148
  </div>
149
149
  )}
150
150
  {schema && (
151
- <div role='none' className='px-2'>
151
+ <div role='none'>
152
152
  <Input.Root>
153
153
  <InputHeader>
154
154
  <Input.Label>{t('creating object type label')}</Input.Label>
@@ -4,13 +4,15 @@
4
4
 
5
5
  import React, { useCallback, useRef } from 'react';
6
6
 
7
- import { useIntentDispatcher } from '@dxos/app-framework';
7
+ import { createIntent, useIntentDispatcher } from '@dxos/app-framework';
8
8
  import { type S } from '@dxos/echo-schema';
9
9
  import { Button, Dialog, Icon, useTranslation } from '@dxos/react-ui';
10
10
  import { Form } from '@dxos/react-ui-form';
11
11
 
12
- import { SPACE_PLUGIN, SpaceAction } from '../../meta';
13
- import { SpaceForm } from '../../types';
12
+ import { SPACE_PLUGIN } from '../../meta';
13
+ import { SpaceAction, SpaceForm } from '../../types';
14
+
15
+ export const CREATE_SPACE_DIALOG = `${SPACE_PLUGIN}/CreateSpaceDialog`;
14
16
 
15
17
  type FormValues = S.Schema.Type<typeof SpaceForm>;
16
18
  const initialValues: FormValues = { edgeReplication: true };
@@ -18,20 +20,14 @@ const initialValues: FormValues = { edgeReplication: true };
18
20
  export const CreateSpaceDialog = () => {
19
21
  const closeRef = useRef<HTMLButtonElement | null>(null);
20
22
  const { t } = useTranslation(SPACE_PLUGIN);
21
- const dispatch = useIntentDispatcher();
23
+ const { dispatchPromise: dispatch } = useIntentDispatcher();
22
24
 
23
25
  const handleCreateSpace = useCallback(
24
26
  async (data: FormValues) => {
25
- const result = await dispatch({
26
- action: SpaceAction.CREATE,
27
- data,
28
- });
29
- const target = result?.data.space;
27
+ const result = await dispatch(createIntent(SpaceAction.Create, data));
28
+ const target = result.data?.space;
30
29
  if (target) {
31
- await dispatch({
32
- action: SpaceAction.OPEN_CREATE_OBJECT,
33
- data: { target },
34
- });
30
+ await dispatch(createIntent(SpaceAction.OpenCreateObject, { target }));
35
31
  }
36
32
  },
37
33
  [dispatch],
@@ -41,7 +37,7 @@ export const CreateSpaceDialog = () => {
41
37
  // TODO(wittjosiah): The tablist dialog pattern is copied from @dxos/plugin-manager.
42
38
  // Consider factoring it out to the tabs package.
43
39
  <Dialog.Content classNames='p-0 bs-content min-bs-[15rem] max-bs-full md:max-is-[40rem] overflow-hidden'>
44
- <div role='none' className='flex justify-between pbs-3 pis-2 pie-3 @md:pbs-4 @md:pis-4 @md:pie-5'>
40
+ <div role='none' className='flex justify-between pbs-2 pis-2 pie-2 @md:pbs-4 @md:pis-4 @md:pie-4'>
45
41
  <Dialog.Title>{t('create space dialog title')}</Dialog.Title>
46
42
  <Dialog.Close asChild>
47
43
  <Button ref={closeRef} density='fine' variant='ghost' autoFocus>
@@ -4,9 +4,9 @@
4
4
 
5
5
  import React, { useCallback } from 'react';
6
6
 
7
- import { LayoutAction, NavigationAction, useIntentDispatcher } from '@dxos/app-framework';
7
+ import { createIntent, LayoutAction, NavigationAction, useIntentDispatcher } from '@dxos/app-framework';
8
8
  import { useGraph } from '@dxos/plugin-graph';
9
- import { ObservabilityAction } from '@dxos/plugin-observability/meta';
9
+ import { ObservabilityAction } from '@dxos/plugin-observability/types';
10
10
  import { useSpaces } from '@dxos/react-client/echo';
11
11
  import { type InvitationResult } from '@dxos/react-client/invitations';
12
12
  import { Dialog, useTranslation } from '@dxos/react-ui';
@@ -14,13 +14,15 @@ import { JoinPanel, type JoinPanelProps } from '@dxos/shell/react';
14
14
 
15
15
  import { SPACE_PLUGIN } from '../meta';
16
16
 
17
+ export const JOIN_DIALOG = `${SPACE_PLUGIN}/JoinDialog`;
18
+
17
19
  export type JoinDialogProps = JoinPanelProps & {
18
20
  navigableCollections?: boolean;
19
21
  };
20
22
 
21
23
  export const JoinDialog = ({ navigableCollections, onDone, ...props }: JoinDialogProps) => {
22
24
  const { t } = useTranslation(SPACE_PLUGIN);
23
- const dispatch = useIntentDispatcher();
25
+ const { dispatchPromise: dispatch } = useIntentDispatcher();
24
26
  const spaces = useSpaces();
25
27
  const { graph } = useGraph();
26
28
 
@@ -28,9 +30,8 @@ export const JoinDialog = ({ navigableCollections, onDone, ...props }: JoinDialo
28
30
  async (result: InvitationResult | null) => {
29
31
  if (result?.spaceKey) {
30
32
  await Promise.all([
31
- dispatch({
32
- action: LayoutAction.SET_LAYOUT,
33
- data: {
33
+ dispatch(
34
+ createIntent(LayoutAction.SetLayout, {
34
35
  element: 'toast',
35
36
  subject: {
36
37
  id: `${SPACE_PLUGIN}/join-success`,
@@ -38,15 +39,14 @@ export const JoinDialog = ({ navigableCollections, onDone, ...props }: JoinDialo
38
39
  title: t('join success label'),
39
40
  closeLabel: t('dismiss label'),
40
41
  },
41
- },
42
- }),
43
- dispatch({
44
- action: LayoutAction.SET_LAYOUT,
45
- data: {
42
+ }),
43
+ ),
44
+ dispatch(
45
+ createIntent(LayoutAction.SetLayout, {
46
46
  element: 'dialog',
47
47
  state: false,
48
- },
49
- }),
48
+ }),
49
+ ),
50
50
  ]);
51
51
  }
52
52
 
@@ -60,33 +60,17 @@ export const JoinDialog = ({ navigableCollections, onDone, ...props }: JoinDialo
60
60
  // If the target has not yet replicated, this will trigger a loading toast.
61
61
  await graph.waitForPath({ target }).catch(() => {});
62
62
  await Promise.all([
63
- dispatch({
64
- action: NavigationAction.OPEN,
65
- data: {
66
- activeParts: { main: [target] },
67
- },
68
- }),
69
- dispatch({
70
- action: NavigationAction.EXPOSE,
71
- data: {
72
- id: target,
73
- },
74
- }),
63
+ dispatch(createIntent(NavigationAction.Open, { activeParts: { main: [target] } })),
64
+ dispatch(createIntent(NavigationAction.Expose, { id: target })),
75
65
  ]);
76
66
  }
77
67
 
78
68
  await onDone?.(result);
79
69
 
80
70
  if (space) {
81
- await dispatch({
82
- action: ObservabilityAction.SEND_EVENT,
83
- data: {
84
- name: 'space.join',
85
- properties: {
86
- spaceId: space.id,
87
- },
88
- },
89
- });
71
+ await dispatch(
72
+ createIntent(ObservabilityAction.SendEvent, { name: 'space.join', properties: { spaceId: space.id } }),
73
+ );
90
74
  }
91
75
  },
92
76
  [dispatch, spaces],
@@ -76,7 +76,7 @@ export const PersistenceStatus = ({ db }: { db: EchoDatabase }) => {
76
76
  )}
77
77
  </Tooltip.Trigger>
78
78
  <Tooltip.Portal>
79
- <Tooltip.Content classNames='z-10'>
79
+ <Tooltip.Content>
80
80
  {t('persisted locally message')}
81
81
  <Tooltip.Arrow />
82
82
  </Tooltip.Content>
@@ -10,6 +10,8 @@ import { Button, Input, Popover, useTranslation } from '@dxos/react-ui';
10
10
 
11
11
  import { SPACE_PLUGIN } from '../meta';
12
12
 
13
+ export const POPOVER_RENAME_OBJECT = `${SPACE_PLUGIN}/PopoverRenameObject`;
14
+
13
15
  export const PopoverRenameObject = ({ object: obj }: { object: ReactiveObject<any> }) => {
14
16
  const { t } = useTranslation(SPACE_PLUGIN);
15
17
  const doneButton = useRef<HTMLButtonElement>(null);
@@ -9,6 +9,8 @@ import { Button, Input, Popover, useTranslation } from '@dxos/react-ui';
9
9
 
10
10
  import { SPACE_PLUGIN } from '../meta';
11
11
 
12
+ export const POPOVER_RENAME_SPACE = `${SPACE_PLUGIN}/PopoverRenameSpace`;
13
+
12
14
  export const PopoverRenameSpace = ({ space }: { space: Space }) => {
13
15
  const { t } = useTranslation(SPACE_PLUGIN);
14
16
  const doneButton = useRef<HTMLButtonElement>(null);
@@ -4,16 +4,17 @@
4
4
 
5
5
  import React from 'react';
6
6
 
7
- import { useIntentDispatcher } from '@dxos/app-framework';
7
+ import { createIntent, useIntentDispatcher } from '@dxos/app-framework';
8
8
  import { type Space } from '@dxos/react-client/echo';
9
9
  import { IconButton, useTranslation } from '@dxos/react-ui';
10
10
 
11
- import { SPACE_PLUGIN, SpaceAction } from '../meta';
11
+ import { SPACE_PLUGIN } from '../meta';
12
+ import { SpaceAction } from '../types';
12
13
 
13
14
  export const ShareSpaceButton = ({ space }: { space: Space }) => {
14
- const dispatch = useIntentDispatcher();
15
+ const { dispatchPromise: dispatch } = useIntentDispatcher();
15
16
 
16
- return <ShareSpaceButtonImpl onClick={() => dispatch({ action: SpaceAction.SHARE, data: { space } })} />;
17
+ return <ShareSpaceButtonImpl onClick={() => dispatch(createIntent(SpaceAction.Share, { space }))} />;
17
18
  };
18
19
 
19
20
  // TODO(wittjosiah): Better way to name pure/impure components?
@@ -4,29 +4,23 @@
4
4
 
5
5
  import React from 'react';
6
6
 
7
- import { useIntentDispatcher } from '@dxos/app-framework';
7
+ import { createIntent, useIntentDispatcher } from '@dxos/app-framework';
8
8
  import { Input, useTranslation } from '@dxos/react-ui';
9
9
  import { DeprecatedFormInput } from '@dxos/react-ui-form';
10
10
 
11
- import { SpaceAction, SPACE_PLUGIN } from '../meta';
12
- import { type SpaceSettingsProps } from '../types';
11
+ import { SPACE_PLUGIN } from '../meta';
12
+ import { SpaceAction, type SpaceSettingsProps } from '../types';
13
13
 
14
14
  export const SpacePluginSettings = ({ settings }: { settings: SpaceSettingsProps }) => {
15
15
  const { t } = useTranslation(SPACE_PLUGIN);
16
- const dispatch = useIntentDispatcher();
16
+ const { dispatchPromise: dispatch } = useIntentDispatcher();
17
17
 
18
18
  return (
19
19
  <>
20
20
  <DeprecatedFormInput label={t('show hidden spaces label')}>
21
21
  <Input.Switch
22
22
  checked={settings.showHidden}
23
- onCheckedChange={(checked) =>
24
- dispatch({
25
- plugin: SPACE_PLUGIN,
26
- action: SpaceAction.TOGGLE_HIDDEN,
27
- data: { state: !!checked },
28
- })
29
- }
23
+ onCheckedChange={(checked) => dispatch(createIntent(SpaceAction.ToggleHidden, { state: !!checked }))}
30
24
  />
31
25
  </DeprecatedFormInput>
32
26
  </>
@@ -61,30 +61,38 @@ export const Full = (props: MemberPresenceProps) => {
61
61
  );
62
62
  };
63
63
 
64
- export const Small = (props: MemberPresenceProps) => {
65
- const p: MemberPresenceProps = {
66
- ...props,
67
- };
68
-
64
+ export const Small = () => {
69
65
  return (
70
66
  <div className='p-4'>
71
- <div className='p-3'>
72
- <SmallPresence count={0} {...p} />
67
+ <div className='flex gap-3 p-3'>
68
+ <SmallPresence count={0} />
69
+ <SmallPresence count={0} attended />
70
+ <SmallPresence count={0} containsAttended />
73
71
  </div>
74
- <div className='p-3'>
75
- <SmallPresence count={1} {...p} />
72
+ <div className='flex gap-3 p-3'>
73
+ <SmallPresence count={1} />
74
+ <SmallPresence count={1} attended />
75
+ <SmallPresence count={1} containsAttended />
76
76
  </div>
77
- <div className='p-3'>
78
- <SmallPresence count={2} {...p} />
77
+ <div className='flex gap-3 p-3'>
78
+ <SmallPresence count={2} />
79
+ <SmallPresence count={2} attended />
80
+ <SmallPresence count={2} containsAttended />
79
81
  </div>
80
- <div className='p-3'>
81
- <SmallPresence count={3} {...p} />
82
+ <div className='flex gap-3 p-3'>
83
+ <SmallPresence count={3} />
84
+ <SmallPresence count={3} attended />
85
+ <SmallPresence count={3} containsAttended />
82
86
  </div>
83
- <div className='p-3'>
84
- <SmallPresence count={4} {...p} />
87
+ <div className='flex gap-3 p-3'>
88
+ <SmallPresence count={4} />
89
+ <SmallPresence count={4} attended />
90
+ <SmallPresence count={4} containsAttended />
85
91
  </div>
86
- <div className='p-3'>
87
- <SmallPresence count={5} {...p} />
92
+ <div className='flex gap-3 p-3'>
93
+ <SmallPresence count={5} />
94
+ <SmallPresence count={5} attended />
95
+ <SmallPresence count={5} containsAttended />
88
96
  </div>
89
97
  </div>
90
98
  );
@@ -7,6 +7,7 @@ import React, { useCallback, useEffect, useState } from 'react';
7
7
  import { usePlugin } from '@dxos/app-framework';
8
8
  import { generateName } from '@dxos/display-name';
9
9
  import { type Expando } from '@dxos/echo-schema';
10
+ import { useGraph } from '@dxos/plugin-graph';
10
11
  import { PublicKey, useClient } from '@dxos/react-client';
11
12
  import { getSpace, useMembers, type SpaceMember, fullyQualifiedId } from '@dxos/react-client/echo';
12
13
  import { type Identity, useIdentity } from '@dxos/react-client/halo';
@@ -22,9 +23,10 @@ import {
22
23
  ListItem,
23
24
  useDefaultValue,
24
25
  } from '@dxos/react-ui';
25
- import { AttentionGlyph, useAttention } from '@dxos/react-ui-attention';
26
+ import { AttentionGlyph, useAttended, useAttention, type AttentionGlyphProps } from '@dxos/react-ui-attention';
26
27
  import { ComplexMap, keyToFallback } from '@dxos/util';
27
28
 
29
+ import { usePath } from '../hooks';
28
30
  import { SPACE_PLUGIN } from '../meta';
29
31
  import type { ObjectViewerProps, SpacePluginProvides } from '../types';
30
32
 
@@ -194,16 +196,30 @@ const PrensenceAvatar = ({ identity, showName, match, group, index, onClick }: P
194
196
  );
195
197
  };
196
198
 
197
- export const SmallPresenceLive = ({
198
- id,
199
- viewers,
200
- }: {
199
+ export type SmallPresenceLiveProps = {
201
200
  id?: string;
201
+ open?: boolean;
202
202
  viewers?: ComplexMap<PublicKey, ObjectViewerProps>;
203
- }) => {
203
+ };
204
+
205
+ export const SmallPresenceLive = ({ id, open, viewers }: SmallPresenceLiveProps) => {
206
+ const { hasAttention, isAncestor, isRelated } = useAttention(id);
207
+ const isAttended = hasAttention || isAncestor || isRelated;
208
+
209
+ // TODO(wittjosiah): If the attended node is deep in the graph and the graph is not fully loaded
210
+ // this will result in an empty path until the graph is connected.
211
+ // TODO(wittjosiah): Consider using this indicator for all open nodes instead of just attended.
212
+ const { graph } = useGraph();
213
+ const attended = useAttended();
214
+ const startOfAttention = attended.at(-1);
215
+ const path = usePath(graph, startOfAttention);
216
+ const containsAttended = !open && !isAttended && id && path ? path.includes(id) : false;
217
+
204
218
  const getActiveViewers = (viewers: ComplexMap<PublicKey, ObjectViewerProps>): ObjectViewerProps[] => {
205
219
  const moment = Date.now();
206
- return Array.from(viewers.values()).filter(({ lastSeen }) => moment - lastSeen < ACTIVITY_DURATION);
220
+ return Array.from<ObjectViewerProps>(viewers.values()).filter(
221
+ (viewer) => moment - viewer.lastSeen < ACTIVITY_DURATION,
222
+ );
207
223
  };
208
224
 
209
225
  const [activeViewers, setActiveViewers] = useState(viewers ? getActiveViewers(viewers) : []);
@@ -218,24 +234,28 @@ export const SmallPresenceLive = ({
218
234
  }
219
235
  }, [viewers]);
220
236
 
221
- return <SmallPresence id={id} count={activeViewers.length} />;
237
+ return <SmallPresence count={activeViewers.length} attended={isAttended} containsAttended={containsAttended} />;
222
238
  };
223
239
 
224
- export const SmallPresence = ({ id, count }: { id?: string; count: number }) => {
240
+ export type SmallPresenceProps = {
241
+ count?: number;
242
+ } & Pick<AttentionGlyphProps, 'attended' | 'containsAttended'>;
243
+
244
+ export const SmallPresence = ({ count = 0, attended, containsAttended }: SmallPresenceProps) => {
225
245
  const { t } = useTranslation(SPACE_PLUGIN);
226
- const { hasAttention, isAncestor, isRelated } = useAttention(id);
227
- const attention = hasAttention || isAncestor || isRelated;
228
246
 
229
247
  return (
230
248
  <Tooltip.Root>
231
249
  <Tooltip.Trigger asChild>
232
- {/* TODO(wittjosiah): Don't depend on data attribute just pass prop to AttentionGlyph. */}
233
- <div role='none' className='flex' data-attention={attention}>
234
- <AttentionGlyph presence={count > 1 ? 'many' : count === 1 ? 'one' : 'none'} classNames='self-center mie-1' />
235
- </div>
250
+ <AttentionGlyph
251
+ attended={attended}
252
+ containsAttended={containsAttended}
253
+ presence={count > 1 ? 'many' : count === 1 ? 'one' : 'none'}
254
+ classNames='self-center mie-1'
255
+ />
236
256
  </Tooltip.Trigger>
237
257
  <Tooltip.Portal>
238
- <Tooltip.Content side='bottom' classNames='z-[70]'>
258
+ <Tooltip.Content side='bottom'>
239
259
  <span>{t('presence label', { count })}</span>
240
260
  <Tooltip.Arrow />
241
261
  </Tooltip.Content>
@@ -7,7 +7,7 @@ import '@dxos-theme';
7
7
  import { type Meta, type StoryObj } from '@storybook/react';
8
8
  import React from 'react';
9
9
 
10
- import { useStoryClientData, withClientProvider } from '@dxos/react-client/testing';
10
+ import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
11
11
  import { Dialog } from '@dxos/react-ui';
12
12
  import { osTranslations } from '@dxos/shell/react';
13
13
  import { withLayout, withTheme } from '@dxos/storybook-utils';
@@ -16,8 +16,7 @@ import { SpaceSettingsDialog, type SpaceSettingsDialogProps } from './SpaceSetti
16
16
  import translations from '../../translations';
17
17
 
18
18
  const Story = (args: Partial<SpaceSettingsDialogProps>) => {
19
- const { space } = useStoryClientData();
20
-
19
+ const { space } = useClientProvider();
21
20
  return (
22
21
  <Dialog.Root open>
23
22
  <Dialog.Overlay blockAlign='start'>