@dxos/plugin-space 0.6.13-main.ed424a1 → 0.6.13
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-DTVUOG2C.mjs} +5 -24
- package/dist/lib/browser/chunk-DTVUOG2C.mjs.map +7 -0
- package/dist/lib/browser/{chunk-AVLRQF6L.mjs → chunk-LZEGRS7H.mjs} +1 -1
- package/dist/lib/browser/chunk-LZEGRS7H.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +249 -595
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/meta.mjs +1 -1
- package/dist/lib/browser/types/index.mjs +3 -7
- package/dist/lib/node/{chunk-P4XUXM7Y.cjs → chunk-6CNYF6YU.cjs} +4 -4
- package/dist/lib/node/chunk-6CNYF6YU.cjs.map +7 -0
- package/dist/lib/node/{chunk-CTYDNFGG.cjs → chunk-CVZPI2P3.cjs} +9 -30
- package/dist/lib/node/chunk-CVZPI2P3.cjs.map +7 -0
- package/dist/lib/node/index.cjs +470 -813
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.cjs +5 -5
- package/dist/lib/node/meta.cjs.map +1 -1
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/types/index.cjs +10 -14
- package/dist/lib/node/types/index.cjs.map +2 -2
- package/dist/types/src/SpacePlugin.d.ts.map +1 -1
- 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 +1 -1
- package/dist/types/src/components/MenuFooter.d.ts.map +1 -1
- package/dist/types/src/components/ShareSpaceButton.stories.d.ts +0 -4
- package/dist/types/src/components/ShareSpaceButton.stories.d.ts.map +1 -1
- package/dist/types/src/components/SpaceMain/SpaceMain.d.ts.map +1 -1
- package/dist/types/src/components/SpacePresence.stories.d.ts +0 -4
- package/dist/types/src/components/SpacePresence.stories.d.ts.map +1 -1
- package/dist/types/src/components/SpaceSettings.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +2 -3
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +0 -4
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/thread.d.ts +1 -15
- package/dist/types/src/types/thread.d.ts.map +1 -1
- package/dist/types/src/types/types.d.ts +1 -18
- package/dist/types/src/types/types.d.ts.map +1 -1
- package/dist/types/src/util.d.ts +6 -3
- package/dist/types/src/util.d.ts.map +1 -1
- package/package.json +36 -44
- package/src/SpacePlugin.tsx +86 -149
- package/src/components/EmptySpace.tsx +25 -0
- package/src/components/EmptyTree.tsx +25 -0
- package/src/components/MenuFooter.tsx +2 -2
- package/src/components/SpaceMain/SpaceMain.tsx +22 -1
- package/src/components/SpacePresence.tsx +1 -1
- package/src/components/SpaceSettings.tsx +3 -32
- package/src/components/index.ts +2 -3
- package/src/meta.ts +1 -3
- package/src/translations.ts +0 -4
- package/src/types/collection.ts +1 -1
- package/src/types/thread.ts +2 -12
- package/src/types/types.ts +1 -25
- package/src/util.tsx +55 -20
- package/dist/lib/browser/chunk-47SVNCZM.mjs.map +0 -7
- package/dist/lib/browser/chunk-AVLRQF6L.mjs.map +0 -7
- package/dist/lib/node/chunk-CTYDNFGG.cjs.map +0 -7
- package/dist/lib/node/chunk-P4XUXM7Y.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-PLPMYTLC.mjs +0 -116
- package/dist/lib/node-esm/chunk-PLPMYTLC.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-YPQGKWHJ.mjs +0 -37
- package/dist/lib/node-esm/chunk-YPQGKWHJ.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -2987
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/meta.mjs +0 -14
- package/dist/lib/node-esm/meta.mjs.map +0 -7
- package/dist/lib/node-esm/types/index.mjs +0 -26
- package/dist/lib/node-esm/types/index.mjs.map +0 -7
- package/dist/types/src/components/FallbackSettings.d.ts +0 -8
- package/dist/types/src/components/FallbackSettings.d.ts.map +0 -1
- package/dist/types/src/components/SaveStatus.d.ts +0 -3
- package/dist/types/src/components/SaveStatus.d.ts.map +0 -1
- package/dist/types/src/components/SyncStatus/SyncStatus.d.ts +0 -13
- package/dist/types/src/components/SyncStatus/SyncStatus.d.ts.map +0 -1
- package/dist/types/src/components/SyncStatus/SyncStatus.stories.d.ts +0 -117
- package/dist/types/src/components/SyncStatus/SyncStatus.stories.d.ts.map +0 -1
- package/dist/types/src/components/SyncStatus/index.d.ts +0 -2
- package/dist/types/src/components/SyncStatus/index.d.ts.map +0 -1
- package/dist/types/src/components/SyncStatus/types.d.ts +0 -14
- package/dist/types/src/components/SyncStatus/types.d.ts.map +0 -1
- package/src/components/FallbackSettings.tsx +0 -35
- package/src/components/SaveStatus.tsx +0 -95
- package/src/components/SyncStatus/SyncStatus.stories.tsx +0 -62
- package/src/components/SyncStatus/SyncStatus.tsx +0 -188
- package/src/components/SyncStatus/index.ts +0 -5
- package/src/components/SyncStatus/types.ts +0 -77
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import React, { type HTMLAttributes, useEffect, useState } from 'react';
|
|
6
|
-
|
|
7
|
-
import { StatusBar } from '@dxos/plugin-status-bar';
|
|
8
|
-
import { Icon, Popover, useTranslation } from '@dxos/react-ui';
|
|
9
|
-
import { type ThemedClassName } from '@dxos/react-ui';
|
|
10
|
-
import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
11
|
-
import { mx } from '@dxos/react-ui-theme';
|
|
12
|
-
|
|
13
|
-
import { type Progress, type PeerSyncState, type SpaceSyncStateMap, getSyncSummary, useSyncState } from './types';
|
|
14
|
-
import { SPACE_PLUGIN } from '../../meta';
|
|
15
|
-
|
|
16
|
-
const SYNC_STALLED_TIMEOUT = 5_000;
|
|
17
|
-
|
|
18
|
-
const styles = {
|
|
19
|
-
barBg: 'bg-neutral-50 dark:bg-green-900 text-black',
|
|
20
|
-
barFg: 'bg-neutral-100 bg-green-500',
|
|
21
|
-
barHover: 'dark:hover:bg-green-500',
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export const SyncStatus = () => {
|
|
25
|
-
const state = useSyncState();
|
|
26
|
-
return <SyncStatusIndicator state={state} />;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export const SyncStatusIndicator = ({ state }: { state: SpaceSyncStateMap }) => {
|
|
30
|
-
const summary = getSyncSummary(state);
|
|
31
|
-
const offline = false;
|
|
32
|
-
|
|
33
|
-
const needsToUpload = summary.differentDocuments > 0 || summary.missingOnRemote > 0;
|
|
34
|
-
const needsToDownload = summary.differentDocuments > 0 || summary.missingOnLocal > 0;
|
|
35
|
-
const [classNames, setClassNames] = useState<string>();
|
|
36
|
-
useEffect(() => {
|
|
37
|
-
setClassNames(undefined);
|
|
38
|
-
if (!needsToUpload && !needsToDownload) {
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const t = setTimeout(() => {
|
|
43
|
-
setClassNames('text-orange-500');
|
|
44
|
-
}, SYNC_STALLED_TIMEOUT);
|
|
45
|
-
return () => clearTimeout(t);
|
|
46
|
-
}, [needsToUpload, needsToDownload]);
|
|
47
|
-
|
|
48
|
-
return (
|
|
49
|
-
<StatusBar.Item>
|
|
50
|
-
<Popover.Root>
|
|
51
|
-
<Popover.Trigger>
|
|
52
|
-
<Icon
|
|
53
|
-
icon={
|
|
54
|
-
offline
|
|
55
|
-
? 'ph--cloud-x--regular'
|
|
56
|
-
: needsToUpload
|
|
57
|
-
? 'ph--cloud-arrow-up--regular'
|
|
58
|
-
: needsToDownload
|
|
59
|
-
? 'ph--cloud-arrow-down--regular'
|
|
60
|
-
: 'ph--cloud-check--regular'
|
|
61
|
-
}
|
|
62
|
-
size={4}
|
|
63
|
-
classNames={classNames}
|
|
64
|
-
/>
|
|
65
|
-
</Popover.Trigger>
|
|
66
|
-
<Popover.Content>
|
|
67
|
-
<SyncStatusDetail state={state} summary={summary} debug={false} />
|
|
68
|
-
</Popover.Content>
|
|
69
|
-
</Popover.Root>
|
|
70
|
-
</StatusBar.Item>
|
|
71
|
-
);
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
export const SyncStatusDetail = ({
|
|
75
|
-
classNames,
|
|
76
|
-
state,
|
|
77
|
-
summary,
|
|
78
|
-
debug,
|
|
79
|
-
}: ThemedClassName<{
|
|
80
|
-
state: SpaceSyncStateMap;
|
|
81
|
-
summary: PeerSyncState;
|
|
82
|
-
debug?: boolean;
|
|
83
|
-
}>) => {
|
|
84
|
-
const { t } = useTranslation(SPACE_PLUGIN);
|
|
85
|
-
const entries = Object.entries(state).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
86
|
-
|
|
87
|
-
// TODO(burdon): Normalize to max document count?
|
|
88
|
-
return (
|
|
89
|
-
<div className={mx('flex flex-col text-xs min-w-[16rem]', classNames)}>
|
|
90
|
-
<h1 className='p-2'>{t('sync status title')}</h1>
|
|
91
|
-
<div className='flex flex-col gap-[2px] my-[2px]'>
|
|
92
|
-
{entries.map(([spaceId, state]) => (
|
|
93
|
-
<SpaceRow key={spaceId} spaceId={spaceId} state={state} />
|
|
94
|
-
))}
|
|
95
|
-
</div>
|
|
96
|
-
{debug && <SyntaxHighlighter language='json'>{JSON.stringify(summary, null, 2)}</SyntaxHighlighter>}
|
|
97
|
-
</div>
|
|
98
|
-
);
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
const useActive = (count: number) => {
|
|
102
|
-
const [current, setCurrent] = useState(count);
|
|
103
|
-
const [active, setActive] = useState(false);
|
|
104
|
-
useEffect(() => {
|
|
105
|
-
let t: NodeJS.Timeout | undefined;
|
|
106
|
-
if (count !== current) {
|
|
107
|
-
setActive(true);
|
|
108
|
-
setCurrent(count);
|
|
109
|
-
t && clearTimeout(t);
|
|
110
|
-
t = setTimeout(() => {
|
|
111
|
-
setActive(false);
|
|
112
|
-
}, SYNC_STALLED_TIMEOUT);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return () => {
|
|
116
|
-
setActive(false);
|
|
117
|
-
clearTimeout(t);
|
|
118
|
-
};
|
|
119
|
-
}, [count, current]);
|
|
120
|
-
return active;
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
const SpaceRow = ({
|
|
124
|
-
spaceId,
|
|
125
|
-
state: { localDocumentCount, remoteDocumentCount, missingOnLocal, missingOnRemote },
|
|
126
|
-
}: {
|
|
127
|
-
spaceId: string;
|
|
128
|
-
state: PeerSyncState;
|
|
129
|
-
}) => {
|
|
130
|
-
const downActive = useActive(localDocumentCount);
|
|
131
|
-
const upActive = useActive(remoteDocumentCount);
|
|
132
|
-
|
|
133
|
-
return (
|
|
134
|
-
<div
|
|
135
|
-
className={mx('flex items-center mx-[2px] gap-[2px] cursor-pointer', styles.barHover)}
|
|
136
|
-
title={spaceId}
|
|
137
|
-
onClick={() => {
|
|
138
|
-
void navigator.clipboard.writeText(spaceId);
|
|
139
|
-
}}
|
|
140
|
-
>
|
|
141
|
-
<Icon
|
|
142
|
-
icon='ph--arrow-fat-line-left--regular'
|
|
143
|
-
size={3}
|
|
144
|
-
classNames={mx(downActive && 'animate-[pulse_1s_infinite]')}
|
|
145
|
-
/>
|
|
146
|
-
<Candle
|
|
147
|
-
up={{ count: remoteDocumentCount, total: remoteDocumentCount + missingOnRemote }}
|
|
148
|
-
down={{ count: localDocumentCount, total: localDocumentCount + missingOnLocal }}
|
|
149
|
-
title={spaceId}
|
|
150
|
-
/>
|
|
151
|
-
<Icon
|
|
152
|
-
icon='ph--arrow-fat-line-right--regular'
|
|
153
|
-
size={3}
|
|
154
|
-
classNames={mx(upActive && 'animate-[pulse_1s_step-start_infinite]')}
|
|
155
|
-
/>
|
|
156
|
-
</div>
|
|
157
|
-
);
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
type CandleProps = ThemedClassName<Pick<HTMLAttributes<HTMLDivElement>, 'title'>> & { up: Progress; down: Progress };
|
|
161
|
-
|
|
162
|
-
const Candle = ({ classNames, up, down }: CandleProps) => {
|
|
163
|
-
return (
|
|
164
|
-
<div className={mx('grid grid-cols-[1fr_2rem_1fr] w-full h-3', classNames)}>
|
|
165
|
-
<Bar classNames='justify-end' {...up} />
|
|
166
|
-
<div className='relative'>
|
|
167
|
-
<div className={mx('absolute inset-0 flex items-center justify-center text-xs', styles.barBg)}>{up.total}</div>
|
|
168
|
-
</div>
|
|
169
|
-
<Bar {...down} />
|
|
170
|
-
</div>
|
|
171
|
-
);
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
const Bar = ({ classNames, count, total }: ThemedClassName<Progress>) => {
|
|
175
|
-
let p = (count / total) * 100;
|
|
176
|
-
if (count < total) {
|
|
177
|
-
p = Math.min(p, 95);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return (
|
|
181
|
-
<div className={mx('relative flex w-full', styles.barBg, classNames)}>
|
|
182
|
-
<div className={mx('shrink-0', styles.barFg)} style={{ width: `${p}%` }}></div>
|
|
183
|
-
{count !== total && (
|
|
184
|
-
<div className='absolute top-0 bottom-0 flex items-center mx-0.5 text-black text-xs'>{count}</div>
|
|
185
|
-
)}
|
|
186
|
-
</div>
|
|
187
|
-
);
|
|
188
|
-
};
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { useEffect, useState } from 'react';
|
|
6
|
-
|
|
7
|
-
import { type Space, type SpaceId, type SpaceSyncState } from '@dxos/client/echo';
|
|
8
|
-
import { Context } from '@dxos/context';
|
|
9
|
-
import { EdgeService } from '@dxos/protocols';
|
|
10
|
-
import { useClient } from '@dxos/react-client';
|
|
11
|
-
|
|
12
|
-
export type Progress = { count: number; total: number };
|
|
13
|
-
|
|
14
|
-
export type PeerSyncState = Omit<SpaceSyncState.PeerState, 'peerId'>;
|
|
15
|
-
|
|
16
|
-
export type SpaceSyncStateMap = Record<SpaceId, PeerSyncState>;
|
|
17
|
-
|
|
18
|
-
export const createEmptyEdgeSyncState = (): PeerSyncState => ({
|
|
19
|
-
missingOnLocal: 0,
|
|
20
|
-
missingOnRemote: 0,
|
|
21
|
-
localDocumentCount: 0,
|
|
22
|
-
remoteDocumentCount: 0,
|
|
23
|
-
differentDocuments: 0,
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
export const getSyncSummary = (syncMap: SpaceSyncStateMap): PeerSyncState => {
|
|
27
|
-
return Object.entries(syncMap).reduce<PeerSyncState>((summary, [_spaceId, peerState]) => {
|
|
28
|
-
summary.missingOnLocal += peerState.missingOnLocal;
|
|
29
|
-
summary.missingOnRemote += peerState.missingOnRemote;
|
|
30
|
-
summary.localDocumentCount += peerState.localDocumentCount;
|
|
31
|
-
summary.remoteDocumentCount += peerState.remoteDocumentCount;
|
|
32
|
-
summary.differentDocuments += peerState.differentDocuments;
|
|
33
|
-
return summary;
|
|
34
|
-
}, createEmptyEdgeSyncState());
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const isEdgePeerId = (peerId: string, spaceId: SpaceId) =>
|
|
38
|
-
peerId.startsWith(`${EdgeService.AUTOMERGE_REPLICATOR}:${spaceId}`);
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Hook Subscribes to sync state for each space.
|
|
42
|
-
*/
|
|
43
|
-
export const useSyncState = (): SpaceSyncStateMap => {
|
|
44
|
-
const client = useClient();
|
|
45
|
-
const [spaceState, setSpaceState] = useState<SpaceSyncStateMap>({});
|
|
46
|
-
|
|
47
|
-
useEffect(() => {
|
|
48
|
-
const ctx = new Context();
|
|
49
|
-
const createSubscriptions = (spaces: Space[]) => {
|
|
50
|
-
for (const space of spaces) {
|
|
51
|
-
if (spaceState[space.id]) {
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
ctx.onDispose(
|
|
56
|
-
space.crud.subscribeToSyncState(ctx, ({ peers = [] }) => {
|
|
57
|
-
const syncState = peers.find((state) => isEdgePeerId(state.peerId, space.id));
|
|
58
|
-
if (syncState) {
|
|
59
|
-
setSpaceState((spaceState) => ({ ...spaceState, [space.id]: syncState }));
|
|
60
|
-
}
|
|
61
|
-
}),
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
createSubscriptions(client.spaces.get());
|
|
67
|
-
client.spaces.subscribe((spaces) => {
|
|
68
|
-
createSubscriptions(spaces);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
return () => {
|
|
72
|
-
void ctx.dispose();
|
|
73
|
-
};
|
|
74
|
-
}, [client]);
|
|
75
|
-
|
|
76
|
-
return spaceState;
|
|
77
|
-
};
|