From 15c22863d71dedd5817bca8d57780c1f15d5ce4f Mon Sep 17 00:00:00 2001 From: Chewbacca Date: Thu, 10 Nov 2022 11:56:36 -0500 Subject: [PATCH] Fetch relationships everytime we fetch a single chat --- app/soapbox/jest/test-helpers.tsx | 4 +- app/soapbox/queries/__tests__/chats.test.ts | 20 +++- .../queries/__tests__/relationships.test.ts | 107 ++++++++++++++++++ app/soapbox/queries/chats.ts | 8 +- app/soapbox/queries/relationships.ts | 25 ++++ 5 files changed, 157 insertions(+), 7 deletions(-) create mode 100644 app/soapbox/queries/__tests__/relationships.test.ts create mode 100644 app/soapbox/queries/relationships.ts diff --git a/app/soapbox/jest/test-helpers.tsx b/app/soapbox/jest/test-helpers.tsx index b5c4db700..ad0e22b0e 100644 --- a/app/soapbox/jest/test-helpers.tsx +++ b/app/soapbox/jest/test-helpers.tsx @@ -36,7 +36,9 @@ const TestApp: FC = ({ children, storeProps, routerProps = {} }) => { let store: ReturnType; let appState = rootState; - if (storeProps) { + if (storeProps && typeof storeProps.getState !== 'undefined') { // storeProps is a store + store = storeProps; + } else if (storeProps) { // storeProps is state appState = merge(rootState, storeProps); store = createTestStore(appState); } else { diff --git a/app/soapbox/queries/__tests__/chats.test.ts b/app/soapbox/queries/__tests__/chats.test.ts index 58decdd18..7640fd511 100644 --- a/app/soapbox/queries/__tests__/chats.test.ts +++ b/app/soapbox/queries/__tests__/chats.test.ts @@ -3,8 +3,9 @@ import sumBy from 'lodash/sumBy'; import { useEffect } from 'react'; import { __stub } from 'soapbox/api'; -import { mockStore, queryClient, renderHook, rootState, waitFor } from 'soapbox/jest/test-helpers'; +import { createTestStore, mockStore, queryClient, renderHook, rootState, waitFor } from 'soapbox/jest/test-helpers'; import { normalizeRelationship } from 'soapbox/normalizers'; +import { Store } from 'soapbox/store'; import { flattenPages } from 'soapbox/utils/queries'; import { IAccount } from '../accounts'; @@ -179,7 +180,7 @@ describe('useChats', () => { describe('with a successful request', () => { beforeEach(() => { - store = mockStore(ImmutableMap()); + store = mockStore(rootState); __stub((mock) => { mock.onGet('/api/v1/pleroma/chats') @@ -218,7 +219,13 @@ describe('useChats', () => { }); describe('useChat()', () => { + const relationshipId = '123'; + let store: Store; + beforeEach(() => { + const state = rootState; + store = createTestStore(state); + queryClient.clear(); }); @@ -226,14 +233,21 @@ describe('useChat()', () => { beforeEach(() => { __stub((mock) => { mock.onGet(`/api/v1/pleroma/chats/${chat.id}`).reply(200, chat); + mock + .onGet(`/api/v1/accounts/relationships?id[]=${chat.account.id}`) + .reply(200, [normalizeRelationship({ id: relationshipId, blocked_by: true })]); }); }); it('is successful', async () => { - const { result } = renderHook(() => useChat(chat.id)); + expect(store.getState().relationships.getIn([relationshipId, 'blocked_by'])).toBeUndefined(); + + const { result } = renderHook(() => useChat(chat.id), undefined, store); await waitFor(() => expect(result.current.isFetching).toBe(false)); + expect(store.getState().relationships.getIn([relationshipId, 'id'])).toBe(relationshipId); + expect(store.getState().relationships.getIn([relationshipId, 'blocked_by'])).toBe(true); expect(result.current.data.id).toBe(chat.id); }); }); diff --git a/app/soapbox/queries/__tests__/relationships.test.ts b/app/soapbox/queries/__tests__/relationships.test.ts new file mode 100644 index 000000000..65b5e544f --- /dev/null +++ b/app/soapbox/queries/__tests__/relationships.test.ts @@ -0,0 +1,107 @@ +import { useEffect } from 'react'; + +import { __stub } from 'soapbox/api'; +import { createTestStore, queryClient, renderHook, rootState, waitFor } from 'soapbox/jest/test-helpers'; +import { normalizeRelationship } from 'soapbox/normalizers'; +import { Store } from 'soapbox/store'; + +import { useFetchRelationships } from '../relationships'; + +describe('useFetchRelationships()', () => { + let store: Store; + + beforeEach(() => { + const state = rootState; + store = createTestStore(state); + + queryClient.clear(); + }); + + describe('with a successful query', () => { + describe('with one relationship', () => { + const id = '123'; + + beforeEach(() => { + __stub((mock) => { + mock + .onGet(`/api/v1/accounts/relationships?id[]=${id}`) + .reply(200, [normalizeRelationship({ id, blocked_by: true })]); + }); + }); + + it('is successful', async() => { + const { result } = renderHook(() => { + const fetchRelationships = useFetchRelationships(); + + useEffect(() => { + fetchRelationships.mutate({ accountIds: [id] }); + }, []); + + return fetchRelationships; + }, undefined, store); + + await waitFor(() => expect(result.current.isLoading).toBe(false)); + + expect(store.getState().relationships.size).toBe(1); + expect(store.getState().relationships.getIn([id, 'id'])).toBe(id); + expect(store.getState().relationships.getIn([id, 'blocked_by'])).toBe(true); + }); + }); + + describe('with multiple relationships', () => { + const ids = ['123', '456']; + + beforeEach(() => { + __stub((mock) => { + mock + .onGet(`/api/v1/accounts/relationships?id[]=${ids[0]}&id[]=${ids[1]}`) + .reply(200, ids.map((id) => normalizeRelationship({ id, blocked_by: true }))); + }); + }); + + it('is successful', async() => { + const { result } = renderHook(() => { + const fetchRelationships = useFetchRelationships(); + + useEffect(() => { + fetchRelationships.mutate({ accountIds: ids }); + }, []); + + return fetchRelationships; + }, undefined, store); + + await waitFor(() => expect(result.current.isLoading).toBe(false)); + + expect(store.getState().relationships.size).toBe(2); + expect(store.getState().relationships.getIn([ids[0], 'id'])).toBe(ids[0]); + expect(store.getState().relationships.getIn([ids[1], 'id'])).toBe(ids[1]); + }); + }); + }); + + describe('with an unsuccessful query', () => { + const id = '123'; + + beforeEach(() => { + __stub((mock) => { + mock.onGet(`/api/v1/accounts/relationships?id[]=${id}`).networkError(); + }); + }); + + it('is successful', async() => { + const { result } = renderHook(() => { + const fetchRelationships = useFetchRelationships(); + + useEffect(() => { + fetchRelationships.mutate({ accountIds: [id] }); + }, []); + + return fetchRelationships; + }, undefined, store); + + await waitFor(() => expect(result.current.isLoading).toBe(false)); + + expect(result.current.error).toBeDefined(); + }); + }); +}); diff --git a/app/soapbox/queries/chats.ts b/app/soapbox/queries/chats.ts index 1336f90ee..d98101e44 100644 --- a/app/soapbox/queries/chats.ts +++ b/app/soapbox/queries/chats.ts @@ -1,7 +1,6 @@ import { InfiniteData, useInfiniteQuery, useMutation, useQuery } from '@tanstack/react-query'; import sumBy from 'lodash/sumBy'; -import { fetchRelationships } from 'soapbox/actions/accounts'; import { importFetchedAccount, importFetchedAccounts } from 'soapbox/actions/importer'; import snackbar from 'soapbox/actions/snackbar'; import { getNextLink } from 'soapbox/api'; @@ -13,6 +12,7 @@ import { normalizeChatMessage } from 'soapbox/normalizers'; import { flattenPages, PaginatedResult, updatePageItem } from 'soapbox/utils/queries'; import { queryClient } from './client'; +import { useFetchRelationships } from './relationships'; import type { IAccount } from './accounts'; @@ -129,6 +129,7 @@ const useChats = (search?: string) => { const dispatch = useAppDispatch(); const features = useFeatures(); const { setUnreadChatsCount } = useStatContext(); + const fetchRelationships = useFetchRelationships(); const getChats = async (pageParam?: any): Promise> => { const endpoint = features.chatsV2 ? '/api/v2/pleroma/chats' : '/api/v1/pleroma/chats'; @@ -147,7 +148,7 @@ const useChats = (search?: string) => { setUnreadChatsCount(Number(response.headers['x-unread-messages-count']) || sumBy(data, (chat) => chat.unread)); // Set the relationships to these users in the redux store. - dispatch(fetchRelationships(data.map((item) => item.account.id))); + fetchRelationships.mutate({ accountIds: data.map((item) => item.account.id) }); dispatch(importFetchedAccounts(data.map((item) => item.account))); return { @@ -183,12 +184,13 @@ const useChats = (search?: string) => { const useChat = (chatId?: string) => { const api = useApi(); const dispatch = useAppDispatch(); + const fetchRelationships = useFetchRelationships(); const getChat = async () => { if (chatId) { const { data } = await api.get(`/api/v1/pleroma/chats/${chatId}`); - dispatch(fetchRelationships([data.account.id])); + fetchRelationships.mutate({ accountIds: [data.account.id] }); dispatch(importFetchedAccount(data.account)); return data; diff --git a/app/soapbox/queries/relationships.ts b/app/soapbox/queries/relationships.ts new file mode 100644 index 000000000..bbe306d3c --- /dev/null +++ b/app/soapbox/queries/relationships.ts @@ -0,0 +1,25 @@ +import { useMutation } from '@tanstack/react-query'; +import { AxiosError } from 'axios'; + +import { fetchRelationshipsFail, fetchRelationshipsSuccess } from 'soapbox/actions/accounts'; +import { useApi, useAppDispatch } from 'soapbox/hooks'; + +const useFetchRelationships = () => { + const api = useApi(); + const dispatch = useAppDispatch(); + + return useMutation(({ accountIds }: { accountIds: string[]}) => { + const ids = accountIds.map((id) => `id[]=${id}`).join('&'); + + return api.get(`/api/v1/accounts/relationships?${ids}`); + }, { + onSuccess(response) { + dispatch(fetchRelationshipsSuccess(response.data)); + }, + onError(error: AxiosError) { + dispatch(fetchRelationshipsFail(error)); + }, + }); +}; + +export { useFetchRelationships }; \ No newline at end of file