Skip to content

Commit e2c283d

Browse files
authored
Merge pull request #259 from docknetwork/fix/update-credential-list-on-network-update
fix: update credential list when network is switched
2 parents f1a7ffb + 4c98b9f commit e2c283d

File tree

5 files changed

+248
-80
lines changed

5 files changed

+248
-80
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import { useDocument, useDocuments } from './documentsHooks';
2+
import { WalletEvents } from '@docknetwork/wallet-sdk-wasm/src/modules/wallet';
3+
import { getWallet } from './wallet';
4+
import { act, renderHook } from '@testing-library/react-hooks';
5+
6+
const mockDocument = { id: 'mock-document-id' };
7+
8+
const mockWallet = {
9+
getDocumentById: jest.fn(() => Promise.resolve(mockDocument)),
10+
getDocumentsByType: jest.fn(() => Promise.resolve([])),
11+
eventManager: {
12+
on: jest.fn(),
13+
removeListener: jest.fn(),
14+
},
15+
};
16+
17+
jest.mock('./wallet', () => ({
18+
getWallet: jest.fn(() => mockWallet),
19+
}));
20+
21+
describe('useDocument', () => {
22+
23+
beforeEach(() => {
24+
getWallet.mockReturnValue(mockWallet);
25+
mockWallet.getDocumentById.mockReset();
26+
mockWallet.eventManager.on.mockReset();
27+
mockWallet.eventManager.removeListener.mockReset();
28+
});
29+
30+
it('should fetch the document by documentId', async () => {
31+
const documentId = 'document-id';
32+
const document = { id: documentId, content: 'content' };
33+
mockWallet.getDocumentById.mockResolvedValue(document);
34+
35+
const { result, waitFor } = renderHook(() =>
36+
useDocument(documentId),
37+
);
38+
39+
await waitFor(() => expect(getWallet).toHaveBeenCalled())
40+
expect(mockWallet.getDocumentById).toHaveBeenCalledWith(documentId);
41+
expect(result.current).toEqual(document);
42+
});
43+
44+
it('should fetch the document when documentUpdated event is emitted', async () => {
45+
const documentId = 'document-id';
46+
const initialDocument = { id: documentId, content: 'content' };
47+
const updatedDocument = { id: documentId, content: 'updated content' };
48+
mockWallet.getDocumentById
49+
.mockResolvedValueOnce(initialDocument)
50+
.mockResolvedValueOnce(updatedDocument);
51+
52+
const { result, waitFor } = renderHook(() =>
53+
useDocument(documentId),
54+
);
55+
56+
act(() => {
57+
mockWallet.eventManager.on.mock.calls[0][1](updatedDocument);
58+
});
59+
60+
await waitFor(() => expect(mockWallet.getDocumentById).toHaveBeenCalledTimes(2));
61+
expect(result.current).toEqual(updatedDocument);
62+
});
63+
64+
it('should fetch the document when documentAdded event is emitted', async () => {
65+
const documentId = 'document-id';
66+
const initialDocument = null;
67+
const newDocument = { id: documentId, content: 'added content' };
68+
mockWallet.getDocumentById
69+
.mockResolvedValueOnce(initialDocument)
70+
.mockResolvedValueOnce(newDocument);
71+
72+
const { result, waitFor } = renderHook(() =>
73+
useDocument(documentId),
74+
);
75+
76+
act(() => {
77+
mockWallet.eventManager.on.mock.calls[0][1](newDocument);
78+
});
79+
80+
await waitFor(() => expect(mockWallet.getDocumentById).toHaveBeenCalledTimes(2));
81+
expect(result.current).toEqual(newDocument);
82+
});
83+
84+
it('should fetch the document when documentRemoved event is emitted', async () => {
85+
const documentId = 'document-id';
86+
const initialDocument = { id: documentId, content: 'content' };
87+
const newDocument = { id: documentId, content: 'updated content' };
88+
mockWallet.getDocumentById
89+
.mockResolvedValueOnce(initialDocument)
90+
.mockResolvedValueOnce(newDocument);
91+
92+
const { result, waitFor } = renderHook(() =>
93+
useDocument(documentId),
94+
);
95+
96+
act(() => {
97+
mockWallet.eventManager.on.mock.calls[0][1](newDocument);
98+
});
99+
100+
await waitFor(() => expect(mockWallet.getDocumentById).toHaveBeenCalledTimes(2));
101+
expect(result.current).toEqual(newDocument);
102+
});
103+
104+
});
105+
106+
describe('useDocuments', () => {
107+
beforeEach(() => {
108+
getWallet.mockReturnValue(mockWallet);
109+
mockWallet.getDocumentById.mockReset();
110+
mockWallet.eventManager.on.mockReset();
111+
mockWallet.eventManager.removeListener.mockReset();
112+
});
113+
114+
it('should fetch the document correctly', async () => {
115+
const type = 'type1';
116+
const documents = [
117+
{ id: 'doc1', type },
118+
{ id: 'doc2', type },
119+
];
120+
mockWallet.getDocumentsByType.mockResolvedValue(documents);
121+
122+
const { result, waitFor } = renderHook(() => useDocuments({ type }));
123+
124+
await waitFor(() => expect(mockWallet.getDocumentsByType).toHaveBeenCalledWith(type));
125+
expect(result.current.documents).toEqual(documents);
126+
expect(result.current.loading).toEqual(false);
127+
});
128+
it('should refetch documents on networkUpdated event', async () => {
129+
const { waitFor } = renderHook(() =>
130+
useDocuments({ type: 'mockType' }),
131+
);
132+
await mockWallet.eventManager.on.mock.calls[2][1]();
133+
134+
expect(mockWallet.getDocumentsByType).toHaveBeenCalledWith('mockType');
135+
});
136+
137+
it('should force refetch documents on networkUpdated event if type is not set', async () => {
138+
const { waitFor } = renderHook(() =>
139+
useDocuments(),
140+
);
141+
mockWallet.getDocumentsByType.mockReset();
142+
await mockWallet.eventManager.on.mock.calls[3][1]();
143+
144+
expect(mockWallet.getDocumentsByType).toHaveBeenCalledTimes(1);
145+
});
146+
});
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import {WalletEvents} from '@docknetwork/wallet-sdk-wasm/src/modules/wallet';
2+
import {useCallback, useEffect, useState} from 'react';
3+
import {getWallet} from './wallet';
4+
5+
const useEventListener = (eventManager, eventNames, listener) => {
6+
useEffect(() => {
7+
eventNames.forEach(eventName => eventManager.on(eventName, listener));
8+
return () =>
9+
eventNames.forEach(eventName =>
10+
eventManager.removeListener(eventName, listener),
11+
);
12+
}, [eventManager, eventNames, listener]);
13+
};
14+
15+
const events = [
16+
WalletEvents.documentAdded,
17+
WalletEvents.documentRemoved,
18+
WalletEvents.documentUpdated,
19+
];
20+
21+
export function useDocument(id) {
22+
const [document, setDocument] = useState(null);
23+
24+
const refetchDocument = useCallback(
25+
async updatedDoc => {
26+
if (updatedDoc.id !== id) return;
27+
const doc = await getWallet().getDocumentById(id);
28+
setDocument(doc);
29+
},
30+
[id],
31+
);
32+
33+
useEffect(() => {
34+
getWallet().getDocumentById(id).then(setDocument);
35+
}, [id]);
36+
37+
useEventListener(getWallet().eventManager, events, refetchDocument);
38+
39+
return document;
40+
}
41+
42+
export function useDocuments({type = null} = {}) {
43+
const [documents, setDocuments] = useState([]);
44+
const [loading, setLoading] = useState(true);
45+
46+
const fetchDocuments = useCallback(
47+
async (updatedDoc, forceFetch = false) => {
48+
console.log('fetching documents', updatedDoc, forceFetch);
49+
if (
50+
forceFetch ||
51+
updatedDoc?.type === type ||
52+
updatedDoc?.type?.includes(type)
53+
) {
54+
const docs = await getWallet().getDocumentsByType(type);
55+
setDocuments(docs);
56+
setLoading(false);
57+
}
58+
},
59+
[type],
60+
);
61+
62+
useEffect(() => {
63+
fetchDocuments(null, true);
64+
}, [fetchDocuments, setLoading]);
65+
66+
useEventListener(getWallet().eventManager, events, fetchDocuments);
67+
useEventListener(
68+
getWallet().eventManager,
69+
[WalletEvents.networkUpdated],
70+
async () => fetchDocuments(null, true),
71+
);
72+
73+
return {
74+
documents,
75+
loading,
76+
};
77+
}

packages/react-native/lib/index.tsx

Lines changed: 3 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,12 @@ export const WalletSDKContext = React.createContext<WalletSDKContextProps>({
5353
});
5454

5555
setStorage(AsyncStorage);
56+
5657
export {useAccounts};
5758
export {useDIDManagement};
5859
export {useCredentialUtils, useCredentialStatus};
60+
export {useDocument, useDocuments} from './documentsHooks';
61+
5962
export function getStorage() {
6063
return AsyncStorage;
6164
}
@@ -103,75 +106,6 @@ export function useAccount(address) {
103106
onDelete,
104107
};
105108
}
106-
107-
const useEventListener = (eventManager, eventNames, listener) => {
108-
useEffect(() => {
109-
eventNames.forEach(eventName => eventManager.on(eventName, listener));
110-
return () =>
111-
eventNames.forEach(eventName =>
112-
eventManager.removeListener(eventName, listener),
113-
);
114-
}, [eventManager, eventNames, listener]);
115-
};
116-
117-
const events = [
118-
WalletEvents.documentAdded,
119-
WalletEvents.documentRemoved,
120-
WalletEvents.documentUpdated,
121-
];
122-
123-
export function useDocument(id) {
124-
const [document, setDocument] = useState(null);
125-
126-
const refetchDocument = useCallback(
127-
async updatedDoc => {
128-
if (updatedDoc.id !== id) return;
129-
const doc = await getWallet().getDocumentById(id);
130-
setDocument(doc);
131-
},
132-
[id],
133-
);
134-
135-
useEffect(() => {
136-
getWallet().getDocumentById(id).then(setDocument);
137-
}, [id]);
138-
139-
useEventListener(getWallet().eventManager, events, refetchDocument);
140-
141-
return document;
142-
}
143-
144-
export function useDocuments({type}) {
145-
const [documents, setDocuments] = useState([]);
146-
const [loading, setLoading] = useState(true);
147-
148-
const fetchDocuments = useCallback(
149-
async (updatedDoc, forceFetch = false) => {
150-
if (
151-
forceFetch ||
152-
updatedDoc?.type === type ||
153-
updatedDoc?.type?.includes(type)
154-
) {
155-
const docs = await getWallet().getDocumentsByType(type);
156-
setDocuments(docs);
157-
setLoading(false);
158-
}
159-
},
160-
[type],
161-
);
162-
163-
useEffect(() => {
164-
fetchDocuments(null, true);
165-
}, [fetchDocuments, setLoading]);
166-
167-
useEventListener(getWallet().eventManager, events, fetchDocuments);
168-
169-
return {
170-
documents,
171-
loading,
172-
};
173-
}
174-
175109
export function useWallet() {
176110
return useContext(WalletSDKContext);
177111
}

packages/react-native/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
"ts-node": "^10.9.1",
3737
"typescript": "^5.0.4",
3838
"@testing-library/react-hooks": "^8.0.0",
39-
"react-test-renderer": "17.0.2",
39+
"react-test-renderer": "18.2.0",
4040
"react": "^18.2.0",
4141
"@types/react": "^18.0.24",
4242
"react-dom": "^17.0.2",

yarn.lock

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13201,11 +13201,16 @@ react-is@^16.13.1:
1320113201
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
1320213202
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
1320313203

13204-
react-is@^17.0.1, react-is@^17.0.2:
13204+
react-is@^17.0.1:
1320513205
version "17.0.2"
1320613206
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
1320713207
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
1320813208

13209+
react-is@^18.2.0:
13210+
version "18.3.1"
13211+
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e"
13212+
integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==
13213+
1320913214
react-native-keychain@^8.0.0:
1321013215
version "8.1.2"
1321113216
resolved "https://registry.yarnpkg.com/react-native-keychain/-/react-native-keychain-8.1.2.tgz#34291ae472878e5124d081211af5ede7d810e64f"
@@ -13226,23 +13231,22 @@ react-native-webview@^11.4.3:
1322613231
escape-string-regexp "2.0.0"
1322713232
invariant "2.2.4"
1322813233

13229-
react-shallow-renderer@^16.13.1:
13234+
react-shallow-renderer@^16.15.0:
1323013235
version "16.15.0"
1323113236
resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457"
1323213237
integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==
1323313238
dependencies:
1323413239
object-assign "^4.1.1"
1323513240
react-is "^16.12.0 || ^17.0.0 || ^18.0.0"
1323613241

13237-
react-test-renderer@17.0.2:
13238-
version "17.0.2"
13239-
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-17.0.2.tgz#4cd4ae5ef1ad5670fc0ef776e8cc7e1231d9866c"
13240-
integrity sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ==
13242+
react-test-renderer@18.2.0:
13243+
version "18.2.0"
13244+
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.2.0.tgz#1dd912bd908ff26da5b9fca4fd1c489b9523d37e"
13245+
integrity sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA==
1324113246
dependencies:
13242-
object-assign "^4.1.1"
13243-
react-is "^17.0.2"
13244-
react-shallow-renderer "^16.13.1"
13245-
scheduler "^0.20.2"
13247+
react-is "^18.2.0"
13248+
react-shallow-renderer "^16.15.0"
13249+
scheduler "^0.23.0"
1324613250

1324713251
react@^17.0.2:
1324813252
version "17.0.2"
@@ -13904,6 +13908,13 @@ scheduler@^0.20.2:
1390413908
loose-envify "^1.1.0"
1390513909
object-assign "^4.1.1"
1390613910

13911+
scheduler@^0.23.0:
13912+
version "0.23.2"
13913+
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3"
13914+
integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==
13915+
dependencies:
13916+
loose-envify "^1.1.0"
13917+
1390713918
schema-utils@^2.6.5:
1390813919
version "2.7.1"
1390913920
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7"

0 commit comments

Comments
 (0)