@dxos/react-client 0.8.4-main.f5c0578 → 0.8.4-main.fcc0d83b33
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/.storybook/main.mts +9 -0
- package/.storybook/preview.mts +8 -0
- package/dist/lib/browser/chunk-3LVU7RNK.mjs +16 -0
- package/dist/lib/browser/chunk-3LVU7RNK.mjs.map +7 -0
- package/dist/lib/browser/chunk-CZMIVAGI.mjs +8 -0
- package/dist/lib/browser/chunk-CZMIVAGI.mjs.map +7 -0
- package/dist/lib/browser/chunk-XT352YI6.mjs +134 -0
- package/dist/lib/browser/chunk-XT352YI6.mjs.map +7 -0
- package/dist/lib/browser/{chunk-BXOLJYAY.mjs → chunk-YHE6WD44.mjs} +4 -14
- package/dist/lib/browser/chunk-YHE6WD44.mjs.map +7 -0
- package/dist/lib/browser/devtools/index.mjs +3 -7
- package/dist/lib/browser/devtools/index.mjs.map +2 -2
- package/dist/lib/browser/echo/index.mjs +58 -127
- package/dist/lib/browser/echo/index.mjs.map +4 -4
- package/dist/lib/browser/halo/index.mjs +4 -4
- package/dist/lib/browser/halo/index.mjs.map +2 -2
- package/dist/lib/browser/index.mjs +99 -12
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/invitations/index.mjs +1 -1
- package/dist/lib/browser/mesh/index.mjs +2 -1
- package/dist/lib/browser/mesh/index.mjs.map +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +111 -154
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/chunk-CUDKNAMV.mjs +17 -0
- package/dist/lib/node-esm/chunk-CUDKNAMV.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-256IHY6F.mjs → chunk-RQJPEHDY.mjs} +4 -14
- package/dist/lib/node-esm/chunk-RQJPEHDY.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-WL3Z5HOH.mjs +135 -0
- package/dist/lib/node-esm/chunk-WL3Z5HOH.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-WUTK2IJF.mjs +10 -0
- package/dist/lib/node-esm/chunk-WUTK2IJF.mjs.map +7 -0
- package/dist/lib/node-esm/devtools/index.mjs +3 -7
- package/dist/lib/node-esm/devtools/index.mjs.map +2 -2
- package/dist/lib/node-esm/echo/index.mjs +58 -127
- package/dist/lib/node-esm/echo/index.mjs.map +4 -4
- package/dist/lib/node-esm/halo/index.mjs +4 -4
- package/dist/lib/node-esm/halo/index.mjs.map +2 -2
- package/dist/lib/node-esm/index.mjs +99 -12
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/invitations/index.mjs +1 -1
- package/dist/lib/node-esm/mesh/index.mjs +2 -1
- package/dist/lib/node-esm/mesh/index.mjs.map +1 -1
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +111 -154
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/types/src/banner.d.ts.map +1 -1
- package/dist/types/src/client/AgentHostingProvider.d.ts.map +1 -1
- package/dist/types/src/client/ClientProvider.d.ts +1 -11
- package/dist/types/src/client/ClientProvider.d.ts.map +1 -1
- package/dist/types/src/client/useClientStatus.d.ts.map +1 -1
- package/dist/types/src/devtools/useStream.d.ts.map +1 -1
- package/dist/types/src/echo/index.d.ts +2 -2
- package/dist/types/src/echo/index.d.ts.map +1 -1
- package/dist/types/src/echo/useMembers.d.ts +2 -2
- package/dist/types/src/echo/useMembers.d.ts.map +1 -1
- package/dist/types/src/echo/useQueue.d.ts +3 -2
- package/dist/types/src/echo/useQueue.d.ts.map +1 -1
- package/dist/types/src/echo/useSpaceInvitations.d.ts +3 -2
- package/dist/types/src/echo/useSpaceInvitations.d.ts.map +1 -1
- package/dist/types/src/echo/useSpaceProperties.d.ts +13 -0
- package/dist/types/src/echo/useSpaceProperties.d.ts.map +1 -0
- package/dist/types/src/echo/useSpaces.d.ts +19 -9
- package/dist/types/src/echo/useSpaces.d.ts.map +1 -1
- package/dist/types/src/echo/useSubscription.d.ts +1 -1
- package/dist/types/src/echo/useSubscription.d.ts.map +1 -1
- package/dist/types/src/echo/useSyncState.d.ts.map +1 -1
- package/dist/types/src/halo/Passkey.stories.d.ts +5 -5
- package/dist/types/src/halo/Passkey.stories.d.ts.map +1 -1
- package/dist/types/src/halo/useHaloInvitations.d.ts.map +1 -1
- package/dist/types/src/halo/useKeyStore.d.ts.map +1 -1
- package/dist/types/src/invitations/useInvitationStatus.d.ts +1 -1
- package/dist/types/src/invitations/useInvitationStatus.d.ts.map +1 -1
- package/dist/types/src/testing/ClientRepeater.d.ts +6 -5
- package/dist/types/src/testing/ClientRepeater.d.ts.map +1 -1
- package/dist/types/src/testing/ClientRepeater.stories.d.ts +6 -10
- package/dist/types/src/testing/ClientRepeater.stories.d.ts.map +1 -1
- package/dist/types/src/testing/context.d.ts +4 -2
- package/dist/types/src/testing/context.d.ts.map +1 -1
- package/dist/types/src/testing/util.d.ts.map +1 -1
- package/dist/types/src/testing/withClientProvider.d.ts +5 -9
- package/dist/types/src/testing/withClientProvider.d.ts.map +1 -1
- package/dist/types/src/testing/withClientProvider.stories.d.ts +8 -9
- package/dist/types/src/testing/withClientProvider.stories.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/docs/README.md +38 -38
- package/moon.yml +3 -2
- package/package.json +35 -58
- package/src/client/ClientProvider.test.tsx +0 -1
- package/src/client/ClientProvider.tsx +29 -48
- package/src/client/useConfig.test.tsx +0 -1
- package/src/echo/index.ts +2 -2
- package/src/echo/useMembers.ts +5 -3
- package/src/echo/useQueue.ts +3 -4
- package/src/echo/useSpaceInvitations.ts +9 -5
- package/src/echo/useSpaceProperties.ts +28 -0
- package/src/echo/useSpaces.test.tsx +7 -14
- package/src/echo/useSpaces.ts +32 -30
- package/src/echo/useSubscription.ts +1 -1
- package/src/echo/useSyncState.ts +4 -3
- package/src/halo/Passkey.stories.tsx +17 -18
- package/src/halo/useDevices.test.tsx +0 -1
- package/src/invitations/useInvitationStatus.ts +9 -8
- package/src/testing/ClientRepeater.stories.tsx +21 -16
- package/src/testing/ClientRepeater.tsx +31 -40
- package/src/testing/context.tsx +4 -3
- package/src/testing/util.tsx +0 -1
- package/src/testing/withClientProvider.stories.tsx +33 -20
- package/src/testing/withClientProvider.tsx +74 -92
- package/tsconfig.json +3 -6
- package/typedoc.json +1 -1
- package/vitest.config.ts +10 -16
- package/.swc/plugins/v7_linux_x86_64_13.0.0/c614d7475354583212fbd7669acbae95b9832c305bf51bdaabe2e6de05abb6bf +0 -0
- package/dist/lib/browser/chunk-BXOLJYAY.mjs.map +0 -7
- package/dist/lib/browser/chunk-FQAPI3I4.mjs +0 -312
- package/dist/lib/browser/chunk-FQAPI3I4.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-256IHY6F.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-MNTJNI2M.mjs +0 -314
- package/dist/lib/node-esm/chunk-MNTJNI2M.mjs.map +0 -7
- package/dist/types/src/echo/useQuery.d.ts +0 -11
- package/dist/types/src/echo/useQuery.d.ts.map +0 -1
- package/dist/types/src/echo/useQuery.test.d.ts +0 -2
- package/dist/types/src/echo/useQuery.test.d.ts.map +0 -1
- package/dist/types/src/echo/useSchema.d.ts +0 -8
- package/dist/types/src/echo/useSchema.d.ts.map +0 -1
- package/dist/types/src/playwright/playwright.config.d.ts +0 -3
- package/dist/types/src/playwright/playwright.config.d.ts.map +0 -1
- package/dist/types/src/playwright/smoke.spec.d.ts +0 -2
- package/dist/types/src/playwright/smoke.spec.d.ts.map +0 -1
- package/src/echo/useQuery.test.tsx +0 -129
- package/src/echo/useQuery.ts +0 -56
- package/src/echo/useSchema.ts +0 -54
- package/src/playwright/playwright.config.ts +0 -17
- package/src/playwright/smoke.spec.ts +0 -24
package/src/echo/useSpaces.ts
CHANGED
|
@@ -2,51 +2,53 @@
|
|
|
2
2
|
// Copyright 2020 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
import { type PublicKeyLike } from '@dxos/client';
|
|
8
|
-
import { type Space, SpaceState } from '@dxos/client/echo';
|
|
5
|
+
import { PublicKey } from '@dxos/client';
|
|
6
|
+
import { type Space, type SpaceId, SpaceState } from '@dxos/client/echo';
|
|
9
7
|
import { useMulticastObservable } from '@dxos/react-hooks';
|
|
10
8
|
|
|
11
9
|
import { useClient } from '../client';
|
|
12
10
|
|
|
13
11
|
/**
|
|
14
|
-
* Get a specific Space using its
|
|
12
|
+
* Get a specific Space using its id.
|
|
15
13
|
* The space is not guaranteed to be in the ready state.
|
|
16
|
-
* Returns
|
|
14
|
+
* Returns undefined if no id is provided.
|
|
17
15
|
* Requires a ClientProvider somewhere in the parent tree.
|
|
18
16
|
*
|
|
19
|
-
* @param
|
|
17
|
+
* @param spaceId the id of the space to look for.
|
|
20
18
|
*/
|
|
21
|
-
|
|
19
|
+
// TODO(wittjosiah): Currently unable to remove `PublicKey` from this api.
|
|
20
|
+
// When initially joining a space that is all that is returned.
|
|
21
|
+
export const useSpace = (spaceId?: SpaceId | PublicKey): Space | undefined => {
|
|
22
22
|
const client = useClient();
|
|
23
23
|
const spaces = useMulticastObservable<Space[]>(client.spaces);
|
|
24
|
-
|
|
24
|
+
if (!spaceId) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return;
|
|
28
|
+
return spaces.find((space) => {
|
|
29
|
+
if (spaceId instanceof PublicKey) {
|
|
30
|
+
return space.key.equals(spaceId);
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
return () => clearTimeout(timeout);
|
|
38
|
-
}, [client, spaceKeyLike]);
|
|
39
|
-
|
|
40
|
-
if (spaceKeyLike) {
|
|
41
|
-
return spaces.find((space) => space.key.equals(spaceKeyLike) || space.id === spaceKeyLike);
|
|
42
|
-
}
|
|
33
|
+
return space.id === spaceId;
|
|
34
|
+
});
|
|
35
|
+
};
|
|
43
36
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Get a Space database by the id of the space.
|
|
39
|
+
* The space is not guaranteed to be in the ready state.
|
|
40
|
+
* Returns undefined if no id is provided.
|
|
41
|
+
* Requires a ClientProvider somewhere in the parent tree.
|
|
42
|
+
*
|
|
43
|
+
* @param spaceId the id of the space to look for.
|
|
44
|
+
* @deprecated Use useSpace.
|
|
45
|
+
*/
|
|
46
|
+
export const useDatabase = (spaceId?: SpaceId): Space['db'] | undefined => {
|
|
47
|
+
const space = useSpace(spaceId);
|
|
48
|
+
return space?.db;
|
|
47
49
|
};
|
|
48
50
|
|
|
49
|
-
export type
|
|
51
|
+
export type UseSpacesProps = {
|
|
50
52
|
/**
|
|
51
53
|
* Return uninitialized spaces as well.
|
|
52
54
|
*/
|
|
@@ -57,9 +59,9 @@ export type UseSpacesParams = {
|
|
|
57
59
|
* Get all Spaces available to current user.
|
|
58
60
|
* Requires a ClientProvider somewhere in the parent tree.
|
|
59
61
|
* By default, only ready spaces are returned.
|
|
60
|
-
* @returns an array of Spaces
|
|
62
|
+
* @returns an array of Spaces.
|
|
61
63
|
*/
|
|
62
|
-
export const useSpaces = ({ all = false }:
|
|
64
|
+
export const useSpaces = ({ all = false }: UseSpacesProps = {}): Space[] => {
|
|
63
65
|
const client = useClient();
|
|
64
66
|
const spaces = useMulticastObservable<Space[]>(client.spaces);
|
|
65
67
|
|
|
@@ -18,7 +18,7 @@ export const useSubscription = (cb: () => void, selection: Selection) => {
|
|
|
18
18
|
const callbackRef = useRef(cb);
|
|
19
19
|
callbackRef.current = cb;
|
|
20
20
|
|
|
21
|
-
const subscriptionRef = useRef<SubscriptionHandle>();
|
|
21
|
+
const subscriptionRef = useRef<SubscriptionHandle>(null);
|
|
22
22
|
|
|
23
23
|
useEffect(() => {
|
|
24
24
|
subscriptionRef.current = createSubscription(() => {
|
package/src/echo/useSyncState.ts
CHANGED
|
@@ -8,11 +8,12 @@ import { type PeerSyncState, type Space, type SpaceSyncStateMap } from '@dxos/cl
|
|
|
8
8
|
import { Context } from '@dxos/context';
|
|
9
9
|
import { type SpaceId } from '@dxos/keys';
|
|
10
10
|
import { EdgeService } from '@dxos/protocols';
|
|
11
|
+
import { compositeKey } from '@dxos/util';
|
|
11
12
|
|
|
12
13
|
import { useClient } from '../client';
|
|
13
14
|
|
|
14
15
|
const isEdgePeerId = (peerId: string, spaceId: SpaceId) =>
|
|
15
|
-
peerId.startsWith(
|
|
16
|
+
peerId.startsWith(compositeKey(EdgeService.AUTOMERGE_REPLICATOR, spaceId));
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Hook Subscribes to sync state for each space.
|
|
@@ -30,7 +31,7 @@ export const useSyncState = (): SpaceSyncStateMap => {
|
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
ctx.onDispose(
|
|
33
|
-
space.db.subscribeToSyncState(ctx, ({ peers = [] }) => {
|
|
34
|
+
space.internal.db.subscribeToSyncState(ctx, ({ peers = [] }) => {
|
|
34
35
|
const syncState = peers.find((state) => isEdgePeerId(state.peerId, space.id));
|
|
35
36
|
if (syncState) {
|
|
36
37
|
setSpaceState((spaceState) => ({ ...spaceState, [space.id]: syncState }));
|
|
@@ -62,7 +63,7 @@ export const useSpaceSyncState = (space: Space): PeerSyncState | undefined => {
|
|
|
62
63
|
|
|
63
64
|
useEffect(() => {
|
|
64
65
|
const ctx = new Context();
|
|
65
|
-
space.db.subscribeToSyncState(ctx, ({ peers = [] }) => {
|
|
66
|
+
space.internal.db.subscribeToSyncState(ctx, ({ peers = [] }) => {
|
|
66
67
|
const syncState = peers.find((state) => isEdgePeerId(state.peerId, space.id));
|
|
67
68
|
if (syncState) {
|
|
68
69
|
setSpaceState(syncState);
|
|
@@ -2,20 +2,18 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@
|
|
6
|
-
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
7
6
|
import React, { useCallback } from 'react';
|
|
8
7
|
|
|
9
8
|
import { Config, PublicKey } from '@dxos/client';
|
|
10
9
|
import { invariant } from '@dxos/invariant';
|
|
11
10
|
import { log } from '@dxos/log';
|
|
12
11
|
import { Button } from '@dxos/react-ui';
|
|
13
|
-
import {
|
|
14
|
-
import { withTheme } from '@dxos/
|
|
12
|
+
import { JsonHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
13
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
15
14
|
|
|
16
15
|
import { useClient } from '../client';
|
|
17
16
|
import { withClientProvider } from '../testing';
|
|
18
|
-
|
|
19
17
|
import { useCredentials } from './useCredentials';
|
|
20
18
|
import { useIdentity } from './useIdentity';
|
|
21
19
|
|
|
@@ -106,7 +104,7 @@ const Test = () => {
|
|
|
106
104
|
|
|
107
105
|
return (
|
|
108
106
|
<>
|
|
109
|
-
<div className='
|
|
107
|
+
<div className='mb-4 flex gap-2'>
|
|
110
108
|
<Button disabled={!!identity} onClick={handleCreateIdentity}>
|
|
111
109
|
Create Identity
|
|
112
110
|
</Button>
|
|
@@ -117,20 +115,13 @@ const Test = () => {
|
|
|
117
115
|
Authenticate with Passkey
|
|
118
116
|
</Button>
|
|
119
117
|
</div>
|
|
120
|
-
<div className='flex flex-col min-w-[28rem] divide-y divide-separator border border-separator rounded'>
|
|
121
|
-
<
|
|
122
|
-
{JSON.stringify({ identity, credentials: credentials.length }, null, 2)}
|
|
123
|
-
</SyntaxHighlighter>
|
|
118
|
+
<div className='flex flex-col min-w-[28rem] divide-y divide-separator border border-separator rounded-sm'>
|
|
119
|
+
<JsonHighlighter data={{ identity, credentials: credentials.length }} />
|
|
124
120
|
</div>
|
|
125
121
|
</>
|
|
126
122
|
);
|
|
127
123
|
};
|
|
128
124
|
|
|
129
|
-
export default {
|
|
130
|
-
title: 'sdk/react-client/Passkeys',
|
|
131
|
-
render: Test,
|
|
132
|
-
};
|
|
133
|
-
|
|
134
125
|
const config = new Config({
|
|
135
126
|
runtime: {
|
|
136
127
|
client: {
|
|
@@ -151,6 +142,14 @@ const config = new Config({
|
|
|
151
142
|
},
|
|
152
143
|
});
|
|
153
144
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
145
|
+
const meta = {
|
|
146
|
+
title: 'sdk/react-client/Passkeys',
|
|
147
|
+
render: Test,
|
|
148
|
+
decorators: [withClientProvider({ config }), withTheme()],
|
|
149
|
+
} satisfies Meta;
|
|
150
|
+
|
|
151
|
+
export default meta;
|
|
152
|
+
|
|
153
|
+
type Story = StoryObj<typeof meta>;
|
|
154
|
+
|
|
155
|
+
export const Default: Story = {};
|
|
@@ -6,7 +6,6 @@ import { renderHook } from '@testing-library/react';
|
|
|
6
6
|
import { describe, expect, test } from 'vitest';
|
|
7
7
|
|
|
8
8
|
import { createClient, createClientContextProvider } from '../testing/util';
|
|
9
|
-
|
|
10
9
|
import { useDevices } from './useDevices';
|
|
11
10
|
|
|
12
11
|
describe('useDevices', () => {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { useCallback, useEffect, useMemo, useReducer } from 'react';
|
|
6
6
|
|
|
7
7
|
import { type PublicKey } from '@dxos/client';
|
|
8
8
|
import {
|
|
@@ -24,9 +24,10 @@ interface InvitationReducerState {
|
|
|
24
24
|
status: Invitation.State; // TODO(burdon): Rename state.
|
|
25
25
|
haltedAt?: Invitation.State;
|
|
26
26
|
result: InvitationResult;
|
|
27
|
-
error?:
|
|
27
|
+
error?: Error;
|
|
28
28
|
id?: string;
|
|
29
29
|
multiUse?: boolean;
|
|
30
|
+
shareable?: boolean;
|
|
30
31
|
invitationCode?: string;
|
|
31
32
|
authCode?: string;
|
|
32
33
|
authMethod?: Invitation.AuthMethod;
|
|
@@ -72,7 +73,7 @@ export type InvitationStatus = {
|
|
|
72
73
|
multiUse?: boolean;
|
|
73
74
|
shareable?: boolean;
|
|
74
75
|
result: InvitationResult;
|
|
75
|
-
error?:
|
|
76
|
+
error?: Error;
|
|
76
77
|
cancel(): void;
|
|
77
78
|
// TODO(wittjosiah): Remove?
|
|
78
79
|
connect(observable: CancellableInvitationObservable): void;
|
|
@@ -82,11 +83,11 @@ export type InvitationStatus = {
|
|
|
82
83
|
// Without private key, the invitation code cannot be created.
|
|
83
84
|
// These invitations are only available to be accepted but not shared.
|
|
84
85
|
const isShareableInvitation = (invitation: Invitation) =>
|
|
85
|
-
invitation.authMethod !== Invitation.AuthMethod.KNOWN_PUBLIC_KEY || invitation.guestKeypair?.privateKey;
|
|
86
|
+
invitation.authMethod !== Invitation.AuthMethod.KNOWN_PUBLIC_KEY || !!invitation.guestKeypair?.privateKey;
|
|
86
87
|
|
|
87
88
|
export const useInvitationStatus = (observable?: CancellableInvitationObservable): InvitationStatus => {
|
|
88
|
-
const [state, dispatch] = useReducer
|
|
89
|
-
(prev, action) => {
|
|
89
|
+
const [state, dispatch] = useReducer(
|
|
90
|
+
(prev: InvitationReducerState, action: InvitationAction): InvitationReducerState => {
|
|
90
91
|
log('useInvitationStatus', { action });
|
|
91
92
|
const invitationProps =
|
|
92
93
|
'invitation' in action
|
|
@@ -115,10 +116,10 @@ export const useInvitationStatus = (observable?: CancellableInvitationObservable
|
|
|
115
116
|
action.status === Invitation.State.TIMEOUT) && {
|
|
116
117
|
haltedAt: typeof prev.haltedAt === 'undefined' ? action.haltedAt : prev.haltedAt,
|
|
117
118
|
}),
|
|
118
|
-
}
|
|
119
|
+
};
|
|
119
120
|
},
|
|
120
121
|
null,
|
|
121
|
-
(_arg: null) => {
|
|
122
|
+
(_arg: null): InvitationReducerState => {
|
|
122
123
|
const invitation = observable?.get();
|
|
123
124
|
return {
|
|
124
125
|
status: Invitation.State.INIT,
|
|
@@ -2,36 +2,37 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@
|
|
6
|
-
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
7
6
|
import React from 'react';
|
|
8
7
|
|
|
8
|
+
import { Obj } from '@dxos/echo';
|
|
9
9
|
import { Input } from '@dxos/react-ui';
|
|
10
|
-
import {
|
|
11
|
-
import { withTheme } from '@dxos/
|
|
10
|
+
import { JsonHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
11
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
12
12
|
|
|
13
13
|
import { useClient } from '../client';
|
|
14
14
|
import { useSpace } from '../echo';
|
|
15
|
-
|
|
16
15
|
import { type ClientRepeatedComponentProps, ClientRepeater } from './ClientRepeater';
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
const meta = {
|
|
19
18
|
title: 'sdk/react-client/ClientRepeater',
|
|
20
|
-
decorators: [withTheme],
|
|
21
|
-
}
|
|
19
|
+
decorators: [withTheme()],
|
|
20
|
+
} satisfies Meta<typeof Input>;
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
|
|
24
|
+
type Story = StoryObj<typeof meta>;
|
|
22
25
|
|
|
23
|
-
const JsonPanel = ({ value }: { value: any }) =>
|
|
24
|
-
<SyntaxHighlighter language='json'>{JSON.stringify(value, undefined, 2)}</SyntaxHighlighter>
|
|
25
|
-
);
|
|
26
|
+
const JsonPanel = ({ value }: { value: any }) => <JsonHighlighter data={value} />;
|
|
26
27
|
|
|
27
28
|
const ClientStory = () => {
|
|
28
29
|
const client = useClient();
|
|
29
30
|
return <JsonPanel value={client.toJSON()} />;
|
|
30
31
|
};
|
|
31
32
|
|
|
32
|
-
const ClientSpace = ({
|
|
33
|
+
const ClientSpace = ({ spaceId }: ClientRepeatedComponentProps) => {
|
|
33
34
|
const client = useClient();
|
|
34
|
-
const space = useSpace(
|
|
35
|
+
const space = useSpace(spaceId);
|
|
35
36
|
if (!space?.isOpen) {
|
|
36
37
|
return null;
|
|
37
38
|
}
|
|
@@ -42,7 +43,11 @@ const ClientSpace = ({ spaceKey }: ClientRepeatedComponentProps) => {
|
|
|
42
43
|
<Input.TextInput
|
|
43
44
|
placeholder='Name'
|
|
44
45
|
value={space.properties.name}
|
|
45
|
-
onChange={(event) =>
|
|
46
|
+
onChange={(event) =>
|
|
47
|
+
Obj.update(space.properties, (obj) => {
|
|
48
|
+
obj.name = event.target.value;
|
|
49
|
+
})
|
|
50
|
+
}
|
|
46
51
|
/>
|
|
47
52
|
</Input.Root>
|
|
48
53
|
<JsonPanel value={client.toJSON()} />
|
|
@@ -50,10 +55,10 @@ const ClientSpace = ({ spaceKey }: ClientRepeatedComponentProps) => {
|
|
|
50
55
|
);
|
|
51
56
|
};
|
|
52
57
|
|
|
53
|
-
export const Default = {
|
|
58
|
+
export const Default: Story = {
|
|
54
59
|
render: () => <ClientRepeater component={ClientStory} count={2} />,
|
|
55
60
|
};
|
|
56
61
|
|
|
57
|
-
export const Space = {
|
|
62
|
+
export const Space: Story = {
|
|
58
63
|
render: () => <ClientRepeater component={ClientSpace} count={2} createSpace />,
|
|
59
64
|
};
|
|
@@ -2,19 +2,19 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import React, { type FC,
|
|
5
|
+
import React, { type FC, useRef, useState } from 'react';
|
|
6
6
|
|
|
7
|
-
import { Client
|
|
7
|
+
import { Client } from '@dxos/client';
|
|
8
8
|
import { TestBuilder, performInvitation } from '@dxos/client/testing';
|
|
9
|
-
import { type
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
9
|
+
import { type Type } from '@dxos/echo';
|
|
10
|
+
import { random } from '@dxos/random';
|
|
11
|
+
import { useAsyncEffect } from '@dxos/react-hooks';
|
|
12
12
|
|
|
13
13
|
import { ClientProvider } from '../client';
|
|
14
|
-
|
|
14
|
+
import { type SpaceId } from '../echo';
|
|
15
15
|
import { type WithClientProviderProps } from './withClientProvider';
|
|
16
16
|
|
|
17
|
-
export type ClientRepeatedComponentProps = { id: number; count: number;
|
|
17
|
+
export type ClientRepeatedComponentProps = { id: number; count: number; spaceId?: SpaceId };
|
|
18
18
|
|
|
19
19
|
export type ClientRepeaterControlsProps = { clients: Client[] };
|
|
20
20
|
|
|
@@ -25,9 +25,9 @@ export type ClientRepeaterProps<P extends ClientRepeatedComponentProps> = {
|
|
|
25
25
|
controls?: FC<ClientRepeaterControlsProps>;
|
|
26
26
|
count?: number;
|
|
27
27
|
clients?: Client[];
|
|
28
|
-
types?:
|
|
28
|
+
types?: Type.AnyObj[];
|
|
29
29
|
args?: Omit<P, 'id' | 'count'>;
|
|
30
|
-
} & Pick<WithClientProviderProps, 'createIdentity' | 'createSpace' | '
|
|
30
|
+
} & Pick<WithClientProviderProps, 'createIdentity' | 'createSpace' | 'onCreateSpace'>;
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Utility component for Storybook stories which sets up clients for n peers.
|
|
@@ -47,41 +47,32 @@ export const ClientRepeater = <P extends ClientRepeatedComponentProps>(props: Cl
|
|
|
47
47
|
types,
|
|
48
48
|
createIdentity,
|
|
49
49
|
createSpace,
|
|
50
|
-
|
|
50
|
+
onCreateSpace,
|
|
51
51
|
} = props;
|
|
52
|
-
useEffect(() => {
|
|
53
|
-
registerSignalsRuntime();
|
|
54
|
-
}, []);
|
|
55
52
|
|
|
56
53
|
const [clients, setClients] = useState(props.clients ?? []);
|
|
57
|
-
const [
|
|
54
|
+
const [spaceId, setSpaceId] = useState<SpaceId>();
|
|
58
55
|
|
|
59
56
|
const testBuilder = useRef(new TestBuilder());
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
setClients(clients);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
return () => clearTimeout(timeout);
|
|
57
|
+
useAsyncEffect(async () => {
|
|
58
|
+
const clients = [...Array(count)].map(
|
|
59
|
+
(_) => new Client({ services: testBuilder.current.createLocalClientServices(), types }),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
await Promise.all(clients.map((client) => client.initialize()));
|
|
63
|
+
if (createIdentity || createSpace) {
|
|
64
|
+
await Promise.all(clients.map((client) => client.halo.createIdentity()));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (createSpace) {
|
|
68
|
+
const client = clients[0];
|
|
69
|
+
const space = await client.spaces.create({ name: random.commerce.productName() });
|
|
70
|
+
setSpaceId(space.id);
|
|
71
|
+
await onCreateSpace?.({ client, space }, {});
|
|
72
|
+
await Promise.all(clients.slice(1).flatMap((client) => performInvitation({ host: space, guest: client.spaces })));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
setClients(clients);
|
|
85
76
|
}, []);
|
|
86
77
|
|
|
87
78
|
if (clients.length === 0) {
|
|
@@ -93,7 +84,7 @@ export const ClientRepeater = <P extends ClientRepeatedComponentProps>(props: Cl
|
|
|
93
84
|
{Controls && <Controls clients={clients} />}
|
|
94
85
|
{clients.map((client, index) => (
|
|
95
86
|
<ClientProvider key={index} client={client}>
|
|
96
|
-
<Component id={index} count={clients.length}
|
|
87
|
+
<Component id={index} count={clients.length} spaceId={spaceId} {...{ ...props.args }} />
|
|
97
88
|
</ClientProvider>
|
|
98
89
|
))}
|
|
99
90
|
</>
|
package/src/testing/context.tsx
CHANGED
|
@@ -4,16 +4,17 @@
|
|
|
4
4
|
|
|
5
5
|
import { createContext, useContext } from 'react';
|
|
6
6
|
|
|
7
|
-
import { type Space } from '@dxos/client/echo';
|
|
7
|
+
import { type Space, type SpaceId } from '@dxos/client/echo';
|
|
8
8
|
import { raise } from '@dxos/debug';
|
|
9
9
|
|
|
10
10
|
export type ClientStory = {
|
|
11
|
+
index?: number;
|
|
12
|
+
spaceId?: SpaceId;
|
|
11
13
|
space?: Space;
|
|
12
14
|
};
|
|
13
15
|
|
|
14
|
-
// TODO(wittjosiah): Add to multi-client as well.
|
|
15
16
|
export const ClientStory = createContext<ClientStory | undefined>(undefined);
|
|
16
17
|
|
|
17
|
-
export const
|
|
18
|
+
export const useClientStory = (): ClientStory => {
|
|
18
19
|
return useContext(ClientStory) ?? raise(new Error('Missing ClientStory'));
|
|
19
20
|
};
|
package/src/testing/util.tsx
CHANGED
|
@@ -2,34 +2,33 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@
|
|
6
|
-
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
7
6
|
import React from 'react';
|
|
7
|
+
import { expect, within } from 'storybook/test';
|
|
8
8
|
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
|
-
import {
|
|
11
|
-
import { withLayout, withTheme } from '@dxos/
|
|
10
|
+
import { JsonHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
11
|
+
import { withLayout, withTheme, Loading } from '@dxos/react-ui/testing';
|
|
12
12
|
|
|
13
13
|
import { useClient } from '../client';
|
|
14
14
|
import { type Space, useSpaces } from '../echo';
|
|
15
|
-
|
|
16
15
|
import { type WithClientProviderProps, withClientProvider, withMultiClientProvider } from './withClientProvider';
|
|
17
16
|
|
|
18
17
|
const SpaceInfo = ({ space }: { space: Space }) => {
|
|
19
18
|
const name = space.isOpen ? space.properties.name : '';
|
|
20
|
-
return <
|
|
19
|
+
return <JsonHighlighter data={{ id: space.id, name }} />;
|
|
21
20
|
};
|
|
22
21
|
|
|
23
|
-
const
|
|
22
|
+
const DefaultStory = () => {
|
|
24
23
|
const client = useClient();
|
|
25
24
|
const spaces = useSpaces();
|
|
26
25
|
if (!client) {
|
|
27
|
-
return
|
|
26
|
+
return <Loading data={{ client: !!client }} />;
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
return (
|
|
31
|
-
<div className='flex flex-col
|
|
32
|
-
<
|
|
30
|
+
<div className='flex flex-col divide-y divide-separator border border-separator'>
|
|
31
|
+
<JsonHighlighter data={client.toJSON()} />
|
|
33
32
|
{spaces.map((space) => (
|
|
34
33
|
<SpaceInfo key={space.id} space={space} />
|
|
35
34
|
))}
|
|
@@ -37,10 +36,16 @@ const Test = () => {
|
|
|
37
36
|
);
|
|
38
37
|
};
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
const meta = {
|
|
41
40
|
title: 'sdk/react-client/withClientProvider',
|
|
42
|
-
render:
|
|
43
|
-
|
|
41
|
+
render: DefaultStory,
|
|
42
|
+
decorators: [withTheme()],
|
|
43
|
+
tags: ['test'],
|
|
44
|
+
} satisfies Meta<typeof DefaultStory>;
|
|
45
|
+
|
|
46
|
+
export default meta;
|
|
47
|
+
|
|
48
|
+
type Story = StoryObj<typeof meta>;
|
|
44
49
|
|
|
45
50
|
const clientProps: WithClientProviderProps = {
|
|
46
51
|
createIdentity: true,
|
|
@@ -48,19 +53,27 @@ const clientProps: WithClientProviderProps = {
|
|
|
48
53
|
onInitialized: async (client) => {
|
|
49
54
|
log.info('onInitialized', { client });
|
|
50
55
|
},
|
|
51
|
-
|
|
52
|
-
log.info('
|
|
56
|
+
onCreateSpace: async ({ client, space }) => {
|
|
57
|
+
log.info('onCreateSpace', { client, space });
|
|
53
58
|
},
|
|
54
59
|
};
|
|
55
60
|
|
|
56
|
-
export const Default = {
|
|
57
|
-
decorators: [withClientProvider(clientProps)
|
|
61
|
+
export const Default: Story = {
|
|
62
|
+
decorators: [withClientProvider(clientProps)],
|
|
63
|
+
parameters: {
|
|
64
|
+
layout: 'centered',
|
|
65
|
+
},
|
|
66
|
+
play: async ({ canvasElement }) => {
|
|
67
|
+
const canvas = within(canvasElement);
|
|
68
|
+
const initializedText = await canvas.findByText(/"initialized": true/i, {}, { timeout: 30_000 });
|
|
69
|
+
await expect(initializedText).toBeVisible();
|
|
70
|
+
},
|
|
58
71
|
};
|
|
59
72
|
|
|
60
|
-
export const Multiple = {
|
|
73
|
+
export const Multiple: Story = {
|
|
61
74
|
decorators: [
|
|
75
|
+
withTheme(),
|
|
76
|
+
withLayout({ classNames: 'grid grid-cols-3' }),
|
|
62
77
|
withMultiClientProvider({ ...clientProps, numClients: 3 }),
|
|
63
|
-
withTheme,
|
|
64
|
-
withLayout({ classNames: ['flex gap-2'] }),
|
|
65
78
|
],
|
|
66
79
|
};
|