Skip to content

Commit

Permalink
Merge pull request #259 from docknetwork/fix/update-credential-list-o…
Browse files Browse the repository at this point in the history
…n-network-update

fix: update credential list when network is switched
  • Loading branch information
Jayteekay authored May 9, 2024
2 parents f1a7ffb + 4c98b9f commit e2c283d
Show file tree
Hide file tree
Showing 5 changed files with 248 additions and 80 deletions.
146 changes: 146 additions & 0 deletions packages/react-native/lib/documentsHooks.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { useDocument, useDocuments } from './documentsHooks';
import { WalletEvents } from '@docknetwork/wallet-sdk-wasm/src/modules/wallet';
import { getWallet } from './wallet';
import { act, renderHook } from '@testing-library/react-hooks';

const mockDocument = { id: 'mock-document-id' };

const mockWallet = {
getDocumentById: jest.fn(() => Promise.resolve(mockDocument)),
getDocumentsByType: jest.fn(() => Promise.resolve([])),
eventManager: {
on: jest.fn(),
removeListener: jest.fn(),
},
};

jest.mock('./wallet', () => ({
getWallet: jest.fn(() => mockWallet),
}));

describe('useDocument', () => {

beforeEach(() => {
getWallet.mockReturnValue(mockWallet);
mockWallet.getDocumentById.mockReset();
mockWallet.eventManager.on.mockReset();
mockWallet.eventManager.removeListener.mockReset();
});

it('should fetch the document by documentId', async () => {
const documentId = 'document-id';
const document = { id: documentId, content: 'content' };
mockWallet.getDocumentById.mockResolvedValue(document);

const { result, waitFor } = renderHook(() =>
useDocument(documentId),
);

await waitFor(() => expect(getWallet).toHaveBeenCalled())
expect(mockWallet.getDocumentById).toHaveBeenCalledWith(documentId);
expect(result.current).toEqual(document);
});

it('should fetch the document when documentUpdated event is emitted', async () => {
const documentId = 'document-id';
const initialDocument = { id: documentId, content: 'content' };
const updatedDocument = { id: documentId, content: 'updated content' };
mockWallet.getDocumentById
.mockResolvedValueOnce(initialDocument)
.mockResolvedValueOnce(updatedDocument);

const { result, waitFor } = renderHook(() =>
useDocument(documentId),
);

act(() => {
mockWallet.eventManager.on.mock.calls[0][1](updatedDocument);
});

await waitFor(() => expect(mockWallet.getDocumentById).toHaveBeenCalledTimes(2));
expect(result.current).toEqual(updatedDocument);
});

it('should fetch the document when documentAdded event is emitted', async () => {
const documentId = 'document-id';
const initialDocument = null;
const newDocument = { id: documentId, content: 'added content' };
mockWallet.getDocumentById
.mockResolvedValueOnce(initialDocument)
.mockResolvedValueOnce(newDocument);

const { result, waitFor } = renderHook(() =>
useDocument(documentId),
);

act(() => {
mockWallet.eventManager.on.mock.calls[0][1](newDocument);
});

await waitFor(() => expect(mockWallet.getDocumentById).toHaveBeenCalledTimes(2));
expect(result.current).toEqual(newDocument);
});

it('should fetch the document when documentRemoved event is emitted', async () => {
const documentId = 'document-id';
const initialDocument = { id: documentId, content: 'content' };
const newDocument = { id: documentId, content: 'updated content' };
mockWallet.getDocumentById
.mockResolvedValueOnce(initialDocument)
.mockResolvedValueOnce(newDocument);

const { result, waitFor } = renderHook(() =>
useDocument(documentId),
);

act(() => {
mockWallet.eventManager.on.mock.calls[0][1](newDocument);
});

await waitFor(() => expect(mockWallet.getDocumentById).toHaveBeenCalledTimes(2));
expect(result.current).toEqual(newDocument);
});

});

describe('useDocuments', () => {
beforeEach(() => {
getWallet.mockReturnValue(mockWallet);
mockWallet.getDocumentById.mockReset();
mockWallet.eventManager.on.mockReset();
mockWallet.eventManager.removeListener.mockReset();
});

it('should fetch the document correctly', async () => {
const type = 'type1';
const documents = [
{ id: 'doc1', type },
{ id: 'doc2', type },
];
mockWallet.getDocumentsByType.mockResolvedValue(documents);

const { result, waitFor } = renderHook(() => useDocuments({ type }));

await waitFor(() => expect(mockWallet.getDocumentsByType).toHaveBeenCalledWith(type));
expect(result.current.documents).toEqual(documents);
expect(result.current.loading).toEqual(false);
});
it('should refetch documents on networkUpdated event', async () => {
const { waitFor } = renderHook(() =>
useDocuments({ type: 'mockType' }),
);
await mockWallet.eventManager.on.mock.calls[2][1]();

expect(mockWallet.getDocumentsByType).toHaveBeenCalledWith('mockType');
});

it('should force refetch documents on networkUpdated event if type is not set', async () => {
const { waitFor } = renderHook(() =>
useDocuments(),
);
mockWallet.getDocumentsByType.mockReset();
await mockWallet.eventManager.on.mock.calls[3][1]();

expect(mockWallet.getDocumentsByType).toHaveBeenCalledTimes(1);
});
});
77 changes: 77 additions & 0 deletions packages/react-native/lib/documentsHooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import {WalletEvents} from '@docknetwork/wallet-sdk-wasm/src/modules/wallet';
import {useCallback, useEffect, useState} from 'react';
import {getWallet} from './wallet';

const useEventListener = (eventManager, eventNames, listener) => {
useEffect(() => {
eventNames.forEach(eventName => eventManager.on(eventName, listener));
return () =>
eventNames.forEach(eventName =>
eventManager.removeListener(eventName, listener),
);
}, [eventManager, eventNames, listener]);
};

const events = [
WalletEvents.documentAdded,
WalletEvents.documentRemoved,
WalletEvents.documentUpdated,
];

export function useDocument(id) {
const [document, setDocument] = useState(null);

const refetchDocument = useCallback(
async updatedDoc => {
if (updatedDoc.id !== id) return;
const doc = await getWallet().getDocumentById(id);
setDocument(doc);
},
[id],
);

useEffect(() => {
getWallet().getDocumentById(id).then(setDocument);
}, [id]);

useEventListener(getWallet().eventManager, events, refetchDocument);

return document;
}

export function useDocuments({type = null} = {}) {
const [documents, setDocuments] = useState([]);
const [loading, setLoading] = useState(true);

const fetchDocuments = useCallback(
async (updatedDoc, forceFetch = false) => {
console.log('fetching documents', updatedDoc, forceFetch);
if (
forceFetch ||
updatedDoc?.type === type ||
updatedDoc?.type?.includes(type)
) {
const docs = await getWallet().getDocumentsByType(type);
setDocuments(docs);
setLoading(false);
}
},
[type],
);

useEffect(() => {
fetchDocuments(null, true);
}, [fetchDocuments, setLoading]);

useEventListener(getWallet().eventManager, events, fetchDocuments);
useEventListener(
getWallet().eventManager,
[WalletEvents.networkUpdated],
async () => fetchDocuments(null, true),
);

return {
documents,
loading,
};
}
72 changes: 3 additions & 69 deletions packages/react-native/lib/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,12 @@ export const WalletSDKContext = React.createContext<WalletSDKContextProps>({
});

setStorage(AsyncStorage);

export {useAccounts};
export {useDIDManagement};
export {useCredentialUtils, useCredentialStatus};
export {useDocument, useDocuments} from './documentsHooks';

export function getStorage() {
return AsyncStorage;
}
Expand Down Expand Up @@ -103,75 +106,6 @@ export function useAccount(address) {
onDelete,
};
}

const useEventListener = (eventManager, eventNames, listener) => {
useEffect(() => {
eventNames.forEach(eventName => eventManager.on(eventName, listener));
return () =>
eventNames.forEach(eventName =>
eventManager.removeListener(eventName, listener),
);
}, [eventManager, eventNames, listener]);
};

const events = [
WalletEvents.documentAdded,
WalletEvents.documentRemoved,
WalletEvents.documentUpdated,
];

export function useDocument(id) {
const [document, setDocument] = useState(null);

const refetchDocument = useCallback(
async updatedDoc => {
if (updatedDoc.id !== id) return;
const doc = await getWallet().getDocumentById(id);
setDocument(doc);
},
[id],
);

useEffect(() => {
getWallet().getDocumentById(id).then(setDocument);
}, [id]);

useEventListener(getWallet().eventManager, events, refetchDocument);

return document;
}

export function useDocuments({type}) {
const [documents, setDocuments] = useState([]);
const [loading, setLoading] = useState(true);

const fetchDocuments = useCallback(
async (updatedDoc, forceFetch = false) => {
if (
forceFetch ||
updatedDoc?.type === type ||
updatedDoc?.type?.includes(type)
) {
const docs = await getWallet().getDocumentsByType(type);
setDocuments(docs);
setLoading(false);
}
},
[type],
);

useEffect(() => {
fetchDocuments(null, true);
}, [fetchDocuments, setLoading]);

useEventListener(getWallet().eventManager, events, fetchDocuments);

return {
documents,
loading,
};
}

export function useWallet() {
return useContext(WalletSDKContext);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"ts-node": "^10.9.1",
"typescript": "^5.0.4",
"@testing-library/react-hooks": "^8.0.0",
"react-test-renderer": "17.0.2",
"react-test-renderer": "18.2.0",
"react": "^18.2.0",
"@types/react": "^18.0.24",
"react-dom": "^17.0.2",
Expand Down
31 changes: 21 additions & 10 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -13201,11 +13201,16 @@ react-is@^16.13.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==

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

react-is@^18.2.0:
version "18.3.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e"
integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==

react-native-keychain@^8.0.0:
version "8.1.2"
resolved "https://registry.yarnpkg.com/react-native-keychain/-/react-native-keychain-8.1.2.tgz#34291ae472878e5124d081211af5ede7d810e64f"
Expand All @@ -13226,23 +13231,22 @@ react-native-webview@^11.4.3:
escape-string-regexp "2.0.0"
invariant "2.2.4"

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

react-test-renderer@17.0.2:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-17.0.2.tgz#4cd4ae5ef1ad5670fc0ef776e8cc7e1231d9866c"
integrity sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ==
react-test-renderer@18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.2.0.tgz#1dd912bd908ff26da5b9fca4fd1c489b9523d37e"
integrity sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA==
dependencies:
object-assign "^4.1.1"
react-is "^17.0.2"
react-shallow-renderer "^16.13.1"
scheduler "^0.20.2"
react-is "^18.2.0"
react-shallow-renderer "^16.15.0"
scheduler "^0.23.0"

react@^17.0.2:
version "17.0.2"
Expand Down Expand Up @@ -13904,6 +13908,13 @@ scheduler@^0.20.2:
loose-envify "^1.1.0"
object-assign "^4.1.1"

scheduler@^0.23.0:
version "0.23.2"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3"
integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==
dependencies:
loose-envify "^1.1.0"

schema-utils@^2.6.5:
version "2.7.1"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7"
Expand Down

0 comments on commit e2c283d

Please sign in to comment.