@dxos/plugin-space 0.7.4 → 0.7.5-labs.071a3e2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/app-graph-builder-TTM2YZVS.mjs +367 -0
- package/dist/lib/browser/app-graph-builder-TTM2YZVS.mjs.map +7 -0
- package/dist/lib/browser/app-graph-serializer-ZGM5NDXE.mjs +80 -0
- package/dist/lib/browser/app-graph-serializer-ZGM5NDXE.mjs.map +7 -0
- package/dist/lib/browser/chunk-2NMUVDMZ.mjs +1715 -0
- package/dist/lib/browser/chunk-2NMUVDMZ.mjs.map +7 -0
- package/dist/lib/browser/chunk-ENRYFGYE.mjs +133 -0
- package/dist/lib/browser/chunk-ENRYFGYE.mjs.map +7 -0
- package/dist/lib/browser/chunk-H2AR4OLP.mjs +316 -0
- package/dist/lib/browser/chunk-H2AR4OLP.mjs.map +7 -0
- package/dist/lib/browser/chunk-PQXZCNAU.mjs +13 -0
- package/dist/lib/browser/chunk-PQXZCNAU.mjs.map +7 -0
- package/dist/lib/browser/chunk-RLZQJD47.mjs +22 -0
- package/dist/lib/browser/chunk-RLZQJD47.mjs.map +7 -0
- package/dist/lib/browser/chunk-S5IGZNXJ.mjs +528 -0
- package/dist/lib/browser/chunk-S5IGZNXJ.mjs.map +7 -0
- package/dist/lib/browser/identity-created-VICTPQX7.mjs +28 -0
- package/dist/lib/browser/identity-created-VICTPQX7.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +193 -3521
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/intent-resolver-A5274MUR.mjs +537 -0
- package/dist/lib/browser/intent-resolver-A5274MUR.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/react-root-N6QTWYCV.mjs +28 -0
- package/dist/lib/browser/react-root-N6QTWYCV.mjs.map +7 -0
- package/dist/lib/browser/react-surface-STMNA7W7.mjs +231 -0
- package/dist/lib/browser/react-surface-STMNA7W7.mjs.map +7 -0
- package/dist/lib/browser/settings-HN5UIYQO.mjs +24 -0
- package/dist/lib/browser/settings-HN5UIYQO.mjs.map +7 -0
- package/dist/lib/browser/spaces-ready-F57ITJDR.mjs +199 -0
- package/dist/lib/browser/spaces-ready-F57ITJDR.mjs.map +7 -0
- package/dist/lib/browser/state-6DCY5YJP.mjs +47 -0
- package/dist/lib/browser/state-6DCY5YJP.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +14 -5
- package/dist/lib/node/app-graph-builder-6N4TEVHH.cjs +370 -0
- package/dist/lib/node/app-graph-builder-6N4TEVHH.cjs.map +7 -0
- package/dist/lib/node/app-graph-serializer-AWKVTYAB.cjs +88 -0
- package/dist/lib/node/app-graph-serializer-AWKVTYAB.cjs.map +7 -0
- package/dist/lib/node/chunk-2RCJT3P2.cjs +1712 -0
- package/dist/lib/node/chunk-2RCJT3P2.cjs.map +7 -0
- package/dist/lib/node/chunk-I2LRRRMV.cjs +150 -0
- package/dist/lib/node/chunk-I2LRRRMV.cjs.map +7 -0
- package/dist/lib/node/chunk-MMXP2NHE.cjs +556 -0
- package/dist/lib/node/chunk-MMXP2NHE.cjs.map +7 -0
- package/dist/lib/node/chunk-SPCSJ2CY.cjs +345 -0
- package/dist/lib/node/chunk-SPCSJ2CY.cjs.map +7 -0
- package/dist/lib/node/chunk-UX3U4RU2.cjs +42 -0
- package/dist/lib/node/chunk-UX3U4RU2.cjs.map +7 -0
- package/dist/lib/node/{meta.cjs → chunk-WZR6OAN3.cjs} +13 -13
- package/dist/lib/node/chunk-WZR6OAN3.cjs.map +7 -0
- package/dist/lib/node/identity-created-JNDKMFKI.cjs +44 -0
- package/dist/lib/node/identity-created-JNDKMFKI.cjs.map +7 -0
- package/dist/lib/node/index.cjs +176 -3506
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/intent-resolver-NVTAESKB.cjs +536 -0
- package/dist/lib/node/intent-resolver-NVTAESKB.cjs.map +7 -0
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/react-root-YCHSAYQE.cjs +50 -0
- package/dist/lib/node/react-root-YCHSAYQE.cjs.map +7 -0
- package/dist/lib/node/react-surface-ANSZ4FKK.cjs +229 -0
- package/dist/lib/node/react-surface-ANSZ4FKK.cjs.map +7 -0
- package/dist/lib/node/settings-RBBL22DJ.cjs +38 -0
- package/dist/lib/node/settings-RBBL22DJ.cjs.map +7 -0
- package/dist/lib/node/spaces-ready-WHU4J6E5.cjs +210 -0
- package/dist/lib/node/spaces-ready-WHU4J6E5.cjs.map +7 -0
- package/dist/lib/node/state-WPZC4JXB.cjs +61 -0
- package/dist/lib/node/state-WPZC4JXB.cjs.map +7 -0
- package/dist/lib/node/types/index.cjs +23 -14
- package/dist/lib/node/types/index.cjs.map +2 -2
- package/dist/lib/node-esm/app-graph-builder-MS6BI5EW.mjs +368 -0
- package/dist/lib/node-esm/app-graph-builder-MS6BI5EW.mjs.map +7 -0
- package/dist/lib/node-esm/app-graph-serializer-AWAWDSCM.mjs +81 -0
- package/dist/lib/node-esm/app-graph-serializer-AWAWDSCM.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-4HICD7AU.mjs +1716 -0
- package/dist/lib/node-esm/chunk-4HICD7AU.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-77RE7Y5J.mjs +529 -0
- package/dist/lib/node-esm/chunk-77RE7Y5J.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-ESWV7ICX.mjs +134 -0
- package/dist/lib/node-esm/chunk-ESWV7ICX.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-ICCM4YRJ.mjs +15 -0
- package/dist/lib/node-esm/chunk-ICCM4YRJ.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-LGL4A5B5.mjs +23 -0
- package/dist/lib/node-esm/chunk-LGL4A5B5.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-S6VAU6VJ.mjs +317 -0
- package/dist/lib/node-esm/chunk-S6VAU6VJ.mjs.map +7 -0
- package/dist/lib/node-esm/identity-created-3AUSSVEK.mjs +29 -0
- package/dist/lib/node-esm/identity-created-3AUSSVEK.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +193 -3521
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/intent-resolver-KJ67TU34.mjs +538 -0
- package/dist/lib/node-esm/intent-resolver-KJ67TU34.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/react-root-NBQQKAZD.mjs +29 -0
- package/dist/lib/node-esm/react-root-NBQQKAZD.mjs.map +7 -0
- package/dist/lib/node-esm/react-surface-G2H5T2D2.mjs +232 -0
- package/dist/lib/node-esm/react-surface-G2H5T2D2.mjs.map +7 -0
- package/dist/lib/node-esm/settings-VBAUB37B.mjs +25 -0
- package/dist/lib/node-esm/settings-VBAUB37B.mjs.map +7 -0
- package/dist/lib/node-esm/spaces-ready-ABADUX2P.mjs +200 -0
- package/dist/lib/node-esm/spaces-ready-ABADUX2P.mjs.map +7 -0
- package/dist/lib/node-esm/state-5GH2D5U4.mjs +48 -0
- package/dist/lib/node-esm/state-5GH2D5U4.mjs.map +7 -0
- package/dist/lib/node-esm/types/index.mjs +14 -5
- package/dist/types/src/SpacePlugin.d.ts +3 -22
- package/dist/types/src/SpacePlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts +181 -0
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -0
- package/dist/types/src/capabilities/app-graph-serializer.d.ts +4 -0
- package/dist/types/src/capabilities/app-graph-serializer.d.ts.map +1 -0
- package/dist/types/src/capabilities/capabilities.d.ts +21 -0
- package/dist/types/src/capabilities/capabilities.d.ts.map +1 -0
- package/dist/types/src/capabilities/identity-created.d.ts +4 -0
- package/dist/types/src/capabilities/identity-created.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +196 -0
- package/dist/types/src/capabilities/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/intent-resolver.d.ts +9 -0
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-root.d.ts +7 -0
- package/dist/types/src/capabilities/react-root.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface.d.ts +7 -0
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
- package/dist/types/src/capabilities/settings.d.ts +4 -0
- package/dist/types/src/capabilities/settings.d.ts.map +1 -0
- package/dist/types/src/capabilities/spaces-ready.d.ts +4 -0
- package/dist/types/src/capabilities/spaces-ready.d.ts.map +1 -0
- package/dist/types/src/capabilities/state.d.ts +5 -0
- package/dist/types/src/capabilities/state.d.ts.map +1 -0
- package/dist/types/src/components/AdvancedObjectSettings/AdvancedObjectSettings.d.ts +7 -0
- package/dist/types/src/components/AdvancedObjectSettings/AdvancedObjectSettings.d.ts.map +1 -0
- package/dist/types/src/components/AdvancedObjectSettings/ForeignKeys.d.ts +8 -0
- package/dist/types/src/components/AdvancedObjectSettings/ForeignKeys.d.ts.map +1 -0
- package/dist/types/src/components/AdvancedObjectSettings/index.d.ts +2 -0
- package/dist/types/src/components/AdvancedObjectSettings/index.d.ts.map +1 -0
- package/dist/types/src/components/AwaitingObject.d.ts.map +1 -1
- package/dist/types/src/components/BaseObjectSettings.d.ts +7 -0
- package/dist/types/src/components/BaseObjectSettings.d.ts.map +1 -0
- package/dist/types/src/components/CreateDialog/CreateObjectDialog.d.ts +5 -4
- package/dist/types/src/components/CreateDialog/CreateObjectDialog.d.ts.map +1 -1
- package/dist/types/src/components/CreateDialog/CreateObjectPanel.d.ts +9 -9
- package/dist/types/src/components/CreateDialog/CreateObjectPanel.d.ts.map +1 -1
- package/dist/types/src/components/CreateDialog/CreateSpaceDialog.d.ts +1 -0
- package/dist/types/src/components/CreateDialog/CreateSpaceDialog.d.ts.map +1 -1
- package/dist/types/src/components/JoinDialog.d.ts +1 -0
- package/dist/types/src/components/JoinDialog.d.ts.map +1 -1
- package/dist/types/src/components/PopoverRenameObject.d.ts +1 -0
- package/dist/types/src/components/PopoverRenameObject.d.ts.map +1 -1
- package/dist/types/src/components/PopoverRenameSpace.d.ts +1 -0
- package/dist/types/src/components/PopoverRenameSpace.d.ts.map +1 -1
- package/dist/types/src/components/ShareSpaceButton.d.ts.map +1 -1
- package/dist/types/src/components/SpacePluginSettings.d.ts.map +1 -1
- package/dist/types/src/components/SpacePresence.d.ts +9 -6
- package/dist/types/src/components/SpacePresence.d.ts.map +1 -1
- package/dist/types/src/components/SpacePresence.stories.d.ts +1 -1
- package/dist/types/src/components/SpacePresence.stories.d.ts.map +1 -1
- package/dist/types/src/components/SpaceSettings/SpaceSettingsDialog.d.ts +1 -0
- package/dist/types/src/components/SpaceSettings/SpaceSettingsDialog.d.ts.map +1 -1
- package/dist/types/src/components/SpaceSettings/SpaceSettingsDialog.stories.d.ts.map +1 -1
- package/dist/types/src/components/SpaceSettings/SpaceSettingsPanel.d.ts +4 -3
- package/dist/types/src/components/SpaceSettings/SpaceSettingsPanel.d.ts.map +1 -1
- package/dist/types/src/components/SpaceSettings/SpaceSettingsPanel.stories.d.ts.map +1 -1
- package/dist/types/src/components/SyncStatus/InlineSyncStatus.d.ts +3 -3
- package/dist/types/src/components/SyncStatus/InlineSyncStatus.d.ts.map +1 -1
- package/dist/types/src/components/SyncStatus/SyncStatus.d.ts.map +1 -1
- package/dist/types/src/components/SyncStatus/SyncStatusDetail.stories.d.ts +2 -2
- package/dist/types/src/components/SyncStatus/SyncStatusDetail.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +2 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/events.d.ts +5 -0
- package/dist/types/src/events.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +2 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -0
- package/dist/types/src/hooks/usePath.d.ts +11 -0
- package/dist/types/src/hooks/usePath.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +3 -5
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +2 -27
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +18 -3
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/collection.d.ts +8 -12
- package/dist/types/src/types/collection.d.ts.map +1 -1
- package/dist/types/src/types/thread.d.ts +180 -186
- package/dist/types/src/types/thread.d.ts.map +1 -1
- package/dist/types/src/types/types.d.ts +228 -16
- package/dist/types/src/types/types.d.ts.map +1 -1
- package/dist/types/src/util.d.ts +9 -8
- package/dist/types/src/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/package.json +39 -46
- package/src/SpacePlugin.tsx +121 -1538
- package/src/capabilities/app-graph-builder.ts +393 -0
- package/src/capabilities/app-graph-serializer.ts +73 -0
- package/src/capabilities/capabilities.ts +26 -0
- package/src/capabilities/identity-created.ts +26 -0
- package/src/capabilities/index.ts +17 -0
- package/src/capabilities/intent-resolver.ts +518 -0
- package/src/capabilities/react-root.tsx +20 -0
- package/src/capabilities/react-surface.tsx +226 -0
- package/src/capabilities/settings.ts +17 -0
- package/src/capabilities/spaces-ready.ts +230 -0
- package/src/capabilities/state.ts +45 -0
- package/src/components/AdvancedObjectSettings/AdvancedObjectSettings.tsx +72 -0
- package/src/components/AdvancedObjectSettings/ForeignKeys.tsx +51 -0
- package/src/components/AdvancedObjectSettings/index.ts +5 -0
- package/src/components/AwaitingObject.tsx +15 -19
- package/src/components/{DefaultObjectSettings.tsx → BaseObjectSettings.tsx} +2 -2
- package/src/components/CreateDialog/CreateObjectDialog.tsx +49 -38
- package/src/components/CreateDialog/CreateObjectPanel.tsx +137 -101
- package/src/components/CreateDialog/CreateSpaceDialog.tsx +22 -17
- package/src/components/JoinDialog.tsx +40 -48
- package/src/components/PersistenceStatus.tsx +1 -1
- package/src/components/PopoverRenameObject.tsx +2 -0
- package/src/components/PopoverRenameSpace.tsx +2 -0
- package/src/components/ShareSpaceButton.tsx +5 -4
- package/src/components/SpacePluginSettings.tsx +5 -16
- package/src/components/SpacePresence.stories.tsx +27 -19
- package/src/components/SpacePresence.tsx +41 -21
- package/src/components/SpaceSettings/SpaceSettingsDialog.stories.tsx +2 -3
- package/src/components/SpaceSettings/SpaceSettingsDialog.tsx +19 -2
- package/src/components/SpaceSettings/SpaceSettingsPanel.stories.tsx +7 -5
- package/src/components/SpaceSettings/SpaceSettingsPanel.tsx +7 -7
- package/src/components/SyncStatus/InlineSyncStatus.tsx +37 -27
- package/src/components/SyncStatus/SyncStatus.tsx +2 -1
- package/src/components/SyncStatus/SyncStatusDetail.stories.tsx +55 -51
- package/src/components/index.ts +2 -1
- package/src/events.ts +12 -0
- package/src/hooks/index.ts +5 -0
- package/src/hooks/usePath.ts +44 -0
- package/src/index.ts +3 -7
- package/src/meta.ts +2 -29
- package/src/translations.ts +7 -2
- package/src/types/collection.ts +3 -3
- package/src/types/thread.ts +6 -6
- package/src/types/types.ts +177 -42
- package/src/util.tsx +82 -65
- package/dist/lib/browser/chunk-FTKV32QZ.mjs +0 -43
- package/dist/lib/browser/chunk-FTKV32QZ.mjs.map +0 -7
- package/dist/lib/browser/chunk-MWKXNS5S.mjs +0 -124
- package/dist/lib/browser/chunk-MWKXNS5S.mjs.map +0 -7
- package/dist/lib/browser/meta.mjs +0 -15
- package/dist/lib/browser/meta.mjs.map +0 -7
- package/dist/lib/node/chunk-6SNOZF7Y.cjs +0 -152
- package/dist/lib/node/chunk-6SNOZF7Y.cjs.map +0 -7
- package/dist/lib/node/chunk-QNVEU2UD.cjs +0 -69
- package/dist/lib/node/chunk-QNVEU2UD.cjs.map +0 -7
- package/dist/lib/node/meta.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-OHEAWSCA.mjs +0 -126
- package/dist/lib/node-esm/chunk-OHEAWSCA.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-UMV7XREB.mjs +0 -45
- package/dist/lib/node-esm/chunk-UMV7XREB.mjs.map +0 -7
- package/dist/lib/node-esm/meta.mjs +0 -16
- package/dist/lib/node-esm/meta.mjs.map +0 -7
- package/dist/types/src/components/DefaultObjectSettings.d.ts +0 -7
- package/dist/types/src/components/DefaultObjectSettings.d.ts.map +0 -1
- package/dist/types/src/components/SyncStatus/InlineSyncStatus.stories.d.ts +0 -6
- package/dist/types/src/components/SyncStatus/InlineSyncStatus.stories.d.ts.map +0 -1
- package/src/components/SyncStatus/InlineSyncStatus.stories.tsx +0 -57
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useCallback } from 'react';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
Capabilities,
|
|
9
|
+
contributes,
|
|
10
|
+
createSurface,
|
|
11
|
+
Surface,
|
|
12
|
+
useCapability,
|
|
13
|
+
useCapabilities,
|
|
14
|
+
usePluginManager,
|
|
15
|
+
} from '@dxos/app-framework';
|
|
16
|
+
import { SettingsStore } from '@dxos/local-storage';
|
|
17
|
+
import { ClientCapabilities } from '@dxos/plugin-client';
|
|
18
|
+
import {
|
|
19
|
+
getSpace,
|
|
20
|
+
isEchoObject,
|
|
21
|
+
isReactiveObject,
|
|
22
|
+
isSpace,
|
|
23
|
+
SpaceState,
|
|
24
|
+
type ReactiveEchoObject,
|
|
25
|
+
type Space,
|
|
26
|
+
} from '@dxos/react-client/echo';
|
|
27
|
+
import { type JoinPanelProps } from '@dxos/shell/react';
|
|
28
|
+
|
|
29
|
+
import { SpaceCapabilities } from './capabilities';
|
|
30
|
+
import {
|
|
31
|
+
AdvancedObjectSettings,
|
|
32
|
+
BaseObjectSettings,
|
|
33
|
+
CollectionMain,
|
|
34
|
+
CollectionSection,
|
|
35
|
+
CREATE_OBJECT_DIALOG,
|
|
36
|
+
CREATE_SPACE_DIALOG,
|
|
37
|
+
CreateObjectDialog,
|
|
38
|
+
CreateSpaceDialog,
|
|
39
|
+
InlineSyncStatus,
|
|
40
|
+
JOIN_DIALOG,
|
|
41
|
+
JoinDialog,
|
|
42
|
+
MenuFooter,
|
|
43
|
+
POPOVER_RENAME_OBJECT,
|
|
44
|
+
POPOVER_RENAME_SPACE,
|
|
45
|
+
PopoverRenameObject,
|
|
46
|
+
PopoverRenameSpace,
|
|
47
|
+
SmallPresenceLive,
|
|
48
|
+
SPACE_SETTINGS_DIALOG,
|
|
49
|
+
SpacePluginSettings,
|
|
50
|
+
SpacePresence,
|
|
51
|
+
SpaceSettingsDialog,
|
|
52
|
+
SpaceSettingsPanel,
|
|
53
|
+
SyncStatus,
|
|
54
|
+
type CreateObjectDialogProps,
|
|
55
|
+
type SpaceSettingsDialogProps,
|
|
56
|
+
} from '../components';
|
|
57
|
+
import { SPACE_PLUGIN } from '../meta';
|
|
58
|
+
import { CollectionType, type SpaceSettingsProps } from '../types';
|
|
59
|
+
|
|
60
|
+
type ReactSurfaceOptions = {
|
|
61
|
+
createInvitationUrl: (invitationCode: string) => string;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export default ({ createInvitationUrl }: ReactSurfaceOptions) =>
|
|
65
|
+
contributes(Capabilities.ReactSurface, [
|
|
66
|
+
createSurface({
|
|
67
|
+
id: `${SPACE_PLUGIN}/article`,
|
|
68
|
+
role: 'article',
|
|
69
|
+
filter: (data): data is { subject: Space } =>
|
|
70
|
+
// TODO(wittjosiah): Need to avoid shotgun parsing space state everywhere.
|
|
71
|
+
isSpace(data.subject) && data.subject.state.get() === SpaceState.SPACE_READY,
|
|
72
|
+
component: ({ data, role, ...rest }) => (
|
|
73
|
+
<Surface
|
|
74
|
+
data={{ id: data.subject.id, subject: data.subject.properties[CollectionType.typename]?.target }}
|
|
75
|
+
role={role}
|
|
76
|
+
{...rest}
|
|
77
|
+
/>
|
|
78
|
+
),
|
|
79
|
+
}),
|
|
80
|
+
createSurface({
|
|
81
|
+
id: `${SPACE_PLUGIN}/collection-fallback`,
|
|
82
|
+
role: 'article',
|
|
83
|
+
position: 'fallback',
|
|
84
|
+
filter: (data): data is { subject: CollectionType } => data.subject instanceof CollectionType,
|
|
85
|
+
component: ({ data }) => <CollectionMain collection={data.subject} />,
|
|
86
|
+
}),
|
|
87
|
+
createSurface({
|
|
88
|
+
id: `${SPACE_PLUGIN}/settings-panel`,
|
|
89
|
+
// TODO(burdon): Add role name syntax to minimal plugin docs.
|
|
90
|
+
role: 'complementary--settings',
|
|
91
|
+
filter: (data): data is { subject: Space } => isSpace(data.subject),
|
|
92
|
+
component: ({ data }) => <SpaceSettingsPanel space={data.subject} />,
|
|
93
|
+
}),
|
|
94
|
+
createSurface({
|
|
95
|
+
id: `${SPACE_PLUGIN}/object-settings-base-panel`,
|
|
96
|
+
role: 'complementary--settings',
|
|
97
|
+
position: 'hoist',
|
|
98
|
+
filter: (data): data is { subject: ReactiveEchoObject<any> } => isEchoObject(data.subject),
|
|
99
|
+
component: ({ data }) => <BaseObjectSettings object={data.subject} />,
|
|
100
|
+
}),
|
|
101
|
+
createSurface({
|
|
102
|
+
id: `${SPACE_PLUGIN}/object-settings-advanced-panel`,
|
|
103
|
+
role: 'complementary--settings',
|
|
104
|
+
position: 'fallback',
|
|
105
|
+
filter: (data): data is { subject: ReactiveEchoObject<any> } => isEchoObject(data.subject),
|
|
106
|
+
component: ({ data }) => <AdvancedObjectSettings object={data.subject} />,
|
|
107
|
+
}),
|
|
108
|
+
createSurface({
|
|
109
|
+
id: SPACE_SETTINGS_DIALOG,
|
|
110
|
+
role: 'dialog',
|
|
111
|
+
filter: (data): data is { props: SpaceSettingsDialogProps } => data.component === SPACE_SETTINGS_DIALOG,
|
|
112
|
+
component: ({ data }) => <SpaceSettingsDialog {...data.props} createInvitationUrl={createInvitationUrl} />,
|
|
113
|
+
}),
|
|
114
|
+
createSurface({
|
|
115
|
+
id: JOIN_DIALOG,
|
|
116
|
+
role: 'dialog',
|
|
117
|
+
filter: (data): data is { props: JoinPanelProps } => data.component === JOIN_DIALOG,
|
|
118
|
+
component: ({ data }) => <JoinDialog {...data.props} />,
|
|
119
|
+
}),
|
|
120
|
+
createSurface({
|
|
121
|
+
id: CREATE_SPACE_DIALOG,
|
|
122
|
+
role: 'dialog',
|
|
123
|
+
filter: (data): data is any => data.component === CREATE_SPACE_DIALOG,
|
|
124
|
+
component: () => <CreateSpaceDialog />,
|
|
125
|
+
}),
|
|
126
|
+
createSurface({
|
|
127
|
+
id: CREATE_OBJECT_DIALOG,
|
|
128
|
+
role: 'dialog',
|
|
129
|
+
filter: (data): data is { props: Partial<CreateObjectDialogProps> } => data.component === CREATE_OBJECT_DIALOG,
|
|
130
|
+
component: ({ data }) => {
|
|
131
|
+
const schemas = useCapabilities(ClientCapabilities.Schema).flat();
|
|
132
|
+
const manager = usePluginManager();
|
|
133
|
+
|
|
134
|
+
const resolve = useCallback(
|
|
135
|
+
(typename: string) => {
|
|
136
|
+
return (
|
|
137
|
+
manager.context.requestCapabilities(Capabilities.Metadata).find(({ id }) => id === typename)?.metadata ??
|
|
138
|
+
{}
|
|
139
|
+
);
|
|
140
|
+
},
|
|
141
|
+
[manager],
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
return <CreateObjectDialog schemas={schemas} resolve={resolve} {...data.props} />;
|
|
145
|
+
},
|
|
146
|
+
}),
|
|
147
|
+
createSurface({
|
|
148
|
+
id: POPOVER_RENAME_SPACE,
|
|
149
|
+
role: 'popover',
|
|
150
|
+
filter: (data): data is { props: Space } => data.component === POPOVER_RENAME_SPACE && isSpace(data.props),
|
|
151
|
+
component: ({ data }) => <PopoverRenameSpace space={data.props} />,
|
|
152
|
+
}),
|
|
153
|
+
createSurface({
|
|
154
|
+
id: POPOVER_RENAME_OBJECT,
|
|
155
|
+
role: 'popover',
|
|
156
|
+
filter: (data): data is { props: ReactiveEchoObject<any> } =>
|
|
157
|
+
data.component === POPOVER_RENAME_OBJECT && isReactiveObject(data.props),
|
|
158
|
+
component: ({ data }) => <PopoverRenameObject object={data.props} />,
|
|
159
|
+
}),
|
|
160
|
+
createSurface({
|
|
161
|
+
id: `${SPACE_PLUGIN}/navtree-presence`,
|
|
162
|
+
role: 'navtree-item-end',
|
|
163
|
+
filter: (data): data is { id: string; subject: ReactiveEchoObject<any>; open?: boolean } =>
|
|
164
|
+
typeof data.id === 'string' && isEchoObject(data.subject),
|
|
165
|
+
component: ({ data }) => {
|
|
166
|
+
// TODO(wittjosiah): Doesn't need to be mutable but readonly type messes with ComplexMap.
|
|
167
|
+
const state = useCapability(SpaceCapabilities.MutableState);
|
|
168
|
+
return <SmallPresenceLive id={data.id} open={data.open} viewers={state.viewersByObject[data.id]} />;
|
|
169
|
+
},
|
|
170
|
+
}),
|
|
171
|
+
createSurface({
|
|
172
|
+
// TODO(wittjosiah): Attention glyph for non-echo items should be handled elsewhere.
|
|
173
|
+
id: `${SPACE_PLUGIN}/navtree-presence-fallback`,
|
|
174
|
+
role: 'navtree-item-end',
|
|
175
|
+
position: 'fallback',
|
|
176
|
+
filter: (data): data is { id: string; open?: boolean } => typeof data.id === 'string',
|
|
177
|
+
component: ({ data }) => <SmallPresenceLive id={data.id} open={data.open} />,
|
|
178
|
+
}),
|
|
179
|
+
createSurface({
|
|
180
|
+
id: `${SPACE_PLUGIN}/navtree-sync-status`,
|
|
181
|
+
role: 'navtree-item-end',
|
|
182
|
+
filter: (data): data is { subject: Space; open?: boolean } => isSpace(data.subject),
|
|
183
|
+
component: ({ data }) => <InlineSyncStatus space={data.subject} open={data.open} />,
|
|
184
|
+
}),
|
|
185
|
+
createSurface({
|
|
186
|
+
id: `${SPACE_PLUGIN}/navbar-presence`,
|
|
187
|
+
role: 'navbar-end',
|
|
188
|
+
position: 'hoist',
|
|
189
|
+
filter: (data): data is { subject: Space | ReactiveEchoObject<any> } =>
|
|
190
|
+
isSpace(data.subject) || isEchoObject(data.subject),
|
|
191
|
+
component: ({ data }) => {
|
|
192
|
+
const space = isSpace(data.subject) ? data.subject : getSpace(data.subject);
|
|
193
|
+
const object = isSpace(data.subject)
|
|
194
|
+
? data.subject.state.get() === SpaceState.SPACE_READY
|
|
195
|
+
? (space?.properties[CollectionType.typename]?.target as CollectionType)
|
|
196
|
+
: undefined
|
|
197
|
+
: data.subject;
|
|
198
|
+
|
|
199
|
+
return object ? <SpacePresence object={object} /> : null;
|
|
200
|
+
},
|
|
201
|
+
}),
|
|
202
|
+
createSurface({
|
|
203
|
+
id: `${SPACE_PLUGIN}/collection-section`,
|
|
204
|
+
role: 'section',
|
|
205
|
+
filter: (data): data is { subject: CollectionType } => data.subject instanceof CollectionType,
|
|
206
|
+
component: ({ data }) => <CollectionSection collection={data.subject} />,
|
|
207
|
+
}),
|
|
208
|
+
createSurface({
|
|
209
|
+
id: `${SPACE_PLUGIN}/settings`,
|
|
210
|
+
role: 'article',
|
|
211
|
+
filter: (data): data is { subject: SettingsStore<SpaceSettingsProps> } =>
|
|
212
|
+
data.subject instanceof SettingsStore && data.subject.prefix === SPACE_PLUGIN,
|
|
213
|
+
component: ({ data: { subject } }) => <SpacePluginSettings settings={subject.value} />,
|
|
214
|
+
}),
|
|
215
|
+
createSurface({
|
|
216
|
+
id: `${SPACE_PLUGIN}/menu-footer`,
|
|
217
|
+
role: 'menu-footer',
|
|
218
|
+
filter: (data): data is { subject: ReactiveEchoObject<any> } => isEchoObject(data.subject),
|
|
219
|
+
component: ({ data }) => <MenuFooter object={data.subject} />,
|
|
220
|
+
}),
|
|
221
|
+
createSurface({
|
|
222
|
+
id: `${SPACE_PLUGIN}/status`,
|
|
223
|
+
role: 'status',
|
|
224
|
+
component: () => <SyncStatus />,
|
|
225
|
+
}),
|
|
226
|
+
]);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Capabilities, contributes } from '@dxos/app-framework';
|
|
6
|
+
import { create } from '@dxos/live-object';
|
|
7
|
+
|
|
8
|
+
import { SPACE_PLUGIN } from '../meta';
|
|
9
|
+
import { type SpaceSettingsProps, SpaceSettingsSchema } from '../types';
|
|
10
|
+
|
|
11
|
+
export default () => {
|
|
12
|
+
const settings = create<SpaceSettingsProps>({
|
|
13
|
+
showHidden: false,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
return contributes(Capabilities.Settings, { schema: SpaceSettingsSchema, prefix: SPACE_PLUGIN, value: settings });
|
|
17
|
+
};
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { contributes, createIntent, type PluginsContext, Capabilities, LayoutAction } from '@dxos/app-framework';
|
|
6
|
+
import { EventSubscriptions } from '@dxos/async';
|
|
7
|
+
import { Expando } from '@dxos/echo-schema';
|
|
8
|
+
import { scheduledEffect } from '@dxos/echo-signals/core';
|
|
9
|
+
import { create } from '@dxos/live-object';
|
|
10
|
+
import { log } from '@dxos/log';
|
|
11
|
+
import { AttentionCapabilities } from '@dxos/plugin-attention';
|
|
12
|
+
import { ClientCapabilities } from '@dxos/plugin-client';
|
|
13
|
+
import { DeckCapabilities } from '@dxos/plugin-deck';
|
|
14
|
+
import { EdgeReplicationSetting } from '@dxos/protocols/proto/dxos/echo/metadata';
|
|
15
|
+
import { PublicKey } from '@dxos/react-client';
|
|
16
|
+
import { Filter, FQ_ID_LENGTH, parseFullyQualifiedId, SpaceState } from '@dxos/react-client/echo';
|
|
17
|
+
import { ComplexMap, reduceGroupBy } from '@dxos/util';
|
|
18
|
+
|
|
19
|
+
import { SpaceCapabilities } from './capabilities';
|
|
20
|
+
import { SpaceAction } from '../types';
|
|
21
|
+
import { COMPOSER_SPACE_LOCK, SHARED } from '../util';
|
|
22
|
+
|
|
23
|
+
const ACTIVE_NODE_BROADCAST_INTERVAL = 30_000;
|
|
24
|
+
const WAIT_FOR_OBJECT_TIMEOUT = 1000;
|
|
25
|
+
|
|
26
|
+
export default async (context: PluginsContext) => {
|
|
27
|
+
const subscriptions = new EventSubscriptions();
|
|
28
|
+
const spaceSubscriptions = new EventSubscriptions();
|
|
29
|
+
|
|
30
|
+
const { dispatchPromise: dispatch } = context.requestCapability(Capabilities.IntentDispatcher);
|
|
31
|
+
const { graph } = context.requestCapability(Capabilities.AppGraph);
|
|
32
|
+
const layout = context.requestCapability(Capabilities.Layout);
|
|
33
|
+
const deck = context.requestCapability(DeckCapabilities.DeckState);
|
|
34
|
+
const attention = context.requestCapability(AttentionCapabilities.Attention);
|
|
35
|
+
const state = context.requestCapability(SpaceCapabilities.MutableState);
|
|
36
|
+
const client = context.requestCapability(ClientCapabilities.Client);
|
|
37
|
+
|
|
38
|
+
const defaultSpace = client.spaces.default;
|
|
39
|
+
await defaultSpace.waitUntilReady();
|
|
40
|
+
|
|
41
|
+
if (deck.activeDeck === 'default') {
|
|
42
|
+
await dispatch(createIntent(LayoutAction.SwitchWorkspace, { part: 'workspace', subject: defaultSpace.id }));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Initialize space sharing lock in default space.
|
|
46
|
+
if (typeof defaultSpace.properties[COMPOSER_SPACE_LOCK] !== 'boolean') {
|
|
47
|
+
defaultSpace.properties[COMPOSER_SPACE_LOCK] = true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const {
|
|
51
|
+
objects: [spacesOrder],
|
|
52
|
+
} = await defaultSpace.db.query(Filter.schema(Expando, { key: SHARED })).run();
|
|
53
|
+
if (!spacesOrder) {
|
|
54
|
+
// TODO(wittjosiah): Cannot be a Folder because Spaces are not TypedObjects so can't be saved in the database.
|
|
55
|
+
// Instead, we store order as an array of space ids.
|
|
56
|
+
defaultSpace.db.add(create({ key: SHARED, order: [] }));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Await missing objects.
|
|
60
|
+
subscriptions.add(
|
|
61
|
+
scheduledEffect(
|
|
62
|
+
() => ({ active: layout.active }),
|
|
63
|
+
({ active }) => {
|
|
64
|
+
if (active.length !== 1) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const node = graph.findNode(active[0]);
|
|
69
|
+
if (!node && active[0].length === FQ_ID_LENGTH) {
|
|
70
|
+
const timeout = setTimeout(async () => {
|
|
71
|
+
const node = graph.findNode(active[0]);
|
|
72
|
+
if (!node) {
|
|
73
|
+
await dispatch(createIntent(SpaceAction.WaitForObject, { id: active[0] }));
|
|
74
|
+
}
|
|
75
|
+
}, WAIT_FOR_OBJECT_TIMEOUT);
|
|
76
|
+
|
|
77
|
+
return () => clearTimeout(timeout);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
),
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// Cache space names.
|
|
84
|
+
subscriptions.add(
|
|
85
|
+
client.spaces.subscribe(async (spaces) => {
|
|
86
|
+
// TODO(wittjosiah): Remove. This is a hack to be able to migrate the default space properties.
|
|
87
|
+
if (defaultSpace.state.get() === SpaceState.SPACE_REQUIRES_MIGRATION) {
|
|
88
|
+
await defaultSpace.internal.migrate();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
spaces
|
|
92
|
+
.filter((space) => space.state.get() === SpaceState.SPACE_READY)
|
|
93
|
+
.forEach((space) => {
|
|
94
|
+
subscriptions.add(
|
|
95
|
+
scheduledEffect(
|
|
96
|
+
() => ({ name: space.properties.name }),
|
|
97
|
+
({ name }) => (state.spaceNames[space.id] = name),
|
|
98
|
+
),
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
}).unsubscribe,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// Broadcast active node to other peers in the space.
|
|
105
|
+
subscriptions.add(
|
|
106
|
+
scheduledEffect(
|
|
107
|
+
() => ({ current: attention.current, active: layout.active, inactive: layout.inactive }),
|
|
108
|
+
({ current, active, inactive }) => {
|
|
109
|
+
const send = () => {
|
|
110
|
+
const spaces = client.spaces.get();
|
|
111
|
+
const identity = client.halo.identity.get();
|
|
112
|
+
if (identity) {
|
|
113
|
+
// Group parts by space for efficient messaging.
|
|
114
|
+
const idsBySpace = reduceGroupBy(active, (id) => {
|
|
115
|
+
try {
|
|
116
|
+
const [spaceId] = parseFullyQualifiedId(id);
|
|
117
|
+
return spaceId;
|
|
118
|
+
} catch {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const removedBySpace = reduceGroupBy(inactive, (id) => {
|
|
124
|
+
try {
|
|
125
|
+
const [spaceId] = parseFullyQualifiedId(id);
|
|
126
|
+
return spaceId;
|
|
127
|
+
} catch {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// NOTE: Ensure all spaces are included so that we send the correct `removed` object arrays.
|
|
133
|
+
for (const space of spaces) {
|
|
134
|
+
if (!idsBySpace.has(space.id)) {
|
|
135
|
+
idsBySpace.set(space.id, []);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
for (const [spaceId, added] of idsBySpace) {
|
|
140
|
+
const removed = removedBySpace.get(spaceId) ?? [];
|
|
141
|
+
const space = spaces.find((space) => space.id === spaceId);
|
|
142
|
+
if (!space) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
void space
|
|
147
|
+
.postMessage('viewing', {
|
|
148
|
+
identityKey: identity.identityKey.toHex(),
|
|
149
|
+
attended: current,
|
|
150
|
+
added,
|
|
151
|
+
removed,
|
|
152
|
+
})
|
|
153
|
+
// TODO(burdon): This seems defensive; why would this fail? Backoff interval.
|
|
154
|
+
.catch((err) => {
|
|
155
|
+
log.warn('Failed to broadcast active node for presence.', { err: err.message });
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
send();
|
|
162
|
+
// Send at interval to allow peers to expire entries if they become disconnected.
|
|
163
|
+
const interval = setInterval(() => send(), ACTIVE_NODE_BROADCAST_INTERVAL);
|
|
164
|
+
return () => clearInterval(interval);
|
|
165
|
+
},
|
|
166
|
+
),
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
// Listen for active nodes from other peers in the space.
|
|
170
|
+
subscriptions.add(
|
|
171
|
+
client.spaces.subscribe((spaces) => {
|
|
172
|
+
spaceSubscriptions.clear();
|
|
173
|
+
spaces.forEach((space) => {
|
|
174
|
+
spaceSubscriptions.add(
|
|
175
|
+
space.listen('viewing', (message) => {
|
|
176
|
+
const { added, removed, attended } = message.payload;
|
|
177
|
+
|
|
178
|
+
const identityKey = PublicKey.safeFrom(message.payload.identityKey);
|
|
179
|
+
const currentIdentity = client.halo.identity.get();
|
|
180
|
+
if (
|
|
181
|
+
identityKey &&
|
|
182
|
+
!currentIdentity?.identityKey.equals(identityKey) &&
|
|
183
|
+
Array.isArray(added) &&
|
|
184
|
+
Array.isArray(removed)
|
|
185
|
+
) {
|
|
186
|
+
added.forEach((id) => {
|
|
187
|
+
if (typeof id === 'string') {
|
|
188
|
+
if (!(id in state.viewersByObject)) {
|
|
189
|
+
state.viewersByObject[id] = new ComplexMap(PublicKey.hash);
|
|
190
|
+
}
|
|
191
|
+
state.viewersByObject[id]!.set(identityKey, {
|
|
192
|
+
lastSeen: Date.now(),
|
|
193
|
+
currentlyAttended: new Set(attended).has(id),
|
|
194
|
+
});
|
|
195
|
+
if (!state.viewersByIdentity.has(identityKey)) {
|
|
196
|
+
state.viewersByIdentity.set(identityKey, new Set());
|
|
197
|
+
}
|
|
198
|
+
state.viewersByIdentity.get(identityKey)!.add(id);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
removed.forEach((id) => {
|
|
203
|
+
if (typeof id === 'string') {
|
|
204
|
+
state.viewersByObject[id]?.delete(identityKey);
|
|
205
|
+
state.viewersByIdentity.get(identityKey)?.delete(id);
|
|
206
|
+
// It’s okay for these to be empty sets/maps, reduces churn.
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}),
|
|
211
|
+
);
|
|
212
|
+
});
|
|
213
|
+
}).unsubscribe,
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
// Enable edge replication for all spaces.
|
|
217
|
+
try {
|
|
218
|
+
await Promise.all(
|
|
219
|
+
client.spaces.get().map((space) => space.internal.setEdgeReplicationPreference(EdgeReplicationSetting.ENABLED)),
|
|
220
|
+
);
|
|
221
|
+
state.enabledEdgeReplication = true;
|
|
222
|
+
} catch (err) {
|
|
223
|
+
log.catch(err);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return contributes(Capabilities.Null, null, () => {
|
|
227
|
+
spaceSubscriptions.clear();
|
|
228
|
+
subscriptions.clear();
|
|
229
|
+
});
|
|
230
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { effect } from '@preact/signals-core';
|
|
6
|
+
|
|
7
|
+
import { Capabilities, contributes, type PluginsContext } from '@dxos/app-framework';
|
|
8
|
+
import { PublicKey } from '@dxos/keys';
|
|
9
|
+
import { LocalStorageStore } from '@dxos/local-storage';
|
|
10
|
+
import { ComplexMap } from '@dxos/util';
|
|
11
|
+
|
|
12
|
+
import { SpaceCapabilities } from './capabilities';
|
|
13
|
+
import { SPACE_PLUGIN } from '../meta';
|
|
14
|
+
import { type PluginState } from '../types';
|
|
15
|
+
|
|
16
|
+
export default (context: PluginsContext) => {
|
|
17
|
+
const state = new LocalStorageStore<PluginState>(SPACE_PLUGIN, {
|
|
18
|
+
awaiting: undefined,
|
|
19
|
+
spaceNames: {},
|
|
20
|
+
viewersByObject: {},
|
|
21
|
+
// TODO(wittjosiah): Stop using (Complex)Map inside reactive object.
|
|
22
|
+
viewersByIdentity: new ComplexMap(PublicKey.hash),
|
|
23
|
+
sdkMigrationRunning: {},
|
|
24
|
+
navigableCollections: false,
|
|
25
|
+
enabledEdgeReplication: false,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
state
|
|
29
|
+
.prop({ key: 'spaceNames', type: LocalStorageStore.json<Record<string, string>>() })
|
|
30
|
+
.prop({ key: 'enabledEdgeReplication', type: LocalStorageStore.bool() });
|
|
31
|
+
|
|
32
|
+
const manager = context.requestCapability(Capabilities.PluginManager);
|
|
33
|
+
const unsubscribe = effect(() => {
|
|
34
|
+
// TODO(wittjosiah): Find a way to make this capability-based.
|
|
35
|
+
const enabled = manager.enabled.includes('dxos.org/plugin/stack');
|
|
36
|
+
if (enabled !== state.values.navigableCollections) {
|
|
37
|
+
state.values.navigableCollections = enabled;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return contributes(SpaceCapabilities.State, state.values, () => {
|
|
42
|
+
unsubscribe();
|
|
43
|
+
state.close();
|
|
44
|
+
});
|
|
45
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useCallback, useState } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type ForeignKey, ForeignKeySchema } from '@dxos/echo-schema';
|
|
8
|
+
import { getMeta, type ReactiveEchoObject } from '@dxos/react-client/echo';
|
|
9
|
+
import { IconButton, useTranslation, Separator } from '@dxos/react-ui';
|
|
10
|
+
import { Form } from '@dxos/react-ui-form';
|
|
11
|
+
|
|
12
|
+
import { ForeignKeys } from './ForeignKeys';
|
|
13
|
+
import { SPACE_PLUGIN } from '../../meta';
|
|
14
|
+
|
|
15
|
+
const initialValues = {
|
|
16
|
+
source: '',
|
|
17
|
+
id: '',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type AdvancedObjectSettingsProps = {
|
|
21
|
+
object: ReactiveEchoObject<any>;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const AdvancedObjectSettings = ({ object }: AdvancedObjectSettingsProps) => {
|
|
25
|
+
const { t } = useTranslation(SPACE_PLUGIN);
|
|
26
|
+
const [adding, setAdding] = useState(false);
|
|
27
|
+
const keys = getMeta(object).keys;
|
|
28
|
+
|
|
29
|
+
const handleNew = useCallback(() => setAdding(true), []);
|
|
30
|
+
const handleCancel = useCallback(() => setAdding(false), []);
|
|
31
|
+
const handleSave = useCallback(
|
|
32
|
+
(key: ForeignKey) => {
|
|
33
|
+
const index = keys.findIndex(({ source, id }) => source === key.source && id === key.id);
|
|
34
|
+
if (index === -1) {
|
|
35
|
+
keys.push(key);
|
|
36
|
+
}
|
|
37
|
+
setAdding(false);
|
|
38
|
+
},
|
|
39
|
+
[keys],
|
|
40
|
+
);
|
|
41
|
+
const handleDelete = useCallback(
|
|
42
|
+
(key: ForeignKey) => {
|
|
43
|
+
const index = keys.findIndex(({ source, id }) => source === key.source && id === key.id);
|
|
44
|
+
if (index !== -1) {
|
|
45
|
+
keys.splice(index, 1);
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
[keys],
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// TODO(wittjosiah): This should be wrapped in an "Advanced" accordion.
|
|
52
|
+
return (
|
|
53
|
+
<>
|
|
54
|
+
<Separator />
|
|
55
|
+
<div className='p-2 flex flex-col gap-4'>
|
|
56
|
+
<h2>{t('advanced settings label')}</h2>
|
|
57
|
+
<div className='flex items-center'>
|
|
58
|
+
<h3 className='text-sm font-semibold'>{t('foreign keys')}</h3>
|
|
59
|
+
<div className='grow' />
|
|
60
|
+
<IconButton
|
|
61
|
+
classNames={adding && 'invisible'}
|
|
62
|
+
icon='ph--plus--regular'
|
|
63
|
+
label={t('add key')}
|
|
64
|
+
onClick={handleNew}
|
|
65
|
+
/>
|
|
66
|
+
</div>
|
|
67
|
+
{!adding && <ForeignKeys keys={keys} onDelete={handleDelete} />}
|
|
68
|
+
</div>
|
|
69
|
+
{adding && <Form schema={ForeignKeySchema} values={initialValues} onSave={handleSave} onCancel={handleCancel} />}
|
|
70
|
+
</>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useCallback } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type ForeignKey } from '@dxos/echo-schema';
|
|
8
|
+
import { IconButton, List, ListItem, useTranslation } from '@dxos/react-ui';
|
|
9
|
+
|
|
10
|
+
import { SPACE_PLUGIN } from '../../meta';
|
|
11
|
+
|
|
12
|
+
export type ForeignKeysProps = {
|
|
13
|
+
keys: ForeignKey[];
|
|
14
|
+
onDelete?: (key: ForeignKey) => void;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// TODO(wittjosiah): This is a clone of `TokenManager`. Consider a form variant for arrays of read-only objects.
|
|
18
|
+
export const ForeignKeys = ({ keys, onDelete }: ForeignKeysProps) => {
|
|
19
|
+
return (
|
|
20
|
+
<List classNames='flex flex-col gap-2'>
|
|
21
|
+
{keys.map((key) => (
|
|
22
|
+
<KeyItem key={key.id} forignKey={key} onDelete={onDelete} />
|
|
23
|
+
))}
|
|
24
|
+
</List>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type KeyItemProps = {
|
|
29
|
+
forignKey: ForeignKey;
|
|
30
|
+
onDelete?: (key: ForeignKey) => void;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const KeyItem = ({ forignKey, onDelete }: KeyItemProps) => {
|
|
34
|
+
const { t } = useTranslation(SPACE_PLUGIN);
|
|
35
|
+
|
|
36
|
+
const handleDelete = useCallback(() => {
|
|
37
|
+
onDelete?.(forignKey);
|
|
38
|
+
}, [forignKey, onDelete]);
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<ListItem.Root classNames='px-2'>
|
|
42
|
+
<ListItem.Heading classNames='flex flex-col grow truncate'>
|
|
43
|
+
<div>{forignKey.source}</div>
|
|
44
|
+
<div className='text-description text-sm truncate'>{forignKey.id}</div>
|
|
45
|
+
</ListItem.Heading>
|
|
46
|
+
<ListItem.Endcap>
|
|
47
|
+
<IconButton iconOnly icon='ph--x--regular' variant='ghost' label={t('delete key')} onClick={handleDelete} />
|
|
48
|
+
</ListItem.Endcap>
|
|
49
|
+
</ListItem.Root>
|
|
50
|
+
);
|
|
51
|
+
};
|