diff --git a/app/mobile/src/context/useCardContext.hook.js b/app/mobile/src/context/useCardContext.hook.js
index 88c224f2..0b58c239 100644
--- a/app/mobile/src/context/useCardContext.hook.js
+++ b/app/mobile/src/context/useCardContext.hook.js
@@ -143,16 +143,26 @@ export function useCardContext() {
for (let card of delta) {
if (card.data) {
const item = setCardItem(card);
- const entry = cards.current.get(card.id) || { card: {}, channels: new Map() };
+ const entry = cards.current.get(card.id) || { card: { cardId: card.id }, channels: new Map() };
const { profileRevision, detailRevision } = entry.card;
if (item.profileRevision !== profileRevision) {
+ if (item.profile) {
+ entry.card.profile = item.profile;
+ }
+ else {
+ entry.card.profile = await getCardProfile(server, token, card.id);
+ }
entry.card.profileRevision = item.profileRevision;
- entry.card.profile = await getCardProfile(server, token, card.id);
await store.actions.setCardItemProfile(guid, card.id, entry.card.profileRevision, entry.card.profile);
}
if (item.detailRevision !== detailRevision) {
+ if (item.detail) {
+ entry.card.detail = item.detail;
+ }
+ else {
+ entry.card.detail = await getCardDetail(server, token, card.id);
+ }
entry.card.detailRevision = item.detailRevision;
- entry.card.detail = await getCardDetail(server, token, card.id);
await store.actions.setCardItemDetail(guid, card.id, entry.card.detailRevision, entry.card.detail);
}
if (entry.card.detail?.state === 'connected' && !entry.card.offsync) {
@@ -427,7 +437,6 @@ export function useCardContext() {
const { detail, profile } = cards.current.get(cardId) || {};
return await addFlag(profile?.node, profile?.guid, channelId, topicId);
},
-
getChannelNotifications: async (cardId, channelId) => {
const { detail, profile } = cards.current.get(cardId) || {};
const token = `${profile?.guid}.${detail?.token}`;
@@ -438,7 +447,6 @@ export function useCardContext() {
const token = `${profile?.guid}.${detail?.token}`;
return await setContactChannelNotifications(profile?.node, token, channelId, notify);
},
-
getTopicItems: async (cardId, channelId) => {
const { guid } = access.current;
return await store.actions.getCardChannelTopicItems(guid, cardId, channelId);
@@ -455,7 +463,6 @@ export function useCardContext() {
const { guid } = access.current;
return await store.actions.clearCardChannelTopicItems(guid, cardId, channelId);
},
-
setUnsealedChannelSubject: async (cardId, channelId, revision, unsealed) => {
const { guid } = access.current;
await store.actions.setCardChannelItemUnsealedDetail(guid, cardId, channelId, revision, unsealed);
diff --git a/app/mobile/test/Card.test.js b/app/mobile/test/Card.test.js
new file mode 100644
index 00000000..29f05dcc
--- /dev/null
+++ b/app/mobile/test/Card.test.js
@@ -0,0 +1,186 @@
+import React, { useState, useEffect, useContext } from 'react';
+import { View, Text } from 'react-native';
+import { useTestStoreContext } from './useTestStoreContext.hook';
+import { prettyDOM } from '@testing-library/dom';
+import {render, act, screen, waitFor, fireEvent} from '@testing-library/react-native';
+import { CardContextProvider, CardContext } from 'context/CardContext';
+import * as fetchUtil from 'api/fetchUtil';
+
+function CardView() {
+ const [renderCount, setRenderCount] = useState(0);
+ const card = useContext(CardContext);
+ const [cards, setCards] = useState([]);
+
+ useEffect(() => {
+ setRenderCount(renderCount + 1);
+ const rendered = [];
+ card.state.cards.forEach((value) => {
+ rendered.push({ value.card.profile.handle });
+ });
+ setCards(rendered);
+ }, [card.state]);
+
+ return (
+
+ { cards }
+
+ );
+}
+
+function CardTestApp() {
+ return (
+
+
+
+ )
+}
+
+const realUseContext = React.useContext;
+const realFetchWithTimeout = fetchUtil.fetchWithTimeout;
+const realFetchWithCustomTimeout = fetchUtil.fetchWithCustomTimeout;
+
+let fetchCards;
+let fetchDetail;
+let fetchProfile;
+beforeEach(() => {
+ fetchCards = [];
+
+ const mockUseContext = jest.fn().mockImplementation((ctx) => {
+ return useTestStoreContext();
+ });
+ React.useContext = mockUseContext;
+
+ const mockFetch = jest.fn().mockImplementation((url, options) => {
+ console.log(url);
+
+ if (url.startsWith('https://test.org/contact/cards?agent')) {
+ return Promise.resolve({
+ json: () => Promise.resolve(fetchCards)
+ });
+ }
+ if (url.startsWith('https://test.org/contact/cards/000a/profile?agent')) {
+ return Promise.resolve({
+ json: () => Promise.resolve(fetchProfile)
+ });
+ }
+ if (url.startsWith('https://test.org/contact/cards/000a/detail?agent')) {
+ return Promise.resolve({
+ json: () => Promise.resolve(fetchDetail)
+ });
+ }
+ else {
+ return Promise.resolve({
+ json: () => Promise.resolve([])
+ });
+ }
+ });
+ fetchUtil.fetchWithTimeout = mockFetch;
+ fetchUtil.fetchWithCustomTimeout = mockFetch;
+
+});
+
+afterEach(() => {
+ React.useContext = realUseContext;
+ fetchUtil.fetchWithTimeout = realFetchWithTimeout;
+ fetchUtil.fetchWithCustomTimeout = realFetchWithCustomTimeout;
+});
+
+test('add, update, and remove', async () => {
+
+ render()
+
+ await act(async () => {
+ const card = screen.getByTestId('card').props.card;
+ await card.actions.setSession({ guid: 'abc', server: 'test.org', token: '123' });
+ await card.actions.setRevision(1);
+ });
+
+ await waitFor(async () => {
+ expect(screen.getByTestId('card').props.children).toHaveLength(0);
+ });
+
+ fetchCards = [{
+ id: '000a',
+ revision: 1,
+ data: {
+ detailRevision: 2,
+ profileRevision: 3,
+ notifiedProfile: 3,
+ notifiedArticle: 5,
+ notifiedChannel: 6,
+ notifiedView: 7,
+ cardDetail: { status: 'connected', statusUpdate: 136, token: '01ab', },
+ cardProfile: { guid: '01ab23', handle: 'test1', name: 'tester', imageSet: false,
+ seal: 'abc', version: '1.1.1', node: 'test.org' },
+ },
+ }];
+
+ await act(async () => {
+ const card = screen.getByTestId('card').props.card;
+ await card.actions.setRevision(2);
+ });
+
+ await waitFor(async () => {
+ expect(screen.getByTestId('card').props.children).toHaveLength(1);
+ expect(screen.getByTestId('000a').props.children).toBe('test1');
+ });
+
+ fetchCards = [{
+ id: '000a',
+ revision: 2,
+ data: {
+ detailRevision: 3,
+ profileRevision: 4,
+ notifiedProfile: 3,
+ notifiedArticle: 5,
+ notifiedChannel: 6,
+ notifiedView: 7,
+ },
+ }];
+
+ fetchProfile = {
+ guid: '01ab23',
+ handle: 'test2',
+ };
+
+ fetchDetail = {
+ status: 'confirmed',
+ }
+
+ await act(async () => {
+ const card = screen.getByTestId('card').props.card;
+ await card.actions.setRevision(3);
+ });
+
+ await waitFor(async () => {
+ expect(screen.getByTestId('card').props.children).toHaveLength(1);
+ expect(screen.getByTestId('000a').props.children).toBe('test2');
+ });
+
+ fetchCards = [{
+ id: '000a',
+ revision: 3,
+ }];
+
+ await act(async () => {
+ const card = screen.getByTestId('card').props.card;
+ await card.actions.setRevision(3);
+ });
+
+ await waitFor(async () => {
+ expect(screen.getByTestId('card').props.children).toHaveLength(1);
+ });
+
+ await act(async () => {
+ const card = screen.getByTestId('card').props.card;
+ await card.actions.setRevision(4);
+ });
+
+ await waitFor(async () => {
+ expect(screen.getByTestId('card').props.children).toHaveLength(0);
+ });
+
+});
+
+
+