@dxos/plugin-space 0.7.4 → 0.7.5-main.937ce75

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
@@ -7,16 +7,18 @@ import React from 'react';
7
7
 
8
8
  import {
9
9
  type GraphProvides,
10
- type IntentDispatcher,
11
10
  type IntentPluginProvides,
12
11
  LayoutAction,
13
12
  type LayoutProvides,
14
13
  type LocationProvides,
15
- type MetadataResolverProvides,
16
14
  NavigationAction,
17
15
  type Plugin,
18
16
  type PluginDefinition,
17
+ type PromiseIntentDispatcher,
19
18
  Surface,
19
+ createIntent,
20
+ createResolver,
21
+ createSurface,
20
22
  filterPlugins,
21
23
  findPlugin,
22
24
  firstIdInPart,
@@ -29,17 +31,17 @@ import {
29
31
  resolvePlugin,
30
32
  } from '@dxos/app-framework';
31
33
  import { EventSubscriptions, type Trigger, type UnsubscribeCallback } from '@dxos/async';
32
- import { S, type AbstractTypedObject, type HasId } from '@dxos/echo-schema';
34
+ import { type HasId, type TypedObject } from '@dxos/echo-schema';
33
35
  import { scheduledEffect } from '@dxos/echo-signals/core';
34
36
  import { invariant } from '@dxos/invariant';
35
- import { create, isDeleted, isReactiveObject } from '@dxos/live-object';
37
+ import { type ReactiveObject, RefArray, create, isDeleted, isReactiveObject, makeRef } from '@dxos/live-object';
36
38
  import { LocalStorageStore } from '@dxos/local-storage';
37
39
  import { log } from '@dxos/log';
38
40
  import { Migrations } from '@dxos/migrations';
39
41
  import { type AttentionPluginProvides, parseAttentionPlugin } from '@dxos/plugin-attention';
40
- import { type ClientPluginProvides, parseClientPlugin } from '@dxos/plugin-client';
42
+ import { type ClientPluginProvides, parseClientPlugin } from '@dxos/plugin-client/types';
41
43
  import { type Node, createExtension, memoize, toSignal } from '@dxos/plugin-graph';
42
- import { ObservabilityAction } from '@dxos/plugin-observability/meta';
44
+ import { ObservabilityAction } from '@dxos/plugin-observability/types';
43
45
  import { EdgeReplicationSetting } from '@dxos/protocols/proto/dxos/echo/metadata';
44
46
  import { type Client, PublicKey } from '@dxos/react-client';
45
47
  import {
@@ -47,6 +49,7 @@ import {
47
49
  FQ_ID_LENGTH,
48
50
  Filter,
49
51
  OBJECT_ID_LENGTH,
52
+ QueryOptions,
50
53
  type ReactiveEchoObject,
51
54
  SPACE_ID_LENGTH,
52
55
  type Space,
@@ -56,7 +59,6 @@ import {
56
59
  getTypename,
57
60
  isEchoObject,
58
61
  isSpace,
59
- loadObjectReferences,
60
62
  parseFullyQualifiedId,
61
63
  parseId,
62
64
  } from '@dxos/react-client/echo';
@@ -65,36 +67,43 @@ import { ComplexMap, nonNullable, reduceGroupBy } from '@dxos/util';
65
67
 
66
68
  import {
67
69
  AwaitingObject,
70
+ CREATE_OBJECT_DIALOG,
71
+ CREATE_SPACE_DIALOG,
68
72
  CollectionMain,
69
73
  CollectionSection,
70
74
  CreateObjectDialog,
71
75
  type CreateObjectDialogProps,
72
76
  CreateSpaceDialog,
73
77
  DefaultObjectSettings,
74
- JoinDialog,
75
78
  InlineSyncStatus,
79
+ JOIN_DIALOG,
80
+ JoinDialog,
81
+ type JoinDialogProps,
76
82
  MenuFooter,
83
+ POPOVER_RENAME_OBJECT,
84
+ POPOVER_RENAME_SPACE,
77
85
  PopoverRenameObject,
78
86
  PopoverRenameSpace,
87
+ SPACE_SETTINGS_DIALOG,
79
88
  ShareSpaceButton,
80
- SmallPresence,
81
89
  SmallPresenceLive,
82
90
  SpacePluginSettings,
83
91
  SpacePresence,
84
92
  SpaceSettingsDialog,
93
+ type SpaceSettingsDialogProps,
85
94
  SpaceSettingsPanel,
86
95
  SyncStatus,
87
- type SpaceSettingsDialogProps,
88
96
  } from './components';
89
- import meta, { CollectionAction, SPACE_PLUGIN, SpaceAction } from './meta';
97
+ import meta, { SPACE_PLUGIN } from './meta';
90
98
  import translations from './translations';
91
99
  import {
100
+ CollectionAction,
92
101
  CollectionType,
93
- parseSchemaPlugin,
94
- SpaceForm,
95
102
  type PluginState,
103
+ SpaceAction,
96
104
  type SpacePluginProvides,
97
105
  type SpaceSettingsProps,
106
+ parseSchemaPlugin,
98
107
  } from './types';
99
108
  import {
100
109
  COMPOSER_SPACE_LOCK,
@@ -145,7 +154,7 @@ export type SpacePluginOptions = {
145
154
  * @param params.client DXOS Client
146
155
  * @param params.dispatch Function to dispatch intents
147
156
  */
148
- onFirstRun?: (params: { client: Client; dispatch: IntentDispatcher }) => Promise<void>;
157
+ onFirstRun?: (params: { client: Client; dispatch: PromiseIntentDispatcher }) => Promise<void>;
149
158
  };
150
159
 
151
160
  export const SpacePlugin = ({
@@ -168,7 +177,7 @@ export const SpacePlugin = ({
168
177
  const subscriptions = new EventSubscriptions();
169
178
  const spaceSubscriptions = new EventSubscriptions();
170
179
  const graphSubscriptions = new Map<string, UnsubscribeCallback>();
171
- const schemas: AbstractTypedObject[] = [];
180
+ const schemas: TypedObject[] = [];
172
181
 
173
182
  let clientPlugin: Plugin<ClientPluginProvides> | undefined;
174
183
  let graphPlugin: Plugin<GraphProvides> | undefined;
@@ -176,7 +185,6 @@ export const SpacePlugin = ({
176
185
  let layoutPlugin: Plugin<LayoutProvides> | undefined;
177
186
  let navigationPlugin: Plugin<LocationProvides> | undefined;
178
187
  let attentionPlugin: Plugin<AttentionPluginProvides> | undefined;
179
- let metadataPlugin: Plugin<MetadataResolverProvides> | undefined;
180
188
 
181
189
  const createSpaceInvitationUrl = (invitationCode: string) => {
182
190
  const baseUrl = new URL(invitationUrl);
@@ -228,11 +236,7 @@ export const SpacePlugin = ({
228
236
  const timeout = setTimeout(async () => {
229
237
  const node = graph.findNode(soloPart.id);
230
238
  if (!node) {
231
- await dispatch({
232
- plugin: SPACE_PLUGIN,
233
- action: SpaceAction.WAIT_FOR_OBJECT,
234
- data: { id: soloPart.id },
235
- });
239
+ await dispatch(createIntent(SpaceAction.WaitForObject, { id: soloPart.id }));
236
240
  }
237
241
  }, WAIT_FOR_OBJECT_TIMEOUT);
238
242
 
@@ -392,7 +396,7 @@ export const SpacePlugin = ({
392
396
 
393
397
  return {
394
398
  meta,
395
- ready: async (plugins) => {
399
+ ready: async ({ plugins }) => {
396
400
  settings.prop({ key: 'showHidden', type: LocalStorageStore.bool({ allowUndefined: true }) });
397
401
  state
398
402
  .prop({ key: 'spaceNames', type: LocalStorageStore.json<Record<string, string>>() })
@@ -406,7 +410,6 @@ export const SpacePlugin = ({
406
410
 
407
411
  graphPlugin = resolvePlugin(plugins, parseGraphPlugin);
408
412
  layoutPlugin = resolvePlugin(plugins, parseLayoutPlugin);
409
- metadataPlugin = resolvePlugin(plugins, parseMetadataResolverPlugin);
410
413
  navigationPlugin = resolvePlugin(plugins, parseNavigationPlugin);
411
414
  attentionPlugin = resolvePlugin(plugins, parseAttentionPlugin);
412
415
  clientPlugin = resolvePlugin(plugins, parseClientPlugin);
@@ -416,7 +419,7 @@ export const SpacePlugin = ({
416
419
  }
417
420
 
418
421
  const client = clientPlugin.provides.client;
419
- const dispatch = intentPlugin.provides.intent.dispatch;
422
+ const dispatch = intentPlugin.provides.intent.dispatchPromise;
420
423
 
421
424
  schemas.push(
422
425
  ...filterPlugins(plugins, parseSchemaPlugin)
@@ -437,7 +440,7 @@ export const SpacePlugin = ({
437
440
  const defaultSpace = client.spaces.default;
438
441
 
439
442
  // Create root collection structure.
440
- defaultSpace.properties[CollectionType.typename] = create(CollectionType, { objects: [], views: {} });
443
+ defaultSpace.properties[CollectionType.typename] = makeRef(create(CollectionType, { objects: [], views: {} }));
441
444
  if (Migrations.versionProperty) {
442
445
  defaultSpace.properties[Migrations.versionProperty] = Migrations.targetVersion;
443
446
  }
@@ -480,15 +483,12 @@ export const SpacePlugin = ({
480
483
  metadata: {
481
484
  records: {
482
485
  [CollectionType.typename]: {
483
- createObject: CollectionAction.CREATE,
486
+ createObject: (props: { name?: string }) => createIntent(CollectionAction.Create, props),
484
487
  placeholder: ['unnamed collection label', { ns: SPACE_PLUGIN }],
485
488
  icon: 'ph--cards-three--regular',
486
489
  // TODO(wittjosiah): Move out of metadata.
487
- loadReferences: (collection: CollectionType) =>
488
- loadObjectReferences(collection, (collection) => [
489
- ...collection.objects,
490
- ...Object.values(collection.views),
491
- ]),
490
+ loadReferences: async (collection: CollectionType) =>
491
+ await RefArray.loadAll([...collection.objects, ...Object.values(collection.views)]),
492
492
  },
493
493
  },
494
494
  },
@@ -496,114 +496,159 @@ export const SpacePlugin = ({
496
496
  schema: [CollectionType],
497
497
  },
498
498
  surface: {
499
- component: ({ data, role, ...rest }) => {
500
- switch (role) {
501
- case 'article':
502
- // TODO(wittjosiah): Need to avoid shotgun parsing space state everywhere.
503
- return isSpace(data.object) && data.object.state.get() === SpaceState.SPACE_READY ? (
499
+ definitions: ({ plugins }) => {
500
+ const resolve = resolvePlugin(plugins, parseMetadataResolverPlugin)?.provides.metadata.resolver;
501
+ const attention = resolvePlugin(plugins, parseAttentionPlugin)?.provides.attention;
502
+
503
+ invariant(resolve, 'Metadata plugin not found.');
504
+ invariant(attention, 'Attention plugin not found.');
505
+
506
+ return [
507
+ createSurface({
508
+ id: `${SPACE_PLUGIN}/article`,
509
+ role: 'article',
510
+ filter: (data): data is { subject: Space } =>
511
+ // TODO(wittjosiah): Need to avoid shotgun parsing space state everywhere.
512
+ isSpace(data.subject) && data.subject.state.get() === SpaceState.SPACE_READY,
513
+ component: ({ data, role, ...rest }) => (
504
514
  <Surface
505
- data={{ object: data.object.properties[CollectionType.typename], id: data.object.id }}
515
+ data={{ id: data.subject.id, subject: data.subject.properties[CollectionType.typename]?.target }}
506
516
  role={role}
507
517
  {...rest}
508
518
  />
509
- ) : data.object instanceof CollectionType ? (
510
- {
511
- node: <CollectionMain collection={data.object} />,
512
- disposition: 'fallback',
513
- }
514
- ) : null;
515
- // TODO(burdon): Add role name syntax to minimal plugin docs.
516
- case 'complementary--settings':
517
- return isSpace(data.subject) ? (
518
- <SpaceSettingsPanel space={data.subject} />
519
- ) : isEchoObject(data.subject) ? (
520
- { node: <DefaultObjectSettings object={data.subject} />, disposition: 'fallback' }
521
- ) : null;
522
- case 'dialog':
523
- if (data.component === 'dxos.org/plugin/space/SpaceSettingsDialog') {
524
- return (
525
- <SpaceSettingsDialog
526
- {...(data.subject as SpaceSettingsDialogProps)}
527
- createInvitationUrl={createSpaceInvitationUrl}
528
- />
529
- );
530
- } else if (data.component === 'dxos.org/plugin/space/JoinDialog') {
531
- return <JoinDialog {...(data.subject as JoinPanelProps)} />;
532
- } else if (data.component === 'dxos.org/plugin/space/CreateSpaceDialog') {
533
- return <CreateSpaceDialog />;
534
- } else if (data.component === 'dxos.org/plugin/space/CreateObjectDialog') {
535
- return (
536
- <CreateObjectDialog
537
- {...(data.subject as CreateObjectDialogProps)}
538
- schemas={schemas}
539
- navigableCollections={state.values.navigableCollections}
540
- resolve={metadataPlugin?.provides.metadata.resolver}
541
- />
542
- );
543
- }
544
- return null;
545
- case 'popover': {
546
- if (data.component === 'dxos.org/plugin/space/RenameSpacePopover' && isSpace(data.subject)) {
547
- return <PopoverRenameSpace space={data.subject} />;
548
- }
549
- if (data.component === 'dxos.org/plugin/space/RenameObjectPopover' && isReactiveObject(data.subject)) {
550
- return <PopoverRenameObject object={data.subject} />;
551
- }
552
- return null;
553
- }
554
- case 'navtree-item-end': {
555
- return isReactiveObject(data.object) ? (
556
- <SmallPresenceLive
557
- id={data.id as string}
558
- viewers={state.values.viewersByObject[fullyQualifiedId(data.object)]}
559
- />
560
- ) : isSpace(data.object) ? (
561
- <InlineSyncStatus space={data.object} />
562
- ) : (
563
- // TODO(wittjosiah): Attention glyph for non-echo items should be handled elsewhere.
564
- <SmallPresence id={data.id as string} count={0} />
565
- );
566
- }
567
- case 'navbar-end': {
568
- if (!isEchoObject(data.object) && !isSpace(data.object)) {
569
- return null;
570
- }
571
-
572
- const space = isSpace(data.object) ? data.object : getSpace(data.object);
573
- const object = isSpace(data.object)
574
- ? data.object.state.get() === SpaceState.SPACE_READY
575
- ? (space?.properties[CollectionType.typename] as CollectionType)
576
- : undefined
577
- : data.object;
578
-
579
- return space && object
580
- ? {
581
- node: (
582
- <>
583
- <SpacePresence object={object} />
584
- {space.properties[COMPOSER_SPACE_LOCK] ? null : <ShareSpaceButton space={space} />}
585
- </>
586
- ),
587
- disposition: 'hoist',
588
- }
589
- : null;
590
- }
591
- case 'section':
592
- return data.object instanceof CollectionType ? <CollectionSection collection={data.object} /> : null;
593
- case 'settings':
594
- return data.plugin === meta.id ? <SpacePluginSettings settings={settings.values} /> : null;
595
- case 'menu-footer':
596
- if (isEchoObject(data.object)) {
597
- return <MenuFooter object={data.object} />;
598
- } else {
599
- return null;
600
- }
601
- case 'status': {
602
- return <SyncStatus />;
603
- }
604
- default:
605
- return null;
606
- }
519
+ ),
520
+ }),
521
+ createSurface({
522
+ id: `${SPACE_PLUGIN}/collection-fallback`,
523
+ role: 'article',
524
+ disposition: 'fallback',
525
+ filter: (data): data is { subject: CollectionType } => data.subject instanceof CollectionType,
526
+ component: ({ data }) => <CollectionMain collection={data.subject} />,
527
+ }),
528
+ createSurface({
529
+ id: `${SPACE_PLUGIN}/settings-panel`,
530
+ // TODO(burdon): Add role name syntax to minimal plugin docs.
531
+ role: 'complementary--settings',
532
+ filter: (data): data is { subject: Space } => isSpace(data.subject),
533
+ component: ({ data }) => <SpaceSettingsPanel space={data.subject} />,
534
+ }),
535
+ createSurface({
536
+ id: `${SPACE_PLUGIN}/object-settings-panel-fallback`,
537
+ role: 'complementary--settings',
538
+ disposition: 'fallback',
539
+ filter: (data): data is { subject: ReactiveEchoObject<any> } => isEchoObject(data.subject),
540
+ component: ({ data }) => <DefaultObjectSettings object={data.subject} />,
541
+ }),
542
+ createSurface({
543
+ id: SPACE_SETTINGS_DIALOG,
544
+ role: 'dialog',
545
+ filter: (data): data is { subject: SpaceSettingsDialogProps } => data.component === SPACE_SETTINGS_DIALOG,
546
+ component: ({ data }) => (
547
+ <SpaceSettingsDialog {...data.subject} createInvitationUrl={createSpaceInvitationUrl} />
548
+ ),
549
+ }),
550
+ createSurface({
551
+ id: JOIN_DIALOG,
552
+ role: 'dialog',
553
+ filter: (data): data is { subject: JoinPanelProps } => data.component === JOIN_DIALOG,
554
+ component: ({ data }) => <JoinDialog {...data.subject} />,
555
+ }),
556
+ createSurface({
557
+ id: CREATE_SPACE_DIALOG,
558
+ role: 'dialog',
559
+ filter: (data): data is any => data.component === CREATE_SPACE_DIALOG,
560
+ component: () => <CreateSpaceDialog />,
561
+ }),
562
+ createSurface({
563
+ id: CREATE_OBJECT_DIALOG,
564
+ role: 'dialog',
565
+ filter: (data): data is { subject: Partial<CreateObjectDialogProps> } =>
566
+ data.component === CREATE_OBJECT_DIALOG,
567
+ component: ({ data }) => <CreateObjectDialog schemas={schemas} resolve={resolve} {...data.subject} />,
568
+ }),
569
+ createSurface({
570
+ id: POPOVER_RENAME_SPACE,
571
+ role: 'popover',
572
+ filter: (data): data is { subject: Space } =>
573
+ data.component === POPOVER_RENAME_SPACE && isSpace(data.subject),
574
+ component: ({ data }) => <PopoverRenameSpace space={data.subject} />,
575
+ }),
576
+ createSurface({
577
+ id: POPOVER_RENAME_OBJECT,
578
+ role: 'popover',
579
+ filter: (data): data is { subject: ReactiveEchoObject<any> } =>
580
+ data.component === POPOVER_RENAME_OBJECT && isReactiveObject(data.subject),
581
+ component: ({ data }) => <PopoverRenameObject object={data.subject} />,
582
+ }),
583
+ createSurface({
584
+ id: `${SPACE_PLUGIN}/navtree-presence`,
585
+ role: 'navtree-item-end',
586
+ filter: (data): data is { id: string; subject: ReactiveEchoObject<any>; open?: boolean } =>
587
+ typeof data.id === 'string' && isEchoObject(data.subject),
588
+ component: ({ data }) => (
589
+ <SmallPresenceLive id={data.id} open={data.open} viewers={state.values.viewersByObject[data.id]} />
590
+ ),
591
+ }),
592
+ createSurface({
593
+ // TODO(wittjosiah): Attention glyph for non-echo items should be handled elsewhere.
594
+ id: `${SPACE_PLUGIN}/navtree-presence-fallback`,
595
+ role: 'navtree-item-end',
596
+ disposition: 'fallback',
597
+ filter: (data): data is { id: string; open?: boolean } => typeof data.id === 'string',
598
+ component: ({ data }) => <SmallPresenceLive id={data.id} open={data.open} />,
599
+ }),
600
+ createSurface({
601
+ id: `${SPACE_PLUGIN}/navtree-sync-status`,
602
+ role: 'navtree-item-end',
603
+ filter: (data): data is { subject: Space; open?: boolean } => isSpace(data.subject),
604
+ component: ({ data }) => <InlineSyncStatus space={data.subject} open={data.open} />,
605
+ }),
606
+ createSurface({
607
+ id: `${SPACE_PLUGIN}/navbar-presence`,
608
+ role: 'navbar-end',
609
+ disposition: 'hoist',
610
+ filter: (data): data is { subject: Space | ReactiveEchoObject<any> } =>
611
+ isSpace(data.subject) || isEchoObject(data.subject),
612
+ component: ({ data }) => {
613
+ const space = isSpace(data.subject) ? data.subject : getSpace(data.subject);
614
+ const object = isSpace(data.subject)
615
+ ? data.subject.state.get() === SpaceState.SPACE_READY
616
+ ? (space?.properties[CollectionType.typename]?.target as CollectionType)
617
+ : undefined
618
+ : data.subject;
619
+
620
+ return space && object ? (
621
+ <>
622
+ <SpacePresence object={object} />
623
+ {space.properties[COMPOSER_SPACE_LOCK] ? null : <ShareSpaceButton space={space} />}
624
+ </>
625
+ ) : null;
626
+ },
627
+ }),
628
+ createSurface({
629
+ id: `${SPACE_PLUGIN}/collection-section`,
630
+ role: 'section',
631
+ filter: (data): data is { subject: CollectionType } => data.subject instanceof CollectionType,
632
+ component: ({ data }) => <CollectionSection collection={data.subject} />,
633
+ }),
634
+ createSurface({
635
+ id: `${SPACE_PLUGIN}/settings`,
636
+ role: 'settings',
637
+ filter: (data): data is any => data.subject === SPACE_PLUGIN,
638
+ component: () => <SpacePluginSettings settings={settings.values} />,
639
+ }),
640
+ createSurface({
641
+ id: `${SPACE_PLUGIN}/menu-footer`,
642
+ role: 'menu-footer',
643
+ filter: (data): data is { subject: ReactiveEchoObject<any> } => isEchoObject(data.subject),
644
+ component: ({ data }) => <MenuFooter object={data.subject} />,
645
+ }),
646
+ createSurface({
647
+ id: `${SPACE_PLUGIN}/status`,
648
+ role: 'status',
649
+ component: () => <SyncStatus />,
650
+ }),
651
+ ];
607
652
  },
608
653
  },
609
654
  graph: {
@@ -613,7 +658,7 @@ export const SpacePlugin = ({
613
658
  const graphPlugin = resolvePlugin(plugins, parseGraphPlugin);
614
659
 
615
660
  const client = clientPlugin?.provides.client;
616
- const dispatch = intentPlugin?.provides.intent.dispatch;
661
+ const dispatch = intentPlugin?.provides.intent.dispatchPromise;
617
662
  const resolve = metadataPlugin?.provides.metadata.resolver;
618
663
  const graph = graphPlugin?.provides.graph;
619
664
  if (!client || !dispatch || !resolve || !graph) {
@@ -653,51 +698,51 @@ export const SpacePlugin = ({
653
698
  };
654
699
 
655
700
  return [
656
- // Create spaces group node.
701
+ // Primary actions.
657
702
  createExtension({
658
- id: `${SPACE_PLUGIN}/root`,
703
+ id: `${SPACE_PLUGIN}/primary-actions`,
659
704
  filter: (node): node is Node<null> => node.id === 'root',
660
- connector: () => [spacesNode],
661
- resolver: ({ id }) => (id === SPACES ? spacesNode : undefined),
662
- }),
663
-
664
- // Create space nodes.
665
- createExtension({
666
- id: SPACES,
667
- filter: (node): node is Node<null> => node.id === SPACES,
668
705
  actions: () => [
669
706
  {
670
- id: SpaceAction.OPEN_CREATE_SPACE,
707
+ id: SpaceAction.OpenCreateSpace._tag,
671
708
  data: async () => {
672
- await dispatch({
673
- plugin: SPACE_PLUGIN,
674
- action: SpaceAction.OPEN_CREATE_SPACE,
675
- });
709
+ await dispatch(createIntent(SpaceAction.OpenCreateSpace));
676
710
  },
677
711
  properties: {
678
712
  label: ['create space label', { ns: SPACE_PLUGIN }],
679
713
  icon: 'ph--plus--regular',
680
714
  testId: 'spacePlugin.createSpace',
681
715
  disposition: 'item',
682
- className: 'border-t border-separator',
683
716
  },
684
717
  },
685
718
  {
686
- id: SpaceAction.JOIN,
719
+ id: SpaceAction.Join._tag,
687
720
  data: async () => {
688
- await dispatch({
689
- plugin: SPACE_PLUGIN,
690
- action: SpaceAction.JOIN,
691
- });
721
+ await dispatch(createIntent(SpaceAction.Join));
692
722
  },
693
723
  properties: {
694
724
  label: ['join space label', { ns: SPACE_PLUGIN }],
695
725
  icon: 'ph--sign-in--regular',
696
726
  testId: 'spacePlugin.joinSpace',
697
727
  disposition: 'item',
728
+ className: 'border-b border-separator',
698
729
  },
699
730
  },
700
731
  ],
732
+ }),
733
+
734
+ // Create spaces group node.
735
+ createExtension({
736
+ id: `${SPACE_PLUGIN}/root`,
737
+ filter: (node): node is Node<null> => node.id === 'root',
738
+ connector: () => [spacesNode],
739
+ resolver: ({ id }) => (id === SPACES ? spacesNode : undefined),
740
+ }),
741
+
742
+ // Create space nodes.
743
+ createExtension({
744
+ id: SPACES,
745
+ filter: (node): node is Node<null> => node.id === SPACES,
701
746
  connector: () => {
702
747
  const spaces = toSignal(
703
748
  (onChange) => client.spaces.subscribe(() => onChange()).unsubscribe,
@@ -806,12 +851,13 @@ export const SpacePlugin = ({
806
851
  return;
807
852
  }
808
853
 
809
- const collection = space.properties[CollectionType.typename] as CollectionType | undefined;
854
+ const collection = space.properties[CollectionType.typename]?.target as CollectionType | undefined;
810
855
  if (!collection) {
811
856
  return;
812
857
  }
813
858
 
814
859
  return collection.objects
860
+ .map((object) => object.target)
815
861
  .filter(nonNullable)
816
862
  .map((object) =>
817
863
  createObjectNode({ object, space, resolve, navigable: state.values.navigableCollections }),
@@ -832,6 +878,7 @@ export const SpacePlugin = ({
832
878
  }
833
879
 
834
880
  return collection.objects
881
+ .map((object) => object.target)
835
882
  .filter(nonNullable)
836
883
  .map((object) =>
837
884
  createObjectNode({ object, space, resolve, navigable: state.values.navigableCollections }),
@@ -866,7 +913,7 @@ export const SpacePlugin = ({
866
913
  memoize(() => {
867
914
  if (!store.value) {
868
915
  void space.db
869
- .query({ id: objectId })
916
+ .query({ id: objectId }, { deleted: QueryOptions.ShowDeletedOption.SHOW_DELETED })
870
917
  .first()
871
918
  .then((o) => (store.value = o))
872
919
  .catch((err) => log.catch(err, { objectId }));
@@ -959,7 +1006,7 @@ export const SpacePlugin = ({
959
1006
  ];
960
1007
  },
961
1008
  serializer: (plugins) => {
962
- const dispatch = resolvePlugin(plugins, parseIntentPlugin)?.provides.intent.dispatch;
1009
+ const dispatch = resolvePlugin(plugins, parseIntentPlugin)?.provides.intent.dispatchPromise;
963
1010
  if (!dispatch) {
964
1011
  return [];
965
1012
  }
@@ -986,12 +1033,10 @@ export const SpacePlugin = ({
986
1033
  type: DIRECTORY_TYPE,
987
1034
  }),
988
1035
  deserialize: async (data) => {
989
- const result = await dispatch({
990
- plugin: SPACE_PLUGIN,
991
- action: SpaceAction.CREATE,
992
- data: { name: data.name },
993
- });
994
- return result?.data.space;
1036
+ const result = await dispatch(
1037
+ createIntent(SpaceAction.Create, { name: data.name, edgeReplication: true }),
1038
+ );
1039
+ return result.data?.space;
995
1040
  },
996
1041
  },
997
1042
  {
@@ -1006,67 +1051,52 @@ export const SpacePlugin = ({
1006
1051
  const space = ancestors.find(isSpace);
1007
1052
  const collection =
1008
1053
  ancestors.findLast((ancestor) => ancestor instanceof CollectionType) ??
1009
- space?.properties[CollectionType.typename];
1054
+ space?.properties[CollectionType.typename]?.target;
1010
1055
  if (!space || !collection) {
1011
1056
  return;
1012
1057
  }
1013
1058
 
1014
- const result = await dispatch({
1015
- plugin: SPACE_PLUGIN,
1016
- action: SpaceAction.ADD_OBJECT,
1017
- data: {
1059
+ const result = await dispatch(
1060
+ createIntent(SpaceAction.AddObject, {
1018
1061
  target: collection,
1019
1062
  object: create(CollectionType, { name: data.name, objects: [], views: {} }),
1020
- },
1021
- });
1063
+ }),
1064
+ );
1022
1065
 
1023
- return result?.data.object;
1066
+ return result.data?.object;
1024
1067
  },
1025
1068
  },
1026
1069
  ];
1027
1070
  },
1028
1071
  },
1029
1072
  intent: {
1030
- resolver: async (intent, plugins) => {
1031
- const clientPlugin = resolvePlugin(plugins, parseClientPlugin);
1032
- const client = clientPlugin?.provides.client;
1033
- switch (intent.action) {
1034
- case SpaceAction.WAIT_FOR_OBJECT: {
1035
- state.values.awaiting = intent.data?.id;
1036
- return { data: true };
1037
- }
1073
+ resolvers: ({ plugins, dispatchPromise: dispatch }) => {
1074
+ const activeParts = resolvePlugin(plugins, parseNavigationPlugin)?.provides.location.active;
1075
+ const client = resolvePlugin(plugins, parseClientPlugin)?.provides.client;
1076
+ const resolve = resolvePlugin(plugins, parseMetadataResolverPlugin)?.provides.metadata.resolver;
1038
1077
 
1039
- case SpaceAction.OPEN_CREATE_SPACE: {
1040
- return {
1041
- data: true,
1042
- intents: [
1043
- [
1044
- {
1045
- action: LayoutAction.SET_LAYOUT,
1046
- data: {
1047
- element: 'dialog',
1048
- component: 'dxos.org/plugin/space/CreateSpaceDialog',
1049
- dialogBlockAlign: 'start',
1050
- subject: intent.data,
1051
- },
1052
- },
1053
- ],
1054
- ],
1055
- };
1056
- }
1078
+ invariant(activeParts, 'Active parts not available.');
1079
+ invariant(client, 'Client not available.');
1080
+ invariant(resolve, 'Metadata resolver not available.');
1057
1081
 
1058
- case SpaceAction.CREATE: {
1059
- if (!client || !S.is(SpaceForm)(intent.data)) {
1060
- return;
1061
- }
1062
-
1063
- const space = await client.spaces.create({ name: intent.data.name });
1064
- if (intent.data.edgeReplication) {
1082
+ return [
1083
+ createResolver(SpaceAction.OpenCreateSpace, () => ({
1084
+ intents: [
1085
+ createIntent(LayoutAction.SetLayout, {
1086
+ element: 'dialog',
1087
+ component: CREATE_SPACE_DIALOG,
1088
+ dialogBlockAlign: 'start',
1089
+ }),
1090
+ ],
1091
+ })),
1092
+ createResolver(SpaceAction.Create, async ({ name, edgeReplication }) => {
1093
+ const space = await client.spaces.create({ name });
1094
+ if (edgeReplication) {
1065
1095
  await space.internal.setEdgeReplicationPreference(EdgeReplicationSetting.ENABLED);
1066
1096
  }
1067
1097
  await space.waitUntilReady();
1068
1098
  const collection = create(CollectionType, { objects: [], views: {} });
1069
- space.properties[CollectionType.typename] = collection;
1099
+ space.properties[CollectionType.typename] = makeRef(collection);
1070
1100
 
1071
1101
  if (Migrations.versionProperty) {
1072
1102
  space.properties[Migrations.versionProperty] = Migrations.targetVersion;
@@ -1079,376 +1109,245 @@ export const SpacePlugin = ({
1079
1109
  activeParts: { main: [space.id] },
1080
1110
  },
1081
1111
  intents: [
1082
- [
1083
- {
1084
- action: ObservabilityAction.SEND_EVENT,
1085
- data: {
1086
- name: 'space.create',
1087
- properties: {
1088
- spaceId: space.id,
1089
- },
1090
- },
1091
- },
1092
- ],
1093
- ],
1094
- };
1095
- }
1096
-
1097
- case SpaceAction.JOIN: {
1098
- return {
1099
- data: true,
1100
- intents: [
1101
- [
1102
- {
1103
- action: LayoutAction.SET_LAYOUT,
1104
- data: {
1105
- element: 'dialog',
1106
- component: 'dxos.org/plugin/space/JoinDialog',
1107
- dialogBlockAlign: 'start',
1108
- subject: {
1109
- initialInvitationCode: intent.data?.invitationCode,
1110
- onDone: intent.data?.onDone,
1111
- } satisfies Partial<JoinPanelProps>,
1112
- },
1112
+ createIntent(ObservabilityAction.SendEvent, {
1113
+ name: 'space.create',
1114
+ properties: {
1115
+ spaceId: space.id,
1113
1116
  },
1114
- ],
1117
+ }),
1115
1118
  ],
1116
1119
  };
1117
- }
1118
-
1119
- case SpaceAction.SHARE: {
1120
- const space = intent.data?.space;
1121
- if (isSpace(space) && !space.properties[COMPOSER_SPACE_LOCK]) {
1120
+ }),
1121
+ createResolver(SpaceAction.Join, ({ invitationCode }) => ({
1122
+ intents: [
1123
+ createIntent(LayoutAction.SetLayout, {
1124
+ element: 'dialog',
1125
+ component: JOIN_DIALOG,
1126
+ dialogBlockAlign: 'start',
1127
+ subject: {
1128
+ initialInvitationCode: invitationCode,
1129
+ } satisfies Partial<JoinDialogProps>,
1130
+ }),
1131
+ ],
1132
+ })),
1133
+ createResolver(
1134
+ SpaceAction.Share,
1135
+ ({ space }) => {
1122
1136
  const active = navigationPlugin?.provides.location.active;
1123
1137
  const mode = layoutPlugin?.provides.layout.layoutMode;
1124
1138
  const current = active ? firstIdInPart(active, mode === 'solo' ? 'solo' : 'main') : undefined;
1125
1139
  const target = current?.startsWith(space.id) ? current : undefined;
1126
1140
 
1127
1141
  return {
1128
- data: true,
1129
- intents: [
1130
- [
1131
- {
1132
- action: LayoutAction.SET_LAYOUT,
1133
- data: {
1134
- element: 'dialog',
1135
- component: 'dxos.org/plugin/space/SpaceSettingsDialog',
1136
- dialogBlockAlign: 'start',
1137
- subject: {
1138
- space,
1139
- target,
1140
- initialTab: 'members',
1141
- createInvitationUrl: createSpaceInvitationUrl,
1142
- } satisfies Partial<SpaceSettingsDialogProps>,
1143
- },
1144
- },
1145
- ],
1146
- [
1147
- {
1148
- action: ObservabilityAction.SEND_EVENT,
1149
- data: {
1150
- name: 'space.share',
1151
- properties: {
1152
- space: space.id,
1153
- },
1154
- },
1155
- },
1156
- ],
1157
- ],
1158
- };
1159
- }
1160
- break;
1161
- }
1162
-
1163
- case SpaceAction.LOCK: {
1164
- const space = intent.data?.space;
1165
- if (isSpace(space)) {
1166
- space.properties[COMPOSER_SPACE_LOCK] = true;
1167
- return {
1168
- data: true,
1169
- intents: [
1170
- [
1171
- {
1172
- action: ObservabilityAction.SEND_EVENT,
1173
- data: {
1174
- name: 'space.lock',
1175
- properties: {
1176
- spaceId: space.id,
1177
- },
1178
- },
1179
- },
1180
- ],
1181
- ],
1182
- };
1183
- }
1184
- break;
1185
- }
1186
-
1187
- case SpaceAction.UNLOCK: {
1188
- const space = intent.data?.space;
1189
- if (isSpace(space)) {
1190
- space.properties[COMPOSER_SPACE_LOCK] = false;
1191
- return {
1192
- data: true,
1193
- intents: [
1194
- [
1195
- {
1196
- action: ObservabilityAction.SEND_EVENT,
1197
- data: {
1198
- name: 'space.unlock',
1199
- properties: {
1200
- spaceId: space.id,
1201
- },
1202
- },
1203
- },
1204
- ],
1205
- ],
1206
- };
1207
- }
1208
- break;
1209
- }
1210
-
1211
- case SpaceAction.RENAME: {
1212
- const { caller, space } = intent.data ?? {};
1213
- if (typeof caller === 'string' && isSpace(space)) {
1214
- return {
1215
- intents: [
1216
- [
1217
- {
1218
- action: LayoutAction.SET_LAYOUT,
1219
- data: {
1220
- element: 'popover',
1221
- anchorId: `dxos.org/ui/${caller}/${space.id}`,
1222
- component: 'dxos.org/plugin/space/RenameSpacePopover',
1223
- subject: space,
1224
- },
1225
- },
1226
- ],
1227
- ],
1228
- };
1229
- }
1230
- break;
1231
- }
1232
-
1233
- case SpaceAction.OPEN_SETTINGS: {
1234
- const space = intent.data?.space;
1235
- if (isSpace(space)) {
1236
- return {
1237
- data: true,
1238
- intents: [
1239
- [
1240
- {
1241
- action: LayoutAction.SET_LAYOUT,
1242
- data: {
1243
- element: 'dialog',
1244
- component: 'dxos.org/plugin/space/SpaceSettingsDialog',
1245
- dialogBlockAlign: 'start',
1246
- subject: {
1247
- space,
1248
- initialTab: 'settings',
1249
- createInvitationUrl: createSpaceInvitationUrl,
1250
- } satisfies Partial<SpaceSettingsDialogProps>,
1251
- },
1252
- },
1253
- ],
1254
- ],
1255
- };
1256
- }
1257
- break;
1258
- }
1259
-
1260
- case SpaceAction.OPEN: {
1261
- const space = intent.data?.space;
1262
- if (isSpace(space)) {
1263
- await space.open();
1264
- return { data: true };
1265
- }
1266
- break;
1267
- }
1268
-
1269
- case SpaceAction.CLOSE: {
1270
- const space = intent.data?.space;
1271
- if (isSpace(space)) {
1272
- await space.close();
1273
- return { data: true };
1274
- }
1275
- break;
1276
- }
1277
-
1278
- case SpaceAction.MIGRATE: {
1279
- const space = intent.data?.space;
1280
- if (isSpace(space)) {
1281
- if (space.state.get() === SpaceState.SPACE_REQUIRES_MIGRATION) {
1282
- state.values.sdkMigrationRunning[space.id] = true;
1283
- await space.internal.migrate();
1284
- state.values.sdkMigrationRunning[space.id] = false;
1285
- }
1286
- const result = await Migrations.migrate(space, intent.data?.version);
1287
- return {
1288
- data: result,
1289
1142
  intents: [
1290
- [
1291
- {
1292
- action: ObservabilityAction.SEND_EVENT,
1293
- data: {
1294
- name: 'space.migrate',
1295
- properties: {
1296
- spaceId: space.id,
1297
- version: intent.data?.version,
1298
- },
1299
- },
1143
+ createIntent(LayoutAction.SetLayout, {
1144
+ element: 'dialog',
1145
+ component: SPACE_SETTINGS_DIALOG,
1146
+ dialogBlockAlign: 'start',
1147
+ subject: {
1148
+ space,
1149
+ target,
1150
+ initialTab: 'members',
1151
+ createInvitationUrl: createSpaceInvitationUrl,
1152
+ } satisfies Partial<SpaceSettingsDialogProps>,
1153
+ }),
1154
+ createIntent(ObservabilityAction.SendEvent, {
1155
+ name: 'space.share',
1156
+ properties: {
1157
+ space: space.id,
1300
1158
  },
1301
- ],
1159
+ }),
1302
1160
  ],
1303
1161
  };
1304
- }
1305
- break;
1306
- }
1307
-
1308
- case SpaceAction.OPEN_CREATE_OBJECT: {
1162
+ },
1163
+ { filter: (data): data is { space: Space } => !data.space.properties[COMPOSER_SPACE_LOCK] },
1164
+ ),
1165
+ createResolver(SpaceAction.Lock, ({ space }) => {
1166
+ space.properties[COMPOSER_SPACE_LOCK] = true;
1309
1167
  return {
1310
- data: true,
1311
1168
  intents: [
1312
- [
1313
- {
1314
- action: LayoutAction.SET_LAYOUT,
1315
- data: {
1316
- element: 'dialog',
1317
- component: 'dxos.org/plugin/space/CreateObjectDialog',
1318
- dialogBlockAlign: 'start',
1319
- subject: intent.data,
1320
- },
1169
+ createIntent(ObservabilityAction.SendEvent, {
1170
+ name: 'space.lock',
1171
+ properties: {
1172
+ spaceId: space.id,
1321
1173
  },
1322
- ],
1174
+ }),
1323
1175
  ],
1324
1176
  };
1325
- }
1326
-
1327
- case SpaceAction.ADD_OBJECT: {
1328
- const object = intent.data?.object ?? intent.data?.result;
1329
- if (!isReactiveObject(object)) {
1330
- return;
1331
- }
1332
-
1333
- const space = isSpace(intent.data?.target) ? intent.data?.target : getSpace(intent.data?.target);
1334
- if (!space) {
1335
- return;
1177
+ }),
1178
+ createResolver(SpaceAction.Unlock, ({ space }) => {
1179
+ space.properties[COMPOSER_SPACE_LOCK] = false;
1180
+ return {
1181
+ intents: [
1182
+ createIntent(ObservabilityAction.SendEvent, {
1183
+ name: 'space.unlock',
1184
+ properties: {
1185
+ spaceId: space.id,
1186
+ },
1187
+ }),
1188
+ ],
1189
+ };
1190
+ }),
1191
+ createResolver(SpaceAction.Rename, ({ caller, space }) => {
1192
+ return {
1193
+ intents: [
1194
+ createIntent(LayoutAction.SetLayout, {
1195
+ element: 'popover',
1196
+ anchorId: `dxos.org/ui/${caller}/${space.id}`,
1197
+ component: POPOVER_RENAME_SPACE,
1198
+ subject: space,
1199
+ }),
1200
+ ],
1201
+ };
1202
+ }),
1203
+ createResolver(SpaceAction.OpenSettings, ({ space }) => {
1204
+ return {
1205
+ intents: [
1206
+ createIntent(LayoutAction.SetLayout, {
1207
+ element: 'dialog',
1208
+ component: SPACE_SETTINGS_DIALOG,
1209
+ dialogBlockAlign: 'start',
1210
+ subject: {
1211
+ space,
1212
+ initialTab: 'settings',
1213
+ createInvitationUrl: createSpaceInvitationUrl,
1214
+ } satisfies Partial<SpaceSettingsDialogProps>,
1215
+ }),
1216
+ ],
1217
+ };
1218
+ }),
1219
+ createResolver(SpaceAction.Open, async ({ space }) => {
1220
+ await space.open();
1221
+ }),
1222
+ createResolver(SpaceAction.Close, async ({ space }) => {
1223
+ await space.close();
1224
+ }),
1225
+ createResolver(SpaceAction.Migrate, async ({ space, version }) => {
1226
+ if (space.state.get() === SpaceState.SPACE_REQUIRES_MIGRATION) {
1227
+ state.values.sdkMigrationRunning[space.id] = true;
1228
+ await space.internal.migrate();
1229
+ state.values.sdkMigrationRunning[space.id] = false;
1336
1230
  }
1231
+ const result = await Migrations.migrate(space, version);
1232
+ return {
1233
+ data: result,
1234
+ intents: [
1235
+ createIntent(ObservabilityAction.SendEvent, {
1236
+ name: 'space.migrate',
1237
+ properties: {
1238
+ spaceId: space.id,
1239
+ version,
1240
+ },
1241
+ }),
1242
+ ],
1243
+ };
1244
+ }),
1245
+ createResolver(SpaceAction.OpenCreateObject, ({ target, navigable = true }) => {
1246
+ return {
1247
+ intents: [
1248
+ createIntent(LayoutAction.SetLayout, {
1249
+ element: 'dialog',
1250
+ component: CREATE_OBJECT_DIALOG,
1251
+ dialogBlockAlign: 'start',
1252
+ subject: {
1253
+ target,
1254
+ shouldNavigate: navigable
1255
+ ? (object: ReactiveObject<any>) =>
1256
+ !(object instanceof CollectionType) || state.values.navigableCollections
1257
+ : () => false,
1258
+ } satisfies Partial<CreateObjectDialogProps>,
1259
+ }),
1260
+ ],
1261
+ };
1262
+ }),
1263
+ createResolver(SpaceAction.AddObject, async ({ target, object }) => {
1264
+ const space = isSpace(target) ? target : getSpace(target);
1265
+ invariant(space, 'Space not found.');
1337
1266
 
1338
1267
  if (space.db.coreDatabase.getAllObjectIds().length >= SPACE_MAX_OBJECTS) {
1339
- return {
1340
- data: false,
1341
- intents: [
1342
- [
1343
- {
1344
- action: LayoutAction.SET_LAYOUT,
1345
- data: {
1346
- element: 'toast',
1347
- subject: {
1348
- id: `${SPACE_PLUGIN}/space-limit`,
1349
- title: translations[0]['en-US'][SPACE_PLUGIN]['space limit label'],
1350
- description: translations[0]['en-US'][SPACE_PLUGIN]['space limit description'],
1351
- duration: 5_000,
1352
- icon: 'ph--warning--regular',
1353
- actionLabel: translations[0]['en-US'][SPACE_PLUGIN]['remove deleted objects label'],
1354
- actionAlt: translations[0]['en-US'][SPACE_PLUGIN]['remove deleted objects alt'],
1355
- // TODO(wittjosiah): Use OS namespace.
1356
- closeLabel: translations[0]['en-US'][SPACE_PLUGIN]['space limit close label'],
1357
- onAction: () => space.db.coreDatabase.unlinkDeletedObjects(),
1358
- },
1359
- },
1360
- },
1361
- ],
1362
- [
1363
- {
1364
- action: ObservabilityAction.SEND_EVENT,
1365
- data: {
1366
- name: 'space.limit',
1367
- properties: {
1368
- spaceId: space.id,
1369
- },
1370
- },
1371
- },
1372
- ],
1373
- ],
1374
- };
1268
+ void dispatch(
1269
+ createIntent(LayoutAction.SetLayout, {
1270
+ element: 'toast',
1271
+ subject: {
1272
+ id: `${SPACE_PLUGIN}/space-limit`,
1273
+ title: ['space limit label', { ns: SPACE_PLUGIN }],
1274
+ description: ['space limit description', { ns: SPACE_PLUGIN }],
1275
+ duration: 5_000,
1276
+ icon: 'ph--warning--regular',
1277
+ actionLabel: ['remove deleted objects label', { ns: SPACE_PLUGIN }],
1278
+ actionAlt: ['remove deleted objects alt', { ns: SPACE_PLUGIN }],
1279
+ closeLabel: ['close label', { ns: 'os' }],
1280
+ onAction: () => space.db.coreDatabase.unlinkDeletedObjects(),
1281
+ },
1282
+ }),
1283
+ );
1284
+ void dispatch(
1285
+ createIntent(ObservabilityAction.SendEvent, {
1286
+ name: 'space.limit',
1287
+ properties: {
1288
+ spaceId: space.id,
1289
+ },
1290
+ }),
1291
+ );
1292
+
1293
+ throw new Error('Space limit reached.');
1375
1294
  }
1376
1295
 
1377
- if (intent.data?.target instanceof CollectionType) {
1378
- intent.data?.target.objects.push(object as HasId);
1379
- } else if (isSpace(intent.data?.target)) {
1380
- const collection = space.properties[CollectionType.typename];
1296
+ if (target instanceof CollectionType) {
1297
+ target.objects.push(makeRef(object as HasId));
1298
+ } else if (isSpace(target)) {
1299
+ const collection = space.properties[CollectionType.typename]?.target;
1381
1300
  if (collection instanceof CollectionType) {
1382
- collection.objects.push(object as HasId);
1301
+ collection.objects.push(makeRef(object as HasId));
1383
1302
  } else {
1384
1303
  // TODO(wittjosiah): Can't add non-echo objects by including in a collection because of types.
1385
- const collection = create(CollectionType, { objects: [object as HasId], views: {} });
1386
- space.properties[CollectionType.typename] = collection;
1304
+ const collection = create(CollectionType, { objects: [makeRef(object as HasId)], views: {} });
1305
+ space.properties[CollectionType.typename] = makeRef(collection);
1387
1306
  }
1388
1307
  }
1389
1308
 
1390
1309
  return {
1391
- data: { id: fullyQualifiedId(object), object, activeParts: { main: [fullyQualifiedId(object)] } },
1310
+ data: {
1311
+ id: fullyQualifiedId(object),
1312
+ object: object as HasId,
1313
+ activeParts: { main: [fullyQualifiedId(object)] },
1314
+ },
1392
1315
  intents: [
1393
- [
1394
- {
1395
- action: ObservabilityAction.SEND_EVENT,
1396
- data: {
1397
- name: 'space.object.add',
1398
- properties: {
1399
- spaceId: space.id,
1400
- objectId: object.id,
1401
- typename: getTypename(object),
1402
- },
1403
- },
1316
+ createIntent(ObservabilityAction.SendEvent, {
1317
+ name: 'space.object.add',
1318
+ properties: {
1319
+ spaceId: space.id,
1320
+ objectId: object.id,
1321
+ typename: getTypename(object),
1404
1322
  },
1405
- ],
1323
+ }),
1406
1324
  ],
1407
1325
  };
1408
- }
1409
-
1410
- case SpaceAction.REMOVE_OBJECTS: {
1411
- const objects = intent.data?.objects ?? intent.data?.result;
1412
- invariant(Array.isArray(objects));
1413
-
1326
+ }),
1327
+ createResolver(SpaceAction.RemoveObjects, async ({ objects, target, deletionData }, undo) => {
1414
1328
  // All objects must be a member of the same space.
1415
1329
  const space = getSpace(objects[0]);
1416
- if (!space || !objects.every((obj) => isEchoObject(obj) && getSpace(obj) === space)) {
1417
- return;
1418
- }
1419
-
1420
- const resolve = resolvePlugin(plugins, parseMetadataResolverPlugin)?.provides.metadata.resolver;
1421
- const activeParts = navigationPlugin?.provides.location.active;
1330
+ invariant(space && objects.every((obj) => isEchoObject(obj) && getSpace(obj) === space));
1422
1331
  const openObjectIds = new Set<string>(openIds(activeParts ?? {}));
1423
1332
 
1424
- if (!intent.undo && resolve) {
1425
- const parentCollection = intent.data?.collection ?? space.properties[CollectionType.typename];
1333
+ if (!undo) {
1334
+ const parentCollection: CollectionType = target ?? space.properties[CollectionType.typename]?.target;
1426
1335
  const nestedObjectsList = await Promise.all(objects.map((obj) => getNestedObjects(obj, resolve)));
1427
1336
 
1428
1337
  const deletionData = {
1429
1338
  objects,
1430
1339
  parentCollection,
1431
1340
  indices: objects.map((obj) =>
1432
- parentCollection instanceof CollectionType ? parentCollection.objects.indexOf(obj as Expando) : -1,
1341
+ parentCollection instanceof CollectionType
1342
+ ? parentCollection.objects.findIndex((object) => object.target === obj)
1343
+ : -1,
1433
1344
  ),
1434
1345
  nestedObjectsList,
1435
1346
  wasActive: objects
1436
1347
  .flatMap((obj, i) => [obj, ...nestedObjectsList[i]])
1437
1348
  .map((obj) => fullyQualifiedId(obj))
1438
1349
  .filter((id) => openObjectIds.has(id)),
1439
- };
1440
-
1441
- if (deletionData.wasActive.length > 0) {
1442
- await intentPlugin?.provides.intent.dispatch({
1443
- action: NavigationAction.CLOSE,
1444
- data: {
1445
- activeParts: {
1446
- main: deletionData.wasActive,
1447
- sidebar: deletionData.wasActive,
1448
- },
1449
- },
1450
- });
1451
- }
1350
+ } satisfies SpaceAction.DeletionData;
1452
1351
 
1453
1352
  if (deletionData.parentCollection instanceof CollectionType) {
1454
1353
  [...deletionData.indices]
@@ -1472,102 +1371,78 @@ export const SpacePlugin = ({
1472
1371
  : 'object deleted label';
1473
1372
 
1474
1373
  return {
1475
- data: true,
1476
1374
  undoable: {
1477
1375
  // TODO(ZaymonFC): Pluralize if more than one object.
1478
- message: translations[0]['en-US'][SPACE_PLUGIN][undoMessageKey],
1479
- data: deletionData,
1376
+ message: [undoMessageKey, { ns: SPACE_PLUGIN }],
1377
+ data: { deletionData },
1480
1378
  },
1379
+ intents:
1380
+ deletionData.wasActive.length > 0
1381
+ ? [createIntent(NavigationAction.Close, { activeParts: { main: deletionData.wasActive } })]
1382
+ : undefined,
1481
1383
  };
1482
1384
  } else {
1483
- const undoData = intent.data;
1484
1385
  if (
1485
- undoData?.objects?.length &&
1486
- undoData.objects.every(isEchoObject) &&
1487
- undoData.parentCollection instanceof CollectionType
1386
+ deletionData?.objects?.length &&
1387
+ deletionData.objects.every(isEchoObject) &&
1388
+ deletionData.parentCollection instanceof CollectionType
1488
1389
  ) {
1489
1390
  // Restore the object to the space.
1490
- const restoredObjects = undoData.objects.map((obj: Expando) => space.db.add(obj));
1391
+ const restoredObjects = deletionData.objects.map((obj: Expando) => space.db.add(obj));
1491
1392
 
1492
1393
  // Restore nested objects to the space.
1493
- undoData.nestedObjectsList.flat().forEach((obj: Expando) => {
1394
+ deletionData.nestedObjectsList.flat().forEach((obj: Expando) => {
1494
1395
  space.db.add(obj);
1495
1396
  });
1496
1397
 
1497
- undoData.indices.forEach((index: number, i: number) => {
1398
+ deletionData.indices.forEach((index: number, i: number) => {
1498
1399
  if (index !== -1) {
1499
- undoData.parentCollection.objects.splice(index, 0, restoredObjects[i] as Expando);
1400
+ deletionData.parentCollection.objects.splice(index, 0, makeRef(restoredObjects[i] as Expando));
1500
1401
  }
1501
1402
  });
1502
1403
 
1503
- if (undoData.wasActive.length > 0) {
1504
- await intentPlugin?.provides.intent.dispatch({
1505
- action: NavigationAction.OPEN,
1506
- data: { activeParts: { main: undoData.wasActive } },
1507
- });
1508
- }
1509
-
1510
- return { data: true };
1404
+ return {
1405
+ intents:
1406
+ deletionData.wasActive.length > 0
1407
+ ? [
1408
+ createIntent(NavigationAction.Open, {
1409
+ activeParts: { main: deletionData.wasActive as string[] },
1410
+ }),
1411
+ ]
1412
+ : undefined,
1413
+ };
1511
1414
  }
1512
-
1513
- return { data: false };
1514
- }
1515
- }
1516
-
1517
- case SpaceAction.RENAME_OBJECT: {
1518
- const object = intent.data?.object ?? intent.data?.result;
1519
- const caller = intent.data?.caller;
1520
- if (isReactiveObject(object) && caller) {
1521
- return {
1522
- intents: [
1523
- [
1524
- {
1525
- action: LayoutAction.SET_LAYOUT,
1526
- data: {
1527
- element: 'popover',
1528
- anchorId: `dxos.org/ui/${caller}/${fullyQualifiedId(object)}`,
1529
- component: 'dxos.org/plugin/space/RenameObjectPopover',
1530
- subject: object,
1531
- },
1532
- },
1533
- ],
1534
- ],
1535
- };
1536
- }
1537
- break;
1538
- }
1539
-
1540
- case SpaceAction.DUPLICATE_OBJECT: {
1541
- const originalObject = intent.data?.object ?? intent.data?.result;
1542
- const resolve = resolvePlugin(plugins, parseMetadataResolverPlugin)?.provides.metadata.resolver;
1543
- const space = isSpace(intent.data?.target) ? intent.data?.target : getSpace(intent.data?.target);
1544
- if (!isEchoObject(originalObject) || !resolve || !space) {
1545
- return;
1546
1415
  }
1416
+ }),
1417
+ createResolver(SpaceAction.RenameObject, async ({ object, caller }) => ({
1418
+ intents: [
1419
+ createIntent(LayoutAction.SetLayout, {
1420
+ element: 'popover',
1421
+ anchorId: `dxos.org/ui/${caller}/${fullyQualifiedId(object)}`,
1422
+ component: POPOVER_RENAME_OBJECT,
1423
+ subject: object,
1424
+ }),
1425
+ ],
1426
+ })),
1427
+ createResolver(SpaceAction.DuplicateObject, async ({ object, target }) => {
1428
+ const space = isSpace(target) ? target : getSpace(target);
1429
+ invariant(space, 'Space not found.');
1547
1430
 
1548
- const newObject = await cloneObject(originalObject, resolve, space);
1431
+ const newObject = await cloneObject(object, resolve, space);
1549
1432
  return {
1550
- intents: [
1551
- [{ action: SpaceAction.ADD_OBJECT, data: { object: newObject, target: intent.data?.target } }],
1552
- ],
1433
+ intents: [createIntent(SpaceAction.AddObject, { object: newObject, target })],
1553
1434
  };
1554
- }
1555
-
1556
- case SpaceAction.TOGGLE_HIDDEN: {
1557
- settings.values.showHidden = intent.data?.state ?? !settings.values.showHidden;
1558
- return { data: true };
1559
- }
1560
-
1561
- case CollectionAction.CREATE: {
1562
- const collection = create(CollectionType, {
1563
- name: intent.data?.name,
1564
- objects: [],
1565
- views: {},
1566
- });
1567
-
1568
- return { data: collection };
1569
- }
1570
- }
1435
+ }),
1436
+ createResolver(SpaceAction.WaitForObject, async ({ id }) => {
1437
+ state.values.awaiting = id;
1438
+ }),
1439
+ createResolver(SpaceAction.ToggleHidden, async ({ state }) => {
1440
+ settings.values.showHidden = state;
1441
+ }),
1442
+ createResolver(CollectionAction.Create, async ({ name }) => ({
1443
+ data: { object: create(CollectionType, { name, objects: [], views: {} }) },
1444
+ })),
1445
+ ];
1571
1446
  },
1572
1447
  },
1573
1448
  },