@dxos/plugin-space 0.6.8-main.046e6cf
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/LICENSE +8 -0
- package/README.md +15 -0
- package/dist/lib/browser/chunk-DTVUOG2C.mjs +95 -0
- package/dist/lib/browser/chunk-DTVUOG2C.mjs.map +7 -0
- package/dist/lib/browser/chunk-LZEGRS7H.mjs +35 -0
- package/dist/lib/browser/chunk-LZEGRS7H.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +2660 -0
- package/dist/lib/browser/index.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -0
- package/dist/lib/browser/meta.mjs +13 -0
- package/dist/lib/browser/meta.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +21 -0
- package/dist/lib/browser/types/index.mjs.map +7 -0
- package/dist/lib/node/chunk-6CNYF6YU.cjs +60 -0
- package/dist/lib/node/chunk-6CNYF6YU.cjs.map +7 -0
- package/dist/lib/node/chunk-CVZPI2P3.cjs +120 -0
- package/dist/lib/node/chunk-CVZPI2P3.cjs.map +7 -0
- package/dist/lib/node/index.cjs +2682 -0
- package/dist/lib/node/index.cjs.map +7 -0
- package/dist/lib/node/meta.cjs +34 -0
- package/dist/lib/node/meta.cjs.map +7 -0
- package/dist/lib/node/meta.json +1 -0
- package/dist/lib/node/types/index.cjs +43 -0
- package/dist/lib/node/types/index.cjs.map +7 -0
- package/dist/types/src/SpacePlugin.d.ts +27 -0
- package/dist/types/src/SpacePlugin.d.ts.map +1 -0
- package/dist/types/src/components/AwaitingObject.d.ts +5 -0
- package/dist/types/src/components/AwaitingObject.d.ts.map +1 -0
- package/dist/types/src/components/CollectionMain.d.ts +6 -0
- package/dist/types/src/components/CollectionMain.d.ts.map +1 -0
- package/dist/types/src/components/CollectionSection.d.ts +6 -0
- package/dist/types/src/components/CollectionSection.d.ts.map +1 -0
- package/dist/types/src/components/EmptySpace.d.ts +3 -0
- package/dist/types/src/components/EmptySpace.d.ts.map +1 -0
- package/dist/types/src/components/EmptyTree.d.ts +3 -0
- package/dist/types/src/components/EmptyTree.d.ts.map +1 -0
- package/dist/types/src/components/MenuFooter.d.ts +6 -0
- package/dist/types/src/components/MenuFooter.d.ts.map +1 -0
- package/dist/types/src/components/MissingObject.d.ts +5 -0
- package/dist/types/src/components/MissingObject.d.ts.map +1 -0
- package/dist/types/src/components/PersistenceStatus.d.ts +6 -0
- package/dist/types/src/components/PersistenceStatus.d.ts.map +1 -0
- package/dist/types/src/components/PopoverRenameObject.d.ts +6 -0
- package/dist/types/src/components/PopoverRenameObject.d.ts.map +1 -0
- package/dist/types/src/components/PopoverRenameSpace.d.ts +6 -0
- package/dist/types/src/components/PopoverRenameSpace.d.ts.map +1 -0
- package/dist/types/src/components/ShareSpaceButton.d.ts +8 -0
- package/dist/types/src/components/ShareSpaceButton.d.ts.map +1 -0
- package/dist/types/src/components/ShareSpaceButton.stories.d.ts +98 -0
- package/dist/types/src/components/ShareSpaceButton.stories.d.ts.map +1 -0
- package/dist/types/src/components/SpaceMain/SpaceMain.d.ts +10 -0
- package/dist/types/src/components/SpaceMain/SpaceMain.d.ts.map +1 -0
- package/dist/types/src/components/SpaceMain/SpaceMembersSection.d.ts +6 -0
- package/dist/types/src/components/SpaceMain/SpaceMembersSection.d.ts.map +1 -0
- package/dist/types/src/components/SpaceMain/index.d.ts +2 -0
- package/dist/types/src/components/SpaceMain/index.d.ts.map +1 -0
- package/dist/types/src/components/SpacePresence.d.ts +34 -0
- package/dist/types/src/components/SpacePresence.d.ts.map +1 -0
- package/dist/types/src/components/SpacePresence.stories.d.ts +97 -0
- package/dist/types/src/components/SpacePresence.stories.d.ts.map +1 -0
- package/dist/types/src/components/SpaceSettings.d.ts +6 -0
- package/dist/types/src/components/SpaceSettings.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +15 -0
- package/dist/types/src/components/index.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +9 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/meta.d.ts +26 -0
- package/dist/types/src/meta.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +83 -0
- package/dist/types/src/translations.d.ts.map +1 -0
- package/dist/types/src/types/collection.d.ts +18 -0
- package/dist/types/src/types/collection.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +4 -0
- package/dist/types/src/types/index.d.ts.map +1 -0
- package/dist/types/src/types/thread.d.ts +225 -0
- package/dist/types/src/types/thread.d.ts.map +1 -0
- package/dist/types/src/types/types.d.ts +54 -0
- package/dist/types/src/types/types.d.ts.map +1 -0
- package/dist/types/src/util.d.ts +85 -0
- package/dist/types/src/util.d.ts.map +1 -0
- package/package.json +101 -0
- package/src/SpacePlugin.tsx +1234 -0
- package/src/components/AwaitingObject.tsx +118 -0
- package/src/components/CollectionMain.tsx +33 -0
- package/src/components/CollectionSection.tsx +20 -0
- package/src/components/EmptySpace.tsx +25 -0
- package/src/components/EmptyTree.tsx +25 -0
- package/src/components/MenuFooter.tsx +33 -0
- package/src/components/MissingObject.tsx +54 -0
- package/src/components/PersistenceStatus.tsx +87 -0
- package/src/components/PopoverRenameObject.tsx +54 -0
- package/src/components/PopoverRenameSpace.tsx +44 -0
- package/src/components/ShareSpaceButton.stories.tsx +23 -0
- package/src/components/ShareSpaceButton.tsx +27 -0
- package/src/components/SpaceMain/SpaceMain.tsx +81 -0
- package/src/components/SpaceMain/SpaceMembersSection.tsx +205 -0
- package/src/components/SpaceMain/index.ts +5 -0
- package/src/components/SpacePresence.stories.tsx +102 -0
- package/src/components/SpacePresence.tsx +244 -0
- package/src/components/SpaceSettings.tsx +34 -0
- package/src/components/index.ts +18 -0
- package/src/index.ts +15 -0
- package/src/meta.ts +31 -0
- package/src/translations.ts +92 -0
- package/src/types/collection.ts +16 -0
- package/src/types/index.ts +7 -0
- package/src/types/thread.ts +68 -0
- package/src/types/types.ts +81 -0
- package/src/util.tsx +642 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { CheckCircle, CircleDashed, CircleNotch } from '@phosphor-icons/react';
|
|
6
|
+
import React, { useEffect, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
import { parseIntentPlugin, useResolvePlugin, parseNavigationPlugin, NavigationAction } from '@dxos/app-framework';
|
|
9
|
+
import { useClient } from '@dxos/react-client';
|
|
10
|
+
import { fullyQualifiedId, useQuery } from '@dxos/react-client/echo';
|
|
11
|
+
import { Button, Toast, useTranslation } from '@dxos/react-ui';
|
|
12
|
+
import { getSize, mx } from '@dxos/react-ui-theme';
|
|
13
|
+
|
|
14
|
+
import { SpaceAction, SPACE_PLUGIN } from '../meta';
|
|
15
|
+
|
|
16
|
+
const WAIT_FOR_OBJECT_TIMEOUT = 180e3; // 3 minutes
|
|
17
|
+
const TOAST_TIMEOUT = 240e3; // 4 minutes
|
|
18
|
+
|
|
19
|
+
export const AwaitingObject = ({ id }: { id: string }) => {
|
|
20
|
+
const [open, setOpen] = useState(true);
|
|
21
|
+
const [waiting, setWaiting] = useState(true);
|
|
22
|
+
const [found, setFound] = useState(false);
|
|
23
|
+
const { t } = useTranslation(SPACE_PLUGIN);
|
|
24
|
+
const intentPlugin = useResolvePlugin(parseIntentPlugin);
|
|
25
|
+
const navigationPlugin = useResolvePlugin(parseNavigationPlugin);
|
|
26
|
+
|
|
27
|
+
const client = useClient();
|
|
28
|
+
const objects = useQuery(client.spaces);
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (!id) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const timeout = setTimeout(() => {
|
|
36
|
+
setWaiting(false);
|
|
37
|
+
}, WAIT_FOR_OBJECT_TIMEOUT);
|
|
38
|
+
|
|
39
|
+
() => clearTimeout(timeout);
|
|
40
|
+
}, [id]);
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (objects.findIndex((object) => fullyQualifiedId(object) === id) > -1) {
|
|
44
|
+
setFound(true);
|
|
45
|
+
|
|
46
|
+
if (navigationPlugin?.provides.location.active === id) {
|
|
47
|
+
setOpen(false);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}, [id, objects, intentPlugin]);
|
|
51
|
+
|
|
52
|
+
const handleClose = async () =>
|
|
53
|
+
intentPlugin?.provides.intent.dispatch({
|
|
54
|
+
plugin: SPACE_PLUGIN,
|
|
55
|
+
action: SpaceAction.WAIT_FOR_OBJECT,
|
|
56
|
+
data: { id: undefined },
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const handleNavigate = () => {
|
|
60
|
+
void intentPlugin?.provides.intent.dispatch({
|
|
61
|
+
action: NavigationAction.OPEN,
|
|
62
|
+
data: { activeParts: { main: [id] } },
|
|
63
|
+
});
|
|
64
|
+
void handleClose();
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<Toast.Root open={open} duration={TOAST_TIMEOUT} onOpenChange={setOpen}>
|
|
69
|
+
<Toast.Body>
|
|
70
|
+
<Toast.Title classNames='flex items-center gap-2'>
|
|
71
|
+
{found ? (
|
|
72
|
+
<>
|
|
73
|
+
<CheckCircle className={getSize(5)} />
|
|
74
|
+
<span>{t('found object label')}</span>
|
|
75
|
+
</>
|
|
76
|
+
) : waiting ? (
|
|
77
|
+
<>
|
|
78
|
+
<CircleNotch className={mx(getSize(5), 'animate-spin')} />
|
|
79
|
+
<span>{t('waiting for object label')}</span>
|
|
80
|
+
</>
|
|
81
|
+
) : (
|
|
82
|
+
<>
|
|
83
|
+
<CircleDashed className={getSize(5)} />
|
|
84
|
+
<span>{t('object not found label')}</span>
|
|
85
|
+
</>
|
|
86
|
+
)}
|
|
87
|
+
</Toast.Title>
|
|
88
|
+
<Toast.Description>
|
|
89
|
+
{t(
|
|
90
|
+
found
|
|
91
|
+
? 'found object description'
|
|
92
|
+
: waiting
|
|
93
|
+
? 'waiting for object description'
|
|
94
|
+
: 'object not found description',
|
|
95
|
+
)}
|
|
96
|
+
</Toast.Description>
|
|
97
|
+
</Toast.Body>
|
|
98
|
+
<Toast.Actions>
|
|
99
|
+
{found ? (
|
|
100
|
+
<>
|
|
101
|
+
<Toast.Action altText={t('go to object alt')} asChild>
|
|
102
|
+
<Button variant='primary' onClick={handleNavigate}>
|
|
103
|
+
{t('go to object label')}
|
|
104
|
+
</Button>
|
|
105
|
+
</Toast.Action>
|
|
106
|
+
<Toast.Close asChild>
|
|
107
|
+
<Button onClick={handleClose}>{t('close label', { ns: 'appkit' })}</Button>
|
|
108
|
+
</Toast.Close>
|
|
109
|
+
</>
|
|
110
|
+
) : (
|
|
111
|
+
<Toast.Close asChild>
|
|
112
|
+
<Button onClick={handleClose}>{t(waiting ? 'close label' : 'confirm label', { ns: 'appkit' })}</Button>
|
|
113
|
+
</Toast.Close>
|
|
114
|
+
)}
|
|
115
|
+
</Toast.Actions>
|
|
116
|
+
</Toast.Root>
|
|
117
|
+
);
|
|
118
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { useTranslation } from '@dxos/react-ui';
|
|
8
|
+
import { baseSurface, descriptionText, mx } from '@dxos/react-ui-theme';
|
|
9
|
+
|
|
10
|
+
import { SPACE_PLUGIN } from '../meta';
|
|
11
|
+
import type { CollectionType } from '../types';
|
|
12
|
+
|
|
13
|
+
export const CollectionMain = ({ collection }: { collection: CollectionType }) => {
|
|
14
|
+
const { t } = useTranslation(SPACE_PLUGIN);
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div
|
|
18
|
+
role='none'
|
|
19
|
+
className={mx(baseSurface, 'min-bs-screen is-full flex items-center justify-center p-8')}
|
|
20
|
+
data-testid='composer.firstRunMessage'
|
|
21
|
+
>
|
|
22
|
+
<p
|
|
23
|
+
role='alert'
|
|
24
|
+
className={mx(
|
|
25
|
+
descriptionText,
|
|
26
|
+
'border border-dashed border-neutral-400/50 rounded-lg p-8 font-normal text-lg max-is-[24rem] break-words',
|
|
27
|
+
)}
|
|
28
|
+
>
|
|
29
|
+
{collection.name ?? t('unnamed collection label')}
|
|
30
|
+
</p>
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { useTranslation } from '@dxos/react-ui';
|
|
8
|
+
|
|
9
|
+
import { SPACE_PLUGIN } from '../meta';
|
|
10
|
+
import type { CollectionType } from '../types';
|
|
11
|
+
|
|
12
|
+
export const CollectionSection = ({ collection }: { collection: CollectionType }) => {
|
|
13
|
+
const { t } = useTranslation(SPACE_PLUGIN);
|
|
14
|
+
// TODO(wittjosiah): Better placeholder.
|
|
15
|
+
return (
|
|
16
|
+
<div className='min-bs-[3.5rem] grid grid-rows-subgrid grid-cols-subgrid items-center'>
|
|
17
|
+
<span className='truncate'>{collection.name ?? t('unnamed collection label')}</span>
|
|
18
|
+
</div>
|
|
19
|
+
);
|
|
20
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { useTranslation } from '@dxos/react-ui';
|
|
8
|
+
import { descriptionText, mx } from '@dxos/react-ui-theme';
|
|
9
|
+
|
|
10
|
+
import { SPACE_PLUGIN } from '../meta';
|
|
11
|
+
|
|
12
|
+
export const EmptySpace = () => {
|
|
13
|
+
const { t } = useTranslation(SPACE_PLUGIN);
|
|
14
|
+
return (
|
|
15
|
+
<div
|
|
16
|
+
role='none'
|
|
17
|
+
className={mx(
|
|
18
|
+
'p-2 mli-2 mbe-2 text-center border border-dashed border-neutral-400/50 rounded-lg',
|
|
19
|
+
descriptionText,
|
|
20
|
+
)}
|
|
21
|
+
>
|
|
22
|
+
{t('empty space message')}
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { useTranslation } from '@dxos/react-ui';
|
|
8
|
+
import { descriptionText, mx } from '@dxos/react-ui-theme';
|
|
9
|
+
|
|
10
|
+
import { SPACE_PLUGIN } from '../meta';
|
|
11
|
+
|
|
12
|
+
export const EmptyTree = () => {
|
|
13
|
+
const { t } = useTranslation(SPACE_PLUGIN);
|
|
14
|
+
return (
|
|
15
|
+
<div
|
|
16
|
+
role='none'
|
|
17
|
+
className={mx(
|
|
18
|
+
'p-2 mli-2 mbe-2 text-center border border-dashed border-neutral-400/50 rounded-lg',
|
|
19
|
+
descriptionText,
|
|
20
|
+
)}
|
|
21
|
+
>
|
|
22
|
+
{t('empty tree message')}
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
import { Planet } from '@phosphor-icons/react';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { getSpace } from '@dxos/client/echo';
|
|
8
|
+
import type { EchoReactiveObject } from '@dxos/echo-schema';
|
|
9
|
+
import { useClient } from '@dxos/react-client';
|
|
10
|
+
import { DropdownMenu, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
11
|
+
|
|
12
|
+
import { SPACE_PLUGIN } from '../meta';
|
|
13
|
+
import { getSpaceDisplayName } from '../util';
|
|
14
|
+
|
|
15
|
+
export const MenuFooter = ({ object }: { object: EchoReactiveObject<any> }) => {
|
|
16
|
+
const { t } = useTranslation(SPACE_PLUGIN);
|
|
17
|
+
const client = useClient();
|
|
18
|
+
const space = getSpace(object);
|
|
19
|
+
const spaceName = space ? getSpaceDisplayName(space, { personal: client.spaces.default === space }) : '';
|
|
20
|
+
return space ? (
|
|
21
|
+
<>
|
|
22
|
+
<DropdownMenu.Separator />
|
|
23
|
+
<DropdownMenu.GroupLabel>{t('menu footer label')}</DropdownMenu.GroupLabel>
|
|
24
|
+
<dl className='pis-2 mbe-2 text-xs grid grid-cols-[max-content_1fr] gap-2'>
|
|
25
|
+
<dt className='uppercase text-[.75em] tracking-wide font-medium mbs-px self-start'>{t('location label')}</dt>
|
|
26
|
+
<dd className='line-clamp-3'>
|
|
27
|
+
<Planet className='inline-block mie-1' />
|
|
28
|
+
{toLocalizedString(spaceName, t)}
|
|
29
|
+
</dd>
|
|
30
|
+
</dl>
|
|
31
|
+
</>
|
|
32
|
+
) : null;
|
|
33
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useEffect, useState } from 'react';
|
|
6
|
+
|
|
7
|
+
import { parseIntentPlugin, useResolvePlugin } from '@dxos/app-framework';
|
|
8
|
+
import { Status, useTranslation } from '@dxos/react-ui';
|
|
9
|
+
import { baseSurface, descriptionText, mx } from '@dxos/react-ui-theme';
|
|
10
|
+
|
|
11
|
+
import { SpaceAction, SPACE_PLUGIN } from '../meta';
|
|
12
|
+
|
|
13
|
+
const WAIT_FOR_OBJECT_TIMEOUT = 1_000;
|
|
14
|
+
|
|
15
|
+
export const MissingObject = ({ id }: { id: string }) => {
|
|
16
|
+
const { t } = useTranslation(SPACE_PLUGIN);
|
|
17
|
+
const [waiting, setWaiting] = useState(false);
|
|
18
|
+
const intentPlugin = useResolvePlugin(parseIntentPlugin);
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!intentPlugin) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const timeout = setTimeout(async () => {
|
|
26
|
+
setWaiting(true);
|
|
27
|
+
await intentPlugin.provides.intent.dispatch({
|
|
28
|
+
plugin: SPACE_PLUGIN,
|
|
29
|
+
action: SpaceAction.WAIT_FOR_OBJECT,
|
|
30
|
+
data: { id },
|
|
31
|
+
});
|
|
32
|
+
}, WAIT_FOR_OBJECT_TIMEOUT);
|
|
33
|
+
|
|
34
|
+
return () => clearTimeout(timeout);
|
|
35
|
+
}, [intentPlugin, id]);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div role='none' className={mx(baseSurface, 'min-bs-screen is-full flex items-center justify-center p-8')}>
|
|
39
|
+
{waiting ? (
|
|
40
|
+
<p
|
|
41
|
+
role='alert'
|
|
42
|
+
className={mx(
|
|
43
|
+
descriptionText,
|
|
44
|
+
'border border-dashed border-neutral-400/50 rounded-lg flex items-center justify-center p-8 font-normal text-lg',
|
|
45
|
+
)}
|
|
46
|
+
>
|
|
47
|
+
{t('missing object message')}
|
|
48
|
+
</p>
|
|
49
|
+
) : (
|
|
50
|
+
<Status indeterminate aria-label='Initializing' />
|
|
51
|
+
)}
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { ArrowsCounterClockwise, CheckCircle, Warning } from '@phosphor-icons/react';
|
|
6
|
+
import React, { useEffect, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
import { debounce } from '@dxos/async';
|
|
9
|
+
import { type EchoDatabase } from '@dxos/react-client/echo';
|
|
10
|
+
import { Tooltip, useTranslation } from '@dxos/react-ui';
|
|
11
|
+
import { getSize, mx, staticPlaceholderText, warningText } from '@dxos/react-ui-theme';
|
|
12
|
+
|
|
13
|
+
import { SPACE_PLUGIN } from '../meta';
|
|
14
|
+
|
|
15
|
+
enum Status {
|
|
16
|
+
PERSISTED_LOCALLY = 0,
|
|
17
|
+
PENDING = 1,
|
|
18
|
+
ERROR = 2,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// TODO(zan): This now has no usages. Remove it?
|
|
22
|
+
export const PersistenceStatus = ({ db }: { db: EchoDatabase }) => {
|
|
23
|
+
const { t } = useTranslation(SPACE_PLUGIN);
|
|
24
|
+
const [displayMessage, setDisplayMessage] = useState(false);
|
|
25
|
+
const [status, naturalSetStatus] = useState<Status>(Status.PERSISTED_LOCALLY);
|
|
26
|
+
const [prevStatus, setPrevStatus] = useState<Status>(Status.PERSISTED_LOCALLY);
|
|
27
|
+
const _setStatus = debounce(naturalSetStatus, 500);
|
|
28
|
+
|
|
29
|
+
// TODO(dmaretskyi): Fix this when we have save status for automerge.
|
|
30
|
+
// useEffect(() => {
|
|
31
|
+
// return db.pendingBatch.on(({ duration, error }) => {
|
|
32
|
+
// if (error) {
|
|
33
|
+
// setStatus(Status.ERROR);
|
|
34
|
+
// } else if (duration === undefined) {
|
|
35
|
+
// setStatus(Status.PENDING);
|
|
36
|
+
// } else {
|
|
37
|
+
// setStatus(Status.PERSISTED_LOCALLY);
|
|
38
|
+
// }
|
|
39
|
+
// });
|
|
40
|
+
// }, [db]);
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
// If this is changed outside the effect it's batched with setStatus and the following condition will never be true.
|
|
44
|
+
setPrevStatus(status);
|
|
45
|
+
|
|
46
|
+
if (prevStatus !== status && status === Status.PERSISTED_LOCALLY) {
|
|
47
|
+
setDisplayMessage(true);
|
|
48
|
+
const timeout = setTimeout(() => setDisplayMessage(false), 5000);
|
|
49
|
+
return () => clearTimeout(timeout);
|
|
50
|
+
}
|
|
51
|
+
}, [status]); // `prevStatus` omitted from dependency array to prevent timeout from being reset.
|
|
52
|
+
|
|
53
|
+
switch (status) {
|
|
54
|
+
case Status.ERROR:
|
|
55
|
+
return (
|
|
56
|
+
<div className='flex items-center'>
|
|
57
|
+
<Warning className={mx(getSize(4), 'me-1')} />
|
|
58
|
+
<span className={mx('text-sm', warningText)}>{t('persistence error label')}</span>
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
case Status.PENDING:
|
|
62
|
+
return (
|
|
63
|
+
<div className='flex items-center'>
|
|
64
|
+
<ArrowsCounterClockwise className={mx(getSize(4), 'me-1')} />
|
|
65
|
+
<span className={mx('text-sm', staticPlaceholderText)}>{t('persistence pending label')}</span>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
case Status.PERSISTED_LOCALLY:
|
|
69
|
+
default:
|
|
70
|
+
return (
|
|
71
|
+
<Tooltip.Root delayDuration={400}>
|
|
72
|
+
<Tooltip.Trigger role='status' className='flex items-center'>
|
|
73
|
+
<CheckCircle className={mx(getSize(4), 'me-1')} />
|
|
74
|
+
{displayMessage && (
|
|
75
|
+
<span className={mx('text-sm', staticPlaceholderText)}>{t('persisted locally label')}</span>
|
|
76
|
+
)}
|
|
77
|
+
</Tooltip.Trigger>
|
|
78
|
+
<Tooltip.Portal>
|
|
79
|
+
<Tooltip.Content classNames='z-10'>
|
|
80
|
+
{t('persisted locally message')}
|
|
81
|
+
<Tooltip.Arrow />
|
|
82
|
+
</Tooltip.Content>
|
|
83
|
+
</Tooltip.Portal>
|
|
84
|
+
</Tooltip.Root>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useCallback, useRef, useState } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type ReactiveObject } from '@dxos/echo-schema';
|
|
8
|
+
import { log } from '@dxos/log';
|
|
9
|
+
import { Button, Input, Popover, useTranslation } from '@dxos/react-ui';
|
|
10
|
+
|
|
11
|
+
import { SPACE_PLUGIN } from '../meta';
|
|
12
|
+
|
|
13
|
+
export const PopoverRenameObject = ({ object: obj }: { object: ReactiveObject<any> }) => {
|
|
14
|
+
const { t } = useTranslation(SPACE_PLUGIN);
|
|
15
|
+
const doneButton = useRef<HTMLButtonElement>(null);
|
|
16
|
+
// TODO(wittjosiah): Use schema here.
|
|
17
|
+
const object = obj as any;
|
|
18
|
+
// TODO(burdon): Field should not be hardcoded field.
|
|
19
|
+
const [name, setName] = useState(object.name || object.title || '');
|
|
20
|
+
|
|
21
|
+
const handleDone = useCallback(() => {
|
|
22
|
+
try {
|
|
23
|
+
object.name = name;
|
|
24
|
+
} catch {
|
|
25
|
+
try {
|
|
26
|
+
object.title = name;
|
|
27
|
+
} catch (err) {
|
|
28
|
+
log.error('Failed to rename object', { err });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}, [object, name]);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div role='none' className='p-1 flex gap-2'>
|
|
35
|
+
<div role='none' className='flex-1'>
|
|
36
|
+
<Input.Root>
|
|
37
|
+
<Input.Label srOnly>{t('object name label')}</Input.Label>
|
|
38
|
+
<Input.TextInput
|
|
39
|
+
placeholder={t('object title placeholder')}
|
|
40
|
+
value={name}
|
|
41
|
+
data-testid='spacePlugin.renameObject.input'
|
|
42
|
+
onChange={({ target: { value } }) => setName(value)}
|
|
43
|
+
onKeyDown={({ key }) => key === 'Enter' && doneButton.current?.click()}
|
|
44
|
+
/>
|
|
45
|
+
</Input.Root>
|
|
46
|
+
</div>
|
|
47
|
+
<Popover.Close asChild>
|
|
48
|
+
<Button ref={doneButton} classNames='self-stretch' onClick={handleDone}>
|
|
49
|
+
{t('done label', { ns: 'os' })}
|
|
50
|
+
</Button>
|
|
51
|
+
</Popover.Close>
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useCallback, useRef, useState } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type Space } from '@dxos/react-client/echo';
|
|
8
|
+
import { Button, Input, Popover, useTranslation } from '@dxos/react-ui';
|
|
9
|
+
|
|
10
|
+
import { SPACE_PLUGIN } from '../meta';
|
|
11
|
+
|
|
12
|
+
export const PopoverRenameSpace = ({ space }: { space: Space }) => {
|
|
13
|
+
const { t } = useTranslation(SPACE_PLUGIN);
|
|
14
|
+
const doneButton = useRef<HTMLButtonElement>(null);
|
|
15
|
+
const [name, setName] = useState(space.properties.name ?? '');
|
|
16
|
+
|
|
17
|
+
const handleDone = useCallback(() => {
|
|
18
|
+
space.properties.name = name;
|
|
19
|
+
}, [space, name]);
|
|
20
|
+
|
|
21
|
+
// TODO(thure): Why does the input value need to be uncontrolled to work?
|
|
22
|
+
return (
|
|
23
|
+
<div role='none' className='p-1 flex gap-2'>
|
|
24
|
+
<div role='none' className='flex-1'>
|
|
25
|
+
<Input.Root>
|
|
26
|
+
<Input.Label srOnly>{t('space name label')}</Input.Label>
|
|
27
|
+
<Input.TextInput
|
|
28
|
+
defaultValue={space.properties.name ?? ''}
|
|
29
|
+
placeholder={t('unnamed space label')}
|
|
30
|
+
onChange={({ target: { value } }) => setName(value)}
|
|
31
|
+
// TODO(wittjosiah): Ideally this should access the popover context to close the popover.
|
|
32
|
+
// Currently this is not possible because Radix does not expose the popover context.
|
|
33
|
+
onKeyDown={({ key }) => key === 'Enter' && doneButton.current?.click()}
|
|
34
|
+
/>
|
|
35
|
+
</Input.Root>
|
|
36
|
+
</div>
|
|
37
|
+
<Popover.Close asChild>
|
|
38
|
+
<Button ref={doneButton} classNames='self-stretch' onClick={handleDone}>
|
|
39
|
+
{t('done label', { ns: 'os' })}
|
|
40
|
+
</Button>
|
|
41
|
+
</Popover.Close>
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import '@dxosTheme';
|
|
6
|
+
|
|
7
|
+
import { withTheme } from '@dxos/storybook-utils';
|
|
8
|
+
|
|
9
|
+
import { ShareSpaceButtonImpl } from './ShareSpaceButton';
|
|
10
|
+
import translations from '../translations';
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
title: 'plugin-space/ShareSpaceButton',
|
|
14
|
+
component: ShareSpaceButtonImpl,
|
|
15
|
+
decorators: [withTheme],
|
|
16
|
+
parameters: { translations },
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const Default = {
|
|
20
|
+
args: {
|
|
21
|
+
onClick: () => console.log('clicked'),
|
|
22
|
+
},
|
|
23
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { useIntentDispatcher } from '@dxos/app-framework';
|
|
8
|
+
import { Button, useTranslation } from '@dxos/react-ui';
|
|
9
|
+
|
|
10
|
+
import { SPACE_PLUGIN, SpaceAction } from '../meta';
|
|
11
|
+
|
|
12
|
+
export const ShareSpaceButton = ({ spaceId }: { spaceId: string }) => {
|
|
13
|
+
const dispatch = useIntentDispatcher();
|
|
14
|
+
|
|
15
|
+
return <ShareSpaceButtonImpl onClick={() => dispatch({ action: SpaceAction.SHARE, data: { spaceId } })} />;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// TODO(wittjosiah): Better way to name pure/impure components?
|
|
19
|
+
export const ShareSpaceButtonImpl = ({ onClick }: { onClick: () => void }) => {
|
|
20
|
+
const { t } = useTranslation(SPACE_PLUGIN);
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<Button data-testid='spacePlugin.shareSpaceButton' onClick={onClick} classNames='mli-1'>
|
|
24
|
+
{t('share space label')}
|
|
25
|
+
</Button>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Command } from '@phosphor-icons/react';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
|
|
8
|
+
import { Surface } from '@dxos/app-framework';
|
|
9
|
+
import { type Action } from '@dxos/plugin-graph';
|
|
10
|
+
import { SpaceState, type Space } from '@dxos/react-client/echo';
|
|
11
|
+
import { Button, Main, useTranslation, toLocalizedString } from '@dxos/react-ui';
|
|
12
|
+
import { getSize, mx, topbarBlockPaddingStart } from '@dxos/react-ui-theme';
|
|
13
|
+
import { ClipboardProvider } from '@dxos/shell/react';
|
|
14
|
+
|
|
15
|
+
import { SpaceMembersSection } from './SpaceMembersSection';
|
|
16
|
+
import { SPACE_PLUGIN } from '../../meta';
|
|
17
|
+
|
|
18
|
+
const _InFlowSpaceActions = ({ actionsMap }: { actionsMap: Record<string, Action> }) => {
|
|
19
|
+
const { t } = useTranslation(SPACE_PLUGIN);
|
|
20
|
+
return (
|
|
21
|
+
<section className='mbe-4 col-start-2 col-end-4 md:col-end-7 grid gap-2 auto-rows-min grid-cols-[repeat(auto-fill,minmax(8rem,1fr))]'>
|
|
22
|
+
{Object.entries(actionsMap)
|
|
23
|
+
.filter(([_, { properties }]) => properties?.mainAreaDisposition === 'in-flow')
|
|
24
|
+
.map(([actionId, { data: invoke, properties }]) => {
|
|
25
|
+
const Icon = properties?.icon;
|
|
26
|
+
const label = properties?.label;
|
|
27
|
+
return (
|
|
28
|
+
<Button key={actionId} classNames='block text-center plb-2 font-normal'>
|
|
29
|
+
{Icon && <Icon className={mx(getSize(5), 'mli-auto')} />}
|
|
30
|
+
<p>{toLocalizedString(label, t)}</p>
|
|
31
|
+
</Button>
|
|
32
|
+
);
|
|
33
|
+
})}
|
|
34
|
+
</section>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const KeyShortcuts = () => {
|
|
39
|
+
const { t } = useTranslation(SPACE_PLUGIN);
|
|
40
|
+
return (
|
|
41
|
+
<section className='mbe-4 col-span-4 md:col-start-5 md:col-end-7 grid grid-cols-subgrid gap-y-2 auto-rows-min'>
|
|
42
|
+
<h2 className='contents'>
|
|
43
|
+
<Command weight='duotone' className={mx(getSize(5), 'place-self-center')} />
|
|
44
|
+
<span className='text-lg col-span-2 md:col-span-1'>{t('keyshortcuts label')}</span>
|
|
45
|
+
</h2>
|
|
46
|
+
<div role='none' className='col-start-2 col-end-4 md:col-end-5 pie-2'>
|
|
47
|
+
<Surface role='keyshortcuts' />
|
|
48
|
+
</div>
|
|
49
|
+
</section>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const spaceMainLayout =
|
|
54
|
+
'grid gap-y-2 auto-rows-min before:bs-2 before:col-span-5 grid-cols-[var(--rail-size)_var(--rail-size)_1fr_var(--rail-size)] md:grid-cols-[var(--rail-size)_var(--rail-size)_minmax(max-content,1fr)_var(--rail-size)_var(--rail-size)_minmax(max-content,2fr)_var(--rail-size)]';
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @deprecated
|
|
58
|
+
*/
|
|
59
|
+
// TODO(wittjosiah): Remove this and re-use space members section as a deck column.
|
|
60
|
+
export const SpaceMain = ({ space, role }: { space: Space; role: 'main' | 'article' }) => {
|
|
61
|
+
// const { graph } = useGraph();
|
|
62
|
+
// const _actionsMap = graph.findNode(space.key.toHex())?.actionsMap;
|
|
63
|
+
const state = space.state.get();
|
|
64
|
+
const ready = state === SpaceState.SPACE_READY;
|
|
65
|
+
const Root = role === 'main' ? Main.Content : 'div';
|
|
66
|
+
return (
|
|
67
|
+
<ClipboardProvider>
|
|
68
|
+
<Root
|
|
69
|
+
{...(role === 'main'
|
|
70
|
+
? { classNames: [topbarBlockPaddingStart, 'min-bs-dvh', spaceMainLayout] }
|
|
71
|
+
: { role: 'none', className: mx(topbarBlockPaddingStart, 'row-span-2', spaceMainLayout) })}
|
|
72
|
+
data-testid={`spacePlugin.${role}`}
|
|
73
|
+
data-isready={ready ? 'true' : 'false'}
|
|
74
|
+
>
|
|
75
|
+
{/* {actionsMap && <InFlowSpaceActions actionsMap={actionsMap} />} */}
|
|
76
|
+
{ready && <SpaceMembersSection space={space} />}
|
|
77
|
+
<KeyShortcuts />
|
|
78
|
+
</Root>
|
|
79
|
+
</ClipboardProvider>
|
|
80
|
+
);
|
|
81
|
+
};
|