@dxos/plugin-space 0.7.1 → 0.7.2-staging.6d26b2a

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.
@@ -27,7 +27,7 @@ import {
27
27
  resolvePlugin,
28
28
  } from '@dxos/app-framework';
29
29
  import { EventSubscriptions, type Trigger, type UnsubscribeCallback } from '@dxos/async';
30
- import { type HasId, isReactiveObject } from '@dxos/echo-schema';
30
+ import { type HasId, isDeleted, isReactiveObject } from '@dxos/echo-schema';
31
31
  import { scheduledEffect } from '@dxos/echo-signals/core';
32
32
  import { invariant } from '@dxos/invariant';
33
33
  import { LocalStorageStore } from '@dxos/local-storage';
@@ -39,7 +39,7 @@ import { type Node, createExtension, memoize, toSignal } from '@dxos/plugin-grap
39
39
  import { ObservabilityAction } from '@dxos/plugin-observability/meta';
40
40
  import { type Client, PublicKey } from '@dxos/react-client';
41
41
  import {
42
- type EchoReactiveObject,
42
+ type ReactiveEchoObject,
43
43
  Expando,
44
44
  Filter,
45
45
  type PropertiesTypeProps,
@@ -54,6 +54,8 @@ import {
54
54
  loadObjectReferences,
55
55
  parseId,
56
56
  FQ_ID_LENGTH,
57
+ SPACE_ID_LENGTH,
58
+ OBJECT_ID_LENGTH,
57
59
  } from '@dxos/react-client/echo';
58
60
  import { type JoinPanelProps, osTranslations } from '@dxos/shell/react';
59
61
  import { ComplexMap, nonNullable, reduceGroupBy } from '@dxos/util';
@@ -571,6 +573,7 @@ export const SpacePlugin = ({
571
573
  {
572
574
  id: SPACES,
573
575
  type: SPACES,
576
+ cacheable: ['label', 'role'],
574
577
  properties: {
575
578
  label: ['spaces label', { ns: SPACE_PLUGIN }],
576
579
  testId: 'spacePlugin.spaces',
@@ -679,39 +682,41 @@ export const SpacePlugin = ({
679
682
  );
680
683
  } catch {}
681
684
  },
682
- }),
683
-
684
- // Find an object by its fully qualified id.
685
- createExtension({
686
- id: `${SPACE_PLUGIN}/objects`,
687
685
  resolver: ({ id }) => {
688
- const [spaceId, objectId] = id.split(':');
689
- const space = client.spaces.get().find((space) => space.id === spaceId);
690
- if (!space) {
686
+ if (id.length !== SPACE_ID_LENGTH) {
691
687
  return;
692
688
  }
693
689
 
694
- const spaceState = toSignal(
695
- (onChange) => space.state.subscribe(() => onChange()).unsubscribe,
696
- () => space.state.get(),
697
- space.id,
690
+ const spaces = toSignal(
691
+ (onChange) => client.spaces.subscribe(() => onChange()).unsubscribe,
692
+ () => client.spaces.get(),
698
693
  );
699
- if (spaceState !== SpaceState.SPACE_READY) {
694
+
695
+ const isReady = toSignal(
696
+ (onChange) => client.spaces.isReady.subscribe(() => onChange()).unsubscribe,
697
+ () => client.spaces.isReady.get(),
698
+ );
699
+
700
+ if (!spaces || !isReady) {
700
701
  return;
701
702
  }
702
703
 
703
- const store = memoize(() => signal(space.db.getObjectById(objectId)), id);
704
- memoize(() => {
705
- if (!store.value) {
706
- void space.db.loadObjectById(objectId).then((o) => (store.value = o));
707
- }
708
- }, id);
709
- const object = store.value;
710
- if (!object) {
704
+ const space = spaces.find((space) => space.id === id);
705
+ if (!space) {
711
706
  return;
712
707
  }
713
708
 
714
- return createObjectNode({ object, space, resolve, navigable: state.values.navigableCollections });
709
+ if (space.state.get() === SpaceState.SPACE_INACTIVE) {
710
+ return false;
711
+ } else {
712
+ return constructSpaceNode({
713
+ space,
714
+ navigable: state.values.navigableCollections,
715
+ personal: space === client.spaces.default,
716
+ namesCache: state.values.spaceNames,
717
+ resolve,
718
+ });
719
+ }
715
720
  },
716
721
  }),
717
722
 
@@ -765,22 +770,9 @@ export const SpacePlugin = ({
765
770
  },
766
771
  }),
767
772
 
768
- // Create collection actions and action groups.
773
+ // Create nodes for objects in a collection or by its fully qualified id.
769
774
  createExtension({
770
- id: `${SPACE_PLUGIN}/object-actions`,
771
- filter: (node): node is Node<EchoReactiveObject<any>> => isEchoObject(node.data),
772
- actionGroups: ({ node }) =>
773
- constructObjectActionGroups({
774
- object: node.data,
775
- dispatch,
776
- navigable: state.values.navigableCollections,
777
- }),
778
- actions: ({ node }) => constructObjectActions({ node, dispatch }),
779
- }),
780
-
781
- // Create nodes for objects in collections.
782
- createExtension({
783
- id: `${SPACE_PLUGIN}/collection-objects`,
775
+ id: `${SPACE_PLUGIN}/objects`,
784
776
  filter: (node): node is Node<CollectionType> => node.data instanceof CollectionType,
785
777
  connector: ({ node }) => {
786
778
  const collection = node.data;
@@ -796,6 +788,63 @@ export const SpacePlugin = ({
796
788
  )
797
789
  .filter(nonNullable);
798
790
  },
791
+ resolver: ({ id }) => {
792
+ if (id.length !== FQ_ID_LENGTH) {
793
+ return;
794
+ }
795
+
796
+ const [spaceId, objectId] = id.split(':');
797
+ if (spaceId.length !== SPACE_ID_LENGTH && objectId.length !== OBJECT_ID_LENGTH) {
798
+ return;
799
+ }
800
+
801
+ const space = client.spaces.get().find((space) => space.id === spaceId);
802
+ if (!space) {
803
+ return;
804
+ }
805
+
806
+ const spaceState = toSignal(
807
+ (onChange) => space.state.subscribe(() => onChange()).unsubscribe,
808
+ () => space.state.get(),
809
+ space.id,
810
+ );
811
+ if (spaceState !== SpaceState.SPACE_READY) {
812
+ return;
813
+ }
814
+
815
+ const store = memoize(() => signal(space.db.getObjectById(objectId)), id);
816
+ memoize(() => {
817
+ if (!store.value) {
818
+ void space.db
819
+ .query({ id: objectId })
820
+ .first()
821
+ .then((o) => (store.value = o));
822
+ }
823
+ }, id);
824
+ const object = store.value;
825
+ if (!object) {
826
+ return;
827
+ }
828
+
829
+ if (isDeleted(object)) {
830
+ return false;
831
+ } else {
832
+ return createObjectNode({ object, space, resolve, navigable: state.values.navigableCollections });
833
+ }
834
+ },
835
+ }),
836
+
837
+ // Create collection actions and action groups.
838
+ createExtension({
839
+ id: `${SPACE_PLUGIN}/object-actions`,
840
+ filter: (node): node is Node<ReactiveEchoObject<any>> => isEchoObject(node.data),
841
+ actionGroups: ({ node }) =>
842
+ constructObjectActionGroups({
843
+ object: node.data,
844
+ dispatch,
845
+ navigable: state.values.navigableCollections,
846
+ }),
847
+ actions: ({ node }) => constructObjectActions({ node, dispatch }),
799
848
  }),
800
849
 
801
850
  // Create nodes for object settings.
@@ -812,7 +861,13 @@ export const SpacePlugin = ({
812
861
 
813
862
  const [subjectId] = id.split('~');
814
863
  const { spaceId, objectId } = parseId(subjectId);
815
- const space = client.spaces.get().find((space) => space.id === spaceId);
864
+ const spaces = toSignal(
865
+ (onChange) => client.spaces.subscribe(() => onChange()).unsubscribe,
866
+ () => client.spaces.get(),
867
+ );
868
+ const space = spaces?.find(
869
+ (space) => space.id === spaceId && space.state.get() === SpaceState.SPACE_READY,
870
+ );
816
871
  if (!objectId) {
817
872
  const label = space
818
873
  ? space.properties.name || ['unnamed space label', { ns: SPACE_PLUGIN }]
@@ -837,7 +892,7 @@ export const SpacePlugin = ({
837
892
  const object = toSignal(
838
893
  (onChange) => {
839
894
  const timeout = setTimeout(async () => {
840
- await space?.db.loadObjectById(objectId);
895
+ await space?.db.query({ id: objectId }).first();
841
896
  onChange();
842
897
  });
843
898
 
@@ -4,13 +4,13 @@
4
4
 
5
5
  import React from 'react';
6
6
 
7
- import { type EchoReactiveObject } from '@dxos/react-client/echo';
7
+ import { type ReactiveEchoObject } from '@dxos/react-client/echo';
8
8
  import { Input, useTranslation } from '@dxos/react-ui';
9
9
 
10
10
  import { SPACE_PLUGIN } from '../meta';
11
11
 
12
12
  export type DefaultObjectSettingsProps = {
13
- object: EchoReactiveObject<any>;
13
+ object: ReactiveEchoObject<any>;
14
14
  };
15
15
 
16
16
  export const DefaultObjectSettings = ({ object }: DefaultObjectSettingsProps) => {
@@ -5,14 +5,14 @@
5
5
  import { Planet } from '@phosphor-icons/react';
6
6
  import React from 'react';
7
7
 
8
- import { type EchoReactiveObject, getSpace } from '@dxos/client/echo';
8
+ import { type ReactiveEchoObject, getSpace } from '@dxos/client/echo';
9
9
  import { useClient } from '@dxos/react-client';
10
10
  import { DropdownMenu, toLocalizedString, useTranslation } from '@dxos/react-ui';
11
11
 
12
12
  import { SPACE_PLUGIN } from '../meta';
13
13
  import { getSpaceDisplayName } from '../util';
14
14
 
15
- export const MenuFooter = ({ object }: { object: EchoReactiveObject<any> }) => {
15
+ export const MenuFooter = ({ object }: { object: ReactiveEchoObject<any> }) => {
16
16
  const { t } = useTranslation(SPACE_PLUGIN);
17
17
  const client = useClient();
18
18
  const space = getSpace(object);
package/src/util.tsx CHANGED
@@ -33,7 +33,7 @@ import {
33
33
  isEchoObject,
34
34
  isSpace,
35
35
  type Echo,
36
- type EchoReactiveObject,
36
+ type ReactiveEchoObject,
37
37
  type FilterSource,
38
38
  type Query,
39
39
  type QueryOptions,
@@ -59,7 +59,7 @@ const EMPTY_ARRAY: never[] = [];
59
59
  * @param options
60
60
  * @returns
61
61
  */
62
- export const memoizeQuery = <T extends EchoReactiveObject<any>>(
62
+ export const memoizeQuery = <T extends ReactiveEchoObject<any>>(
63
63
  spaceOrEcho?: Space | Echo,
64
64
  filter?: FilterSource<T>,
65
65
  options?: QueryOptions,
@@ -111,7 +111,7 @@ const getCollectionGraphNodePartials = ({
111
111
  // Change on disk.
112
112
  collection.objects = nextOrder.filter(isEchoObject);
113
113
  },
114
- onTransferStart: (child: Node<EchoReactiveObject<any>>, index?: number) => {
114
+ onTransferStart: (child: Node<ReactiveEchoObject<any>>, index?: number) => {
115
115
  // TODO(wittjosiah): Support transfer between spaces.
116
116
  // const childSpace = getSpace(child.data);
117
117
  // if (space && childSpace && !childSpace.key.equals(space.key)) {
@@ -139,7 +139,7 @@ const getCollectionGraphNodePartials = ({
139
139
 
140
140
  // }
141
141
  },
142
- onTransferEnd: (child: Node<EchoReactiveObject<any>>, destination: Node) => {
142
+ onTransferEnd: (child: Node<ReactiveEchoObject<any>>, destination: Node) => {
143
143
  // Remove child from origin collection.
144
144
  const index = collection.objects.indexOf(child.data);
145
145
  if (index > -1) {
@@ -155,7 +155,7 @@ const getCollectionGraphNodePartials = ({
155
155
  // childSpace.db.remove(child.data);
156
156
  // }
157
157
  },
158
- onCopy: async (child: Node<EchoReactiveObject<any>>, index?: number) => {
158
+ onCopy: async (child: Node<ReactiveEchoObject<any>>, index?: number) => {
159
159
  // Create clone of child and add to destination space.
160
160
  const newObject = await cloneObject(child.data, resolve, space);
161
161
  space.db.add(newObject);
@@ -200,6 +200,7 @@ export const constructSpaceNode = ({
200
200
  return {
201
201
  id: space.id,
202
202
  type: SPACE_TYPE,
203
+ cacheable: ['label', 'icon', 'role'],
203
204
  data: space,
204
205
  properties: {
205
206
  ...partials,
@@ -412,7 +413,7 @@ export const createObjectNode = ({
412
413
  navigable = false,
413
414
  resolve,
414
415
  }: {
415
- object: EchoReactiveObject<any>;
416
+ object: ReactiveEchoObject<any>;
416
417
  space: Space;
417
418
  navigable?: boolean;
418
419
  resolve: MetadataResolver;
@@ -435,6 +436,7 @@ export const createObjectNode = ({
435
436
  return {
436
437
  id: fullyQualifiedId(object),
437
438
  type,
439
+ cacheable: ['label', 'icon', 'role'],
438
440
  data: object,
439
441
  properties: {
440
442
  ...partials,
@@ -454,7 +456,7 @@ export const constructObjectActionGroups = ({
454
456
  navigable,
455
457
  dispatch,
456
458
  }: {
457
- object: EchoReactiveObject<any>;
459
+ object: ReactiveEchoObject<any>;
458
460
  navigable: boolean;
459
461
  dispatch: IntentDispatcher;
460
462
  }) => {
@@ -512,7 +514,7 @@ export const constructObjectActions = ({
512
514
  node,
513
515
  dispatch,
514
516
  }: {
515
- node: Node<EchoReactiveObject<any>>;
517
+ node: Node<ReactiveEchoObject<any>>;
516
518
  dispatch: IntentDispatcher;
517
519
  }) => {
518
520
  const object = node.data;
@@ -601,9 +603,9 @@ export const getActiveSpace = (graph: Graph, active?: string) => {
601
603
  * @deprecated This is a temporary solution.
602
604
  */
603
605
  export const getNestedObjects = async (
604
- object: EchoReactiveObject<any>,
606
+ object: ReactiveEchoObject<any>,
605
607
  resolve: MetadataResolver,
606
- ): Promise<EchoReactiveObject<any>[]> => {
608
+ ): Promise<ReactiveEchoObject<any>[]> => {
607
609
  const type = getTypename(object);
608
610
  if (!type) {
609
611
  return [];
@@ -615,7 +617,7 @@ export const getNestedObjects = async (
615
617
  return [];
616
618
  }
617
619
 
618
- const objects: EchoReactiveObject<any>[] = await loadReferences(object);
620
+ const objects: ReactiveEchoObject<any>[] = await loadReferences(object);
619
621
  const nested = await Promise.all(objects.map((object) => getNestedObjects(object, resolve)));
620
622
  return [...objects, ...nested.flat()];
621
623
  };