@dxos/plugin-space 0.6.14-main.2b6a0f3 → 0.6.14-main.f49f251
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/{chunk-47SVNCZM.mjs → chunk-FOI7DAUV.mjs} +1 -1
- package/dist/lib/browser/{chunk-47SVNCZM.mjs.map → chunk-FOI7DAUV.mjs.map} +2 -2
- package/dist/lib/browser/index.mjs +208 -108
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/types/index.mjs +1 -1
- package/dist/lib/node/{chunk-CTYDNFGG.cjs → chunk-OTDRTHT4.cjs} +4 -4
- package/dist/lib/node/{chunk-CTYDNFGG.cjs.map → chunk-OTDRTHT4.cjs.map} +2 -2
- package/dist/lib/node/index.cjs +234 -135
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/types/index.cjs +11 -11
- package/dist/lib/node/types/index.cjs.map +1 -1
- package/dist/lib/node-esm/{chunk-PLPMYTLC.mjs → chunk-FYDGMPSC.mjs} +1 -1
- package/dist/lib/node-esm/{chunk-PLPMYTLC.mjs.map → chunk-FYDGMPSC.mjs.map} +2 -2
- package/dist/lib/node-esm/index.mjs +208 -108
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/types/index.mjs +1 -1
- package/dist/types/src/SpacePlugin.d.ts.map +1 -1
- package/dist/types/src/components/DefaultObjectSettings.d.ts +1 -1
- package/dist/types/src/components/DefaultObjectSettings.d.ts.map +1 -1
- package/dist/types/src/components/SpacePresence.d.ts +4 -2
- package/dist/types/src/components/SpacePresence.d.ts.map +1 -1
- package/dist/types/src/components/SpaceSettingsPanel.d.ts +7 -0
- package/dist/types/src/components/SpaceSettingsPanel.d.ts.map +1 -0
- package/dist/types/src/components/SyncStatus/SyncStatus.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -0
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +2 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/types.d.ts +3 -0
- package/dist/types/src/types/types.d.ts.map +1 -1
- package/dist/types/src/util.d.ts +1 -1
- package/dist/types/src/util.d.ts.map +1 -1
- package/package.json +40 -49
- package/src/SpacePlugin.tsx +62 -32
- package/src/components/DefaultObjectSettings.tsx +3 -2
- package/src/components/SpacePresence.tsx +33 -22
- package/src/components/SpaceSettings.tsx +5 -5
- package/src/components/SpaceSettingsPanel.tsx +59 -0
- package/src/components/SyncStatus/SyncStatus.tsx +1 -1
- package/src/components/index.ts +1 -0
- package/src/translations.ts +4 -2
- package/src/types/types.ts +3 -1
- package/src/util.tsx +3 -3
package/src/SpacePlugin.tsx
CHANGED
|
@@ -34,8 +34,8 @@ import { type Node, createExtension, memoize, toSignal } from '@dxos/plugin-grap
|
|
|
34
34
|
import { ObservabilityAction } from '@dxos/plugin-observability/meta';
|
|
35
35
|
import { type Client, PublicKey } from '@dxos/react-client';
|
|
36
36
|
import {
|
|
37
|
-
Expando,
|
|
38
37
|
type EchoReactiveObject,
|
|
38
|
+
Expando,
|
|
39
39
|
Filter,
|
|
40
40
|
type PropertiesTypeProps,
|
|
41
41
|
type Space,
|
|
@@ -47,7 +47,7 @@ import {
|
|
|
47
47
|
isEchoObject,
|
|
48
48
|
isSpace,
|
|
49
49
|
loadObjectReferences,
|
|
50
|
-
|
|
50
|
+
parseId,
|
|
51
51
|
} from '@dxos/react-client/echo';
|
|
52
52
|
import { Dialog } from '@dxos/react-ui';
|
|
53
53
|
import { ClipboardProvider, InvitationManager, type InvitationManagerProps, osTranslations } from '@dxos/shell/react';
|
|
@@ -68,6 +68,7 @@ import {
|
|
|
68
68
|
SmallPresenceLive,
|
|
69
69
|
SpacePresence,
|
|
70
70
|
SpaceSettings,
|
|
71
|
+
SpaceSettingsPanel,
|
|
71
72
|
SyncStatus,
|
|
72
73
|
} from './components';
|
|
73
74
|
import meta, { SPACE_PLUGIN, SpaceAction } from './meta';
|
|
@@ -128,6 +129,7 @@ export const SpacePlugin = ({
|
|
|
128
129
|
awaiting: undefined,
|
|
129
130
|
spaceNames: {},
|
|
130
131
|
viewersByObject: {},
|
|
132
|
+
// TODO(wittjosiah): Stop using (Complex)Map inside reactive object.
|
|
131
133
|
viewersByIdentity: new ComplexMap(PublicKey.hash),
|
|
132
134
|
sdkMigrationRunning: {},
|
|
133
135
|
});
|
|
@@ -284,17 +286,8 @@ export const SpacePlugin = ({
|
|
|
284
286
|
return {
|
|
285
287
|
meta,
|
|
286
288
|
ready: async (plugins) => {
|
|
287
|
-
settings.prop({
|
|
288
|
-
|
|
289
|
-
storageKey: 'show-hidden',
|
|
290
|
-
type: LocalStorageStore.bool({ allowUndefined: true }),
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
state.prop({
|
|
294
|
-
key: 'spaceNames',
|
|
295
|
-
storageKey: 'space-names',
|
|
296
|
-
type: LocalStorageStore.json<Record<string, string>>(),
|
|
297
|
-
});
|
|
289
|
+
settings.prop({ key: 'showHidden', type: LocalStorageStore.bool({ allowUndefined: true }) });
|
|
290
|
+
state.prop({ key: 'spaceNames', type: LocalStorageStore.json<Record<string, string>>() });
|
|
298
291
|
|
|
299
292
|
navigationPlugin = resolvePlugin(plugins, parseNavigationPlugin);
|
|
300
293
|
attentionPlugin = resolvePlugin(plugins, parseAttentionPlugin);
|
|
@@ -375,12 +368,19 @@ export const SpacePlugin = ({
|
|
|
375
368
|
{...rest}
|
|
376
369
|
/>
|
|
377
370
|
) : primary instanceof CollectionType ? (
|
|
378
|
-
{
|
|
371
|
+
{
|
|
372
|
+
node: <CollectionMain collection={primary} />,
|
|
373
|
+
disposition: 'fallback',
|
|
374
|
+
}
|
|
379
375
|
) : typeof primary === 'string' && primary.length === OBJECT_ID_LENGTH ? (
|
|
380
376
|
<MissingObject id={primary} />
|
|
381
377
|
) : null;
|
|
382
378
|
case 'complementary--settings':
|
|
383
|
-
return
|
|
379
|
+
return isSpace(data.subject) ? (
|
|
380
|
+
<SpaceSettingsPanel space={data.subject} />
|
|
381
|
+
) : isEchoObject(data.subject) ? (
|
|
382
|
+
{ node: <DefaultObjectSettings object={data.subject} />, disposition: 'fallback' }
|
|
383
|
+
) : null;
|
|
384
384
|
case 'dialog':
|
|
385
385
|
if (data.component === 'dxos.org/plugin/space/InvitationManagerDialog') {
|
|
386
386
|
return (
|
|
@@ -390,10 +390,9 @@ export const SpacePlugin = ({
|
|
|
390
390
|
</ClipboardProvider>
|
|
391
391
|
</Dialog.Content>
|
|
392
392
|
);
|
|
393
|
-
} else {
|
|
394
|
-
return null;
|
|
395
393
|
}
|
|
396
|
-
|
|
394
|
+
return null;
|
|
395
|
+
case 'popover': {
|
|
397
396
|
if (data.component === 'dxos.org/plugin/space/RenameSpacePopover' && isSpace(data.subject)) {
|
|
398
397
|
return <PopoverRenameSpace space={data.subject} />;
|
|
399
398
|
}
|
|
@@ -401,12 +400,16 @@ export const SpacePlugin = ({
|
|
|
401
400
|
return <PopoverRenameObject object={data.subject} />;
|
|
402
401
|
}
|
|
403
402
|
return null;
|
|
403
|
+
}
|
|
404
404
|
// TODO(burdon): Add role name syntax to minimal plugin docs.
|
|
405
405
|
case 'presence--glyph': {
|
|
406
406
|
return isReactiveObject(data.object) ? (
|
|
407
|
-
<SmallPresenceLive
|
|
407
|
+
<SmallPresenceLive
|
|
408
|
+
id={data.id as string}
|
|
409
|
+
viewers={state.values.viewersByObject[fullyQualifiedId(data.object)]}
|
|
410
|
+
/>
|
|
408
411
|
) : (
|
|
409
|
-
<SmallPresence count={0} />
|
|
412
|
+
<SmallPresence id={data.id as string} count={0} />
|
|
410
413
|
);
|
|
411
414
|
}
|
|
412
415
|
case 'navbar-start': {
|
|
@@ -423,6 +426,7 @@ export const SpacePlugin = ({
|
|
|
423
426
|
? (space?.properties[CollectionType.typename] as CollectionType)
|
|
424
427
|
: undefined
|
|
425
428
|
: data.object;
|
|
429
|
+
|
|
426
430
|
return space && object
|
|
427
431
|
? {
|
|
428
432
|
node: (
|
|
@@ -440,10 +444,10 @@ export const SpacePlugin = ({
|
|
|
440
444
|
case 'settings':
|
|
441
445
|
return data.plugin === meta.id ? <SpaceSettings settings={settings.values} /> : null;
|
|
442
446
|
case 'menu-footer':
|
|
443
|
-
if (
|
|
444
|
-
return null;
|
|
445
|
-
} else {
|
|
447
|
+
if (isEchoObject(data.object)) {
|
|
446
448
|
return <MenuFooter object={data.object} />;
|
|
449
|
+
} else {
|
|
450
|
+
return null;
|
|
447
451
|
}
|
|
448
452
|
case 'status': {
|
|
449
453
|
return (
|
|
@@ -468,8 +472,7 @@ export const SpacePlugin = ({
|
|
|
468
472
|
const dispatch = intentPlugin?.provides.intent.dispatch;
|
|
469
473
|
const resolve = metadataPlugin?.provides.metadata.resolver;
|
|
470
474
|
const graph = graphPlugin?.provides.graph;
|
|
471
|
-
|
|
472
|
-
if (!graph || !dispatch || !resolve || !client) {
|
|
475
|
+
if (!client || !dispatch || !resolve || !graph) {
|
|
473
476
|
return [];
|
|
474
477
|
}
|
|
475
478
|
|
|
@@ -503,7 +506,6 @@ export const SpacePlugin = ({
|
|
|
503
506
|
type: SPACES,
|
|
504
507
|
properties: {
|
|
505
508
|
label: ['spaces label', { ns: SPACE_PLUGIN }],
|
|
506
|
-
palette: 'teal',
|
|
507
509
|
testId: 'spacePlugin.spaces',
|
|
508
510
|
role: 'branch',
|
|
509
511
|
childrenPersistenceClass: 'echo',
|
|
@@ -553,8 +555,9 @@ export const SpacePlugin = ({
|
|
|
553
555
|
properties: {
|
|
554
556
|
label: ['create space label', { ns: SPACE_PLUGIN }],
|
|
555
557
|
icon: 'ph--plus--regular',
|
|
556
|
-
disposition: '
|
|
558
|
+
disposition: 'item',
|
|
557
559
|
testId: 'spacePlugin.createSpace',
|
|
560
|
+
className: 'pbs-4',
|
|
558
561
|
},
|
|
559
562
|
},
|
|
560
563
|
{
|
|
@@ -573,7 +576,9 @@ export const SpacePlugin = ({
|
|
|
573
576
|
properties: {
|
|
574
577
|
label: ['join space label', { ns: SPACE_PLUGIN }],
|
|
575
578
|
icon: 'ph--sign-in--regular',
|
|
579
|
+
disposition: 'item',
|
|
576
580
|
testId: 'spacePlugin.joinSpace',
|
|
581
|
+
className: 'pbe-4',
|
|
577
582
|
},
|
|
578
583
|
},
|
|
579
584
|
],
|
|
@@ -727,9 +732,33 @@ export const SpacePlugin = ({
|
|
|
727
732
|
return;
|
|
728
733
|
}
|
|
729
734
|
|
|
735
|
+
const type = 'orphan-settings-for-subject';
|
|
736
|
+
const icon = 'ph--gear--regular';
|
|
737
|
+
|
|
730
738
|
const [subjectId] = id.split('~');
|
|
731
|
-
const
|
|
739
|
+
const { spaceId, objectId } = parseId(subjectId);
|
|
732
740
|
const space = client.spaces.get().find((space) => space.id === spaceId);
|
|
741
|
+
if (!objectId) {
|
|
742
|
+
const label = space
|
|
743
|
+
? space.properties.name || ['unnamed space label', { ns: SPACE_PLUGIN }]
|
|
744
|
+
: ['unnamed object settings label', { ns: SPACE_PLUGIN }];
|
|
745
|
+
|
|
746
|
+
// TODO(wittjosiah): Support comments for arbitrary subjects.
|
|
747
|
+
// This is to ensure that the comments panel is not stuck on an old object.
|
|
748
|
+
return {
|
|
749
|
+
id,
|
|
750
|
+
type,
|
|
751
|
+
data: null,
|
|
752
|
+
properties: {
|
|
753
|
+
icon,
|
|
754
|
+
label,
|
|
755
|
+
showResolvedThreads: false,
|
|
756
|
+
object: null,
|
|
757
|
+
space,
|
|
758
|
+
},
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
|
|
733
762
|
const object = toSignal(
|
|
734
763
|
(onChange) => {
|
|
735
764
|
const timeout = setTimeout(async () => {
|
|
@@ -753,10 +782,10 @@ export const SpacePlugin = ({
|
|
|
753
782
|
|
|
754
783
|
return {
|
|
755
784
|
id,
|
|
756
|
-
type
|
|
785
|
+
type,
|
|
757
786
|
data: null,
|
|
758
787
|
properties: {
|
|
759
|
-
icon
|
|
788
|
+
icon,
|
|
760
789
|
label,
|
|
761
790
|
object,
|
|
762
791
|
},
|
|
@@ -1286,11 +1315,12 @@ export const SpacePlugin = ({
|
|
|
1286
1315
|
case SpaceAction.DUPLICATE_OBJECT: {
|
|
1287
1316
|
const originalObject = intent.data?.object ?? intent.data?.result;
|
|
1288
1317
|
const resolve = resolvePlugin(plugins, parseMetadataResolverPlugin)?.provides.metadata.resolver;
|
|
1289
|
-
|
|
1318
|
+
const space = isSpace(intent.data?.target) ? intent.data?.target : getSpace(intent.data?.target);
|
|
1319
|
+
if (!isEchoObject(originalObject) || !resolve || !space) {
|
|
1290
1320
|
return;
|
|
1291
1321
|
}
|
|
1292
1322
|
|
|
1293
|
-
const newObject = await cloneObject(originalObject, resolve);
|
|
1323
|
+
const newObject = await cloneObject(originalObject, resolve, space);
|
|
1294
1324
|
return {
|
|
1295
1325
|
intents: [
|
|
1296
1326
|
[{ action: SpaceAction.ADD_OBJECT, data: { object: newObject, target: intent.data?.target } }],
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
|
-
import { type EchoReactiveObject } from '@dxos/client/echo';
|
|
7
|
+
import { type EchoReactiveObject } from '@dxos/react-client/echo';
|
|
8
8
|
import { Input, useTranslation } from '@dxos/react-ui';
|
|
9
9
|
|
|
10
10
|
import { SPACE_PLUGIN } from '../meta';
|
|
@@ -15,13 +15,14 @@ export type DefaultObjectSettingsProps = {
|
|
|
15
15
|
|
|
16
16
|
export const DefaultObjectSettings = ({ object }: DefaultObjectSettingsProps) => {
|
|
17
17
|
const { t } = useTranslation(SPACE_PLUGIN);
|
|
18
|
+
// TODO(burdon): Standardize forms.
|
|
18
19
|
return (
|
|
19
20
|
<div role='form' className='flex flex-col w-full p-2 gap-1'>
|
|
20
21
|
<Input.Root>
|
|
21
22
|
<Input.Label>{t('name label')}</Input.Label>
|
|
22
23
|
<Input.TextInput
|
|
23
24
|
placeholder={t('name placeholder')}
|
|
24
|
-
value={object.name}
|
|
25
|
+
value={object.name ?? ''}
|
|
25
26
|
onChange={(event) => {
|
|
26
27
|
object.name = event.target.value;
|
|
27
28
|
}}
|
|
@@ -17,13 +17,12 @@ import {
|
|
|
17
17
|
type Size,
|
|
18
18
|
type ThemedClassName,
|
|
19
19
|
Tooltip,
|
|
20
|
-
useDensityContext,
|
|
21
20
|
useTranslation,
|
|
22
21
|
List,
|
|
23
22
|
ListItem,
|
|
24
23
|
useDefaultValue,
|
|
25
24
|
} from '@dxos/react-ui';
|
|
26
|
-
import { AttentionGlyph } from '@dxos/react-ui-attention';
|
|
25
|
+
import { AttentionGlyph, useAttention } from '@dxos/react-ui-attention';
|
|
27
26
|
import { ComplexMap, keyToFallback } from '@dxos/util';
|
|
28
27
|
|
|
29
28
|
import { SPACE_PLUGIN } from '../meta';
|
|
@@ -40,7 +39,6 @@ const noViewers = new ComplexMap<PublicKey, ObjectViewerProps>(PublicKey.hash);
|
|
|
40
39
|
const getName = (identity: Identity) => identity.profile?.displayName ?? generateName(identity.identityKey.toHex());
|
|
41
40
|
|
|
42
41
|
export const SpacePresence = ({ object, spaceKey }: { object: Expando; spaceKey?: PublicKey }) => {
|
|
43
|
-
const density = useDensityContext();
|
|
44
42
|
const spacePlugin = usePlugin<SpacePluginProvides>(SPACE_PLUGIN);
|
|
45
43
|
const client = useClient();
|
|
46
44
|
const identity = useIdentity();
|
|
@@ -86,11 +84,7 @@ export const SpacePresence = ({ object, spaceKey }: { object: Expando; spaceKey?
|
|
|
86
84
|
})
|
|
87
85
|
.toSorted((a, b) => a.lastSeen - b.lastSeen);
|
|
88
86
|
|
|
89
|
-
return
|
|
90
|
-
<SmallPresence count={membersForObject.length} />
|
|
91
|
-
) : (
|
|
92
|
-
<FullPresence members={membersForObject} />
|
|
93
|
-
);
|
|
87
|
+
return <FullPresence members={membersForObject} />;
|
|
94
88
|
};
|
|
95
89
|
|
|
96
90
|
export type Member = SpaceMember & {
|
|
@@ -200,28 +194,45 @@ const PrensenceAvatar = ({ identity, showName, match, group, index, onClick }: P
|
|
|
200
194
|
);
|
|
201
195
|
};
|
|
202
196
|
|
|
203
|
-
export const SmallPresenceLive = ({
|
|
204
|
-
|
|
197
|
+
export const SmallPresenceLive = ({
|
|
198
|
+
id,
|
|
199
|
+
viewers,
|
|
200
|
+
}: {
|
|
201
|
+
id?: string;
|
|
202
|
+
viewers?: ComplexMap<PublicKey, ObjectViewerProps>;
|
|
203
|
+
}) => {
|
|
204
|
+
const getActiveViewers = (viewers: ComplexMap<PublicKey, ObjectViewerProps>): ObjectViewerProps[] => {
|
|
205
|
+
const moment = Date.now();
|
|
206
|
+
return Array.from(viewers.values()).filter(({ lastSeen }) => moment - lastSeen < ACTIVITY_DURATION);
|
|
207
|
+
};
|
|
205
208
|
|
|
206
|
-
|
|
207
|
-
useEffect(() => {
|
|
208
|
-
const interval = setInterval(() => setMoment(Date.now()), REFRESH_INTERVAL);
|
|
209
|
-
return () => clearInterval(interval);
|
|
210
|
-
}, []);
|
|
211
|
-
|
|
212
|
-
const activeViewers = viewers
|
|
213
|
-
? Array.from(viewers.values()).filter(({ lastSeen }) => moment - lastSeen < ACTIVITY_DURATION)
|
|
214
|
-
: [];
|
|
209
|
+
const [activeViewers, setActiveViewers] = useState(viewers ? getActiveViewers(viewers) : []);
|
|
215
210
|
|
|
216
|
-
|
|
211
|
+
useEffect(() => {
|
|
212
|
+
if (viewers) {
|
|
213
|
+
setActiveViewers(getActiveViewers(viewers));
|
|
214
|
+
const interval = setInterval(() => {
|
|
215
|
+
setActiveViewers(getActiveViewers(viewers));
|
|
216
|
+
}, REFRESH_INTERVAL);
|
|
217
|
+
return () => clearInterval(interval);
|
|
218
|
+
}
|
|
219
|
+
}, [viewers]);
|
|
220
|
+
|
|
221
|
+
return <SmallPresence id={id} count={activeViewers.length} />;
|
|
217
222
|
};
|
|
218
223
|
|
|
219
|
-
export const SmallPresence = ({ count }: { count: number }) => {
|
|
224
|
+
export const SmallPresence = ({ id, count }: { id?: string; count: number }) => {
|
|
220
225
|
const { t } = useTranslation(SPACE_PLUGIN);
|
|
226
|
+
const { hasAttention, isAncestor, isRelated } = useAttention(id);
|
|
227
|
+
const attention = hasAttention || isAncestor || isRelated;
|
|
228
|
+
|
|
221
229
|
return (
|
|
222
230
|
<Tooltip.Root>
|
|
223
231
|
<Tooltip.Trigger asChild>
|
|
224
|
-
|
|
232
|
+
{/* TODO(wittjosiah): Don't depend on data attribute just pass prop to AttentionGlyph. */}
|
|
233
|
+
<div role='none' className='flex' data-attention={attention}>
|
|
234
|
+
<AttentionGlyph presence={count > 1 ? 'many' : count === 1 ? 'one' : 'none'} classNames='self-center mie-1' />
|
|
235
|
+
</div>
|
|
225
236
|
</Tooltip.Trigger>
|
|
226
237
|
<Tooltip.Portal>
|
|
227
238
|
<Tooltip.Content side='bottom' classNames='z-[70]'>
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
7
|
import { useIntentDispatcher, useResolvePlugins } from '@dxos/app-framework';
|
|
8
|
-
import { SettingsValue } from '@dxos/plugin-settings';
|
|
9
8
|
import { Input, Select, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
9
|
+
import { FormInput } from '@dxos/react-ui-data';
|
|
10
10
|
|
|
11
11
|
import { SpaceAction, SPACE_PLUGIN } from '../meta';
|
|
12
12
|
import { parseSpaceInitPlugin, type SpaceSettingsProps } from '../types';
|
|
@@ -18,7 +18,7 @@ export const SpaceSettings = ({ settings }: { settings: SpaceSettingsProps }) =>
|
|
|
18
18
|
|
|
19
19
|
return (
|
|
20
20
|
<>
|
|
21
|
-
<
|
|
21
|
+
<FormInput label={t('show hidden spaces label')}>
|
|
22
22
|
<Input.Switch
|
|
23
23
|
checked={settings.showHidden}
|
|
24
24
|
onCheckedChange={(checked) =>
|
|
@@ -29,9 +29,9 @@ export const SpaceSettings = ({ settings }: { settings: SpaceSettingsProps }) =>
|
|
|
29
29
|
})
|
|
30
30
|
}
|
|
31
31
|
/>
|
|
32
|
-
</
|
|
32
|
+
</FormInput>
|
|
33
33
|
|
|
34
|
-
<
|
|
34
|
+
<FormInput label={t('default on space create label')}>
|
|
35
35
|
<Select.Root
|
|
36
36
|
value={settings.onSpaceCreate}
|
|
37
37
|
onValueChange={(value) => {
|
|
@@ -57,7 +57,7 @@ export const SpaceSettings = ({ settings }: { settings: SpaceSettingsProps }) =>
|
|
|
57
57
|
</Select.Content>
|
|
58
58
|
</Select.Portal>
|
|
59
59
|
</Select.Root>
|
|
60
|
-
</
|
|
60
|
+
</FormInput>
|
|
61
61
|
</>
|
|
62
62
|
);
|
|
63
63
|
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useCallback, useState } from 'react';
|
|
6
|
+
|
|
7
|
+
import { log } from '@dxos/log';
|
|
8
|
+
import { EdgeReplicationSetting } from '@dxos/protocols/proto/dxos/echo/metadata';
|
|
9
|
+
import { type Space } from '@dxos/react-client/echo';
|
|
10
|
+
import { Input, useTranslation } from '@dxos/react-ui';
|
|
11
|
+
|
|
12
|
+
import { SPACE_PLUGIN } from '../meta';
|
|
13
|
+
|
|
14
|
+
export type SpaceSettingsPanelProps = {
|
|
15
|
+
space: Space;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const SpaceSettingsPanel = ({ space }: SpaceSettingsPanelProps) => {
|
|
19
|
+
const { t } = useTranslation(SPACE_PLUGIN);
|
|
20
|
+
const [edgeReplication, setEdgeReplication] = useState(
|
|
21
|
+
space.internal.data.edgeReplication === EdgeReplicationSetting.ENABLED,
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const toggleEdgeReplication = useCallback(
|
|
25
|
+
async (next: boolean) => {
|
|
26
|
+
setEdgeReplication(next);
|
|
27
|
+
await space?.internal
|
|
28
|
+
.setEdgeReplicationPreference(next ? EdgeReplicationSetting.ENABLED : EdgeReplicationSetting.DISABLED)
|
|
29
|
+
.catch((err) => {
|
|
30
|
+
log.catch(err);
|
|
31
|
+
setEdgeReplication(!next);
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
[space],
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div role='form' className='flex flex-col w-full p-2 gap-4'>
|
|
39
|
+
<Input.Root>
|
|
40
|
+
<div role='none' className='flex flex-col gap-1'>
|
|
41
|
+
<Input.Label>{t('name label')}</Input.Label>
|
|
42
|
+
<Input.TextInput
|
|
43
|
+
placeholder={t('name placeholder')}
|
|
44
|
+
value={space.properties.name}
|
|
45
|
+
onChange={(event) => {
|
|
46
|
+
space.properties.name = event.target.value;
|
|
47
|
+
}}
|
|
48
|
+
/>
|
|
49
|
+
</div>
|
|
50
|
+
</Input.Root>
|
|
51
|
+
<Input.Root>
|
|
52
|
+
<div role='none' className='flex justify-between'>
|
|
53
|
+
<Input.Label>{t('edge replication label')}</Input.Label>
|
|
54
|
+
<Input.Switch checked={edgeReplication} onCheckedChange={toggleEdgeReplication} />
|
|
55
|
+
</div>
|
|
56
|
+
</Input.Root>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
@@ -63,7 +63,7 @@ export const SyncStatusIndicator = ({ state }: { state: SpaceSyncStateMap }) =>
|
|
|
63
63
|
classNames={classNames}
|
|
64
64
|
/>
|
|
65
65
|
</Popover.Trigger>
|
|
66
|
-
<Popover.Content>
|
|
66
|
+
<Popover.Content sideOffset={16}>
|
|
67
67
|
<SyncStatusDetail state={state} summary={summary} debug={false} />
|
|
68
68
|
</Popover.Content>
|
|
69
69
|
</Popover.Root>
|
package/src/components/index.ts
CHANGED
package/src/translations.ts
CHANGED
|
@@ -10,8 +10,8 @@ export default [
|
|
|
10
10
|
[SPACE_PLUGIN]: {
|
|
11
11
|
'plugin name': 'Spaces',
|
|
12
12
|
'first run message': 'Nothing selected.',
|
|
13
|
-
'create space label': 'Create
|
|
14
|
-
'join space label': 'Join
|
|
13
|
+
'create space label': 'Create space',
|
|
14
|
+
'join space label': 'Join space',
|
|
15
15
|
'empty space message': 'No documents',
|
|
16
16
|
'empty tree message': 'No spaces',
|
|
17
17
|
'unnamed space label': 'New space',
|
|
@@ -92,6 +92,8 @@ export default [
|
|
|
92
92
|
'join success label': 'Successfully joined space',
|
|
93
93
|
'name label': 'Name',
|
|
94
94
|
'name placeholder': 'Name',
|
|
95
|
+
'unnamed object settings label': 'Settings',
|
|
96
|
+
'edge replication label': 'Enable EDGE Replication',
|
|
95
97
|
},
|
|
96
98
|
},
|
|
97
99
|
},
|
package/src/types/types.ts
CHANGED
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
import { type Expando } from '@dxos/echo-schema';
|
|
16
16
|
import { type SchemaProvides } from '@dxos/plugin-client';
|
|
17
17
|
import { type PublicKey } from '@dxos/react-client';
|
|
18
|
+
import { type Space } from '@dxos/react-client/echo';
|
|
18
19
|
import { type Label } from '@dxos/react-ui';
|
|
19
20
|
import { type ComplexMap } from '@dxos/util';
|
|
20
21
|
|
|
@@ -99,7 +100,8 @@ export interface TypedObjectSerializer<T extends Expando = Expando> {
|
|
|
99
100
|
|
|
100
101
|
/**
|
|
101
102
|
* @param params.content
|
|
103
|
+
* @param params.space Space to use for deserializing schema references.
|
|
102
104
|
* @param params.newId Generate new ID for deserialized object.
|
|
103
105
|
*/
|
|
104
|
-
deserialize(params: { content: string; newId?: boolean }): Promise<T>;
|
|
106
|
+
deserialize(params: { content: string; space: Space; newId?: boolean }): Promise<T>;
|
|
105
107
|
}
|
package/src/util.tsx
CHANGED
|
@@ -154,7 +154,7 @@ const getCollectionGraphNodePartials = ({
|
|
|
154
154
|
},
|
|
155
155
|
onCopy: async (child: Node<EchoReactiveObject<any>>, index?: number) => {
|
|
156
156
|
// Create clone of child and add to destination space.
|
|
157
|
-
const newObject = await cloneObject(child.data, resolve);
|
|
157
|
+
const newObject = await cloneObject(child.data, resolve, space);
|
|
158
158
|
space.db.add(newObject);
|
|
159
159
|
if (typeof index !== 'undefined') {
|
|
160
160
|
collection.objects.splice(index, 0, newObject);
|
|
@@ -596,12 +596,12 @@ export const getNestedObjects = async (
|
|
|
596
596
|
* @deprecated Workaround for ECHO not supporting clone.
|
|
597
597
|
*/
|
|
598
598
|
// TODO(burdon): Remove.
|
|
599
|
-
export const cloneObject = async (object: Expando, resolve: MetadataResolver): Promise<Expando> => {
|
|
599
|
+
export const cloneObject = async (object: Expando, resolve: MetadataResolver, newSpace: Space): Promise<Expando> => {
|
|
600
600
|
const schema = getSchema(object);
|
|
601
601
|
const typename = schema ? getObjectAnnotation(schema)?.typename ?? EXPANDO_TYPENAME : EXPANDO_TYPENAME;
|
|
602
602
|
const metadata = resolve(typename);
|
|
603
603
|
const serializer = metadata.serializer;
|
|
604
604
|
invariant(serializer, `No serializer for type: ${typename}`);
|
|
605
605
|
const content = await serializer.serialize({ object });
|
|
606
|
-
return serializer.deserialize({ content, newId: true });
|
|
606
|
+
return serializer.deserialize({ content, space: newSpace, newId: true });
|
|
607
607
|
};
|