diff --git a/src/actions/__tests__/celebrateComments.ts b/src/actions/__tests__/celebrateComments.ts index bb59c0455a..2e7286b709 100644 --- a/src/actions/__tests__/celebrateComments.ts +++ b/src/actions/__tests__/celebrateComments.ts @@ -1,4 +1,6 @@ -import configureStore from 'redux-mock-store'; +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import configureStore, { MockStore } from 'redux-mock-store'; import thunk from 'redux-thunk'; import { @@ -20,43 +22,51 @@ import { REQUESTS } from '../../api/routes'; import { celebrateCommentsSelector } from '../../selectors/celebrateComments'; import { trackActionWithoutData } from '../analytics'; import { ACTIONS } from '../../constants'; +import { mockFragment } from '../../../testUtils/apolloMockClient'; +import { CELEBRATE_ITEM_FRAGMENT } from '../../components/CelebrateItem/queries'; +import { GetCelebrateFeed_community_celebrationItems_nodes as CelebrateItem } from '../../containers/CelebrateFeed/__generated__/GetCelebrateFeed'; +import { Person } from '../../reducers/people'; jest.mock('../api'); jest.mock('../../selectors/celebrateComments'); jest.mock('../analytics'); +const event = mockFragment(CELEBRATE_ITEM_FRAGMENT); + const orgId = '645654'; -const event = { id: '80890', organization: { id: orgId } }; -const comment = { pagination: { page: 2, hasNextPage: true } }; -const callApiResponse = { result: 'hello world' }; -const celebrateComments = { someProp: 'asdfasdfasdf' }; +const comment = { + pagination: { page: 2, hasNextPage: true }, +}; const baseQuery = { orgId, eventId: event.id }; -const me = { id: 'myId' }; +const me: Person = { id: 'myId' }; const include = 'organization_celebration_item,person'; + +const auth = { person: me }; +const celebrateComments = { comments: [comment] }; + +const callApiResponse = { type: 'call API', result: 'hello world' }; const trackActionResult = { type: 'tracked action' }; const mockStore = configureStore([thunk]); -// @ts-ignore -let store; - -// @ts-ignore -callApi.mockReturnValue(() => callApiResponse); -// @ts-ignore -celebrateCommentsSelector.mockReturnValue(comment); -// @ts-ignore -trackActionWithoutData.mockReturnValue(trackActionResult); +let store: MockStore; beforeEach(() => { - store = mockStore({ auth: { person: me }, celebrateComments }); + store = mockStore({ auth, celebrateComments }); + (callApi as jest.Mock).mockReturnValue(callApiResponse); + ((celebrateCommentsSelector as unknown) as jest.Mock).mockReturnValue( + comment, + ); + (trackActionWithoutData as jest.Mock).mockReturnValue(trackActionResult); }); -describe('getCelebrateCommentsNextPage', () => { - // @ts-ignore - let response; +let response: any; +describe('getCelebrateCommentsNextPage', () => { beforeEach( - // @ts-ignore - () => (response = store.dispatch(getCelebrateCommentsNextPage(event))), + () => + (response = store.dispatch( + getCelebrateCommentsNextPage(event.id, orgId), + )), ); it('should call selector', () => { @@ -78,17 +88,18 @@ describe('getCelebrateCommentsNextPage', () => { }); it('should return api response', () => { - // @ts-ignore expect(response).toEqual(callApiResponse); + expect(store.getActions()).toEqual([callApiResponse]); }); }); describe('reloadCelebrateComments', () => { - // @ts-ignore - let response; - - // @ts-ignore - beforeEach(() => (response = store.dispatch(reloadCelebrateComments(event)))); + beforeEach( + () => + (response = store.dispatch( + reloadCelebrateComments(event.id, orgId), + )), + ); it('should callApi with no page', () => { expect(callApi).toHaveBeenCalledWith(REQUESTS.GET_CELEBRATE_COMMENTS, { @@ -98,20 +109,19 @@ describe('reloadCelebrateComments', () => { }); it('should return api response', () => { - // @ts-ignore expect(response).toEqual(callApiResponse); + expect(store.getActions()).toEqual([callApiResponse]); }); }); describe('createCelebrateComment', () => { const content = 'this is a comment'; - // @ts-ignore - let response; beforeEach( async () => - // @ts-ignore - (response = await store.dispatch(createCelebrateComment(event, content))), + (response = await store.dispatch( + createCelebrateComment(event.id, orgId, content), + )), ); it('should callApi with no page', () => { @@ -123,7 +133,6 @@ describe('createCelebrateComment', () => { }); it('should return api response', () => { - // @ts-ignore expect(response).toEqual(callApiResponse); }); @@ -131,21 +140,17 @@ describe('createCelebrateComment', () => { expect(trackActionWithoutData).toHaveBeenCalledWith( ACTIONS.CELEBRATE_COMMENT_ADDED, ); - // @ts-ignore - expect(store.getActions()).toEqual([trackActionResult]); + expect(store.getActions()).toEqual([callApiResponse, trackActionResult]); }); }); describe('deleteCelebrateComment', () => { const item = { id: 'comment1' }; - // @ts-ignore - let response; beforeEach( async () => - // @ts-ignore - (response = await store.dispatch( - deleteCelebrateComment(event.organization.id, event, item), + (response = await store.dispatch( + deleteCelebrateComment(orgId, event.id, item.id), )), ); @@ -157,7 +162,6 @@ describe('deleteCelebrateComment', () => { }); it('should return api response', () => { - // @ts-ignore expect(response).toEqual(callApiResponse); }); @@ -165,21 +169,19 @@ describe('deleteCelebrateComment', () => { expect(trackActionWithoutData).toHaveBeenCalledWith( ACTIONS.CELEBRATE_COMMENT_DELETED, ); - // @ts-ignore - expect(store.getActions()).toEqual([trackActionResult]); + expect(store.getActions()).toEqual([callApiResponse, trackActionResult]); }); }); describe('updateCelebrateComment', () => { const item = { id: 'comment1', organization_celebration_item: event }; const text = 'text'; - // @ts-ignore - let response; beforeEach( async () => - // @ts-ignore - (response = await store.dispatch(updateCelebrateComment(item, text))), + (response = await store.dispatch( + updateCelebrateComment(event.id, orgId, item.id, text), + )), ); it('should callApi', () => { @@ -198,7 +200,6 @@ describe('updateCelebrateComment', () => { }); it('should return api response', () => { - // @ts-ignore expect(response).toEqual(callApiResponse); }); @@ -206,8 +207,7 @@ describe('updateCelebrateComment', () => { expect(trackActionWithoutData).toHaveBeenCalledWith( ACTIONS.CELEBRATE_COMMENT_EDITED, ); - // @ts-ignore - expect(store.getActions()).toEqual([trackActionResult]); + expect(store.getActions()).toEqual([callApiResponse, trackActionResult]); }); }); @@ -219,9 +219,9 @@ it('resetCelebrateEditingComment', () => { it('setCelebrateEditingComment', () => { const comment = { id: 'test' }; - // @ts-ignore - store.dispatch(setCelebrateEditingComment(comment.id)); - // @ts-ignore + + store.dispatch(setCelebrateEditingComment(comment.id)); + expect(store.getActions()).toEqual([ { type: RESET_CELEBRATE_EDITING_COMMENT, diff --git a/src/actions/__tests__/celebration.ts b/src/actions/__tests__/celebration.ts index 6a492ffcb0..fdd2f6d958 100644 --- a/src/actions/__tests__/celebration.ts +++ b/src/actions/__tests__/celebration.ts @@ -3,19 +3,9 @@ import configureStore, { MockStore } from 'redux-mock-store'; import thunk from 'redux-thunk'; -import { - getGroupCelebrateFeed, - toggleLike, - getGroupCelebrateFeedUnread, - reloadGroupCelebrateFeed, -} from '../celebration'; +import { toggleLike } from '../celebration'; import callApi from '../api'; import { REQUESTS } from '../../api/routes'; -import { - DEFAULT_PAGE_LIMIT, - RESET_CELEBRATION_PAGINATION, -} from '../../constants'; -import { GET_CELEBRATE_INCLUDE } from '../../utils/actions'; jest.mock('../api'); @@ -28,118 +18,8 @@ let store: MockStore; const currentPage = 0; -describe('getGroupCelebrateFeed', () => { - beforeEach(() => { - store = createStore(); - (callApi as jest.Mock).mockReturnValue(apiResult); - }); - - it('gets a page of celebrate feed', () => { - store = createStore({ - organizations: { - all: [ - { - id: orgId, - celebratePagination: { - hasNextPage: true, - page: currentPage, - }, - }, - ], - }, - }); - - store.dispatch(getGroupCelebrateFeed(orgId)); - - expect(callApi).toHaveBeenCalledWith(REQUESTS.GET_GROUP_CELEBRATE_FEED, { - page: { - limit: DEFAULT_PAGE_LIMIT, - offset: DEFAULT_PAGE_LIMIT * currentPage, - }, - orgId, - include: - 'subject_person.organizational_permissions,subject_person.contact_assignments', - }); - expect(store.getActions()).toEqual([apiResult]); - }); - - it('does not get celebrate items if there is no next page', () => { - store = createStore({ - organizations: { - all: [ - { - id: orgId, - celebratePagination: { - hasNextPage: false, - page: currentPage, - }, - }, - ], - }, - }); - - store.dispatch(getGroupCelebrateFeed(orgId)); - - expect(callApi).not.toHaveBeenCalled(); - expect(store.getActions()).toEqual([]); - }); -}); - -describe('reloadGroupCelebrateFeed', () => { - beforeEach(() => { - store = createStore({ - organizations: { - all: [ - { - id: orgId, - celebratePagination: { - hasNextPage: true, - page: currentPage, - }, - }, - ], - }, - }); - (callApi as jest.Mock).mockReturnValue(apiResult); - }); - - it('reloads feed', () => { - store.dispatch(reloadGroupCelebrateFeed(orgId)); - - expect(callApi).toHaveBeenCalledWith(REQUESTS.GET_GROUP_CELEBRATE_FEED, { - page: { - limit: DEFAULT_PAGE_LIMIT, - offset: DEFAULT_PAGE_LIMIT * currentPage, - }, - orgId, - include: - 'subject_person.organizational_permissions,subject_person.contact_assignments', - }); - expect(store.getActions()).toEqual([ - { type: RESET_CELEBRATION_PAGINATION, orgId }, - apiResult, - ]); - }); -}); - -describe('getGroupCelebrateFeedUnread', () => { - beforeEach(() => { - store = createStore({ organizations: { all: [{ id: orgId }] } }); - }); - - it('calls feed of unread items', () => { - store.dispatch(getGroupCelebrateFeedUnread(orgId)); - - expect(callApi).toHaveBeenCalledWith( - REQUESTS.GET_GROUP_CELEBRATE_FEED_UNREAD, - { - orgId, - filters: { has_unread_comments: true }, - include: GET_CELEBRATE_INCLUDE, - }, - ); - expect(store.getActions()).toEqual([apiResult]); - }); +beforeEach(() => { + (callApi as jest.Mock).mockReturnValue(apiResult); }); describe('toggleLike', () => { diff --git a/src/actions/__tests__/challenges.ts b/src/actions/__tests__/challenges.ts index b465bb7aa8..51f4886c32 100644 --- a/src/actions/__tests__/challenges.ts +++ b/src/actions/__tests__/challenges.ts @@ -13,7 +13,6 @@ import { getChallenge, } from '../challenges'; import { trackActionWithoutData } from '../analytics'; -import { reloadGroupCelebrateFeed } from '../celebration'; import { showNotificationPrompt } from '../notifications'; import callApi from '../api'; import { REQUESTS } from '../../api/routes'; @@ -42,7 +41,6 @@ const orgId = '123'; const apiResult = { type: 'done' }; const navigateResult = { type: 'has navigated' }; -const celebrateResult = { type: 'reloaded celebrate feed' }; const resetResult = { type: RESET_CHALLENGE_PAGINATION, orgId }; const trackActionResult = { type: 'track action' }; const showNotificationResult = { type: 'show notification prompt' }; @@ -74,8 +72,6 @@ beforeEach(() => { // @ts-ignore navigatePush.mockReturnValue(navigateResult); // @ts-ignore - reloadGroupCelebrateFeed.mockReturnValue(celebrateResult); - // @ts-ignore trackActionWithoutData.mockReturnValue(trackActionResult); // @ts-ignore showNotificationPrompt.mockReturnValue(showNotificationResult); @@ -165,7 +161,6 @@ describe('completeChallenge', () => { expect(trackActionWithoutData).toHaveBeenCalledWith( ACTIONS.CHALLENGE_COMPLETED, ); - expect(reloadGroupCelebrateFeed).toHaveBeenCalledWith(orgId); // @ts-ignore expect(store.getActions()).toEqual([ apiResult, @@ -173,7 +168,6 @@ describe('completeChallenge', () => { trackActionResult, resetResult, apiResult, - celebrateResult, ]); }); }); @@ -206,7 +200,6 @@ describe('joinChallenge', () => { expect(trackActionWithoutData).toHaveBeenCalledWith( ACTIONS.CHALLENGE_JOINED, ); - expect(reloadGroupCelebrateFeed).toHaveBeenCalledWith(orgId); // @ts-ignore expect(store.getActions()).toEqual([ apiResult, @@ -215,7 +208,6 @@ describe('joinChallenge', () => { trackActionResult, resetResult, apiResult, - celebrateResult, ]); }); }); diff --git a/src/actions/__tests__/interactions.ts b/src/actions/__tests__/interactions.ts index 7af7941452..b2eeb885c8 100644 --- a/src/actions/__tests__/interactions.ts +++ b/src/actions/__tests__/interactions.ts @@ -7,7 +7,6 @@ import { REQUESTS } from '../../api/routes'; import { trackActionWithoutData, trackAction } from '../analytics'; import { refreshImpact } from '../impact'; import { ACTIONS, INTERACTION_TYPES } from '../../constants'; -import { reloadGroupCelebrateFeed } from '../celebration'; import { reloadJourney } from '../journey'; // @ts-ignore @@ -47,7 +46,6 @@ const orgId = 2; const interaction = INTERACTION_TYPES.MHInteractionTypeGospelPresentation; const trackActionResult = { type: 'tracked action' }; const refreshImpactResult = { type: 'refreshed impact' }; -const celebrationFeedResult = { type: 'refreshed celebration feeed' }; const reloadJourneyResult = { type: 'reloaded journey' }; // @ts-ignore @@ -64,8 +62,6 @@ describe('add comment', () => { beforeEach(() => { // @ts-ignore trackAction.mockReturnValue(trackActionResult); - // @ts-ignore - reloadGroupCelebrateFeed.mockReturnValue(celebrationFeedResult); }); describe('without org', () => { @@ -119,7 +115,6 @@ describe('add comment', () => { trackActionResult, reloadJourneyResult, refreshImpactResult, - celebrationFeedResult, ]); }); }); @@ -183,7 +178,6 @@ describe('add comment', () => { trackActionResult, reloadJourneyResult, refreshImpactResult, - celebrationFeedResult, ]); }); diff --git a/src/actions/__tests__/notifications.ts b/src/actions/__tests__/notifications.ts index 271555b89e..8d10767ba0 100644 --- a/src/actions/__tests__/notifications.ts +++ b/src/actions/__tests__/notifications.ts @@ -37,7 +37,6 @@ import { navigateToCelebrateComments, } from '../navigation'; import { refreshCommunity } from '../organizations'; -import { reloadGroupCelebrateFeed } from '../celebration'; import { reloadGroupChallengeFeed } from '../challenges'; import { NOTIFICATION_OFF_SCREEN } from '../../containers/NotificationOffScreen'; import { NOTIFICATION_PRIMER_SCREEN } from '../../containers/NotificationPrimerScreen'; @@ -577,7 +576,6 @@ describe('askNotificationPermissions', () => { const getPersonResult = { type: LOAD_PERSON_DETAILS, person }; const navToPersonScreenResult = { type: 'navigated to person screen' }; const refreshCommunityResult = organization; - const reloadGroupCelebrateFeedResult = { type: 'reload celebrate feed' }; const reloadGroupChallengeFeedResult = { type: 'reload challenge feed' }; const navToCelebrateResult = { type: 'navigated to celebrate comments' }; const navToCommunityResult = { type: 'navigated to community' }; @@ -593,8 +591,6 @@ describe('askNotificationPermissions', () => { // @ts-ignore refreshCommunity.mockReturnValue(() => refreshCommunityResult); // @ts-ignore - reloadGroupCelebrateFeed.mockReturnValue(reloadGroupCelebrateFeedResult); - // @ts-ignore reloadGroupChallengeFeed.mockReturnValue(reloadGroupChallengeFeedResult); // @ts-ignore navigateToCelebrateComments.mockReturnValue(navToCelebrateResult); @@ -792,7 +788,6 @@ describe('askNotificationPermissions', () => { }); expect(refreshCommunity).toHaveBeenCalledWith(organization.id); - expect(reloadGroupCelebrateFeed).toHaveBeenCalledWith(organization.id); expect(navigateToCelebrateComments).toHaveBeenCalledWith( organization, celebration_item_id, @@ -807,7 +802,6 @@ describe('askNotificationPermissions', () => { }); expect(refreshCommunity).not.toHaveBeenCalled(); - expect(reloadGroupCelebrateFeed).not.toHaveBeenCalled(); expect(navigateToCelebrateComments).not.toHaveBeenCalled(); }); }); diff --git a/src/actions/__tests__/reportComments.ts b/src/actions/__tests__/reportComments.ts index 32d2105fdd..46a31bca3a 100644 --- a/src/actions/__tests__/reportComments.ts +++ b/src/actions/__tests__/reportComments.ts @@ -14,6 +14,7 @@ import { celebrateCommentsSelector } from '../../selectors/celebrateComments'; import { trackActionWithoutData } from '../analytics'; import { ACTIONS } from '../../constants'; import { formatApiDate } from '../../utils/common'; +import { CelebrateComment } from '../../reducers/celebrateComments'; jest.mock('../api'); jest.mock('../../selectors/celebrateComments'); @@ -40,7 +41,13 @@ beforeEach(() => { }); describe('report comments', () => { - const item = { id: 'comment1' }; + const item: CelebrateComment = { + id: 'comment1', + person: {}, + created_at: '2019-04-11T13:51:49.888', + updated_at: '2019-04-11T13:51:49.888', + content: '', + }; it('should callApi for report', async () => { const response = await store.dispatch(reportComment(orgId, item)); diff --git a/src/actions/__tests__/steps.ts b/src/actions/__tests__/steps.ts index 5d95ca451a..c830ba66b7 100644 --- a/src/actions/__tests__/steps.ts +++ b/src/actions/__tests__/steps.ts @@ -15,7 +15,6 @@ import { createCustomStep, deleteStepWithTracking, } from '../steps'; -import { reloadGroupCelebrateFeed } from '../celebration'; import { refreshImpact } from '../impact'; import { trackStepAdded, trackAction } from '../analytics'; import * as navigation from '../navigation'; @@ -510,7 +509,6 @@ describe('complete challenge', () => { const trackActionResult = { type: 'tracked action' }; const impactResponse = { type: 'test impact' }; - const celebrateResponse = { type: 'test celebrate' }; const screen = 'contact steps'; beforeEach(() => { @@ -540,8 +538,6 @@ describe('complete challenge', () => { callApi.mockReturnValue(() => Promise.resolve({ type: 'test api' })); // @ts-ignore refreshImpact.mockReturnValue(impactResponse); - // @ts-ignore - reloadGroupCelebrateFeed.mockReturnValue(celebrateResponse); // Call `onSetComplete` within the navigate push // @ts-ignore navigation.navigatePush = jest.fn((a, b) => { @@ -575,7 +571,6 @@ describe('complete challenge', () => { `${ACTIONS.STEP_COMPLETED.name} on ${screen} Screen`, { [ACTIONS.STEP_COMPLETED.key]: null }, ); - expect(reloadGroupCelebrateFeed).toHaveBeenCalledWith(stepOrgId); // @ts-ignore expect(store.getActions()).toEqual([ @@ -593,7 +588,6 @@ describe('complete challenge', () => { trackActionResult, { type: COMPLETED_STEP_COUNT, userId: receiverId }, impactResponse, - celebrateResponse, ]); }); @@ -624,7 +618,6 @@ describe('complete challenge', () => { challengeCompleteQuery, data, ); - expect(reloadGroupCelebrateFeed).not.toHaveBeenCalled(); // @ts-ignore expect(store.getActions()).toEqual([ { diff --git a/src/actions/celebrateComments.ts b/src/actions/celebrateComments.ts index 7b76b5c258..69bffea740 100644 --- a/src/actions/celebrateComments.ts +++ b/src/actions/celebrateComments.ts @@ -1,3 +1,6 @@ +import { ThunkDispatch } from 'redux-thunk'; +import { AnyAction } from 'redux'; + import { DEFAULT_PAGE_LIMIT, RESET_CELEBRATE_EDITING_COMMENT, @@ -6,14 +9,13 @@ import { import { celebrateCommentsSelector } from '../selectors/celebrateComments'; import { ACTIONS } from '../constants'; import { REQUESTS } from '../api/routes'; +import { CelebrateCommentsState } from '../reducers/celebrateComments'; import callApi from './api'; import { trackActionWithoutData } from './analytics'; -// @ts-ignore -export function setCelebrateEditingComment(commentId) { - // @ts-ignore - return dispatch => { +export function setCelebrateEditingComment(commentId: string) { + return (dispatch: ThunkDispatch<{}, null, AnyAction>) => { dispatch(resetCelebrateEditingComment()); dispatch({ type: SET_CELEBRATE_EDITING_COMMENT, commentId }); }; @@ -23,13 +25,14 @@ export function resetCelebrateEditingComment() { return { type: RESET_CELEBRATE_EDITING_COMMENT }; } -// @ts-ignore -export function getCelebrateCommentsNextPage(event) { - // @ts-ignore - return (dispatch, getState) => { +export function getCelebrateCommentsNextPage(eventId: string, orgId: string) { + return ( + dispatch: ThunkDispatch<{}, null, AnyAction>, + getState: () => { celebrateComments: CelebrateCommentsState }, + ) => { const { pagination } = celebrateCommentsSelector( { celebrateComments: getState().celebrateComments }, - { eventId: event.id }, + { eventId }, ); const { page, hasNextPage } = pagination @@ -41,7 +44,7 @@ export function getCelebrateCommentsNextPage(event) { } return dispatch( - getCelebrateComments(event, { + getCelebrateComments(eventId, orgId, { limit: DEFAULT_PAGE_LIMIT, offset: DEFAULT_PAGE_LIMIT * page, }), @@ -49,36 +52,39 @@ export function getCelebrateCommentsNextPage(event) { }; } -// @ts-ignore -export function reloadCelebrateComments(event) { - // @ts-ignore - return dispatch => dispatch(getCelebrateComments(event)); +export function reloadCelebrateComments(eventId: string, orgId: string) { + return (dispatch: ThunkDispatch<{}, null, AnyAction>) => + dispatch(getCelebrateComments(eventId, orgId)); } -// @ts-ignore -function getCelebrateComments(event, page) { - // @ts-ignore - return dispatch => +function getCelebrateComments( + eventId: string, + orgId: string, + page?: { limit: number; offset: number }, +) { + return (dispatch: ThunkDispatch<{}, null, AnyAction>) => dispatch( callApi(REQUESTS.GET_CELEBRATE_COMMENTS, { - orgId: event.organization.id, - eventId: event.id, + orgId, + eventId, page, include: 'organization_celebration_item,person', }), ); } -// @ts-ignore -export function createCelebrateComment(event, content) { - // @ts-ignore - return async dispatch => { +export function createCelebrateComment( + eventId: string, + orgId: string, + content: string, +) { + return async (dispatch: ThunkDispatch<{}, null, AnyAction>) => { const result = await dispatch( callApi( REQUESTS.CREATE_CELEBRATE_COMMENT, { - orgId: event.organization.id, - eventId: event.id, + orgId, + eventId, }, { data: { @@ -93,17 +99,21 @@ export function createCelebrateComment(event, content) { }; } -// @ts-ignore -export function updateCelebrateComment(item, content) { - // @ts-ignore - return async dispatch => { +//eslint-disable-next-line max-params +export function updateCelebrateComment( + eventId: string, + orgId: string, + commentId: string, + content: string, +) { + return async (dispatch: ThunkDispatch<{}, null, AnyAction>) => { const result = await dispatch( callApi( REQUESTS.UPDATE_CELEBRATE_COMMENT, { - orgId: item.organization_celebration_item.organization.id, - eventId: item.organization_celebration_item.id, - commentId: item.id, + orgId, + eventId, + commentId, }, { data: { attributes: { content } } }, ), @@ -114,15 +124,17 @@ export function updateCelebrateComment(item, content) { }; } -// @ts-ignore -export function deleteCelebrateComment(orgId, event, item) { - // @ts-ignore - return async dispatch => { +export function deleteCelebrateComment( + orgId: string, + eventId: string, + commentId: string, +) { + return async (dispatch: ThunkDispatch<{}, null, AnyAction>) => { const result = await dispatch( callApi(REQUESTS.DELETE_CELEBRATE_COMMENT, { orgId, - eventId: event.id, - commentId: item.id, + eventId, + commentId, }), ); diff --git a/src/actions/celebration.ts b/src/actions/celebration.ts index 376b6a9f7e..ff6ff32d31 100644 --- a/src/actions/celebration.ts +++ b/src/actions/celebration.ts @@ -1,51 +1,10 @@ import { AnyAction } from 'redux'; import { ThunkDispatch } from 'redux-thunk'; -import { - getFeed, - reloadFeed, - CELEBRATE, - GET_CELEBRATE_INCLUDE, -} from '../utils/actions'; import { REQUESTS } from '../api/routes'; -import { OrganizationsState } from '../reducers/organizations'; import callApi from './api'; -export function getGroupCelebrateFeedUnread(orgId: string) { - return (dispatch: ThunkDispatch<{}, null, AnyAction>) => - dispatch( - callApi(REQUESTS.GET_GROUP_CELEBRATE_FEED_UNREAD, { - orgId, - filters: { has_unread_comments: true }, - include: GET_CELEBRATE_INCLUDE, - }), - ); -} - -export function getGroupCelebrateFeed( - orgId: string, - personId: string | null = null, -) { - return ( - dispatch: ThunkDispatch< - { organizations: OrganizationsState }, - null, - AnyAction - >, - ) => dispatch(getFeed(CELEBRATE, orgId, personId)); -} - -export function reloadGroupCelebrateFeed(orgId: string) { - return ( - dispatch: ThunkDispatch< - { organizations: OrganizationsState }, - null, - AnyAction - >, - ) => dispatch(reloadFeed(CELEBRATE, orgId)); -} - export function toggleLike(eventId: string, liked: boolean, orgId?: string) { const request = orgId ? liked diff --git a/src/actions/challenges.ts b/src/actions/challenges.ts index ae8dd502cb..97e9f3f982 100644 --- a/src/actions/challenges.ts +++ b/src/actions/challenges.ts @@ -10,7 +10,6 @@ import { import { REQUESTS } from '../api/routes'; import callApi from './api'; -import { reloadGroupCelebrateFeed } from './celebration'; import { showNotificationPrompt } from './notifications'; import { navigatePush, navigateBack } from './navigation'; import { trackActionWithoutData } from './analytics'; @@ -55,8 +54,6 @@ export function completeChallenge(item, orgId) { ); dispatch(trackActionWithoutData(ACTIONS.CHALLENGE_COMPLETED)); dispatch(reloadGroupChallengeFeed(orgId)); - // After completing a challenge, reload the group celebrate feed with this new item - dispatch(reloadGroupCelebrateFeed(orgId)); }; } @@ -89,8 +86,6 @@ export function joinChallenge(item, orgId) { ); dispatch(trackActionWithoutData(ACTIONS.CHALLENGE_JOINED)); dispatch(reloadGroupChallengeFeed(orgId)); - // After joining a challenge, reload the group celebrate feed with this new item - dispatch(reloadGroupCelebrateFeed(orgId)); }; } diff --git a/src/actions/interactions.ts b/src/actions/interactions.ts index 3240b755af..a7f5a0fab0 100644 --- a/src/actions/interactions.ts +++ b/src/actions/interactions.ts @@ -6,7 +6,6 @@ import { REQUESTS } from '../api/routes'; import callApi from './api'; import { trackAction, trackActionWithoutData } from './analytics'; import { refreshImpact } from './impact'; -import { reloadGroupCelebrateFeed } from './celebration'; import { reloadJourney } from './journey'; export function addNewInteraction( @@ -78,7 +77,6 @@ export function addNewInteraction( ); dispatch(reloadJourney(personId, organizationId)); dispatch(refreshImpact(organizationId)); - dispatch(reloadGroupCelebrateFeed(organizationId)); return response; }; diff --git a/src/actions/notifications.ts b/src/actions/notifications.ts index ba9f3b3d23..34839329dc 100644 --- a/src/actions/notifications.ts +++ b/src/actions/notifications.ts @@ -21,7 +21,6 @@ import { REQUESTS } from '../api/routes'; import { refreshCommunity } from './organizations'; import { getPersonDetails, navToPersonScreen } from './person'; import { reloadGroupChallengeFeed } from './challenges'; -import { reloadGroupCelebrateFeed } from './celebration'; import { navigatePush, navigateBack, @@ -162,7 +161,6 @@ function handleNotification(notification) { case 'celebrate': if (organization_id) { const community = await dispatch(refreshCommunity(organization_id)); - await dispatch(reloadGroupCelebrateFeed(organization_id)); return dispatch( navigateToCelebrateComments(community, celebration_item_id), ); diff --git a/src/actions/steps.ts b/src/actions/steps.ts index a6412fa2a3..f8dd330947 100644 --- a/src/actions/steps.ts +++ b/src/actions/steps.ts @@ -21,7 +21,6 @@ import { refreshImpact } from './impact'; import { navigatePush } from './navigation'; import callApi from './api'; import { trackAction, trackStepAdded } from './analytics'; -import { reloadGroupCelebrateFeed } from './celebration'; // @ts-ignore export function getStepSuggestions(isMe, contactStageId) { @@ -194,10 +193,6 @@ function completeChallengeAPI(step) { dispatch(getMySteps()); dispatch(getContactSteps(receiverId, orgId)); - - if (orgId) { - dispatch(reloadGroupCelebrateFeed(orgId)); - } }; } diff --git a/src/components/CelebrateCommentBox/__tests__/CelebrateCommentBox.tsx b/src/components/CelebrateCommentBox/__tests__/CelebrateCommentBox.tsx index 8640679ebc..bf614507a1 100644 --- a/src/components/CelebrateCommentBox/__tests__/CelebrateCommentBox.tsx +++ b/src/components/CelebrateCommentBox/__tests__/CelebrateCommentBox.tsx @@ -2,21 +2,25 @@ import React from 'react'; import { fireEvent } from 'react-native-testing-library'; import { renderWithContext } from '../../../../testUtils'; +import { mockFragment } from '../../../../testUtils/apolloMockClient'; import { createCelebrateComment, resetCelebrateEditingComment, updateCelebrateComment, } from '../../../actions/celebrateComments'; -import { celebrationItemSelector } from '../../../selectors/celebration'; import { celebrateCommentsCommentSelector } from '../../../selectors/celebrateComments'; +import { Organization } from '../../../reducers/organizations'; +import { CELEBRATE_ITEM_FRAGMENT } from '../../CelebrateItem/queries'; +import { GetCelebrateFeed_community_celebrationItems_nodes as CelebrateItem } from '../../../containers/CelebrateFeed/__generated__/GetCelebrateFeed'; import CelebrateCommentBox from '..'; -jest.mock('../../../selectors/celebration'); jest.mock('../../../selectors/celebrateComments'); jest.mock('../../../actions/celebrateComments'); -const event = { text: 'some celebrate item', organization: { id: 'orgId' } }; +const organization: Organization = { id: '123' }; +const event = mockFragment(CELEBRATE_ITEM_FRAGMENT); + const createCelebrateCommentResult = { type: 'created comment' }; const updateCelebrateCommentResult = { type: 'update comment' }; const resetCelebrateEditingCommentResult = { type: 'reset editing' }; @@ -27,7 +31,6 @@ const editingComment = { }; const onAddComplete = jest.fn(); const initialState = { - organizations: [], celebrateComments: { editingCommentId: null, }, @@ -35,7 +38,11 @@ const initialState = { function render() { return renderWithContext( - , + , { initialState }, ); } @@ -49,7 +56,6 @@ beforeEach(() => { (resetCelebrateEditingComment as jest.Mock).mockReturnValue( resetCelebrateEditingCommentResult, ); - ((celebrationItemSelector as unknown) as jest.Mock).mockReturnValue(event); }); it('renders correctly', () => { @@ -68,7 +74,11 @@ describe('onSubmit', () => { await fireEvent(getByTestId('CelebrateCommentBox'), 'onSubmit', null, text); - expect(createCelebrateComment).toHaveBeenCalledWith(event, text); + expect(createCelebrateComment).toHaveBeenCalledWith( + event.id, + organization.id, + text, + ); expect(onAddComplete).toHaveBeenCalled(); }); }); @@ -78,12 +88,15 @@ it('renders editing correctly', () => { editingComment, ); - renderWithContext(, { - initialState: { - ...initialState, - celebrateComments: { editingCommentId: editingComment.id }, + renderWithContext( + , + { + initialState: { + ...initialState, + celebrateComments: { editingCommentId: editingComment.id }, + }, }, - }).snapshot(); + ).snapshot(); }); it('onCancel', () => { @@ -91,7 +104,7 @@ it('onCancel', () => { fireEvent(getByTestId('CelebrateCommentBox'), 'onCancel'); - expect(resetCelebrateEditingComment).toHaveBeenCalled(); + expect(resetCelebrateEditingComment).toHaveBeenCalledWith(); }); it('calls update', async () => { @@ -100,7 +113,7 @@ it('calls update', async () => { ); const { getByTestId } = renderWithContext( - , + , { initialState: { ...initialState, @@ -112,6 +125,11 @@ it('calls update', async () => { const text = 'test update'; await fireEvent(getByTestId('CelebrateCommentBox'), 'onSubmit', null, text); - expect(resetCelebrateEditingComment).toHaveBeenCalled(); - expect(updateCelebrateComment).toHaveBeenCalledWith(editingComment, text); + expect(resetCelebrateEditingComment).toHaveBeenCalledWith(); + expect(updateCelebrateComment).toHaveBeenCalledWith( + event.id, + organization.id, + editingComment.id, + text, + ); }); diff --git a/src/components/CelebrateCommentBox/index.tsx b/src/components/CelebrateCommentBox/index.tsx index 0f5fd6a40a..86893caef3 100644 --- a/src/components/CelebrateCommentBox/index.tsx +++ b/src/components/CelebrateCommentBox/index.tsx @@ -9,22 +9,27 @@ import { resetCelebrateEditingComment, updateCelebrateComment, } from '../../actions/celebrateComments'; -import { celebrationItemSelector } from '../../selectors/celebration'; import { celebrateCommentsCommentSelector } from '../../selectors/celebrateComments'; -import { OrganizationsState } from '../../reducers/organizations'; -import { CelebrateCommentsState } from '../../reducers/celebrateComments'; +import { + CelebrateCommentsState, + CelebrateComment, +} from '../../reducers/celebrateComments'; +import { GetCelebrateFeed_community_celebrationItems_nodes as CelebrateItem } from '../../containers/CelebrateFeed/__generated__/GetCelebrateFeed'; +import { Organization } from '../../reducers/organizations'; import styles from './styles'; interface CelebrateCommentBoxProps { - event: object; - editingComment?: object; + event: CelebrateItem; + organization: Organization; + editingComment?: CelebrateComment; onAddComplete?: () => void; dispatch: ThunkDispatch<{}, {}, AnyAction>; } const CelebrateCommentBox = ({ event, + organization, editingComment, onAddComplete, dispatch, @@ -35,9 +40,18 @@ const CelebrateCommentBox = ({ const submitComment = async (action: object, text: string) => { if (editingComment) { cancel(); - return dispatch(updateCelebrateComment(editingComment, text)); + return dispatch( + updateCelebrateComment( + event.id, + organization.id, + editingComment.id, + text, + ), + ); } - const results = await dispatch(createCelebrateComment(event, text)); + const results = await dispatch( + createCelebrateComment(event.id, organization.id, text), + ); onAddComplete && onAddComplete(); return results; }; @@ -61,24 +75,16 @@ const CelebrateCommentBox = ({ }; const mapStateToProps = ( { - organizations, celebrateComments, }: { - organizations: OrganizationsState; celebrateComments: CelebrateCommentsState; }, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - { event }: any, + { event }: { event: CelebrateItem }, ) => ({ editingComment: celebrateCommentsCommentSelector( { celebrateComments }, { eventId: event.id, commentId: celebrateComments.editingCommentId }, ), - event: - celebrationItemSelector( - { organizations }, - { eventId: event.id, organizationId: event.organization.id }, - ) || event, }); export default connect(mapStateToProps)(CelebrateCommentBox); diff --git a/src/components/CelebrateItem/__tests__/CelebrateItem.tsx b/src/components/CelebrateItem/__tests__/CelebrateItem.tsx index ab3725d5bc..592ad1e120 100644 --- a/src/components/CelebrateItem/__tests__/CelebrateItem.tsx +++ b/src/components/CelebrateItem/__tests__/CelebrateItem.tsx @@ -8,26 +8,34 @@ import { useMutation } from '@apollo/react-hooks'; import { trackActionWithoutData } from '../../../actions/analytics'; import { navigatePush } from '../../../actions/navigation'; import { renderWithContext } from '../../../../testUtils'; +import { mockFragment } from '../../../../testUtils/apolloMockClient'; import { CELEBRATEABLE_TYPES, GLOBAL_COMMUNITY_ID } from '../../../constants'; import { CELEBRATE_DETAIL_SCREEN } from '../../../containers/CelebrateDetailScreen'; import { CELEBRATE_EDIT_STORY_SCREEN } from '../../../containers/Groups/EditStoryScreen'; - -import CelebrateItem, { Event, DELETE_STORY, REPORT_STORY } from '..'; +import { Organization } from '../../../reducers/organizations'; +import { + GetCelebrateFeed_community_celebrationItems_nodes as CelebrateItemData, + GetCelebrateFeed_community_celebrationItems_nodes_subjectPerson as CelebrateItemPerson, +} from '../../../containers/CelebrateFeed/__generated__/GetCelebrateFeed'; +import { + CELEBRATE_ITEM_FRAGMENT, + CELEBRATE_ITEM_PERSON_FRAGMENT, +} from '../queries'; + +import CelebrateItem, { DELETE_STORY, REPORT_STORY } from '..'; jest.mock('../../../actions/analytics'); jest.mock('../../../actions/navigation'); -const myId = '123'; -const subjectPerson = { - id: '234', - first_name: 'John', - last_name: 'Smith', - full_name: 'John Smith', -}; -const globalOrg = { id: GLOBAL_COMMUNITY_ID }; -const organization = { id: '3' }; +const globalOrg: Organization = { id: GLOBAL_COMMUNITY_ID }; +const organization: Organization = { id: '3', name: 'Communidad' }; + +const event = mockFragment(CELEBRATE_ITEM_FRAGMENT); +const mePerson = mockFragment( + CELEBRATE_ITEM_PERSON_FRAGMENT, +); +const myId = mePerson.id; -const date = '2019-08-21T12:00:00.000'; MockDate.set('2019-08-21 12:00:00', 300); let onRefresh = jest.fn(); @@ -36,19 +44,9 @@ let onClearNotification = jest.fn(); const trackActionResult = { type: 'tracked plain action' }; const navigatePushResult = { type: 'navigate push' }; -const event: Event = { - id: '222', - changed_attribute_value: date, - subject_person: subjectPerson, - subject_person_name: subjectPerson.full_name, - celebrateable_id: '2', - celebrateable_type: CELEBRATEABLE_TYPES.completedStep, - organization, - object_description: 'Celebration', -}; -const storyEvent: Event = { +const storyEvent: CelebrateItemData = { ...event, - celebrateable_type: CELEBRATEABLE_TYPES.story, + celebrateableType: CELEBRATEABLE_TYPES.story, }; const initialState = { auth: { person: { id: myId } } }; @@ -67,6 +65,7 @@ describe('global community', () => { event={event} onRefresh={onRefresh} organization={globalOrg} + namePressable={false} />, { initialState }, ).snapshot(); @@ -78,6 +77,7 @@ describe('global community', () => { event={event} onRefresh={onRefresh} organization={globalOrg} + namePressable={false} onClearNotification={onClearNotification} />, { initialState }, @@ -90,6 +90,7 @@ describe('global community', () => { event={storyEvent} onRefresh={onRefresh} organization={globalOrg} + namePressable={false} />, { initialState }, ).snapshot(); @@ -103,6 +104,7 @@ describe('Community', () => { event={event} onRefresh={onRefresh} organization={organization} + namePressable={false} />, { initialState }, ).snapshot(); @@ -115,6 +117,7 @@ describe('Community', () => { onRefresh={onRefresh} organization={organization} onClearNotification={onClearNotification} + namePressable={false} />, { initialState }, ).snapshot(); @@ -126,6 +129,7 @@ describe('Community', () => { event={storyEvent} onRefresh={onRefresh} organization={organization} + namePressable={false} />, { initialState }, ).snapshot(); @@ -151,6 +155,7 @@ describe('press card', () => { event={event} onRefresh={onRefresh} organization={globalOrg} + namePressable={false} />, { initialState }, ); @@ -166,6 +171,7 @@ describe('press card', () => { event={event} onRefresh={onRefresh} organization={organization} + namePressable={false} />, { initialState }, ); @@ -174,15 +180,17 @@ describe('press card', () => { expect(navigatePush).toHaveBeenCalledWith(CELEBRATE_DETAIL_SCREEN, { event, + orgId: organization.id, + onRefreshCelebrateItem: onRefresh, }); }); }); describe('long-press card', () => { describe('story written by me', () => { - const myStoryEvent = { + const myStoryEvent: CelebrateItemData = { ...storyEvent, - subject_person: { ...subjectPerson, id: myId }, + subjectPerson: mePerson, }; it('navigates to edit story screen', () => { @@ -194,6 +202,7 @@ describe('long-press card', () => { event={myStoryEvent} onRefresh={onRefresh} organization={organization} + namePressable={false} />, { initialState }, ); @@ -219,6 +228,7 @@ describe('long-press card', () => { event={myStoryEvent} onRefresh={onRefresh} organization={organization} + namePressable={false} />, { initialState }, ); @@ -242,7 +252,7 @@ describe('long-press card', () => { ], ); expect(useMutation).toHaveBeenMutatedWith(DELETE_STORY, { - variables: { input: { id: event.celebrateable_id } }, + variables: { input: { id: myStoryEvent.celebrateableId } }, }); expect(onRefresh).toHaveBeenCalled(); }); @@ -258,6 +268,7 @@ describe('long-press card', () => { event={storyEvent} onRefresh={onRefresh} organization={organization} + namePressable={false} />, { initialState }, ); @@ -281,7 +292,7 @@ describe('long-press card', () => { ], ); expect(useMutation).toHaveBeenMutatedWith(REPORT_STORY, { - variables: { subjectId: event.celebrateable_id }, + variables: { subjectId: storyEvent.celebrateableId }, }); }); }); @@ -295,6 +306,7 @@ describe('clear notification button', () => { onRefresh={onRefresh} organization={organization} onClearNotification={onClearNotification} + namePressable={false} />, { initialState }, ); diff --git a/src/components/CelebrateItem/__tests__/__snapshots__/CelebrateItem.tsx.snap b/src/components/CelebrateItem/__tests__/__snapshots__/CelebrateItem.tsx.snap index fcc6a5b230..855103cd46 100644 --- a/src/components/CelebrateItem/__tests__/__snapshots__/CelebrateItem.tsx.snap +++ b/src/components/CelebrateItem/__tests__/__snapshots__/CelebrateItem.tsx.snap @@ -82,7 +82,7 @@ exports[`Community renders correctly 1`] = ` ] } > - John Smith + Hayden Zieme - 12:00 PM + 3:58 AM @@ -111,7 +111,6 @@ exports[`Community renders correctly 1`] = ` Object { "marginTop": 14, }, - Object {}, undefined, ] } @@ -133,7 +132,7 @@ exports[`Community renders correctly 1`] = ` ] } > - John S. completed a Step of Faith with someone. + Roscoe J. joined Communidad! Now you can see and celebrate the steps of faith they are taking. @@ -161,9 +160,13 @@ exports[`Community renders correctly 1`] = ` } > + > + 38791 + - John Smith + Hayden Zieme - 12:00 PM + 3:58 AM @@ -388,7 +393,6 @@ exports[`Community renders story item correctly 1`] = ` Object { "marginTop": 14, }, - Object {}, undefined, ] } @@ -410,7 +414,7 @@ exports[`Community renders story item correctly 1`] = ` ] } > - Celebration + qui recusandae ut @@ -438,9 +442,13 @@ exports[`Community renders story item correctly 1`] = ` } > + > + 38791 + - John Smith + Hayden Zieme - 12:00 PM + 3:58 AM @@ -650,7 +660,6 @@ exports[`Community renders with clear notification button correctly 1`] = ` Object { "marginTop": 14, }, - Object {}, undefined, ] } @@ -672,7 +681,7 @@ exports[`Community renders with clear notification button correctly 1`] = ` ] } > - John S. completed a Step of Faith with someone. + Roscoe J. joined Communidad! Now you can see and celebrate the steps of faith they are taking. @@ -700,9 +709,13 @@ exports[`Community renders with clear notification button correctly 1`] = ` } > + > + 38791 + - John Smith + Hayden Zieme - 12:00 PM + 3:58 AM @@ -972,7 +987,6 @@ exports[`global community renders correctly 1`] = ` Object { "marginTop": 14, }, - Object {}, undefined, ] } @@ -994,7 +1008,7 @@ exports[`global community renders correctly 1`] = ` ] } > - John S. completed a Step of Faith with someone. + Roscoe J. joined ! Now you can see and celebrate the steps of faith they are taking. @@ -1022,9 +1036,13 @@ exports[`global community renders correctly 1`] = ` } > + > + 38791 + - John Smith + Hayden Zieme - 12:00 PM + 3:58 AM @@ -1224,7 +1244,6 @@ exports[`global community renders story item correctly 1`] = ` Object { "marginTop": 14, }, - Object {}, undefined, ] } @@ -1246,7 +1265,7 @@ exports[`global community renders story item correctly 1`] = ` ] } > - Celebration + qui recusandae ut @@ -1274,9 +1293,13 @@ exports[`global community renders story item correctly 1`] = ` } > + > + 38791 + - John Smith + Hayden Zieme - 12:00 PM + 3:58 AM @@ -1476,7 +1501,6 @@ exports[`global community renders with clear notification button correctly 1`] = Object { "marginTop": 14, }, - Object {}, undefined, ] } @@ -1498,7 +1522,7 @@ exports[`global community renders with clear notification button correctly 1`] = ] } > - John S. completed a Step of Faith with someone. + Roscoe J. joined ! Now you can see and celebrate the steps of faith they are taking. @@ -1526,9 +1550,13 @@ exports[`global community renders with clear notification button correctly 1`] = } > + > + 38791 + - John Smith + Hayden Zieme @@ -1828,7 +1858,7 @@ exports[`renders with name pressable correctly 1`] = ` } testID="Text" > - 12:00 PM + 3:58 AM @@ -1838,7 +1868,6 @@ exports[`renders with name pressable correctly 1`] = ` Object { "marginTop": 14, }, - Object {}, undefined, ] } @@ -1860,7 +1889,7 @@ exports[`renders with name pressable correctly 1`] = ` ] } > - John S. completed a Step of Faith with someone. + Roscoe J. joined Communidad! Now you can see and celebrate the steps of faith they are taking. @@ -1888,9 +1917,13 @@ exports[`renders with name pressable correctly 1`] = ` } > + > + 38791 + ; - event: Event; - organization: object; - namePressable?: boolean; - onClearNotification?: (event: Event) => void; + event: CelebrateItemData; + organization: Organization; + namePressable: boolean; + onClearNotification?: (event: CelebrateItemData) => void; onRefresh: () => void; me: Person; } @@ -77,11 +67,11 @@ const CelebrateItem = ({ me, }: CelebrateItemProps) => { const { - celebrateable_id, - changed_attribute_value, - subject_person, - subject_person_name, - celebrateable_type, + celebrateableId, + changedAttributeValue, + subjectPerson, + subjectPersonName, + celebrateableType, } = event; const { t } = useTranslation('celebrateItems'); @@ -93,7 +83,13 @@ const CelebrateItem = ({ ); const handlePress = () => - dispatch(navigatePush(CELEBRATE_DETAIL_SCREEN, { event })); + dispatch( + navigatePush(CELEBRATE_DETAIL_SCREEN, { + event, + orgId: organization.id, + onRefreshCelebrateItem: onRefresh, + }), + ); const clearNotification = () => onClearNotification && onClearNotification(event); @@ -113,7 +109,7 @@ const CelebrateItem = ({ text: t('delete.buttonText'), onPress: async () => { await deleteStory({ - variables: { input: { id: celebrateable_id } }, + variables: { input: { id: celebrateableId } }, }); onRefresh(); }, @@ -128,7 +124,7 @@ const CelebrateItem = ({ onPress: () => reportStory({ variables: { - subjectId: celebrateable_id, + subjectId: celebrateableId, }, }), }, @@ -136,8 +132,8 @@ const CelebrateItem = ({ const menuActions = !orgIsGlobal(organization) && - celebrateable_type === CELEBRATEABLE_TYPES.story - ? me.id === subject_person.id + celebrateableType === CELEBRATEABLE_TYPES.story + ? subjectPerson && me.id === subjectPerson.id ? [ { text: t('edit.buttonText'), @@ -162,22 +158,23 @@ const CelebrateItem = ({ - + - {/* - // @ts-ignore */} - + ); diff --git a/src/components/CelebrateItem/queries.ts b/src/components/CelebrateItem/queries.ts new file mode 100644 index 0000000000..b12d7cfbcd --- /dev/null +++ b/src/components/CelebrateItem/queries.ts @@ -0,0 +1,30 @@ +import gql from 'graphql-tag'; + +export const CELEBRATE_ITEM_PERSON_FRAGMENT = gql` + fragment CelebrateItemPerson on Person { + id + firstName + lastName + } +`; + +export const CELEBRATE_ITEM_FRAGMENT = gql` + fragment CelebrateItem on CommunityCelebrationItem { + id + adjectiveAttributeName + adjectiveAttributeValue + celebrateableId + celebrateableType + changedAttributeName + changedAttributeValue + commentsCount + liked + likesCount + objectDescription + subjectPerson { + ...CelebrateItemPerson + } + subjectPersonName + } + ${CELEBRATE_ITEM_PERSON_FRAGMENT} +`; diff --git a/src/components/CelebrateItemContent/__tests__/CelebrateItemContent.tsx b/src/components/CelebrateItemContent/__tests__/CelebrateItemContent.tsx index a98903ddc1..6c1b72c02f 100644 --- a/src/components/CelebrateItemContent/__tests__/CelebrateItemContent.tsx +++ b/src/components/CelebrateItemContent/__tests__/CelebrateItemContent.tsx @@ -6,6 +6,10 @@ import { CHALLENGE_DETAIL_SCREEN } from '../../../containers/ChallengeDetailScre import { trackActionWithoutData } from '../../../actions/analytics'; import { navigatePush } from '../../../actions/navigation'; import { renderWithContext } from '../../../../testUtils'; +import { mockFragment } from '../../../../testUtils/apolloMockClient'; +import { GetCelebrateFeed_community_celebrationItems_nodes as CelebrateItem } from '../../../containers/CelebrateFeed/__generated__/GetCelebrateFeed'; +import { Organization } from '../../../reducers/organizations'; +import { CELEBRATE_ITEM_FRAGMENT } from '../../../components/CelebrateItem/queries'; import CelebrateItemContent, { CelebrateItemContentProps } from '..'; @@ -13,25 +17,26 @@ jest.mock('../../../actions/analytics'); jest.mock('../../../actions/navigation'); const myId = '123'; -const mePerson = { - id: myId, - first_name: 'John', - last_name: 'Smith', -}; const otherId = '456'; -const otherPerson = { - id: otherId, - first_name: 'John', - last_name: 'Smith', +const organization: Organization = { id: '111', name: 'Celebration Community' }; +const event = mockFragment(CELEBRATE_ITEM_FRAGMENT); +const meEvent: CelebrateItem = { + ...event, + subjectPerson: { + __typename: 'Person', + id: myId, + firstName: 'John', + lastName: 'Smith', + }, }; -const orgId = '111'; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type Event = any; - -const baseEvent = { - subject_person_name: 'John Smith', - changed_attribute_value: '2004-04-04 00:00:00 UTC', +const otherEvent: CelebrateItem = { + ...event, + subjectPerson: { + __typename: 'Person', + id: otherId, + firstName: 'John', + lastName: 'Smith', + }, }; const initialState = { auth: { person: { id: myId } } }; @@ -46,87 +51,89 @@ beforeEach(() => { describe('CelebrateItemContent', () => { const testEvent = ( - e: Event, + e: CelebrateItem, otherProps: Partial = {}, ) => { - renderWithContext(, { - initialState, - }).snapshot(); + renderWithContext( + , + { + initialState, + }, + ).snapshot(); }; - it('renders event with fixed height', () => - testEvent(baseEvent, { fixedHeight: true })); - - it('renders event with no subject person (global community event)', () => - testEvent(baseEvent)); + it('renders event with no subjectPerson, defaults to subjectPersonName', () => + testEvent({ ...event, subjectPerson: null })); - it('renders event with no subject person name', () => { - testEvent({ - ...baseEvent, - subject_person_name: null, - }); + it('renders event with no subjectPerson and no subjectPersonName', () => { + testEvent( + { + ...event, + subjectPerson: null, + subjectPersonName: null, + }, + organization, + ); }); it('renders event for subject=me, liked=true, like count>0', () => { testEvent({ - ...baseEvent, - subject_person: mePerson, - likes_count: 1, + ...meEvent, + likesCount: 1, liked: true, }); }); it('renders event for subject=me, liked=false, like count>0', () => { testEvent({ - ...baseEvent, - subject_person: mePerson, - likes_count: 1, + ...meEvent, + likesCount: 1, liked: false, }); }); it('renders event for subject=me, liked=false, like count=0', () => { testEvent({ - ...baseEvent, - subject_person: mePerson, - likes_count: 0, + ...meEvent, + likesCount: 0, liked: false, }); }); it('renders event for subject=other, liked=true, like count>0', () => { testEvent({ - ...baseEvent, - subject_person: otherPerson, - likes_count: 1, + ...otherEvent, + likesCount: 1, liked: true, }); }); it('renders event for subject=other, liked=false, like count=0', () => { testEvent({ - ...baseEvent, - subject_person: otherPerson, - likes_count: 0, + ...otherEvent, + likesCount: 0, liked: false, }); }); describe('message', () => { - const messageBaseEvent = { - ...baseEvent, - subject_person: mePerson, - likes_count: 0, + const messageBaseEvent: CelebrateItem = { + ...meEvent, + likesCount: 0, liked: false, }; it('renders event with no subject person name', () => { testEvent({ ...messageBaseEvent, - subject_person: null, - subject_person_name: null, - celebrateable_type: CELEBRATEABLE_TYPES.completedStep, - adjective_attribute_value: '3', + subjectPerson: null, + subjectPersonName: null, + celebrateableType: CELEBRATEABLE_TYPES.completedStep, + adjectiveAttributeValue: '3', }); }); @@ -134,8 +141,8 @@ describe('CelebrateItemContent', () => { const testEventStage = (stageNum: string) => testEvent({ ...messageBaseEvent, - celebrateable_type: CELEBRATEABLE_TYPES.completedStep, - adjective_attribute_value: stageNum, + celebrateableType: CELEBRATEABLE_TYPES.completedStep, + adjectiveAttributeValue: stageNum, }); it('1', () => testEventStage('1')); @@ -150,7 +157,8 @@ describe('CelebrateItemContent', () => { it('renders step of faith event without stage', () => { testEvent({ ...messageBaseEvent, - celebrateable_type: CELEBRATEABLE_TYPES.completedStep, + celebrateableType: CELEBRATEABLE_TYPES.completedStep, + adjectiveAttributeValue: null, }); }); @@ -158,8 +166,8 @@ describe('CelebrateItemContent', () => { const testEventInteraction = (interaction: string) => testEvent({ ...messageBaseEvent, - celebrateable_type: CELEBRATEABLE_TYPES.completedInteraction, - adjective_attribute_value: interaction, + celebrateableType: CELEBRATEABLE_TYPES.completedInteraction, + adjectiveAttributeValue: interaction, }); it('personal decision', () => @@ -192,60 +200,40 @@ describe('CelebrateItemContent', () => { it('renders accepted challenge event', () => { testEvent({ ...messageBaseEvent, - celebrateable_type: CELEBRATEABLE_TYPES.acceptedCommunityChallenge, - changed_attribute_name: CELEBRATEABLE_TYPES.challengeItemTypes.accepted, - object_description: 'Invite a friend to church', + celebrateableType: CELEBRATEABLE_TYPES.acceptedCommunityChallenge, + changedAttributeName: CELEBRATEABLE_TYPES.challengeItemTypes.accepted, + objectDescription: 'Invite a friend to church', }); }); it('renders completed challenge event', () => { testEvent({ ...messageBaseEvent, - celebrateable_type: CELEBRATEABLE_TYPES.acceptedCommunityChallenge, - changed_attribute_name: - CELEBRATEABLE_TYPES.challengeItemTypes.completed, - object_description: 'Invite a friend to church', + celebrateableType: CELEBRATEABLE_TYPES.acceptedCommunityChallenge, + changedAttributeName: CELEBRATEABLE_TYPES.challengeItemTypes.completed, + objectDescription: 'Invite a friend to church', }); }); it('renders created community event', () => { testEvent({ ...messageBaseEvent, - celebrateable_type: CELEBRATEABLE_TYPES.createdCommunity, - organization: { - name: 'Celebration Community', - }, + celebrateableType: CELEBRATEABLE_TYPES.createdCommunity, }); }); it('renders joined community event', () => { testEvent({ ...messageBaseEvent, - celebrateable_type: CELEBRATEABLE_TYPES.joinedCommunity, - organization: { - name: 'Celebration Community', - }, + celebrateableType: CELEBRATEABLE_TYPES.joinedCommunity, }); }); - it('renders joined community with passed in org name', () => { - testEvent( - { - ...messageBaseEvent, - celebrateable_type: CELEBRATEABLE_TYPES.joinedCommunity, - organization: { - name: 'Celebration Community', - }, - }, - { organization: { id: orgId, name: 'My Real Org' } }, - ); - }); - it('renders story', () => { testEvent({ ...messageBaseEvent, - celebrateable_type: CELEBRATEABLE_TYPES.story, - object_description: 'Once Upon a Time....', + celebrateableType: CELEBRATEABLE_TYPES.story, + objectDescription: 'Once Upon a Time....', }); }); }); @@ -253,33 +241,21 @@ describe('CelebrateItemContent', () => { describe('onPressChallengeLink', () => { it('navigates to challenge detail screen', () => { - const challengeId = '123'; - - const event = { - id: '1', - subject_person_name: 'John Smith', - subject_person: { - id: otherId, - }, - changed_attribute_value: '2004-04-04 00:00:00 UTC', - likes_count: 0, - liked: true, - organization: { id: orgId }, - celebrateable_type: CELEBRATEABLE_TYPES.acceptedCommunityChallenge, - changed_attribute_name: CELEBRATEABLE_TYPES.challengeItemTypes.completed, - adjective_attribute_value: challengeId, - object_description: 'Invite a friend to church', - }; - const { getByTestId, store } = renderWithContext( - , + , { initialState }, ); fireEvent.press(getByTestId('ChallengeLinkButton')); expect(navigatePush).toHaveBeenCalledWith(CHALLENGE_DETAIL_SCREEN, { - challengeId, - orgId, + challengeId: event.adjectiveAttributeValue, + orgId: organization.id, }); expect(store.getActions()).toEqual([navigateResponse]); }); diff --git a/src/components/CelebrateItemContent/__tests__/__snapshots__/CelebrateItemContent.tsx.snap b/src/components/CelebrateItemContent/__tests__/__snapshots__/CelebrateItemContent.tsx.snap index dee45ca8ad..39a3b2b9ec 100644 --- a/src/components/CelebrateItemContent/__tests__/__snapshots__/CelebrateItemContent.tsx.snap +++ b/src/components/CelebrateItemContent/__tests__/__snapshots__/CelebrateItemContent.tsx.snap @@ -7,7 +7,6 @@ exports[`CelebrateItemContent message renders accepted challenge event 1`] = ` Object { "marginTop": 14, }, - Object {}, undefined, ] } @@ -106,7 +105,6 @@ exports[`CelebrateItemContent message renders completed challenge event 1`] = ` Object { "marginTop": 14, }, - Object {}, undefined, ] } @@ -205,7 +203,6 @@ exports[`CelebrateItemContent message renders created community event 1`] = ` Object { "marginTop": 14, }, - Object {}, undefined, ] } @@ -239,7 +236,6 @@ exports[`CelebrateItemContent message renders event with no subject person name Object { "marginTop": 14, }, - Object {}, undefined, ] } @@ -273,7 +269,6 @@ exports[`CelebrateItemContent message renders interaction event discipleship 1`] Object { "marginTop": 14, }, - Object {}, undefined, ] } @@ -307,7 +302,6 @@ exports[`CelebrateItemContent message renders interaction event gospel 1`] = ` Object { "marginTop": 14, }, - Object {}, undefined, ] } @@ -341,7 +335,6 @@ exports[`CelebrateItemContent message renders interaction event holy spirit 1`] Object { "marginTop": 14, }, - Object {}, undefined, ] } @@ -375,7 +368,6 @@ exports[`CelebrateItemContent message renders interaction event not found 1`] = Object { "marginTop": 14, }, - Object {}, undefined, ] } @@ -409,7 +401,6 @@ exports[`CelebrateItemContent message renders interaction event personal decisio Object { "marginTop": 14, }, - Object {}, undefined, ] } @@ -443,7 +434,6 @@ exports[`CelebrateItemContent message renders interaction event something cool 1 Object { "marginTop": 14, }, - Object {}, undefined, ] } @@ -477,7 +467,6 @@ exports[`CelebrateItemContent message renders interaction event spiritual 1`] = Object { "marginTop": 14, }, - Object {}, undefined, ] } @@ -511,7 +500,6 @@ exports[`CelebrateItemContent message renders joined community event 1`] = ` Object { "marginTop": 14, }, - Object {}, undefined, ] } @@ -538,40 +526,6 @@ exports[`CelebrateItemContent message renders joined community event 1`] = ` `; -exports[`CelebrateItemContent message renders joined community with passed in org name 1`] = ` - - - John S. joined My Real Org! Now you can see and celebrate the steps of faith they are taking. - - -`; - exports[`CelebrateItemContent message renders step of faith event with stage 1 1`] = ` + > + John S. joined Celebration Community! Now you can see and celebrate the steps of faith they are taking. + `; @@ -883,7 +830,6 @@ exports[`CelebrateItemContent renders event for subject=me, liked=false, like co Object { "marginTop": 14, }, - Object {}, undefined, ] } @@ -904,7 +850,9 @@ exports[`CelebrateItemContent renders event for subject=me, liked=false, like co }, ] } - /> + > + John S. joined Celebration Community! Now you can see and celebrate the steps of faith they are taking. + `; @@ -915,7 +863,6 @@ exports[`CelebrateItemContent renders event for subject=me, liked=true, like cou Object { "marginTop": 14, }, - Object {}, undefined, ] } @@ -936,7 +883,9 @@ exports[`CelebrateItemContent renders event for subject=me, liked=true, like cou }, ] } - /> + > + John S. joined Celebration Community! Now you can see and celebrate the steps of faith they are taking. + `; @@ -947,7 +896,6 @@ exports[`CelebrateItemContent renders event for subject=other, liked=false, like Object { "marginTop": 14, }, - Object {}, undefined, ] } @@ -968,7 +916,9 @@ exports[`CelebrateItemContent renders event for subject=other, liked=false, like }, ] } - /> + > + John S. joined Celebration Community! Now you can see and celebrate the steps of faith they are taking. + `; @@ -979,41 +929,6 @@ exports[`CelebrateItemContent renders event for subject=other, liked=true, like Object { "marginTop": 14, }, - Object {}, - undefined, - ] - } -> - - -`; - -exports[`CelebrateItemContent renders event with fixed height 1`] = ` - + > + John S. joined Celebration Community! Now you can see and celebrate the steps of faith they are taking. + `; -exports[`CelebrateItemContent renders event with no subject person (global community event) 1`] = ` +exports[`CelebrateItemContent renders event with no subjectPerson and no subjectPersonName 1`] = ` + > + A MissionHub user joined Celebration Community! Now you can see and celebrate the steps of faith they are taking. + `; -exports[`CelebrateItemContent renders event with no subject person name 1`] = ` +exports[`CelebrateItemContent renders event with no subjectPerson, defaults to subjectPersonName 1`] = ` + > + Hayden Zieme joined Celebration Community! Now you can see and celebrate the steps of faith they are taking. + `; diff --git a/src/components/CelebrateItemContent/index.tsx b/src/components/CelebrateItemContent/index.tsx index 13ee22645b..5c5f3bb125 100644 --- a/src/components/CelebrateItemContent/index.tsx +++ b/src/components/CelebrateItemContent/index.tsx @@ -3,6 +3,7 @@ import { View, StyleProp, ViewStyle } from 'react-native'; import { useTranslation } from 'react-i18next'; import { connect } from 'react-redux-legacy'; import { ThunkDispatch } from 'redux-thunk'; +import { AnyAction } from 'redux'; import { Text, Button } from '../../components/common'; import { @@ -13,18 +14,15 @@ import { import { navigatePush } from '../../actions/navigation'; import { CHALLENGE_DETAIL_SCREEN } from '../../containers/ChallengeDetailScreen'; import { getFirstNameAndLastInitial } from '../../utils/common'; +import { GetCelebrateFeed_community_celebrationItems_nodes as CelebrateItem } from '../../containers/CelebrateFeed/__generated__/GetCelebrateFeed'; +import { Organization } from '../../reducers/organizations'; import styles from './styles'; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type Event = any; - export interface CelebrateItemContentProps { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - dispatch: ThunkDispatch; - event: Event; - organization?: object; - fixedHeight?: boolean; + dispatch: ThunkDispatch<{}, {}, AnyAction>; + event: CelebrateItem; + organization: Organization; style?: StyleProp; } @@ -50,25 +48,32 @@ const CelebrateItemContent = ({ dispatch, event, organization, - fixedHeight, style, }: CelebrateItemContentProps) => { const { t } = useTranslation('celebrateFeeds'); - const org = organization || event.organization || {}; - const { name: communityName = '' } = org || {}; + const { name: communityName = '' } = organization || {}; const { - adjective_attribute_value, - changed_attribute_name, - subject_person, - subject_person_name, - celebrateable_type, - object_description, + adjectiveAttributeValue, + changedAttributeName, + subjectPerson, + subjectPersonName, + celebrateableType, + objectDescription, } = event; + const personName = subjectPerson + ? `${getFirstNameAndLastInitial( + subjectPerson.firstName, + subjectPerson.lastName, + )}.` + : subjectPersonName + ? subjectPersonName + : t('aMissionHubUser'); + const onPressChallengeLink = () => { - const orgId = org.id; - const challengeId = adjective_attribute_value; + const orgId = organization.id; + const challengeId = adjectiveAttributeValue; if (orgId && orgId !== GLOBAL_COMMUNITY_ID) { dispatch( navigatePush(CHALLENGE_DETAIL_SCREEN, { @@ -79,53 +84,53 @@ const CelebrateItemContent = ({ } }; - const buildJoinedCommunityMessage = (name: string) => { - return t('joinedCommunity', { initiator: name, communityName }); + const buildJoinedCommunityMessage = () => { + return t('joinedCommunity', { initiator: personName, communityName }); }; - const buildCreateCommunityMessage = (name: string) => { - return t('communityCreated', { initiator: name, communityName }); + const buildCreateCommunityMessage = () => { + return t('communityCreated', { initiator: personName, communityName }); }; - const buildChallengeMessage = (type: string, name: string) => { - switch (type) { + const buildChallengeMessage = () => { + switch (changedAttributeName) { case accepted: - return t('challengeAccepted', { initiator: name }); + return t('challengeAccepted', { initiator: personName }); case completed: - return t('challengeCompleted', { initiator: name }); + return t('challengeCompleted', { initiator: personName }); } }; - const buildInteractionMessage = (type: string, name: string) => { - switch (`${type}`) { + const buildInteractionMessage = () => { + switch (adjectiveAttributeValue) { case MHInteractionTypePersonalDecision.id: - return t('interactionDecision', { initiator: name }); + return t('interactionDecision', { initiator: personName }); case MHInteractionTypeSomethingCoolHappened.id: - return t('somethingCoolHappened', { initiator: name }); + return t('somethingCoolHappened', { initiator: personName }); default: - return t(completedInteraction, { - initiator: name, + return t('interaction', { + initiator: personName, interactionName: renderInteraction(), }); } }; - const renderStepOfFaithMessage = (name: string) => { + const renderStepOfFaithMessage = () => { return t( - adjective_attribute_value - ? adjective_attribute_value === '6' + adjectiveAttributeValue + ? adjectiveAttributeValue === '6' ? 'stepOfFaithNotSureStage' : 'stepOfFaith' : 'stepOfFaithUnknownStage', { - initiator: name, - receiverStage: renderStage(adjective_attribute_value), + initiator: personName, + receiverStage: renderStage(), }, ); }; - const renderStage = (stage: string) => { - switch (stage) { + const renderStage = () => { + switch (adjectiveAttributeValue) { case '1': return t('stages.uninterested.label'); case '2': @@ -142,7 +147,7 @@ const CelebrateItemContent = ({ }; const renderInteraction = () => { - switch (`${adjective_attribute_value}`) { + switch (adjectiveAttributeValue) { case MHInteractionTypeSpiritualConversation.id: return t('actions:interactionSpiritualConversation'); case MHInteractionTypeGospelPresentation.id: @@ -157,32 +162,24 @@ const CelebrateItemContent = ({ }; const renderMessage = () => { - const name = subject_person - ? `${getFirstNameAndLastInitial( - subject_person.first_name, - subject_person.last_name, - )}.` - : subject_person_name - ? subject_person_name - : t('aMissionHubUser'); - switch (celebrateable_type) { + switch (celebrateableType) { case completedStep: - return renderStepOfFaithMessage(name); + return renderStepOfFaithMessage(); case completedInteraction: - return buildInteractionMessage(adjective_attribute_value, name); + return buildInteractionMessage(); case acceptedCommunityChallenge: - return buildChallengeMessage(changed_attribute_name, name); + return buildChallengeMessage(); case createdCommunity: - return buildCreateCommunityMessage(name); + return buildCreateCommunityMessage(); case joinedCommunity: - return buildJoinedCommunityMessage(name); + return buildJoinedCommunityMessage(); case story: - return object_description; + return objectDescription; } }; const renderChallengeLink = () => { - return celebrateable_type === acceptedCommunityChallenge ? ( + return celebrateableType === acceptedCommunityChallenge ? ( @@ -199,16 +196,11 @@ const CelebrateItemContent = ({ }; return ( - + {renderMessage()} {renderChallengeLink()} ); }; + export default connect()(CelebrateItemContent); diff --git a/src/components/CelebrateItemContent/styles.ts b/src/components/CelebrateItemContent/styles.ts index 52a44df08f..aac46d0559 100644 --- a/src/components/CelebrateItemContent/styles.ts +++ b/src/components/CelebrateItemContent/styles.ts @@ -9,9 +9,6 @@ export default StyleSheet.create({ description: { marginTop: 14, }, - fixedHeightDescription: { - height: 70, - }, messageText: { fontSize: 14, lineHeight: 18, diff --git a/src/components/ReportCommentHeaderCard/index.tsx b/src/components/ReportCommentHeaderCard/index.tsx index 3e4a940184..d4b32c0a8f 100644 --- a/src/components/ReportCommentHeaderCard/index.tsx +++ b/src/components/ReportCommentHeaderCard/index.tsx @@ -6,6 +6,7 @@ import { Text, Card, Icon } from '../../components/common'; import styles from './styles'; interface ReportCommentHeaderCardProps { + testID?: string; count: number; onPress: () => void; } diff --git a/src/components/UnreadCommentsCard/index.tsx b/src/components/UnreadCommentsCard/index.tsx index 5205df90a3..ebeae66290 100644 --- a/src/components/UnreadCommentsCard/index.tsx +++ b/src/components/UnreadCommentsCard/index.tsx @@ -9,6 +9,7 @@ import COMMENTS from '../../../assets/images/comments.png'; import styles from './styles'; interface UnreadCommentsCardProps { + testID?: string; count: number; onPress: Function & ((event: GestureResponderEvent) => void); onClose: Function; diff --git a/src/constants.ts b/src/constants.ts index 5bf6b6edbc..f6a186d5db 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -429,8 +429,8 @@ export const INTERACTION_TYPES = { export const DEFAULT_PAGE_LIMIT = 25; export const CELEBRATEABLE_TYPES = { - completedStep: 'accepted_challenge', - completedInteraction: 'interaction', + completedStep: 'V4::AcceptedChallenge', + completedInteraction: 'V4::Interaction', validInteractionTypes: [ INTERACTION_TYPES.MHInteractionTypeDiscipleshipConversation.id, INTERACTION_TYPES.MHInteractionTypeHolySpiritConversation.id, @@ -439,14 +439,14 @@ export const CELEBRATEABLE_TYPES = { INTERACTION_TYPES.MHInteractionTypeSpiritualConversation.id, INTERACTION_TYPES.MHInteractionTypeSomethingCoolHappened.id, ], - acceptedCommunityChallenge: 'accepted_community_challenge', + acceptedCommunityChallenge: 'V4::AcceptedCommunityChallenge', challengeItemTypes: { accepted: 'accepted_at', completed: 'completed_at', }, - createdCommunity: 'organization', - joinedCommunity: 'organizational_permission', - story: 'story', + createdCommunity: 'V4::Organization', + joinedCommunity: 'V4::OrganizationalPermission', + story: 'V4::Story', }; export const DAYS_OF_THE_WEEK = [ diff --git a/src/containers/Auth/SignInScreen/__tests__/SignInScreen.tsx b/src/containers/Auth/SignInScreen/__tests__/SignInScreen.tsx index 7eb0f475c8..d63d295cff 100644 --- a/src/containers/Auth/SignInScreen/__tests__/SignInScreen.tsx +++ b/src/containers/Auth/SignInScreen/__tests__/SignInScreen.tsx @@ -32,8 +32,13 @@ jest.mock('../../../../actions/auth/facebook', () => ({ })); let keyboardListeners: { [name: string]: () => void }; jest.mock('../../../../utils/hooks/useKeyboardListeners', () => ({ - useKeyboardListeners: (onShow: () => void, onHide: () => void) => - (keyboardListeners = { onShow, onHide }), + useKeyboardListeners: ({ + onShow, + onHide, + }: { + onShow: () => void; + onHide: () => void; + }) => (keyboardListeners = { onShow, onHide }), })); jest.mock('../../../../utils/hooks/useAnalytics'); diff --git a/src/containers/Auth/SignInScreen/index.tsx b/src/containers/Auth/SignInScreen/index.tsx index 76b3a6870b..db2050faf1 100644 --- a/src/containers/Auth/SignInScreen/index.tsx +++ b/src/containers/Auth/SignInScreen/index.tsx @@ -58,7 +58,10 @@ const SignInScreen = ({ const [isLoading, setIsLoading] = useState(false); const [showLogo, setShowLogo] = useState(true); - useKeyboardListeners(() => setShowLogo(false), () => setShowLogo(true)); + useKeyboardListeners({ + onShow: () => setShowLogo(false), + onHide: () => setShowLogo(true), + }); const handleForgotPassword = async () => { // @ts-ignore diff --git a/src/containers/CelebrateDetailScreen/__tests__/CelebrateDetailScreen.tsx b/src/containers/CelebrateDetailScreen/__tests__/CelebrateDetailScreen.tsx index 8f42747294..57b57bad40 100644 --- a/src/containers/CelebrateDetailScreen/__tests__/CelebrateDetailScreen.tsx +++ b/src/containers/CelebrateDetailScreen/__tests__/CelebrateDetailScreen.tsx @@ -1,179 +1,205 @@ import 'react-native'; import React from 'react'; -import configureStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; import MockDate from 'mockdate'; +import { fireEvent } from 'react-native-testing-library'; -import { renderShallow, createMockNavState } from '../../../../testUtils'; -import { celebrationItemSelector } from '../../../selectors/celebration'; +import { renderWithContext } from '../../../../testUtils'; +import { mockFragment } from '../../../../testUtils/apolloMockClient'; import { organizationSelector } from '../../../selectors/organizations'; -import * as common from '../../../utils/common'; +import { useKeyboardListeners } from '../../../utils/hooks/useKeyboardListeners'; import { reloadCelebrateComments, resetCelebrateEditingComment, } from '../../../actions/celebrateComments'; +import CommentsList from '../../CommentsList'; import { celebrateCommentsSelector } from '../../../selectors/celebrateComments'; +import { Organization } from '../../../reducers/organizations'; +import { CelebrateComment } from '../../../reducers/celebrateComments'; +import { CELEBRATE_ITEM_FRAGMENT } from '../../../components/CelebrateItem/queries'; +import { GetCelebrateFeed_community_celebrationItems_nodes as CelebrateItem } from '../../CelebrateFeed/__generated__/GetCelebrateFeed'; import CelebrateDetailScreen from '..'; +jest.mock('../../../utils/hooks/useKeyboardListeners'); jest.mock('../../../selectors/celebration'); jest.mock('../../../selectors/celebrateComments'); jest.mock('../../../selectors/organizations'); jest.mock('../../../actions/celebrateComments'); +jest.mock('../../Analytics', () => 'Analytics'); +jest.mock('../../CommentItem', () => 'CommentItem'); jest.useFakeTimers(); -// @ts-ignore -reloadCelebrateComments.mockReturnValue({ type: 'reloadCelebrateComments' }); -// @ts-ignore -resetCelebrateEditingComment.mockReturnValue({ - type: 'resetCelebrateEditingComment', -}); MockDate.set('2019-04-12 12:00:00', 300); -const mockStore = configureStore([thunk]); -let store; - -const celebrateComments = { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const comments: { comments: CelebrateComment[]; pagination: any } = { comments: [ { id: 'comment1', person: { first_name: 'Person', last_name: '1' }, content: 'some comment', created_at: '2019-04-11T13:51:49.888', + updated_at: '2019-04-11T13:51:49.888', }, { id: 'comment2', person: { first_name: 'Person', last_name: '2' }, content: 'some comment', created_at: '2019-04-11T13:51:49.888', + updated_at: '2019-04-11T13:51:49.888', }, ], pagination: {}, }; -const organization = { id: '24234234' }; -const event = { - id: '90001', - organization, - subject_person_name: 'Roger', - changed_attribute_value: '2019-04-11T13:51:49.888', -}; -const organizations = [organization]; const myId = 'myId'; +const orgId = '24234234'; +const organization: Organization = { id: orgId, name: 'Community' }; +const event = mockFragment(CELEBRATE_ITEM_FRAGMENT); +const organizations = [organization]; +const celebrateComments = { editingCommentId: null }; +const auth = { person: { id: myId } }; -// @ts-ignore -let screen; -let instance; -const listRef = { scrollToEnd: jest.fn(), scrollToIndex: jest.fn() }; - -// @ts-ignore -celebrationItemSelector.mockReturnValue(event); -// @ts-ignore -organizationSelector.mockReturnValue(organization); -// @ts-ignore -celebrateCommentsSelector.mockReturnValue(celebrateComments), - beforeEach(() => { - store = mockStore({ - organizations, - celebrateComments: { editingCommentId: null }, - auth: { person: { id: myId } }, - }); - - screen = renderShallow( - // @ts-ignore - , - store, - ); - instance = screen.instance(); - // @ts-ignore - instance.listRef = listRef; - }); +const initialState = { + organizations, + celebrateComments, + auth, +}; -it('renders correctly', () => { - // @ts-ignore - expect(screen).toMatchSnapshot(); -}); +let onShowKeyboard: () => void; -it('should call celebrationItemSelector', () => { - expect(celebrationItemSelector).toHaveBeenCalledWith( - { organizations }, - { eventId: event.id, organizationId: organization.id }, +beforeEach(() => { + (useKeyboardListeners as jest.Mock).mockImplementation( + ({ onShow }: { onShow: () => void }) => (onShowKeyboard = onShow), + ); + (reloadCelebrateComments as jest.Mock).mockReturnValue({ + type: 'reloadCelebrateComments', + }); + (resetCelebrateEditingComment as jest.Mock).mockReturnValue({ + type: 'resetCelebrateEditingComment', + }); + ((organizationSelector as unknown) as jest.Mock).mockReturnValue( + organization, + ); + ((celebrateCommentsSelector as unknown) as jest.Mock).mockReturnValue( + comments, ); }); -it('should call organizationSelector', () => { +it('renders correctly', () => { + renderWithContext(, { + initialState, + navParams: { event, orgId }, + }).snapshot(); + expect(organizationSelector).toHaveBeenCalledWith( { organizations }, - { orgId: organization.id }, + { orgId }, + ); + expect(celebrateCommentsSelector).toHaveBeenCalledWith( + { + celebrateComments, + }, + { eventId: event.id }, ); }); describe('refresh', () => { it('calls refreshComments', () => { - // @ts-ignore - screen.instance().refreshComments(); - expect(reloadCelebrateComments).toHaveBeenCalledWith(event); - }); - it('calls handleRefresh', () => { - // @ts-ignore - common.refresh = jest.fn(); - // @ts-ignore - screen - .childAt(2) - .childAt(2) - .props() - .listProps.refreshControl.props.onRefresh(); - expect(common.refresh).toHaveBeenCalledWith( - // @ts-ignore - screen.instance(), - // @ts-ignore - screen.instance().refreshComments, + const { getByTestId } = renderWithContext(, { + initialState, + navParams: { event, orgId }, + }); + + fireEvent(getByTestId('RefreshControl'), 'onRefresh'); + + expect(reloadCelebrateComments).toHaveBeenCalledWith( + event.id, + organization.id, ); }); }); describe('celebrate add complete', () => { it('scrolls to end on add complete', () => { - // @ts-ignore - screen - .childAt(3) - .props() - .onAddComplete(); - expect(listRef.scrollToEnd).toHaveBeenCalled(); - }); -}); + const scrollToEnd = jest.fn(); + + const { getByType, getByTestId } = renderWithContext( + , + { + initialState, + navParams: { event, orgId }, + }, + ); -it('componentWillUnmount', () => { - // @ts-ignore - const instance = screen.instance(); - const remove = jest.fn(); - instance.keyboardShowListener = { remove }; - instance.componentWillUnmount(); + getByType( + CommentsList, + ).props.listProps.ref.current.scrollToEnd = scrollToEnd; - expect(remove).toHaveBeenCalled(); + fireEvent(getByTestId('CelebrateCommentBox'), 'onAddComplete'); + + expect(scrollToEnd).toHaveBeenCalledWith(); + }); }); describe('keyboard show', () => { it('without editing comment', () => { - // @ts-ignore - screen.instance().keyboardShow(); - expect(listRef.scrollToEnd).toHaveBeenCalled(); + const scrollToEnd = jest.fn(); + + const { getByType } = renderWithContext(, { + initialState, + navParams: { event, orgId }, + }); + + getByType( + CommentsList, + ).props.listProps.ref.current.scrollToEnd = scrollToEnd; + + onShowKeyboard(); + + expect(scrollToEnd).toHaveBeenCalledWith(); }); + it('with editing comment', () => { - // @ts-ignore - screen.setProps({ editingCommentId: celebrateComments.comments[0].id }); - // @ts-ignore - screen.instance().keyboardShow(); - expect(listRef.scrollToIndex).toHaveBeenCalledWith({ + const scrollToIndex = jest.fn(); + + const { getByType } = renderWithContext(, { + initialState: { + ...initialState, + celebrateComments: { editingCommentId: comments.comments[0].id }, + }, + navParams: { event, orgId }, + }); + + getByType( + CommentsList, + ).props.listProps.ref.current.scrollToIndex = scrollToIndex; + + onShowKeyboard(); + + expect(scrollToIndex).toHaveBeenCalledWith({ index: 0, viewPosition: 1, }); }); + it('with editing comment that doesnt exist', () => { - // @ts-ignore - screen.setProps({ editingCommentId: 'doesnt exist' }); - // @ts-ignore - screen.instance().keyboardShow(); - expect(listRef.scrollToEnd).toHaveBeenCalled(); + const scrollToEnd = jest.fn(); + + const { getByType } = renderWithContext(, { + initialState: { + ...initialState, + celebrateComments: { editingCommentId: 'doesnt exist' }, + }, + navParams: { event, orgId }, + }); + + getByType( + CommentsList, + ).props.listProps.ref.current.scrollToEnd = scrollToEnd; + + onShowKeyboard(); + + expect(scrollToEnd).toHaveBeenCalledWith(); }); }); diff --git a/src/containers/CelebrateDetailScreen/__tests__/__snapshots__/CelebrateDetailScreen.tsx.snap b/src/containers/CelebrateDetailScreen/__tests__/__snapshots__/CelebrateDetailScreen.tsx.snap index 0331c75480..bdcfbc2868 100644 --- a/src/containers/CelebrateDetailScreen/__tests__/__snapshots__/CelebrateDetailScreen.tsx.snap +++ b/src/containers/CelebrateDetailScreen/__tests__/__snapshots__/CelebrateDetailScreen.tsx.snap @@ -17,13 +17,9 @@ exports[`renders correctly 1`] = ` ] } /> - - + - - + testID="NameButton" + > + + + Hayden Zieme + + + + + 3:58 AM + - - + + 38791 + + + + + + + + + + + > + + + +  + + + + - + - + } + removeClippedSubviews={false} + renderItem={[Function]} + scrollEventThrottle={50} + stickyHeaderIndices={Array []} + style={ Object { - "ListHeaderComponent": [Function], - "ref": [Function], - "refreshControl": , + "flex": 1, } } - /> + updateCellsBatchingPeriod={50} + viewabilityConfigCallbackPairs={Array []} + windowSize={21} + > + + + + + + Roscoe J. joined Community! Now you can see and celebrate the steps of faith they are taking. + + + + + + + + + + + - + > + + + + + + + + + + `; diff --git a/src/containers/CelebrateDetailScreen/index.tsx b/src/containers/CelebrateDetailScreen/index.tsx index 265f8d19b0..0430824f40 100644 --- a/src/containers/CelebrateDetailScreen/index.tsx +++ b/src/containers/CelebrateDetailScreen/index.tsx @@ -1,10 +1,11 @@ -import React, { Component } from 'react'; -import { Image, View, SafeAreaView, StatusBar } from 'react-native'; +import React, { useRef } from 'react'; +import { Image, View, SafeAreaView, StatusBar, FlatList } from 'react-native'; import { connect } from 'react-redux-legacy'; -import PropTypes from 'prop-types'; +import { AnyAction } from 'redux'; +import { ThunkDispatch } from 'redux-thunk'; +import { useNavigationParam } from 'react-navigation-hooks'; import CommentLikeComponent from '../CommentLikeComponent'; -import { celebrationItemSelector } from '../../selectors/celebration'; import { organizationSelector } from '../../selectors/organizations'; import CommentsList from '../CommentsList'; import BackButton from '../BackButton'; @@ -12,7 +13,6 @@ import CelebrateCommentBox from '../../components/CelebrateCommentBox'; import theme from '../../theme'; import TRAILS1 from '../../../assets/images/Trailss.png'; import TRAILS2 from '../../../assets/images/TrailGrey.png'; -import { refresh, keyboardShow } from '../../utils/common'; import { reloadCelebrateComments } from '../../actions/celebrateComments'; import { celebrateCommentsSelector } from '../../selectors/celebrateComments'; import CardTime from '../../components/CardTime'; @@ -20,170 +20,156 @@ import CelebrateItemName from '../CelebrateItemName'; import CelebrateItemContent from '../../components/CelebrateItemContent'; import { RefreshControl } from '../../components/common'; import Analytics from '../Analytics'; +import { GetCelebrateFeed_community_celebrationItems_nodes } from '../CelebrateFeed/__generated__/GetCelebrateFeed'; +import { Organization, OrganizationsState } from '../../reducers/organizations'; +import { + CelebrateCommentsState, + CelebrateComment, +} from '../../reducers/celebrateComments'; +import { useKeyboardListeners } from '../../utils/hooks/useKeyboardListeners'; +import { useRefreshing } from '../../utils/hooks/useRefreshing'; import styles from './styles'; -class CelebrateDetailScreen extends Component { - state = { refreshing: false }; - - componentDidMount() { - // @ts-ignore - this.keyboardShowListener = keyboardShow(this.keyboardShow, 'did'); - } +export interface CelebrateDetailScreenProps { + dispatch: ThunkDispatch<{}, {}, AnyAction>; + organization: Organization; + celebrateComments: { comments: CelebrateComment[] }; + editingCommentId: string | null; +} - componentWillUnmount() { - // @ts-ignore - this.keyboardShowListener.remove(); - } +const CelebrateDetailScreen = ({ + dispatch, + organization, + celebrateComments, + editingCommentId, +}: CelebrateDetailScreenProps) => { + const event: GetCelebrateFeed_community_celebrationItems_nodes = useNavigationParam( + 'event', + ); + const onRefreshCelebrateItem: () => void = useNavigationParam( + 'onRefreshCelebrateItem', + ); - keyboardShow = () => this.scrollToFocusedRef(); + const listRef = useRef>(null); - // @ts-ignore - scrollToEnd = () => this.listRef && this.listRef.scrollToEnd(); + const scrollToEnd = () => listRef.current && listRef.current.scrollToEnd(); - scrollToFocusedRef = () => { - const { - // @ts-ignore - celebrateComments: { comments }, - // @ts-ignore - editingCommentId, - } = this.props; + const scrollToFocusedRef = () => { if (editingCommentId) { - // @ts-ignore - const index = comments.findIndex(c => c.id === editingCommentId); + const index = celebrateComments.comments.findIndex( + c => c.id === editingCommentId, + ); if (index >= 0) { - // @ts-ignore - this.listRef && this.listRef.scrollToIndex({ index, viewPosition: 1 }); + listRef.current && + listRef.current.scrollToIndex({ index, viewPosition: 1 }); return; } } - this.scrollToEnd(); + scrollToEnd(); }; - refreshComments = () => { - // @ts-ignore - const { dispatch, event } = this.props; - return dispatch(reloadCelebrateComments(event)); - }; + useKeyboardListeners({ onShow: () => scrollToFocusedRef() }); - handleRefresh = () => refresh(this, this.refreshComments); + const refreshComments = () => { + return dispatch(reloadCelebrateComments(event.id, organization.id)); + }; - renderHeader = () => { - // @ts-ignore - const { event, organization } = this.props; - return ( - - - - {/* - // @ts-ignore */} - - {/* - // @ts-ignore */} - - - - - {/* - // @ts-ignore */} - - ( + + + + + + + + + - - ); - }; - - renderCommentsList = () => { - // @ts-ignore - const { event, organization } = this.props; - const { refreshing } = this.state; - return ( - - - - (this.listRef = c), - refreshControl: ( - - ), - ListHeaderComponent: () => ( - - ), - }} - /> - ); - }; - - renderCommentBox = () => { - // @ts-ignore - const { event } = this.props; - return ( - - ); - }; - - render() { - return ( - - - {this.renderHeader()} - {this.renderCommentsList()} - {this.renderCommentBox()} - - ); - } -} - -// @ts-ignore -CelebrateDetailScreen.propTypes = { - event: PropTypes.object.isRequired, + + ); + + const renderCommentsList = () => ( + + + + + ), + ListHeaderComponent: () => ( + + ), + }} + /> + + ); + + const renderCommentBox = () => ( + + ); + + return ( + + + {renderHeader()} + {renderCommentsList()} + {renderCommentBox()} + + ); }; const mapStateToProps = ( - // @ts-ignore - { organizations, celebrateComments }, + { + organizations, + celebrateComments, + }: { + organizations: OrganizationsState; + celebrateComments: CelebrateCommentsState; + }, { navigation: { state: { - // @ts-ignore - params: { event }, + params: { event, orgId }, }, }, - }, + }: // eslint-disable-next-line @typescript-eslint/no-explicit-any + any, ) => ({ - event: - celebrationItemSelector( - { organizations }, - { eventId: event.id, organizationId: event.organization.id }, - ) || event, - organization: organizationSelector( - { organizations }, - { orgId: event.organization.id }, - ), + organization: organizationSelector({ organizations }, { orgId }), celebrateComments: celebrateCommentsSelector( { celebrateComments }, { eventId: event.id }, diff --git a/src/containers/CelebrateFeed/__tests__/CelebrateFeed.tsx b/src/containers/CelebrateFeed/__tests__/CelebrateFeed.tsx index 13a18560ba..1275cccd6f 100644 --- a/src/containers/CelebrateFeed/__tests__/CelebrateFeed.tsx +++ b/src/containers/CelebrateFeed/__tests__/CelebrateFeed.tsx @@ -1,139 +1,205 @@ import React from 'react'; -import configureStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; +import { MockList } from 'graphql-tools'; +import { flushMicrotasksQueue } from 'react-native-testing-library'; +import { useQuery } from '@apollo/react-hooks'; import { navigatePush } from '../../../actions/navigation'; -import { renderShallow } from '../../../../testUtils'; -import { ACCEPTED_STEP } from '../../../constants'; +import { renderWithContext } from '../../../../testUtils'; +import { organizationSelector } from '../../../selectors/organizations'; +import { Organization } from '../../../reducers/organizations'; +import { Person } from '../../../reducers/people'; +import { GET_CELEBRATE_FEED } from '../queries'; import CelebrateFeed from '..'; jest.mock('../../../actions/navigation'); -jest.mock('../../../actions/celebration'); +jest.mock('../../../selectors/organizations'); +jest.mock('../../../components/common', () => ({ + DateComponent: 'DateComponent', +})); +jest.mock('../../../components/CelebrateItem', () => 'CelebrateItem'); +jest.mock('../../Groups/ShareStoryInput', () => 'ShareStoryInput'); +jest.mock('../../CelebrateFeedHeader', () => 'CelebrateFeedHeader'); const myId = '123'; -const organization = { id: '456' }; -const store = configureStore([thunk])({ auth: { person: { id: myId } } }); - -const celebrationItems = [ - { - date: '2018-03-01 12:00:00', - data: [ - { - id: '1', - subject_person_name: 'Roge Dog', - celebrateable_type: ACCEPTED_STEP, - likes_count: 0, - adjective_attribute_value: '2', - changed_attribute_value: '2018-03-01 12:00:00', - }, - { - id: '2', - subject_person_name: 'DG With me?', - celebrateable_type: 'interaction', - likes_count: 0, - adjective_attribute_value: '4', - changed_attribute_value: '2018-03-01 12:00:00', - }, - ], - }, - { - date: '2018-01-01 12:00:00', - data: [ - { - id: '4', - subject_person_name: 'Roge Dog', - celebrateable_type: ACCEPTED_STEP, - likes_count: 11, - adjective_attribute_value: '1', - changed_attribute_value: '2018-01-01 12:00:00', - }, - { - id: '3', - subject_person_name: 'DG With me?', - celebrateable_type: 'interaction', - likes_count: 42, - adjective_attribute_value: '5', - changed_attribute_value: '2018-01-01 12:00:00', - }, - ], - }, -]; +const organization: Organization = { id: '456' }; +const person: Person = { id: '789' }; const navigatePushResult = { type: 'navigated' }; -// @ts-ignore -let component; - -// @ts-ignore -navigatePush.mockReturnValue(dispatch => dispatch(navigatePushResult)); +const initialState = { + auth: { person: { id: myId } }, + organizations: { all: [organization] }, + reportedComments: { all: { [organization.id]: [] } }, + swipe: { groupOnboarding: {} }, +}; beforeEach(() => { - component = renderShallow( - // @ts-ignore - , - store, + (navigatePush as jest.Mock).mockReturnValue(navigatePushResult); + ((organizationSelector as unknown) as jest.Mock).mockReturnValue( + organization, ); + jest.useFakeTimers(); +}); + +it('renders empty correctly', () => { + renderWithContext( + , + { + initialState, + mocks: { + CommunityCelebrationItemConnection: () => ({ + nodes: () => new MockList(10), + }), + }, + }, + ).snapshot(); }); -describe('Member Feed rendering', () => { - it('renders correctly for member feed', () => { - // @ts-ignore - expect(component).toMatchSnapshot(); +it('renders with celebration items correctly', async () => { + const { snapshot } = renderWithContext( + , + { + initialState, + mocks: { + CommunityCelebrationItemConnection: () => ({ + nodes: () => new MockList(10), + }), + }, + }, + ); + + await flushMicrotasksQueue(); + snapshot(); + expect(useQuery).toHaveBeenCalledWith(GET_CELEBRATE_FEED, { + pollInterval: 30000, + variables: { + communityId: organization.id, + hasUnreadComments: undefined, + personIds: undefined, + }, }); }); -describe('no header rendering', () => { - it('renders correctly for no header', () => { - component = renderShallow( +describe('renders for member', () => { + it('renders correctly', async () => { + const { snapshot } = renderWithContext( , - store, + { + initialState, + mocks: { + CommunityCelebrationItemConnection: () => ({ + nodes: () => new MockList(10), + }), + }, + }, ); - expect(component).toMatchSnapshot(); + + await flushMicrotasksQueue(); + snapshot(); + expect(useQuery).toHaveBeenCalledWith(GET_CELEBRATE_FEED, { + pollInterval: 30000, + variables: { + communityId: organization.id, + hasUnreadComments: undefined, + personIds: [person.id], + }, + }); }); -}); -describe('renders with clear notification set', () => { - it('renders correctly with clear notification set', () => { - component = renderShallow( + it('renders without header', async () => { + const { snapshot } = renderWithContext( , - store, + { + initialState, + mocks: { + CommunityCelebrationItemConnection: () => ({ + nodes: () => new MockList(10), + }), + }, + }, ); - expect(component).toMatchSnapshot(); + + await flushMicrotasksQueue(); + snapshot(); + expect(useQuery).toHaveBeenCalledWith(GET_CELEBRATE_FEED, { + pollInterval: 30000, + variables: { + communityId: organization.id, + hasUnreadComments: undefined, + personIds: [person.id], + }, + }); }); }); -it('renders section header', () => { - // @ts-ignore - const renderedItem = component - .instance() - .renderSectionHeader({ section: { date: '2018-08-13T12:00:00.000Z' } }); - expect(renderedItem).toMatchSnapshot(); -}); +describe('renders with clear notification', () => { + it('renders correctly', async () => { + const { snapshot } = renderWithContext( + , + { + initialState, + mocks: { + CommunityCelebrationItemConnection: () => ({ + nodes: () => new MockList(10), + }), + }, + }, + ); -describe('item', () => { - it('renders correctly', () => { - // @ts-ignore - const renderedItem = component - .instance() - .renderItem({ item: celebrationItems[0] }); - expect(renderedItem).toMatchSnapshot(); + await flushMicrotasksQueue(); + snapshot(); + expect(useQuery).toHaveBeenCalledWith(GET_CELEBRATE_FEED, { + pollInterval: 30000, + variables: { + communityId: organization.id, + hasUnreadComments: undefined, + personIds: undefined, + }, + }); }); }); -it('renderHeader match snapshot', () => { - // @ts-ignore - const header = component.instance().renderHeader(); - expect(header).toMatchSnapshot(); +describe('renders for Unread Comments', () => { + it('renders correctly', async () => { + const { snapshot } = renderWithContext( + , + { + initialState, + mocks: { + CommunityCelebrationItemConnection: () => ({ + nodes: () => new MockList(10), + }), + }, + }, + ); + + await flushMicrotasksQueue(); + snapshot(); + expect(useQuery).toHaveBeenCalledWith(GET_CELEBRATE_FEED, { + pollInterval: 30000, + variables: { + communityId: organization.id, + hasUnreadComments: true, + personIds: undefined, + }, + }); + }); }); diff --git a/src/containers/CelebrateFeed/__tests__/__snapshots__/CelebrateFeed.tsx.snap b/src/containers/CelebrateFeed/__tests__/__snapshots__/CelebrateFeed.tsx.snap index 672deb9821..83fb6c164c 100644 --- a/src/containers/CelebrateFeed/__tests__/__snapshots__/CelebrateFeed.tsx.snap +++ b/src/containers/CelebrateFeed/__tests__/__snapshots__/CelebrateFeed.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Member Feed rendering renders correctly for member feed 1`] = ` - + } + refreshing={true} + renderItem={[Function]} + renderSectionHeader={[Function]} + scrollEventThrottle={50} + sections={Array []} + stickyHeaderIndices={Array []} + stickySectionHeadersEnabled={true} + style={ + Object { + "backgroundColor": "#ECEEF2", + "flex": 1, + } + } + updateCellsBatchingPeriod={50} + windowSize={21} +> + + + + + + + + +`; + +exports[`renders for Unread Comments renders correctly 1`] = ` + + } + refreshing={false} + renderItem={[Function]} + renderSectionHeader={[Function]} + scrollEventThrottle={50} + sections={ + Array [ + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "sit vitae soluta", + "adjectiveAttributeValue": "sint non voluptates", + "celebrateableId": "5", + "celebrateableType": "V4::AcceptedCommunityChallenge", + "changedAttributeName": "ut ullam quos", + "changedAttributeValue": "2019-02-10T23:08:19.603Z", + "commentsCount": 58438, + "id": "4", + "liked": false, + "likesCount": 56591, + "objectDescription": "sunt laborum ratione", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Vern", + "id": "6", + "lastName": "McKenzie", + }, + "subjectPersonName": "Zola Corkery", + }, + ], + "date": "2019-02-10T23:08:19.603Z", + "id": 0, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "quasi ipsum rem", + "adjectiveAttributeValue": "eos quod recusandae", + "celebrateableId": "26", + "celebrateableType": "V4::Story", + "changedAttributeName": "consectetur optio laudantium", + "changedAttributeValue": "2018-01-20T22:18:35.430Z", + "commentsCount": 6600, + "id": "25", + "liked": false, + "likesCount": 75546, + "objectDescription": "molestias facere quia", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Therese", + "id": "27", + "lastName": "Beatty", + }, + "subjectPersonName": "Hipolito Connelly", + }, + ], + "date": "2018-01-20T22:18:35.430Z", + "id": 1, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "ullam minima ducimus", + "adjectiveAttributeValue": "temporibus modi aut", + "celebrateableId": "2", + "celebrateableType": "V4::AcceptedChallenge", + "changedAttributeName": "architecto ducimus totam", + "changedAttributeValue": "2017-01-05T11:40:00.737Z", + "commentsCount": 90859, + "id": "1", + "liked": false, + "likesCount": 29361, + "objectDescription": "ut quis sed", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Breana", + "id": "3", + "lastName": "Ledner", + }, + "subjectPersonName": "Torey Abbott", + }, + ], + "date": "2017-01-05T11:40:00.737Z", + "id": 2, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "quaerat cumque incidunt", + "adjectiveAttributeValue": "aut provident esse", + "celebrateableId": "8", + "celebrateableType": "V4::AcceptedCommunityChallenge", + "changedAttributeName": "hic eligendi quos", + "changedAttributeValue": "2016-07-31T16:52:49.085Z", + "commentsCount": 41417, + "id": "7", + "liked": true, + "likesCount": 4995, + "objectDescription": "voluptas sed quae", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Malcolm", + "id": "9", + "lastName": "Connelly", + }, + "subjectPersonName": "Grace Dibbert", + }, + ], + "date": "2016-07-31T16:52:49.085Z", + "id": 3, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "sint autem rerum", + "adjectiveAttributeValue": "doloremque officia aut", + "celebrateableId": "11", + "celebrateableType": "V4::Story", + "changedAttributeName": "ut ut eos", + "changedAttributeValue": "2014-08-26T07:39:14.970Z", + "commentsCount": 44798, + "id": "10", + "liked": false, + "likesCount": 89213, + "objectDescription": "consequatur vel in", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Kaley", + "id": "12", + "lastName": "Cummerata", + }, + "subjectPersonName": "Marcellus Hyatt", + }, + ], + "date": "2014-08-26T07:39:14.970Z", + "id": 4, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "non aut rerum", + "adjectiveAttributeValue": "impedit ex rem", + "celebrateableId": "23", + "celebrateableType": "V4::Organization", + "changedAttributeName": "voluptates voluptas fuga", + "changedAttributeValue": "2014-06-14T18:46:15.611Z", + "commentsCount": 46739, + "id": "22", + "liked": false, + "likesCount": 26900, + "objectDescription": "non fugiat eligendi", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Kathryne", + "id": "24", + "lastName": "Zieme", + }, + "subjectPersonName": "Tatyana Johns", + }, + ], + "date": "2014-06-14T18:46:15.611Z", + "id": 5, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "ex culpa earum", + "adjectiveAttributeValue": "voluptate vel labore", + "celebrateableId": "20", + "celebrateableType": "V4::Organization", + "changedAttributeName": "omnis ut est", + "changedAttributeValue": "2013-05-02T19:02:46.486Z", + "commentsCount": 57367, + "id": "19", + "liked": false, + "likesCount": 287, + "objectDescription": "est et sequi", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Ericka", + "id": "21", + "lastName": "Mayert", + }, + "subjectPersonName": "Daphney Balistreri", + }, + ], + "date": "2013-05-02T19:02:46.486Z", + "id": 6, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "et molestiae ea", + "adjectiveAttributeValue": "ut sunt placeat", + "celebrateableId": "14", + "celebrateableType": "V4::AcceptedCommunityChallenge", + "changedAttributeName": "quidem est consequuntur", + "changedAttributeValue": "2013-03-09T22:46:17.951Z", + "commentsCount": 88330, + "id": "13", + "liked": true, + "likesCount": 62367, + "objectDescription": "est placeat ex", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Favian", + "id": "15", + "lastName": "Ruecker", + }, + "subjectPersonName": "Roscoe Johnson", + }, + ], + "date": "2013-03-09T22:46:17.951Z", + "id": 7, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "enim ex eveniet", + "adjectiveAttributeValue": "facere molestiae aut", + "celebrateableId": "17", + "celebrateableType": "V4::Organization", + "changedAttributeName": "delectus aut nam", + "changedAttributeValue": "2011-04-05T20:54:44.943Z", + "commentsCount": 49810, + "id": "16", + "liked": true, + "likesCount": 7379, + "objectDescription": "fugit repellendus hic", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Anissa", + "id": "18", + "lastName": "Kirlin", + }, + "subjectPersonName": "Judge Doyle", + }, + ], + "date": "2011-04-05T20:54:44.943Z", + "id": 8, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "quo occaecati quia", + "adjectiveAttributeValue": "ipsum sit qui", + "celebrateableId": "29", + "celebrateableType": "V4::OrganizationalPermission", + "changedAttributeName": "accusantium odit doloremque", + "changedAttributeValue": "2010-05-11T20:17:10.674Z", + "commentsCount": 50981, + "id": "28", + "liked": false, + "likesCount": 10213, + "objectDescription": "eos quis ut", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Dario", + "id": "30", + "lastName": "Lueilwitz", + }, + "subjectPersonName": "Orlando Schmidt", + }, + ], + "date": "2010-05-11T20:17:10.674Z", + "id": 9, + }, + ] + } + stickyHeaderIndices={ + Array [ + 1, + 4, + 7, + 10, + ] + } + stickySectionHeadersEnabled={true} + style={ + Object { + "backgroundColor": "#ECEEF2", + "flex": 1, + } + } + updateCellsBatchingPeriod={50} + windowSize={21} +> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; + +exports[`renders for member renders correctly 1`] = ` + + } + refreshing={false} + renderItem={[Function]} + renderSectionHeader={[Function]} + scrollEventThrottle={50} + sections={ + Array [ + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "sit vitae soluta", + "adjectiveAttributeValue": "sint non voluptates", + "celebrateableId": "5", + "celebrateableType": "V4::AcceptedCommunityChallenge", + "changedAttributeName": "ut ullam quos", + "changedAttributeValue": "2019-02-10T23:08:19.603Z", + "commentsCount": 58438, + "id": "4", + "liked": false, + "likesCount": 56591, + "objectDescription": "sunt laborum ratione", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Vern", + "id": "6", + "lastName": "McKenzie", + }, + "subjectPersonName": "Zola Corkery", + }, + ], + "date": "2019-02-10T23:08:19.603Z", + "id": 0, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "quasi ipsum rem", + "adjectiveAttributeValue": "eos quod recusandae", + "celebrateableId": "26", + "celebrateableType": "V4::Story", + "changedAttributeName": "consectetur optio laudantium", + "changedAttributeValue": "2018-01-20T22:18:35.430Z", + "commentsCount": 6600, + "id": "25", + "liked": false, + "likesCount": 75546, + "objectDescription": "molestias facere quia", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Therese", + "id": "27", + "lastName": "Beatty", + }, + "subjectPersonName": "Hipolito Connelly", + }, + ], + "date": "2018-01-20T22:18:35.430Z", + "id": 1, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "ullam minima ducimus", + "adjectiveAttributeValue": "temporibus modi aut", + "celebrateableId": "2", + "celebrateableType": "V4::AcceptedChallenge", + "changedAttributeName": "architecto ducimus totam", + "changedAttributeValue": "2017-01-05T11:40:00.737Z", + "commentsCount": 90859, + "id": "1", + "liked": false, + "likesCount": 29361, + "objectDescription": "ut quis sed", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Breana", + "id": "3", + "lastName": "Ledner", + }, + "subjectPersonName": "Torey Abbott", + }, + ], + "date": "2017-01-05T11:40:00.737Z", + "id": 2, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "quaerat cumque incidunt", + "adjectiveAttributeValue": "aut provident esse", + "celebrateableId": "8", + "celebrateableType": "V4::AcceptedCommunityChallenge", + "changedAttributeName": "hic eligendi quos", + "changedAttributeValue": "2016-07-31T16:52:49.085Z", + "commentsCount": 41417, + "id": "7", + "liked": true, + "likesCount": 4995, + "objectDescription": "voluptas sed quae", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Malcolm", + "id": "9", + "lastName": "Connelly", + }, + "subjectPersonName": "Grace Dibbert", + }, + ], + "date": "2016-07-31T16:52:49.085Z", + "id": 3, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "sint autem rerum", + "adjectiveAttributeValue": "doloremque officia aut", + "celebrateableId": "11", + "celebrateableType": "V4::Story", + "changedAttributeName": "ut ut eos", + "changedAttributeValue": "2014-08-26T07:39:14.970Z", + "commentsCount": 44798, + "id": "10", + "liked": false, + "likesCount": 89213, + "objectDescription": "consequatur vel in", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Kaley", + "id": "12", + "lastName": "Cummerata", + }, + "subjectPersonName": "Marcellus Hyatt", + }, + ], + "date": "2014-08-26T07:39:14.970Z", + "id": 4, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "non aut rerum", + "adjectiveAttributeValue": "impedit ex rem", + "celebrateableId": "23", + "celebrateableType": "V4::Organization", + "changedAttributeName": "voluptates voluptas fuga", + "changedAttributeValue": "2014-06-14T18:46:15.611Z", + "commentsCount": 46739, + "id": "22", + "liked": false, + "likesCount": 26900, + "objectDescription": "non fugiat eligendi", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Kathryne", + "id": "24", + "lastName": "Zieme", + }, + "subjectPersonName": "Tatyana Johns", + }, + ], + "date": "2014-06-14T18:46:15.611Z", + "id": 5, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "ex culpa earum", + "adjectiveAttributeValue": "voluptate vel labore", + "celebrateableId": "20", + "celebrateableType": "V4::Organization", + "changedAttributeName": "omnis ut est", + "changedAttributeValue": "2013-05-02T19:02:46.486Z", + "commentsCount": 57367, + "id": "19", + "liked": false, + "likesCount": 287, + "objectDescription": "est et sequi", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Ericka", + "id": "21", + "lastName": "Mayert", + }, + "subjectPersonName": "Daphney Balistreri", + }, + ], + "date": "2013-05-02T19:02:46.486Z", + "id": 6, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "et molestiae ea", + "adjectiveAttributeValue": "ut sunt placeat", + "celebrateableId": "14", + "celebrateableType": "V4::AcceptedCommunityChallenge", + "changedAttributeName": "quidem est consequuntur", + "changedAttributeValue": "2013-03-09T22:46:17.951Z", + "commentsCount": 88330, + "id": "13", + "liked": true, + "likesCount": 62367, + "objectDescription": "est placeat ex", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Favian", + "id": "15", + "lastName": "Ruecker", + }, + "subjectPersonName": "Roscoe Johnson", + }, + ], + "date": "2013-03-09T22:46:17.951Z", + "id": 7, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "enim ex eveniet", + "adjectiveAttributeValue": "facere molestiae aut", + "celebrateableId": "17", + "celebrateableType": "V4::Organization", + "changedAttributeName": "delectus aut nam", + "changedAttributeValue": "2011-04-05T20:54:44.943Z", + "commentsCount": 49810, + "id": "16", + "liked": true, + "likesCount": 7379, + "objectDescription": "fugit repellendus hic", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Anissa", + "id": "18", + "lastName": "Kirlin", + }, + "subjectPersonName": "Judge Doyle", + }, + ], + "date": "2011-04-05T20:54:44.943Z", + "id": 8, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "quo occaecati quia", + "adjectiveAttributeValue": "ipsum sit qui", + "celebrateableId": "29", + "celebrateableType": "V4::OrganizationalPermission", + "changedAttributeName": "accusantium odit doloremque", + "changedAttributeValue": "2010-05-11T20:17:10.674Z", + "commentsCount": 50981, + "id": "28", + "liked": false, + "likesCount": 10213, + "objectDescription": "eos quis ut", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Dario", + "id": "30", + "lastName": "Lueilwitz", + }, + "subjectPersonName": "Orlando Schmidt", + }, + ], + "date": "2010-05-11T20:17:10.674Z", + "id": 9, + }, + ] + } + stickyHeaderIndices={ + Array [ + 1, + 4, + 7, + 10, + ] + } + stickySectionHeadersEnabled={true} + style={ + Object { + "backgroundColor": "#ECEEF2", + "flex": 1, + } + } + updateCellsBatchingPeriod={50} + windowSize={21} +> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; + +exports[`renders for member renders without header 1`] = ` + + } + refreshing={false} + renderItem={[Function]} + renderSectionHeader={[Function]} + scrollEventThrottle={50} + sections={ + Array [ + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "sit vitae soluta", + "adjectiveAttributeValue": "sint non voluptates", + "celebrateableId": "5", + "celebrateableType": "V4::AcceptedCommunityChallenge", + "changedAttributeName": "ut ullam quos", + "changedAttributeValue": "2019-02-10T23:08:19.603Z", + "commentsCount": 58438, + "id": "4", + "liked": false, + "likesCount": 56591, + "objectDescription": "sunt laborum ratione", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Vern", + "id": "6", + "lastName": "McKenzie", + }, + "subjectPersonName": "Zola Corkery", + }, + ], + "date": "2019-02-10T23:08:19.603Z", + "id": 0, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "quasi ipsum rem", + "adjectiveAttributeValue": "eos quod recusandae", + "celebrateableId": "26", + "celebrateableType": "V4::Story", + "changedAttributeName": "consectetur optio laudantium", + "changedAttributeValue": "2018-01-20T22:18:35.430Z", + "commentsCount": 6600, + "id": "25", + "liked": false, + "likesCount": 75546, + "objectDescription": "molestias facere quia", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Therese", + "id": "27", + "lastName": "Beatty", + }, + "subjectPersonName": "Hipolito Connelly", + }, + ], + "date": "2018-01-20T22:18:35.430Z", + "id": 1, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "ullam minima ducimus", + "adjectiveAttributeValue": "temporibus modi aut", + "celebrateableId": "2", + "celebrateableType": "V4::AcceptedChallenge", + "changedAttributeName": "architecto ducimus totam", + "changedAttributeValue": "2017-01-05T11:40:00.737Z", + "commentsCount": 90859, + "id": "1", + "liked": false, + "likesCount": 29361, + "objectDescription": "ut quis sed", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Breana", + "id": "3", + "lastName": "Ledner", + }, + "subjectPersonName": "Torey Abbott", + }, + ], + "date": "2017-01-05T11:40:00.737Z", + "id": 2, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "quaerat cumque incidunt", + "adjectiveAttributeValue": "aut provident esse", + "celebrateableId": "8", + "celebrateableType": "V4::AcceptedCommunityChallenge", + "changedAttributeName": "hic eligendi quos", + "changedAttributeValue": "2016-07-31T16:52:49.085Z", + "commentsCount": 41417, + "id": "7", + "liked": true, + "likesCount": 4995, + "objectDescription": "voluptas sed quae", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Malcolm", + "id": "9", + "lastName": "Connelly", + }, + "subjectPersonName": "Grace Dibbert", + }, + ], + "date": "2016-07-31T16:52:49.085Z", + "id": 3, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "sint autem rerum", + "adjectiveAttributeValue": "doloremque officia aut", + "celebrateableId": "11", + "celebrateableType": "V4::Story", + "changedAttributeName": "ut ut eos", + "changedAttributeValue": "2014-08-26T07:39:14.970Z", + "commentsCount": 44798, + "id": "10", + "liked": false, + "likesCount": 89213, + "objectDescription": "consequatur vel in", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Kaley", + "id": "12", + "lastName": "Cummerata", + }, + "subjectPersonName": "Marcellus Hyatt", + }, + ], + "date": "2014-08-26T07:39:14.970Z", + "id": 4, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "non aut rerum", + "adjectiveAttributeValue": "impedit ex rem", + "celebrateableId": "23", + "celebrateableType": "V4::Organization", + "changedAttributeName": "voluptates voluptas fuga", + "changedAttributeValue": "2014-06-14T18:46:15.611Z", + "commentsCount": 46739, + "id": "22", + "liked": false, + "likesCount": 26900, + "objectDescription": "non fugiat eligendi", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Kathryne", + "id": "24", + "lastName": "Zieme", + }, + "subjectPersonName": "Tatyana Johns", + }, + ], + "date": "2014-06-14T18:46:15.611Z", + "id": 5, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "ex culpa earum", + "adjectiveAttributeValue": "voluptate vel labore", + "celebrateableId": "20", + "celebrateableType": "V4::Organization", + "changedAttributeName": "omnis ut est", + "changedAttributeValue": "2013-05-02T19:02:46.486Z", + "commentsCount": 57367, + "id": "19", + "liked": false, + "likesCount": 287, + "objectDescription": "est et sequi", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Ericka", + "id": "21", + "lastName": "Mayert", + }, + "subjectPersonName": "Daphney Balistreri", + }, + ], + "date": "2013-05-02T19:02:46.486Z", + "id": 6, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "et molestiae ea", + "adjectiveAttributeValue": "ut sunt placeat", + "celebrateableId": "14", + "celebrateableType": "V4::AcceptedCommunityChallenge", + "changedAttributeName": "quidem est consequuntur", + "changedAttributeValue": "2013-03-09T22:46:17.951Z", + "commentsCount": 88330, + "id": "13", + "liked": true, + "likesCount": 62367, + "objectDescription": "est placeat ex", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Favian", + "id": "15", + "lastName": "Ruecker", + }, + "subjectPersonName": "Roscoe Johnson", + }, + ], + "date": "2013-03-09T22:46:17.951Z", + "id": 7, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "enim ex eveniet", + "adjectiveAttributeValue": "facere molestiae aut", + "celebrateableId": "17", + "celebrateableType": "V4::Organization", + "changedAttributeName": "delectus aut nam", + "changedAttributeValue": "2011-04-05T20:54:44.943Z", + "commentsCount": 49810, + "id": "16", + "liked": true, + "likesCount": 7379, + "objectDescription": "fugit repellendus hic", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Anissa", + "id": "18", + "lastName": "Kirlin", + }, + "subjectPersonName": "Judge Doyle", + }, + ], + "date": "2011-04-05T20:54:44.943Z", + "id": 8, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "quo occaecati quia", + "adjectiveAttributeValue": "ipsum sit qui", + "celebrateableId": "29", + "celebrateableType": "V4::OrganizationalPermission", + "changedAttributeName": "accusantium odit doloremque", + "changedAttributeValue": "2010-05-11T20:17:10.674Z", + "commentsCount": 50981, + "id": "28", + "liked": false, + "likesCount": 10213, + "objectDescription": "eos quis ut", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Dario", + "id": "30", + "lastName": "Lueilwitz", + }, + "subjectPersonName": "Orlando Schmidt", + }, + ], + "date": "2010-05-11T20:17:10.674Z", + "id": 9, + }, + ] + } + stickyHeaderIndices={ + Array [ + 0, + 3, + 6, + 9, + ] + } + stickySectionHeadersEnabled={true} + style={ + Object { + "backgroundColor": "#ECEEF2", + "flex": 1, + } + } + updateCellsBatchingPeriod={50} + windowSize={21} +> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; + +exports[`renders with celebration items correctly 1`] = ` + + } refreshing={false} renderItem={[Function]} renderSectionHeader={[Function]} @@ -32,241 +2960,862 @@ exports[`Member Feed rendering renders correctly for member feed 1`] = ` Object { "data": Array [ Object { - "adjective_attribute_value": "2", - "celebrateable_type": "accepted_challenge", - "changed_attribute_value": "2018-03-01 12:00:00", + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "sit vitae soluta", + "adjectiveAttributeValue": "sint non voluptates", + "celebrateableId": "5", + "celebrateableType": "V4::AcceptedCommunityChallenge", + "changedAttributeName": "ut ullam quos", + "changedAttributeValue": "2019-02-10T23:08:19.603Z", + "commentsCount": 58438, + "id": "4", + "liked": false, + "likesCount": 56591, + "objectDescription": "sunt laborum ratione", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Vern", + "id": "6", + "lastName": "McKenzie", + }, + "subjectPersonName": "Zola Corkery", + }, + ], + "date": "2019-02-10T23:08:19.603Z", + "id": 0, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "quasi ipsum rem", + "adjectiveAttributeValue": "eos quod recusandae", + "celebrateableId": "26", + "celebrateableType": "V4::Story", + "changedAttributeName": "consectetur optio laudantium", + "changedAttributeValue": "2018-01-20T22:18:35.430Z", + "commentsCount": 6600, + "id": "25", + "liked": false, + "likesCount": 75546, + "objectDescription": "molestias facere quia", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Therese", + "id": "27", + "lastName": "Beatty", + }, + "subjectPersonName": "Hipolito Connelly", + }, + ], + "date": "2018-01-20T22:18:35.430Z", + "id": 1, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "ullam minima ducimus", + "adjectiveAttributeValue": "temporibus modi aut", + "celebrateableId": "2", + "celebrateableType": "V4::AcceptedChallenge", + "changedAttributeName": "architecto ducimus totam", + "changedAttributeValue": "2017-01-05T11:40:00.737Z", + "commentsCount": 90859, "id": "1", - "likes_count": 0, - "subject_person_name": "Roge Dog", + "liked": false, + "likesCount": 29361, + "objectDescription": "ut quis sed", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Breana", + "id": "3", + "lastName": "Ledner", + }, + "subjectPersonName": "Torey Abbott", + }, + ], + "date": "2017-01-05T11:40:00.737Z", + "id": 2, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "quaerat cumque incidunt", + "adjectiveAttributeValue": "aut provident esse", + "celebrateableId": "8", + "celebrateableType": "V4::AcceptedCommunityChallenge", + "changedAttributeName": "hic eligendi quos", + "changedAttributeValue": "2016-07-31T16:52:49.085Z", + "commentsCount": 41417, + "id": "7", + "liked": true, + "likesCount": 4995, + "objectDescription": "voluptas sed quae", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Malcolm", + "id": "9", + "lastName": "Connelly", + }, + "subjectPersonName": "Grace Dibbert", }, + ], + "date": "2016-07-31T16:52:49.085Z", + "id": 3, + }, + Object { + "data": Array [ Object { - "adjective_attribute_value": "4", - "celebrateable_type": "interaction", - "changed_attribute_value": "2018-03-01 12:00:00", - "id": "2", - "likes_count": 0, - "subject_person_name": "DG With me?", + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "sint autem rerum", + "adjectiveAttributeValue": "doloremque officia aut", + "celebrateableId": "11", + "celebrateableType": "V4::Story", + "changedAttributeName": "ut ut eos", + "changedAttributeValue": "2014-08-26T07:39:14.970Z", + "commentsCount": 44798, + "id": "10", + "liked": false, + "likesCount": 89213, + "objectDescription": "consequatur vel in", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Kaley", + "id": "12", + "lastName": "Cummerata", + }, + "subjectPersonName": "Marcellus Hyatt", }, ], - "date": "2018-03-01 12:00:00", + "date": "2014-08-26T07:39:14.970Z", + "id": 4, }, Object { "data": Array [ Object { - "adjective_attribute_value": "1", - "celebrateable_type": "accepted_challenge", - "changed_attribute_value": "2018-01-01 12:00:00", - "id": "4", - "likes_count": 11, - "subject_person_name": "Roge Dog", + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "non aut rerum", + "adjectiveAttributeValue": "impedit ex rem", + "celebrateableId": "23", + "celebrateableType": "V4::Organization", + "changedAttributeName": "voluptates voluptas fuga", + "changedAttributeValue": "2014-06-14T18:46:15.611Z", + "commentsCount": 46739, + "id": "22", + "liked": false, + "likesCount": 26900, + "objectDescription": "non fugiat eligendi", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Kathryne", + "id": "24", + "lastName": "Zieme", + }, + "subjectPersonName": "Tatyana Johns", + }, + ], + "date": "2014-06-14T18:46:15.611Z", + "id": 5, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "ex culpa earum", + "adjectiveAttributeValue": "voluptate vel labore", + "celebrateableId": "20", + "celebrateableType": "V4::Organization", + "changedAttributeName": "omnis ut est", + "changedAttributeValue": "2013-05-02T19:02:46.486Z", + "commentsCount": 57367, + "id": "19", + "liked": false, + "likesCount": 287, + "objectDescription": "est et sequi", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Ericka", + "id": "21", + "lastName": "Mayert", + }, + "subjectPersonName": "Daphney Balistreri", + }, + ], + "date": "2013-05-02T19:02:46.486Z", + "id": 6, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "et molestiae ea", + "adjectiveAttributeValue": "ut sunt placeat", + "celebrateableId": "14", + "celebrateableType": "V4::AcceptedCommunityChallenge", + "changedAttributeName": "quidem est consequuntur", + "changedAttributeValue": "2013-03-09T22:46:17.951Z", + "commentsCount": 88330, + "id": "13", + "liked": true, + "likesCount": 62367, + "objectDescription": "est placeat ex", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Favian", + "id": "15", + "lastName": "Ruecker", + }, + "subjectPersonName": "Roscoe Johnson", + }, + ], + "date": "2013-03-09T22:46:17.951Z", + "id": 7, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "enim ex eveniet", + "adjectiveAttributeValue": "facere molestiae aut", + "celebrateableId": "17", + "celebrateableType": "V4::Organization", + "changedAttributeName": "delectus aut nam", + "changedAttributeValue": "2011-04-05T20:54:44.943Z", + "commentsCount": 49810, + "id": "16", + "liked": true, + "likesCount": 7379, + "objectDescription": "fugit repellendus hic", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Anissa", + "id": "18", + "lastName": "Kirlin", + }, + "subjectPersonName": "Judge Doyle", }, + ], + "date": "2011-04-05T20:54:44.943Z", + "id": 8, + }, + Object { + "data": Array [ Object { - "adjective_attribute_value": "5", - "celebrateable_type": "interaction", - "changed_attribute_value": "2018-01-01 12:00:00", - "id": "3", - "likes_count": 42, - "subject_person_name": "DG With me?", + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "quo occaecati quia", + "adjectiveAttributeValue": "ipsum sit qui", + "celebrateableId": "29", + "celebrateableType": "V4::OrganizationalPermission", + "changedAttributeName": "accusantium odit doloremque", + "changedAttributeValue": "2010-05-11T20:17:10.674Z", + "commentsCount": 50981, + "id": "28", + "liked": false, + "likesCount": 10213, + "objectDescription": "eos quis ut", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Dario", + "id": "30", + "lastName": "Lueilwitz", + }, + "subjectPersonName": "Orlando Schmidt", }, ], - "date": "2018-01-01 12:00:00", + "date": "2010-05-11T20:17:10.674Z", + "id": 9, }, ] } + stickyHeaderIndices={ + Array [ + 1, + 4, + 7, + 10, + ] + } stickySectionHeadersEnabled={true} style={ Object { "backgroundColor": "#ECEEF2", + "flex": 1, } } updateCellsBatchingPeriod={50} windowSize={21} -/> -`; - -exports[`item renders correctly 1`] = ` - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "height": 0, + } + } + /> + + `; -exports[`no header rendering renders correctly for no header 1`] = ` - -`; - -exports[`renderHeader match snapshot 1`] = ` - - - - -`; - -exports[`renders section header 1`] = ` - - - -`; - -exports[`renders with clear notification set renders correctly with clear notification set 1`] = ` - + } refreshing={false} renderItem={[Function]} renderSectionHeader={[Function]} @@ -276,54 +3825,557 @@ exports[`renders with clear notification set renders correctly with clear notifi Object { "data": Array [ Object { - "adjective_attribute_value": "2", - "celebrateable_type": "accepted_challenge", - "changed_attribute_value": "2018-03-01 12:00:00", + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "sit vitae soluta", + "adjectiveAttributeValue": "sint non voluptates", + "celebrateableId": "5", + "celebrateableType": "V4::AcceptedCommunityChallenge", + "changedAttributeName": "ut ullam quos", + "changedAttributeValue": "2019-02-10T23:08:19.603Z", + "commentsCount": 58438, + "id": "4", + "liked": false, + "likesCount": 56591, + "objectDescription": "sunt laborum ratione", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Vern", + "id": "6", + "lastName": "McKenzie", + }, + "subjectPersonName": "Zola Corkery", + }, + ], + "date": "2019-02-10T23:08:19.603Z", + "id": 0, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "quasi ipsum rem", + "adjectiveAttributeValue": "eos quod recusandae", + "celebrateableId": "26", + "celebrateableType": "V4::Story", + "changedAttributeName": "consectetur optio laudantium", + "changedAttributeValue": "2018-01-20T22:18:35.430Z", + "commentsCount": 6600, + "id": "25", + "liked": false, + "likesCount": 75546, + "objectDescription": "molestias facere quia", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Therese", + "id": "27", + "lastName": "Beatty", + }, + "subjectPersonName": "Hipolito Connelly", + }, + ], + "date": "2018-01-20T22:18:35.430Z", + "id": 1, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "ullam minima ducimus", + "adjectiveAttributeValue": "temporibus modi aut", + "celebrateableId": "2", + "celebrateableType": "V4::AcceptedChallenge", + "changedAttributeName": "architecto ducimus totam", + "changedAttributeValue": "2017-01-05T11:40:00.737Z", + "commentsCount": 90859, "id": "1", - "likes_count": 0, - "subject_person_name": "Roge Dog", + "liked": false, + "likesCount": 29361, + "objectDescription": "ut quis sed", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Breana", + "id": "3", + "lastName": "Ledner", + }, + "subjectPersonName": "Torey Abbott", }, + ], + "date": "2017-01-05T11:40:00.737Z", + "id": 2, + }, + Object { + "data": Array [ Object { - "adjective_attribute_value": "4", - "celebrateable_type": "interaction", - "changed_attribute_value": "2018-03-01 12:00:00", - "id": "2", - "likes_count": 0, - "subject_person_name": "DG With me?", + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "quaerat cumque incidunt", + "adjectiveAttributeValue": "aut provident esse", + "celebrateableId": "8", + "celebrateableType": "V4::AcceptedCommunityChallenge", + "changedAttributeName": "hic eligendi quos", + "changedAttributeValue": "2016-07-31T16:52:49.085Z", + "commentsCount": 41417, + "id": "7", + "liked": true, + "likesCount": 4995, + "objectDescription": "voluptas sed quae", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Malcolm", + "id": "9", + "lastName": "Connelly", + }, + "subjectPersonName": "Grace Dibbert", }, ], - "date": "2018-03-01 12:00:00", + "date": "2016-07-31T16:52:49.085Z", + "id": 3, }, Object { "data": Array [ Object { - "adjective_attribute_value": "1", - "celebrateable_type": "accepted_challenge", - "changed_attribute_value": "2018-01-01 12:00:00", - "id": "4", - "likes_count": 11, - "subject_person_name": "Roge Dog", + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "sint autem rerum", + "adjectiveAttributeValue": "doloremque officia aut", + "celebrateableId": "11", + "celebrateableType": "V4::Story", + "changedAttributeName": "ut ut eos", + "changedAttributeValue": "2014-08-26T07:39:14.970Z", + "commentsCount": 44798, + "id": "10", + "liked": false, + "likesCount": 89213, + "objectDescription": "consequatur vel in", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Kaley", + "id": "12", + "lastName": "Cummerata", + }, + "subjectPersonName": "Marcellus Hyatt", + }, + ], + "date": "2014-08-26T07:39:14.970Z", + "id": 4, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "non aut rerum", + "adjectiveAttributeValue": "impedit ex rem", + "celebrateableId": "23", + "celebrateableType": "V4::Organization", + "changedAttributeName": "voluptates voluptas fuga", + "changedAttributeValue": "2014-06-14T18:46:15.611Z", + "commentsCount": 46739, + "id": "22", + "liked": false, + "likesCount": 26900, + "objectDescription": "non fugiat eligendi", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Kathryne", + "id": "24", + "lastName": "Zieme", + }, + "subjectPersonName": "Tatyana Johns", + }, + ], + "date": "2014-06-14T18:46:15.611Z", + "id": 5, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "ex culpa earum", + "adjectiveAttributeValue": "voluptate vel labore", + "celebrateableId": "20", + "celebrateableType": "V4::Organization", + "changedAttributeName": "omnis ut est", + "changedAttributeValue": "2013-05-02T19:02:46.486Z", + "commentsCount": 57367, + "id": "19", + "liked": false, + "likesCount": 287, + "objectDescription": "est et sequi", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Ericka", + "id": "21", + "lastName": "Mayert", + }, + "subjectPersonName": "Daphney Balistreri", + }, + ], + "date": "2013-05-02T19:02:46.486Z", + "id": 6, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "et molestiae ea", + "adjectiveAttributeValue": "ut sunt placeat", + "celebrateableId": "14", + "celebrateableType": "V4::AcceptedCommunityChallenge", + "changedAttributeName": "quidem est consequuntur", + "changedAttributeValue": "2013-03-09T22:46:17.951Z", + "commentsCount": 88330, + "id": "13", + "liked": true, + "likesCount": 62367, + "objectDescription": "est placeat ex", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Favian", + "id": "15", + "lastName": "Ruecker", + }, + "subjectPersonName": "Roscoe Johnson", + }, + ], + "date": "2013-03-09T22:46:17.951Z", + "id": 7, + }, + Object { + "data": Array [ + Object { + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "enim ex eveniet", + "adjectiveAttributeValue": "facere molestiae aut", + "celebrateableId": "17", + "celebrateableType": "V4::Organization", + "changedAttributeName": "delectus aut nam", + "changedAttributeValue": "2011-04-05T20:54:44.943Z", + "commentsCount": 49810, + "id": "16", + "liked": true, + "likesCount": 7379, + "objectDescription": "fugit repellendus hic", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Anissa", + "id": "18", + "lastName": "Kirlin", + }, + "subjectPersonName": "Judge Doyle", }, + ], + "date": "2011-04-05T20:54:44.943Z", + "id": 8, + }, + Object { + "data": Array [ Object { - "adjective_attribute_value": "5", - "celebrateable_type": "interaction", - "changed_attribute_value": "2018-01-01 12:00:00", - "id": "3", - "likes_count": 42, - "subject_person_name": "DG With me?", + "__typename": "CommunityCelebrationItem", + "adjectiveAttributeName": "quo occaecati quia", + "adjectiveAttributeValue": "ipsum sit qui", + "celebrateableId": "29", + "celebrateableType": "V4::OrganizationalPermission", + "changedAttributeName": "accusantium odit doloremque", + "changedAttributeValue": "2010-05-11T20:17:10.674Z", + "commentsCount": 50981, + "id": "28", + "liked": false, + "likesCount": 10213, + "objectDescription": "eos quis ut", + "subjectPerson": Object { + "__typename": "Person", + "firstName": "Dario", + "id": "30", + "lastName": "Lueilwitz", + }, + "subjectPersonName": "Orlando Schmidt", }, ], - "date": "2018-01-01 12:00:00", + "date": "2010-05-11T20:17:10.674Z", + "id": 9, }, ] } + stickyHeaderIndices={ + Array [ + 1, + 4, + 7, + 10, + ] + } stickySectionHeadersEnabled={true} style={ Object { "backgroundColor": "#ECEEF2", + "flex": 1, } } updateCellsBatchingPeriod={50} windowSize={21} -/> +> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + `; diff --git a/src/containers/CelebrateFeed/index.tsx b/src/containers/CelebrateFeed/index.tsx index a68d69b6d0..ce0bd2f027 100644 --- a/src/containers/CelebrateFeed/index.tsx +++ b/src/containers/CelebrateFeed/index.tsx @@ -1,8 +1,9 @@ -import React, { Component } from 'react'; -import { SectionList } from 'react-native'; +import React from 'react'; +import { SectionList, View, SectionListData } from 'react-native'; import { connect } from 'react-redux-legacy'; -import PropTypes from 'prop-types'; -import { View } from 'react-native'; +import { ThunkDispatch } from 'redux-thunk'; +import { AnyAction } from 'redux'; +import { useQuery } from '@apollo/react-hooks'; import { DateComponent } from '../../components/common'; import CelebrateItem from '../../components/CelebrateItem'; @@ -10,128 +11,162 @@ import { DateConstants } from '../../components/DateComponent'; import { keyExtractorId } from '../../utils/common'; import CelebrateFeedHeader from '../CelebrateFeedHeader'; import ShareStoryInput from '../Groups/ShareStoryInput'; +import { + celebrationSelector, + CelebrateFeedSection, +} from '../../selectors/celebration'; +import { Organization } from '../../reducers/organizations'; +import { Person } from '../../reducers/people'; +import { GET_CELEBRATE_FEED } from './queries'; +import { + GetCelebrateFeed, + GetCelebrateFeed_community_celebrationItems_nodes, +} from './__generated__/GetCelebrateFeed'; import styles from './styles'; -class CelebrateFeed extends Component { - // @ts-ignore - constructor(props) { - super(props); - // isListScrolled works around a known issue with SectionList in RN. see commit msg for details. - this.state = { ...this.state, isListScrolled: false }; - } - - // @ts-ignore - renderSectionHeader = ({ section: { date } }) => { - const { title, header } = styles; +export interface CelebrateFeedProps { + dispatch: ThunkDispatch<{}, {}, AnyAction>; + organization: Organization; + person?: Person; + itemNamePressable: boolean; + noHeader?: boolean; + showUnreadOnly?: boolean; + onRefetch?: () => void; + onFetchMore?: () => void; + onClearNotification?: ( + event: GetCelebrateFeed_community_celebrationItems_nodes, + ) => void; + testID?: string; +} - return ( - - - - ); +const CelebrateFeed = ({ + dispatch, + organization, + person, + itemNamePressable, + noHeader, + showUnreadOnly, + onRefetch, + onFetchMore, + onClearNotification, +}: CelebrateFeedProps) => { + const queryVariables = { + communityId: organization.id, + personIds: (person && [person.id]) || undefined, + hasUnreadComments: showUnreadOnly, }; - // @ts-ignore - renderItem = ({ item }) => { - const { - // @ts-ignore - organization, - // @ts-ignore - itemNamePressable, - // @ts-ignore - onClearNotification, - // @ts-ignore - refreshCallback, - } = this.props; + const { + data: { + community: { + celebrationItems: { + nodes = [], + pageInfo: { endCursor = null, hasNextPage = false } = {}, + } = {}, + } = {}, + } = {}, + loading, + fetchMore, + refetch, + } = useQuery(GET_CELEBRATE_FEED, { + variables: queryVariables, + pollInterval: 30000, + }); - return ( - - ); - }; + const celebrationItems = celebrationSelector({ celebrateItems: nodes }); - handleOnEndReached = () => { - // @ts-ignore - if (this.state.isListScrolled) { - // @ts-ignore - this.props.loadMoreItemsCallback(); - this.setState({ isListScrolled: false }); - } + const handleRefreshing = () => { + refetch(); + onRefetch && onRefetch(); }; - handleEndDrag = () => { - // @ts-ignore - if (!this.state.isListScrolled) { - this.setState({ isListScrolled: true }); + const handleOnEndReached = () => { + if (hasNextPage) { + fetchMore({ + variables: { + ...queryVariables, + celebrateCursor: endCursor, + }, + updateQuery: (prev, { fetchMoreResult }) => + fetchMoreResult + ? { + ...prev, + ...fetchMoreResult, + community: { + ...prev.community, + ...fetchMoreResult.community, + celebrationItems: { + ...prev.community.celebrationItems, + ...fetchMoreResult.community.celebrationItems, + nodes: [ + ...(prev.community.celebrationItems.nodes || []), + ...(fetchMoreResult.community.celebrationItems.nodes || + []), + ], + }, + }, + } + : prev, + }); + onFetchMore && onFetchMore(); } }; - handleRefreshing = () => { - // @ts-ignore - this.props.refreshCallback(); - }; - - renderHeader = () => { - // @ts-ignore - const { isMember, organization, dispatch, refreshCallback } = this.props; - return ( - <> - {/* - // @ts-ignore */} - - - - ); - }; + const renderSectionHeader = ({ + section: { date }, + }: { + section: SectionListData; + }) => ( + + + + ); - render() { - // @ts-ignore - const { items, refreshing, noHeader } = this.props; + const renderItem = ({ + item, + }: { + item: GetCelebrateFeed_community_celebrationItems_nodes; + }) => ( + + ); - return ( - ( + <> + + - ); - } -} + + ); -// @ts-ignore -CelebrateFeed.propTypes = { - items: PropTypes.array.isRequired, - organization: PropTypes.object.isRequired, - refreshing: PropTypes.bool, - refreshingCallback: PropTypes.func, - itemNamePressable: PropTypes.bool, - isMember: PropTypes.bool, - noHeader: PropTypes.bool, + return ( + + ); }; export default connect()(CelebrateFeed); diff --git a/src/containers/CelebrateFeed/queries.ts b/src/containers/CelebrateFeed/queries.ts new file mode 100644 index 0000000000..c011776ebb --- /dev/null +++ b/src/containers/CelebrateFeed/queries.ts @@ -0,0 +1,31 @@ +import gql from 'graphql-tag'; + +import { CELEBRATE_ITEM_FRAGMENT } from '../../components/CelebrateItem/queries'; + +export const GET_CELEBRATE_FEED = gql` + query GetCelebrateFeed( + $communityId: ID! + $personIds: [ID!] = null + $hasUnreadComments: Boolean = false + $celebrateCursor: String + ) { + community(id: $communityId) { + celebrationItems( + sortBy: createdAt_DESC + first: 25 + after: $celebrateCursor + subjectPersonIds: $personIds + hasUnreadComments: $hasUnreadComments + ) { + nodes { + ...CelebrateItem + } + pageInfo { + endCursor + hasNextPage + } + } + } + } + ${CELEBRATE_ITEM_FRAGMENT} +`; diff --git a/src/containers/CelebrateFeed/styles.ts b/src/containers/CelebrateFeed/styles.ts index 67f22a1e43..e638a1c32f 100644 --- a/src/containers/CelebrateFeed/styles.ts +++ b/src/containers/CelebrateFeed/styles.ts @@ -4,6 +4,7 @@ import theme from '../../theme'; export default StyleSheet.create({ list: { + flex: 1, backgroundColor: theme.extraLightGrey, }, listContent: { diff --git a/src/containers/CelebrateFeedHeader/__tests__/CelebrateFeedHeader.tsx b/src/containers/CelebrateFeedHeader/__tests__/CelebrateFeedHeader.tsx index 929304377a..7d31cd2720 100644 --- a/src/containers/CelebrateFeedHeader/__tests__/CelebrateFeedHeader.tsx +++ b/src/containers/CelebrateFeedHeader/__tests__/CelebrateFeedHeader.tsx @@ -1,8 +1,7 @@ import React from 'react'; -import configureStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; +import { fireEvent } from 'react-native-testing-library'; -import { renderShallow } from '../../../../testUtils'; +import { renderWithContext } from '../../../../testUtils'; import { getReportedComments } from '../../../actions/reportComments'; import { orgPermissionSelector } from '../../../selectors/people'; import { organizationSelector } from '../../../selectors/organizations'; @@ -11,6 +10,9 @@ import { navigatePush } from '../../../actions/navigation'; import { GROUPS_REPORT_SCREEN } from '../../Groups/GroupReport'; import { markCommentsRead } from '../../../actions/unreadComments'; import { GROUP_UNREAD_FEED_SCREEN } from '../../Groups/GroupUnreadFeed'; +import { GROUP_ONBOARDING_TYPES } from '../../Groups/OnboardingCard'; +import { Organization } from '../../../reducers/organizations'; +import { Person } from '../../../reducers/people'; import CelebrateFeedHeader from '..'; @@ -19,25 +21,22 @@ jest.mock('../../../selectors/organizations'); jest.mock('../../../actions/reportComments'); jest.mock('../../../actions/navigation'); jest.mock('../../../actions/unreadComments'); +jest.mock('../../../components/UnreadCommentsCard', () => 'UnreadCommentsCard'); +jest.mock( + '../../../components/ReportCommentHeaderCard', + () => 'ReportCommentHeaderCard', +); -// @ts-ignore -getReportedComments.mockReturnValue(() => ({ type: 'getReportedComments' })); -// @ts-ignore -markCommentsRead.mockReturnValue(() => ({ type: 'markCommentsRead' })); -// @ts-ignore -navigatePush.mockReturnValue(() => ({ type: 'navigatePush' })); - -const mockStore = configureStore([thunk]); const comment1 = { id: 'reported1' }; -const organization = { +const organization: Organization = { id: '1', user_created: true, reportedComments: [comment1], unread_comments_count: 12, }; -const me = { id: 'myId' }; +const me: Person = { id: 'myId' }; -const mockStoreObj = { +const initialState = { organizations: [], auth: { person: me, @@ -47,178 +46,221 @@ const mockStoreObj = { [organization.id]: [comment1], }, }, + swipe: { groupOnboarding: { [GROUP_ONBOARDING_TYPES.celebrate]: true } }, }; -// @ts-ignore -let store; beforeEach(() => { - // @ts-ignore - organizationSelector.mockReturnValue(organization); - // @ts-ignore - orgPermissionSelector.mockReturnValue({ + ((organizationSelector as unknown) as jest.Mock).mockReturnValue( + organization, + ); + ((orgPermissionSelector as unknown) as jest.Mock).mockReturnValue({ permission_id: ORG_PERMISSIONS.OWNER, }); - store = mockStore(mockStoreObj); + (getReportedComments as jest.Mock).mockReturnValue(() => ({ + type: 'getReportedComments', + })); + (markCommentsRead as jest.Mock).mockReturnValue(() => ({ + type: 'markCommentsRead', + })); + (navigatePush as jest.Mock).mockReturnValue(() => ({ type: 'navigatePush' })); }); -function buildScreen() { - return renderShallow( - , - // @ts-ignore - store, - ); -} - describe('owner', () => { describe('user created community', () => { it('renders with 1 reported comment', () => { - const screen = buildScreen(); + renderWithContext( + , + { + initialState, + }, + ).snapshot(); - expect(screen).toMatchSnapshot(); expect(getReportedComments).toHaveBeenCalledWith(organization.id); }); }); describe('cru community', () => { it('renders with 1 reported comment', () => { - // @ts-ignore - organizationSelector.mockReturnValue({ + ((organizationSelector as unknown) as jest.Mock).mockReturnValue({ ...organization, user_created: false, }); - const screen = buildScreen(); - expect(screen).toMatchSnapshot(); + renderWithContext( + , + { + initialState, + }, + ).snapshot(); + expect(getReportedComments).toHaveBeenCalledWith(organization.id); }); }); describe('global community', () => { it('renders without reported comments', () => { - // @ts-ignore - organizationSelector.mockReturnValue({ + ((organizationSelector as unknown) as jest.Mock).mockReturnValue({ ...organization, id: GLOBAL_COMMUNITY_ID, user_created: false, }); - const screen = buildScreen(); - expect(screen).toMatchSnapshot(); + renderWithContext( + , + { + initialState, + }, + ).snapshot(); + expect(getReportedComments).not.toHaveBeenCalled(); }); }); it('renders with 0 reported comments', () => { - store = mockStore({ ...mockStoreObj, reportedComments: { all: {} } }); - const screen = buildScreen(); + renderWithContext( + , + { + initialState: { + ...initialState, + reportedComments: { all: {} }, + }, + }, + ).snapshot(); - expect(screen).toMatchSnapshot(); expect(getReportedComments).toHaveBeenCalledWith(organization.id); }); }); describe('admin', () => { beforeEach(() => { - // @ts-ignore - orgPermissionSelector.mockReturnValue({ + ((orgPermissionSelector as unknown) as jest.Mock).mockReturnValue({ permission_id: ORG_PERMISSIONS.ADMIN, }); }); describe('user created community', () => { it('renders with 1 reported comment', () => { - const screen = buildScreen(); + renderWithContext( + , + { + initialState, + }, + ).snapshot(); - expect(screen).toMatchSnapshot(); expect(getReportedComments).not.toHaveBeenCalled(); }); }); describe('cru community', () => { it('renders without reported comments', () => { - // @ts-ignore - organizationSelector.mockReturnValue({ + ((organizationSelector as unknown) as jest.Mock).mockReturnValue({ ...organization, user_created: false, }); - const screen = buildScreen(); - expect(screen).toMatchSnapshot(); + renderWithContext( + , + { + initialState, + }, + ).snapshot(); + expect(getReportedComments).toHaveBeenCalledWith(organization.id); }); }); describe('global community', () => { it('renders without reported comments', () => { - // @ts-ignore - organizationSelector.mockReturnValue({ + ((organizationSelector as unknown) as jest.Mock).mockReturnValue({ ...organization, id: GLOBAL_COMMUNITY_ID, user_created: false, }); - const screen = buildScreen(); - expect(screen).toMatchSnapshot(); + renderWithContext( + , + { + initialState, + }, + ).snapshot(); + expect(getReportedComments).not.toHaveBeenCalled(); }); }); it('renders with 0 reported comments', () => { - // @ts-ignore - organizationSelector.mockReturnValue({ + ((organizationSelector as unknown) as jest.Mock).mockReturnValue({ ...organization, user_created: false, }); - store = mockStore({ ...mockStoreObj, reportedComments: { all: {} } }); - const screen = buildScreen(); - expect(screen).toMatchSnapshot(); + renderWithContext( + , + { + initialState: { + ...initialState, + reportedComments: { all: {} }, + }, + }, + ).snapshot(); + expect(getReportedComments).toHaveBeenCalledWith(organization.id); }); }); describe('members', () => { beforeEach(() => { - // @ts-ignore - orgPermissionSelector.mockReturnValue({ + ((orgPermissionSelector as unknown) as jest.Mock).mockReturnValue({ permission_id: ORG_PERMISSIONS.USER, }); }); describe('user created community', () => { it('renders without reported comments', () => { - const screen = buildScreen(); + renderWithContext( + , + { + initialState, + }, + ).snapshot(); - expect(screen).toMatchSnapshot(); expect(getReportedComments).not.toHaveBeenCalled(); }); }); describe('cru community', () => { it('renders without reported comments', () => { - // @ts-ignore - organizationSelector.mockReturnValue({ + ((organizationSelector as unknown) as jest.Mock).mockReturnValue({ ...organization, user_created: false, }); - const screen = buildScreen(); - expect(screen).toMatchSnapshot(); + renderWithContext( + , + { + initialState, + }, + ).snapshot(); + expect(getReportedComments).not.toHaveBeenCalled(); }); }); describe('global community', () => { it('renders without reported comments', () => { - // @ts-ignore - organizationSelector.mockReturnValue({ + ((organizationSelector as unknown) as jest.Mock).mockReturnValue({ ...organization, id: GLOBAL_COMMUNITY_ID, user_created: false, }); - const screen = buildScreen(); - expect(screen).toMatchSnapshot(); + renderWithContext( + , + { + initialState, + }, + ).snapshot(); + expect(getReportedComments).not.toHaveBeenCalled(); }); }); @@ -226,36 +268,52 @@ describe('members', () => { describe('unread comments card', () => { it('renders comment card', () => { - const screen = buildScreen(); - expect(screen).toMatchSnapshot(); + renderWithContext( + , + { + initialState, + }, + ).snapshot(); }); + it('renders no comment card when global org', () => { - // @ts-ignore - organizationSelector.mockReturnValue({ + ((organizationSelector as unknown) as jest.Mock).mockReturnValue({ ...organization, id: GLOBAL_COMMUNITY_ID, }); - const screen = buildScreen(); - expect(screen).toMatchSnapshot(); + + renderWithContext( + , + { + initialState, + }, + ).snapshot(); }); + it('renders no comment card when no new comments', () => { - // @ts-ignore - organizationSelector.mockReturnValue({ + ((organizationSelector as unknown) as jest.Mock).mockReturnValue({ ...organization, unread_comments_count: 0, }); - const screen = buildScreen(); - expect(screen).toMatchSnapshot(); + + renderWithContext( + , + { + initialState, + }, + ).snapshot(); }); }); it('navigates to unread comments screen', () => { - const screen = buildScreen(); - screen - .childAt(0) - .childAt(0) - .props() - .onPress(); + const { getByTestId } = renderWithContext( + , + { + initialState, + }, + ); + + fireEvent.press(getByTestId('UnreadCommentsCard')); expect(navigatePush).toHaveBeenCalledWith(GROUP_UNREAD_FEED_SCREEN, { organization, @@ -263,23 +321,27 @@ it('navigates to unread comments screen', () => { }); it('closes comment card', () => { - const screen = buildScreen(); - screen - .childAt(0) - .childAt(0) - .props() - .onClose(); + const { getByTestId } = renderWithContext( + , + { + initialState, + }, + ); + + fireEvent(getByTestId('UnreadCommentsCard'), 'onClose'); expect(markCommentsRead).toHaveBeenCalled(); }); it('navigates to group report screen', () => { - const screen = buildScreen(); - screen - .childAt(0) - .childAt(2) - .props() - .onPress(); + const { getByTestId } = renderWithContext( + , + { + initialState, + }, + ); + + fireEvent.press(getByTestId('ReportCommentCard')); expect(navigatePush).toHaveBeenCalledWith(GROUPS_REPORT_SCREEN, { organization, diff --git a/src/containers/CelebrateFeedHeader/__tests__/__snapshots__/CelebrateFeedHeader.tsx.snap b/src/containers/CelebrateFeedHeader/__tests__/__snapshots__/CelebrateFeedHeader.tsx.snap index ff35c0df7a..d0d8e983ee 100644 --- a/src/containers/CelebrateFeedHeader/__tests__/__snapshots__/CelebrateFeedHeader.tsx.snap +++ b/src/containers/CelebrateFeedHeader/__tests__/__snapshots__/CelebrateFeedHeader.tsx.snap @@ -1,279 +1,1249 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`admin cru community renders without reported comments 1`] = ` - - + + + + +`; + +exports[`admin global community renders without reported comments 1`] = ` + + - - - - - -`; - -exports[`admin global community renders without reported comments 1`] = ` - - - + + celebrate one another + + + See and celebrate one another's Steps of Faith. + + + + + + +  + + + + + `; exports[`admin renders with 0 reported comments 1`] = ` - - - - - + }, + Object {}, + ] + } +> + + `; exports[`admin user created community renders with 1 reported comment 1`] = ` - - - - - + }, + Object {}, + ] + } +> + + `; exports[`members cru community renders without reported comments 1`] = ` - - - - - + }, + Object {}, + ] + } +> + + `; exports[`members global community renders without reported comments 1`] = ` - - - + + + + + celebrate one another + + + See and celebrate one another's Steps of Faith. + + + + + + +  + + + + + `; exports[`members user created community renders without reported comments 1`] = ` - - - - - + }, + Object {}, + ] + } +> + + `; exports[`owner cru community renders with 1 reported comment 1`] = ` - - + + + + +`; + +exports[`owner global community renders without reported comments 1`] = ` + + - - - - - -`; - -exports[`owner global community renders without reported comments 1`] = ` - - - + + celebrate one another + + + See and celebrate one another's Steps of Faith. + + + + + + +  + + + + + `; exports[`owner renders with 0 reported comments 1`] = ` - - - - - + }, + Object {}, + ] + } +> + + `; exports[`owner user created community renders with 1 reported comment 1`] = ` - - - - + + - - - + }, + Object {}, + ] + } + /> + + `; exports[`unread comments card renders comment card 1`] = ` - - + + + + +`; + +exports[`unread comments card renders no comment card when global org 1`] = ` + + - - - - - -`; - -exports[`unread comments card renders no comment card when global org 1`] = ` - - - + + celebrate one another + + + See and celebrate one another's Steps of Faith. + + + + + + +  + + + + + `; exports[`unread comments card renders no comment card when no new comments 1`] = ` - - - + + + + celebrate one another + + + See and celebrate one another's Steps of Faith. + + + + + + +  + + + + + , + - - + , +] `; diff --git a/src/containers/CelebrateFeedHeader/index.tsx b/src/containers/CelebrateFeedHeader/index.tsx index 2e3ebf0209..9568eff857 100644 --- a/src/containers/CelebrateFeedHeader/index.tsx +++ b/src/containers/CelebrateFeedHeader/index.tsx @@ -1,6 +1,7 @@ -import React, { Component, Fragment } from 'react'; +import React, { Fragment, useEffect } from 'react'; import { connect } from 'react-redux-legacy'; -import PropTypes from 'prop-types'; +import { ThunkDispatch } from 'redux-thunk'; +import { AnyAction } from 'redux'; import { getReportedComments } from '../../actions/reportComments'; import { Flex } from '../../components/common'; @@ -11,123 +12,127 @@ import { navigatePush } from '../../actions/navigation'; import { GROUPS_REPORT_SCREEN } from '../Groups/GroupReport'; import OnboardingCard, { GROUP_ONBOARDING_TYPES, -} from '../../containers/Groups/OnboardingCard'; +} from '../Groups/OnboardingCard'; import { markCommentsRead } from '../../actions/unreadComments'; import UnreadCommentsCard from '../../components/UnreadCommentsCard'; import ReportCommentHeaderCard from '../../components/ReportCommentHeaderCard'; import { GROUP_UNREAD_FEED_SCREEN } from '../Groups/GroupUnreadFeed'; +import { AuthState } from '../../reducers/auth'; +import { OrganizationsState, Organization } from '../../reducers/organizations'; +import { ReportedCommentsState } from '../../reducers/reportedComments'; import styles from './styles'; -class CelebrateFeedHeader extends Component { - componentDidMount() { - const { - // @ts-ignore - dispatch, - // @ts-ignore - organization: { id: orgId }, - // @ts-ignore - shouldQueryReport, - } = this.props; - if (shouldQueryReport) { - dispatch(getReportedComments(orgId)); - } - } - - closeCommentCard = () => { - const { - // @ts-ignore - dispatch, - // @ts-ignore - organization: { id: orgId }, - } = this.props; - dispatch(markCommentsRead(orgId)); +export interface CelebrateFeedHeaderProps { + dispatch: ThunkDispatch<{}, {}, AnyAction>; + organization: Organization; + shouldQueryReport: boolean; + isReportVisible: boolean; + isCommentCardVisible: boolean; + isMember: boolean; + reportedCount: number; + newCommentsCount: number; +} + +const CelebrateFeedHeader = ({ + dispatch, + organization, + shouldQueryReport, + isReportVisible, + isCommentCardVisible, + isMember, + reportedCount, + newCommentsCount, +}: CelebrateFeedHeaderProps) => { + useEffect(() => { + shouldQueryReport && dispatch(getReportedComments(organization.id)); + }, []); + + const closeCommentCard = () => { + dispatch(markCommentsRead(organization.id)); }; - report = () => { - // @ts-ignore - const { dispatch, organization } = this.props; + const report = () => { dispatch(navigatePush(GROUPS_REPORT_SCREEN, { organization })); }; - commentCard = () => { - // @ts-ignore - const { dispatch, organization } = this.props; + const commentCard = () => { dispatch(navigatePush(GROUP_UNREAD_FEED_SCREEN, { organization })); }; - renderCommentCard() { - // @ts-ignore - const { isCommentCardVisible, newCommentsCount } = this.props; + const renderCommentCard = () => { if (!isCommentCardVisible) { return null; } + return ( ); - } + }; - renderReport() { - // @ts-ignore - const { reportedCount, isReportVisible } = this.props; + const renderReport = () => { if (!isReportVisible) { return null; } - return ( - - ); - } - render() { - // @ts-ignore - const { isMember, isReportVisible, isCommentCardVisible } = this.props; return ( - - {isCommentCardVisible ? null : ( - // @ts-ignore - - )} - {isMember || (!isReportVisible && !isCommentCardVisible) ? null : ( - - {this.renderCommentCard()} - {isReportVisible && isCommentCardVisible ? ( - - ) : null} - {this.renderReport()} - - )} - + ); - } -} + }; -// @ts-ignore -CelebrateFeedHeader.propTypes = { - organization: PropTypes.object.isRequired, - isMember: PropTypes.bool, + return ( + + {isCommentCardVisible ? null : ( + //@ts-ignore + + )} + {isMember || (!isReportVisible && !isCommentCardVisible) ? null : ( + + {renderCommentCard()} + {isReportVisible && isCommentCardVisible ? ( + + ) : null} + {renderReport()} + + )} + + ); }; export const mapStateToProps = ( - // @ts-ignore - { auth, organizations, reportedComments }, - { organization = {} }, + { + auth, + organizations, + reportedComments, + }: { + auth: AuthState; + organizations: OrganizationsState; + reportedComments: ReportedCommentsState; + }, + { organization }: { organization: Organization }, ) => { const selectorOrg = - // @ts-ignore organizationSelector({ organizations }, { orgId: organization.id }) || organization; - // @ts-ignore - const myOrgPerm = orgPermissionSelector(null, { - person: auth.person, - organization: { id: selectorOrg.id }, - }); - const allReportedComments = reportedComments.all[selectorOrg.id] || []; - const reportedCount = allReportedComments.length; + const myOrgPerm = orgPermissionSelector( + {}, + { + person: auth.person, + organization: { id: selectorOrg.id }, + }, + ); + + const reportedCount = (reportedComments.all[selectorOrg.id] || []).length; const shouldQueryReport = shouldQueryReportedComments(selectorOrg, myOrgPerm); const newCommentsCount = selectorOrg.unread_comments_count; diff --git a/src/containers/CelebrateItemName/__tests__/CelebrateItemName.tsx b/src/containers/CelebrateItemName/__tests__/CelebrateItemName.tsx index a44c5812dd..411648d84a 100644 --- a/src/containers/CelebrateItemName/__tests__/CelebrateItemName.tsx +++ b/src/containers/CelebrateItemName/__tests__/CelebrateItemName.tsx @@ -1,90 +1,75 @@ import React from 'react'; -import configureStore from 'redux-mock-store'; +import { fireEvent } from 'react-native-testing-library'; import CelebrateItemName from '../index'; -import { renderShallow } from '../../../../testUtils'; +import { renderWithContext } from '../../../../testUtils'; +import { mockFragment } from '../../../../testUtils/apolloMockClient'; import { navToPersonScreen } from '../../../actions/person'; - -const mockStore = configureStore(); -// @ts-ignore -let store; - -const navToPersonScreenResult = { type: 'navigated to person screen' }; -const person = { id: '1234123' }; -const organization = { id: '235234' }; - -// @ts-ignore -let name; -// @ts-ignore -let pressable; -// @ts-ignore -let screen; +import { Organization } from '../../../reducers/organizations'; +import { CELEBRATE_ITEM_PERSON_FRAGMENT } from '../../../components/CelebrateItem/queries'; +import { GetCelebrateFeed_community_celebrationItems_nodes_subjectPerson as CelebrateItemPerson } from '../../CelebrateFeed/__generated__/GetCelebrateFeed'; jest.mock('../../../actions/person'); -// @ts-ignore -navToPersonScreen.mockReturnValue(navToPersonScreenResult); +const person = mockFragment( + CELEBRATE_ITEM_PERSON_FRAGMENT, +); +const name = `${person.firstName} ${person.lastName}`; +const organization: Organization = { + id: '235234', +}; + +const navToPersonScreenResult = { type: 'navigated to person screen' }; beforeEach(() => { - store = mockStore(); + (navToPersonScreen as jest.Mock).mockReturnValue(navToPersonScreenResult); +}); - screen = renderShallow( +it('renders correctly without name', () => { + renderWithContext( , - store, - ); + ).snapshot(); }); -describe('does not have name', () => { - beforeAll(() => { - name = null; - }); - - it('renders correctly', () => { - // @ts-ignore - expect(screen).toMatchSnapshot(); - }); +it('renders correctly with name', () => { + renderWithContext( + , + ).snapshot(); }); -describe('has name', () => { - beforeAll(() => { - name = 'Roger Goers'; - }); - - describe('is not pressable', () => { - beforeAll(() => { - pressable = false; - }); - - it('renders correctly', () => { - // @ts-ignore - expect(screen).toMatchSnapshot(); - }); - }); - - describe('is pressable', () => { - beforeAll(() => { - pressable = true; - }); +it('renders correctly not pressable', () => { + renderWithContext( + , + ).snapshot(); +}); - it('renders correctly', () => { - // @ts-ignore - expect(screen).toMatchSnapshot(); - }); +it('navigates to person screen', () => { + const { store, getByTestId } = renderWithContext( + , + ); - it('navigates to person screen', () => { - // @ts-ignore - screen.props().onPress(); + fireEvent.press(getByTestId('NameButton')); - expect(navToPersonScreen).toHaveBeenCalledWith(person, organization); - // @ts-ignore - expect(store.getActions()).toEqual([navToPersonScreenResult]); - }); - }); + expect(navToPersonScreen).toHaveBeenCalledWith(person, organization); + expect(store.getActions()).toEqual([navToPersonScreenResult]); }); diff --git a/src/containers/CelebrateItemName/__tests__/__snapshots__/CelebrateItemName.tsx.snap b/src/containers/CelebrateItemName/__tests__/__snapshots__/CelebrateItemName.tsx.snap index 69b7aab52b..18200f0d84 100644 --- a/src/containers/CelebrateItemName/__tests__/__snapshots__/CelebrateItemName.tsx.snap +++ b/src/containers/CelebrateItemName/__tests__/__snapshots__/CelebrateItemName.tsx.snap @@ -1,24 +1,108 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`does not have name renders correctly 1`] = ` - +exports[`renders correctly not pressable 1`] = ` + + Hayden Zieme + `; -exports[`has name is not pressable renders correctly 1`] = ` - +exports[`renders correctly with name 1`] = ` + + + + Hayden Zieme + + + `; -exports[`has name is pressable renders correctly 1`] = ` - + MissionHub user + `; diff --git a/src/containers/CelebrateItemName/index.tsx b/src/containers/CelebrateItemName/index.tsx index 6a13afee2d..131d07ccb9 100644 --- a/src/containers/CelebrateItemName/index.tsx +++ b/src/containers/CelebrateItemName/index.tsx @@ -1,47 +1,54 @@ -import React, { Component } from 'react'; -import { withTranslation } from 'react-i18next'; -import PropTypes from 'prop-types'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; import { connect } from 'react-redux-legacy'; +import { ThunkDispatch } from 'redux-thunk'; +import { AnyAction } from 'redux'; import { Button } from '../../components/common'; import ItemHeaderText from '../../components/ItemHeaderText/index'; import { navToPersonScreen } from '../../actions/person'; +import { GetCelebrateFeed_community_celebrationItems_nodes_subjectPerson } from '../CelebrateFeed/__generated__/GetCelebrateFeed'; +import { Organization } from '../../reducers/organizations'; +import { Person } from '../../reducers/people'; + +export interface CelebrateItemNameProps { + dispatch: ThunkDispatch<{}, {}, AnyAction>; + name: string | null; + person: + | GetCelebrateFeed_community_celebrationItems_nodes_subjectPerson + | Person + | null; + organization: Organization; + pressable: boolean; + customContent?: JSX.Element; +} -// @ts-ignore -@withTranslation('celebrateFeeds') -class CelebrateItemName extends Component { - onPressNameLink = () => { - // @ts-ignore - const { dispatch, person, organization } = this.props; - - dispatch(navToPersonScreen(person, organization)); - }; - - render() { - // @ts-ignore - const { name, t, customContent, pressable } = this.props; - const content = customContent || ( - - ); - - if (!name || !pressable) { - return content; - } - - return ( - - ); +const CelebrateItemName = ({ + dispatch, + name, + person, + organization, + pressable, + customContent, +}: CelebrateItemNameProps) => { + const { t } = useTranslation('celebrateFeeds'); + + const onPressNameLink = () => + person && dispatch(navToPersonScreen(person, organization)); + + const content = customContent || ( + + ); + + if (!name || !pressable) { + return content; } -} -// @ts-ignore -CelebrateItemName.propTypes = { - name: PropTypes.string, - person: PropTypes.object, - organization: PropTypes.object, - pressable: PropTypes.bool, + return ( + + ); }; export default connect()(CelebrateItemName); diff --git a/src/containers/CommentItem/__tests__/CommentItem.tsx b/src/containers/CommentItem/__tests__/CommentItem.tsx index 23eeee86f6..c725f70c27 100644 --- a/src/containers/CommentItem/__tests__/CommentItem.tsx +++ b/src/containers/CommentItem/__tests__/CommentItem.tsx @@ -2,123 +2,106 @@ import 'react-native'; import React from 'react'; import Enzyme from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; -import configureStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; -import { renderShallow } from '../../../../testUtils'; +import { renderWithContext } from '../../../../testUtils'; +import { CelebrateComment } from '../../../reducers/celebrateComments'; +import { Organization } from '../../../reducers/organizations'; import CommentItem from '..'; Enzyme.configure({ adapter: new Adapter() }); -const item = { +const item: CelebrateComment = { + id: '1', content: 'hello roge', created_at: '2018-06-11 12:00:00 UTC', + updated_at: '2018-06-11 12:00:00 UTC', person: { id: 'notme', first_name: 'Roge', last_name: 'Goers' }, }; -const organization = { id: '7342342' }; +const me = { id: 'me' }; +const organization: Organization = { id: '7342342' }; -const menuActions = [{ text: 'text', onPress: jest.fn() }]; +const menuActions: { + text: string; + onPress: () => void; + destructive?: boolean; +}[] = [{ text: 'text', onPress: jest.fn() }]; -// @ts-ignore -let screen; -const me = { id: 'me' }; -// @ts-ignore -let store; +const initialState = { + auth: { person: me }, + celebrateComments: { editingCommentId: null }, +}; -beforeEach(() => { - store = configureStore([thunk])({ - auth: { person: me }, - celebrateComments: { editingCommentId: null }, - }); - screen = renderShallow( +it('renders correctly', () => { + renderWithContext( , - store, - ); -}); - -it('renders correctly', () => { - // @ts-ignore - expect(screen).toMatchSnapshot(); + { + initialState, + }, + ).snapshot(); }); it('renders without menu actions', () => { - screen = renderShallow( - // @ts-ignore - , - // @ts-ignore - store, - ); - - expect(screen).toMatchSnapshot(); + renderWithContext(, { + initialState, + }).snapshot(); }); it('renders reported comment', () => { - screen = renderShallow( + renderWithContext( , - // @ts-ignore - store, - ); - expect(screen).toMatchSnapshot(); + { + initialState, + }, + ).snapshot(); }); it('renders my reported comment', () => { - screen = renderShallow( + renderWithContext( , - // @ts-ignore - store, - ); - expect(screen).toMatchSnapshot(); + { + initialState, + }, + ).snapshot(); }); it('renders editing correctly', () => { - store = configureStore([thunk])({ - auth: { person: me }, - // @ts-ignore - celebrateComments: { editingCommentId: item.id }, - }); - screen = renderShallow( + renderWithContext( , - store, - ); - expect(screen).toMatchSnapshot(); + { + initialState: { ...initialState, editingCommentId: item.id }, + }, + ).snapshot(); }); it('renders correctly as mine', () => { - expect( - renderShallow( - , - // @ts-ignore - store, - ), - ).toMatchSnapshot(); + renderWithContext( + , + { + initialState, + }, + ).snapshot(); }); diff --git a/src/containers/CommentItem/__tests__/__snapshots__/CommentItem.tsx.snap b/src/containers/CommentItem/__tests__/__snapshots__/CommentItem.tsx.snap index fa2c715712..479967908c 100644 --- a/src/containers/CommentItem/__tests__/__snapshots__/CommentItem.tsx.snap +++ b/src/containers/CommentItem/__tests__/__snapshots__/CommentItem.tsx.snap @@ -13,57 +13,115 @@ exports[`renders correctly 1`] = ` ] } > - - - Roge Goers - - } - name="Roge Goers" - organization={ + + - - - - + + Roge Goers + + + + + June 11, 2018 @ 12:00 PM + + + + - hello roge - + - - + - + `; @@ -113,34 +186,84 @@ exports[`renders correctly as mine 1`] = ` ] } > - - - - - + June 11, 2018 @ 12:00 PM + + + - - + - hello roge - + - - + + `; @@ -187,63 +318,119 @@ exports[`renders editing correctly 1`] = ` "paddingHorizontal": 20, "paddingVertical": 8, }, - Object { - "backgroundColor": "rgba(0, 0, 0, 0.5)", - }, + null, ] } > - - - Roge Goers - - } - name="Roge Goers" - organization={ + + - - - - + + Roge Goers + + + + + June 11, 2018 @ 12:00 PM + + + + - hello roge - + - - + - + `; @@ -293,58 +495,83 @@ exports[`renders my reported comment 1`] = ` ] } > - - - Roge Goers - - } - name="Roge Goers" - organization={ - Object { - "id": "7342342", - } - } - person={ + - - - - + Roge Goers + + + June 11, 2018 @ 12:00 PM + + + + - hello roge - + - - + - + `; @@ -394,58 +636,83 @@ exports[`renders reported comment 1`] = ` ] } > - - - Roge Goers - - } - name="Roge Goers" - organization={ - Object { - "id": "7342342", - } - } - person={ + - - - - + Roge Goers + + + June 11, 2018 @ 12:00 PM + + + + - hello roge - + - - + - + `; @@ -495,46 +777,97 @@ exports[`renders without menu actions 1`] = ` ] } > - - + + Roge Goers - - } - name="Roge Goers" - organization={ - Object { - "id": "7342342", - } + + + + + June 11, 2018 @ 12:00 PM + + + - - - - hello roge - + - - + `; diff --git a/src/containers/CommentItem/index.tsx b/src/containers/CommentItem/index.tsx index ae26c0375c..857e99f4b6 100644 --- a/src/containers/CommentItem/index.tsx +++ b/src/containers/CommentItem/index.tsx @@ -1,5 +1,4 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import React from 'react'; import { View } from 'react-native'; import { connect } from 'react-redux-legacy'; @@ -8,24 +7,53 @@ import CardTime from '../../components/CardTime'; import PopupMenu from '../../components/PopupMenu'; import CelebrateItemName from '../../containers/CelebrateItemName'; import { DateConstants } from '../../components/DateComponent'; +import { AuthState } from '../../reducers/auth'; +import { + CelebrateCommentsState, + CelebrateComment, +} from '../../reducers/celebrateComments'; +import { Organization } from '../../reducers/organizations'; +import { Person } from '../../reducers/people'; import styles from './styles'; -class CommentItem extends Component { - renderContent = () => { - const { - // @ts-ignore - item: { content, person }, - // @ts-ignore - me, - // @ts-ignore - isReported, - } = this.props; - const { itemStyle, myStyle, text, myText } = styles; +export interface CommentItemProps { + testID?: string; + item: CelebrateComment; + menuActions?: { + text: string; + onPress: () => void; + destructive?: boolean; + }[]; + organization: Organization; + isReported?: boolean; + me: Person; + isEditing: boolean; +} - const isMine = person.id === me.id; - const isMineNotReported = isMine && !isReported; +const CommentItem = ({ + item, + menuActions, + organization, + isReported, + me, + isEditing, +}: CommentItemProps) => { + const { content, person, created_at } = item; + const { + itemStyle, + myStyle, + text, + myText, + content: contentStyle, + editingStyle, + name: nameStyle, + } = styles; + const name = `${person.first_name} ${person.last_name}`; + const isMine = person.id === me.id; + const isMineNotReported = isMine && !isReported; + const renderContent = () => { return ( {content} @@ -33,97 +61,56 @@ class CommentItem extends Component { ); }; - render() { - const { - // @ts-ignore - item: { created_at, person }, - // @ts-ignore - organization, - // @ts-ignore - me, - // @ts-ignore - isEditing, - // @ts-ignore - isReported, - // @ts-ignore - menuActions, - } = this.props; - const { content: contentStyle, editingStyle, name: nameStyle } = styles; - - const name = `${person.first_name} ${person.last_name}`; - const isMine = person.id === me.id; - const isMineNotReported = isMine && !isReported; - - return ( - // Android needs the collapsable property to use '.measure' properly within the - // https://github.com/facebook/react-native/issues/3282#issuecomment-201934117 - - - {isMineNotReported ? ( - - ) : ( - {name}} - /> - )} - - - - {isMineNotReported ? : null} - {menuActions ? ( - - {this.renderContent()} - - ) : ( - this.renderContent() - )} - {!isMineNotReported ? : null} - - - ); - } -} - -// @ts-ignore -CommentItem.propTypes = { - item: PropTypes.object.isRequired, - organization: PropTypes.object, - isReported: PropTypes.bool, - menuActions: PropTypes.arrayOf( - PropTypes.shape({ - text: PropTypes.string.isRequired, - onPress: PropTypes.func.isRequired, - }), - ), + return ( + // Android needs the collapsable property to use '.measure' properly within the + // https://github.com/facebook/react-native/issues/3282#issuecomment-201934117 + + + {isMineNotReported ? ( + + ) : ( + {name}} + /> + )} + + + + {isMineNotReported ? : null} + {menuActions ? ( + + {renderContent()} + + ) : ( + renderContent() + )} + {!isMineNotReported ? : null} + + + ); }; + const mapStateToProps = ( - // @ts-ignore - { auth, celebrateComments: { editingCommentId } }, - // @ts-ignore - { item }, + { + auth, + celebrateComments: { editingCommentId }, + }: { auth: AuthState; celebrateComments: CelebrateCommentsState }, + { item }: { item: CelebrateComment }, ) => ({ me: auth.person, isEditing: editingCommentId === item.id, }); -export default connect( - mapStateToProps, - undefined, - undefined, - { withRef: true }, -)(CommentItem); +export default connect(mapStateToProps)(CommentItem); diff --git a/src/containers/CommentLikeComponent/__tests__/CommentLikeComponent.tsx b/src/containers/CommentLikeComponent/__tests__/CommentLikeComponent.tsx index 94123be32c..61c43a0aa1 100644 --- a/src/containers/CommentLikeComponent/__tests__/CommentLikeComponent.tsx +++ b/src/containers/CommentLikeComponent/__tests__/CommentLikeComponent.tsx @@ -1,142 +1,247 @@ import 'react-native'; import React from 'react'; -import configureStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; +import { MockStore } from 'redux-mock-store'; +import { fireEvent, flushMicrotasksQueue } from 'react-native-testing-library'; +import { ReactTestInstance } from 'react-test-renderer'; -import { renderShallow, testSnapshotShallow } from '../../../../testUtils'; +import { renderWithContext } from '../../../../testUtils'; +import { mockFragment } from '../../../../testUtils/apolloMockClient'; import { toggleLike } from '../../../actions/celebration'; import { ACTIONS } from '../../../constants'; import { trackActionWithoutData } from '../../../actions/analytics'; +import { Organization } from '../../../reducers/organizations'; +import { + CELEBRATE_ITEM_FRAGMENT, + CELEBRATE_ITEM_PERSON_FRAGMENT, +} from '../../../components/CelebrateItem/queries'; +import { + GetCelebrateFeed_community_celebrationItems_nodes as CelebrateItem, + GetCelebrateFeed_community_celebrationItems_nodes_subjectPerson as CelebrateItemPerson, +} from '../../CelebrateFeed/__generated__/GetCelebrateFeed'; import CommentLikeComponent from '..'; jest.mock('../../../actions/celebration'); jest.mock('../../../actions/analytics'); -const mockStore = configureStore([thunk]); -// @ts-ignore -let store; +const mePerson = mockFragment( + CELEBRATE_ITEM_PERSON_FRAGMENT, +); +const otherPerson = mockFragment( + CELEBRATE_ITEM_PERSON_FRAGMENT, +); +const myId = mePerson.id; +const organization: Organization = { id: '567' }; +const event = mockFragment(CELEBRATE_ITEM_FRAGMENT); -const myId = '2342'; const toggleLikeResponse = { type: 'item was liked' }; const trackActionResponse = { type: 'tracked action' }; -// @ts-ignore -toggleLike.mockReturnValue(dispatch => dispatch(toggleLikeResponse)); -// @ts-ignore -trackActionWithoutData.mockReturnValue(dispatch => - dispatch(trackActionResponse), -); +const initialState = { auth: { person: { id: myId } } }; + +let onRefresh: jest.Mock; beforeEach(() => { - store = mockStore({ auth: { person: { id: myId } } }); + onRefresh = jest.fn(); + (toggleLike as jest.Mock).mockReturnValue(toggleLikeResponse); + (trackActionWithoutData as jest.Mock).mockReturnValue(trackActionResponse); }); it('renders nothing with no subject person', () => { - // @ts-ignore - testSnapshotShallow(, store); + renderWithContext( + , + { + initialState, + }, + ).snapshot(); }); -it('renders with custom style', () => { - testSnapshotShallow( - // @ts-ignore - , - // @ts-ignore - store, - ); -}); - -const event = { - id: '777711', - liked: false, - subject_person: {}, - likes_count: 54, - comments_count: 15, - organization: { id: '88732' }, -}; describe('with subject person', () => { it('renders for me', () => { - // @ts-ignore - testSnapshotShallow(, store); + renderWithContext( + , + { + initialState, + }, + ).snapshot(); }); it('renders for someone else', () => { - testSnapshotShallow( + renderWithContext( , - // @ts-ignore - store, - ); + { + initialState, + }, + ).snapshot(); }); - it('renders when liked', () => { - testSnapshotShallow( - // @ts-ignore - , - // @ts-ignore - store, - ); + it('renders when not liked', () => { + renderWithContext( + , + { + initialState, + }, + ).snapshot(); }); it('renders 0 comments_count', () => { - testSnapshotShallow( - // @ts-ignore - , - // @ts-ignore - store, - ); + renderWithContext( + , + { + initialState, + }, + ).snapshot(); }); it('renders 0 likes_count', () => { - testSnapshotShallow( - // @ts-ignore - , - // @ts-ignore - store, - ); - }); - - it('renders disabled heart button', () => { - const component = renderShallow( - // @ts-ignore - , - // @ts-ignore - store, - ); - component.setState({ isLikeDisabled: true }); - component.update(); - expect(component).toMatchSnapshot(); + renderWithContext( + , + { + initialState, + }, + ).snapshot(); }); describe('onPress like button', () => { - beforeEach(() => - // @ts-ignore - renderShallow(, store) - .childAt(3) - .props() - .onPress(), - ); - - it('toggles like', () => { - expect(toggleLike).toHaveBeenCalledWith( - event.id, - event.liked, - event.organization.id, - ); - // @ts-ignore - expect(store.getActions()).toEqual( - expect.arrayContaining([toggleLikeResponse]), - ); + describe('unlike -> like', () => { + let screen: { + store: MockStore; + recordSnapshot: () => void; + diffSnapshot: () => void; + getByTestId: (id: string) => ReactTestInstance; + }; + + beforeEach(() => { + screen = renderWithContext( + , + { + initialState, + }, + ); + }); + + it('renders disabled heart button', async () => { + screen.recordSnapshot(); + + fireEvent.press(screen.getByTestId('LikeIconButton')); + + screen.diffSnapshot(); + + await flushMicrotasksQueue(); + }); + + it('toggles like', async () => { + await fireEvent.press(screen.getByTestId('LikeIconButton')); + + expect(toggleLike).toHaveBeenCalledWith( + event.id, + false, + organization.id, + ); + expect(trackActionWithoutData).toHaveBeenCalledWith(ACTIONS.ITEM_LIKED); + expect(screen.store.getActions()).toEqual([ + toggleLikeResponse, + trackActionResponse, + ]); + }); }); - it('tracks action', () => { - expect(trackActionWithoutData).toHaveBeenCalledWith(ACTIONS.ITEM_LIKED); - // @ts-ignore - expect(store.getActions()).toEqual( - expect.arrayContaining([trackActionResponse]), - ); + describe('like -> unlike', () => { + let screen: { + store: MockStore; + recordSnapshot: () => void; + diffSnapshot: () => void; + getByTestId: (id: string) => ReactTestInstance; + }; + + beforeEach(() => { + screen = renderWithContext( + , + { + initialState, + }, + ); + }); + + it('renders disabled heart button', async () => { + screen.recordSnapshot(); + + fireEvent.press(screen.getByTestId('LikeIconButton')); + + screen.diffSnapshot(); + + await flushMicrotasksQueue(); + }); + + it('toggles like', async () => { + await fireEvent.press(screen.getByTestId('LikeIconButton')); + + expect(toggleLike).toHaveBeenCalledWith( + event.id, + true, + organization.id, + ); + expect(trackActionWithoutData).not.toHaveBeenCalled(); + expect(screen.store.getActions()).toEqual([toggleLikeResponse]); + }); }); }); }); diff --git a/src/containers/CommentLikeComponent/__tests__/__snapshots__/CommentLikeComponent.tsx.snap b/src/containers/CommentLikeComponent/__tests__/__snapshots__/CommentLikeComponent.tsx.snap index ac18af6223..6b9ae7402a 100644 --- a/src/containers/CommentLikeComponent/__tests__/__snapshots__/CommentLikeComponent.tsx.snap +++ b/src/containers/CommentLikeComponent/__tests__/__snapshots__/CommentLikeComponent.tsx.snap @@ -2,100 +2,151 @@ exports[`renders nothing with no subject person 1`] = ` - - + /> + + `; -exports[`renders with custom style 1`] = ` - - - - +exports[`with subject person onPress like button like -> unlike renders disabled heart button 1`] = ` +"Snapshot Diff: +- First value ++ Second value + +@@ -80,11 +80,13 @@ + style={ + Array [ + Object { + \\"backgroundColor\\": \\"transparent\\", + }, +- null, ++ Object { ++ \\"opacity\\": 0.6, ++ }, + Object { + \\"height\\": 24, + \\"width\\": 24, + }, + null," +`; + +exports[`with subject person onPress like button unlike -> like renders disabled heart button 1`] = ` +"Snapshot Diff: +- First value ++ Second value + +@@ -80,11 +80,13 @@ + style={ + Array [ + Object { + \\"backgroundColor\\": \\"transparent\\", + }, +- null, ++ Object { ++ \\"opacity\\": 0.6, ++ }, + Object { + \\"height\\": 24, + \\"width\\": 24, + }, + null," `; exports[`with subject person renders 0 comments_count 1`] = ` - - - + /> + + `; exports[`with subject person renders 0 likes_count 1`] = ` - - 15 - - - - - -`; - -exports[`with subject person renders disabled heart button 1`] = ` - - - 15 - + 93553 + - - + /> + + `; exports[`with subject person renders for me 1`] = ` - - 15 - + 93553 + - - + /> + + `; exports[`with subject person renders for someone else 1`] = ` - - 15 - + 93553 + - - 54 - - + /> + + `; -exports[`with subject person renders when liked 1`] = ` +exports[`with subject person renders when not liked 1`] = ` - - 15 - + 93553 + - - + /> + + `; diff --git a/src/containers/CommentLikeComponent/index.tsx b/src/containers/CommentLikeComponent/index.tsx index 79560a194d..f2b1372599 100644 --- a/src/containers/CommentLikeComponent/index.tsx +++ b/src/containers/CommentLikeComponent/index.tsx @@ -1,7 +1,8 @@ -import React, { Component } from 'react'; +import React, { useState } from 'react'; import { Image, View } from 'react-native'; -import PropTypes from 'prop-types'; import { connect } from 'react-redux-legacy'; +import { ThunkDispatch } from 'redux-thunk'; +import { AnyAction } from 'redux'; import { Text, Button } from '../../components/common'; import { trackActionWithoutData } from '../../actions/analytics'; @@ -10,95 +11,87 @@ import BLUE_HEART from '../../../assets/images/heart-blue.png'; import COMMENTS from '../../../assets/images/comments.png'; import { toggleLike } from '../../actions/celebration'; import { ACTIONS } from '../../constants'; +import { GetCelebrateFeed_community_celebrationItems_nodes } from '../CelebrateFeed/__generated__/GetCelebrateFeed'; +import { AuthState } from '../../reducers/auth'; +import { Organization } from '../../reducers/organizations'; import styles from './styles'; -class CommentLikeComponent extends Component { - state = { isLikeDisabled: false }; +export interface CommentLikeComponentProps { + dispatch: ThunkDispatch<{}, {}, AnyAction>; + organization: Organization; + event: GetCelebrateFeed_community_celebrationItems_nodes; + myId: string; + onRefresh: () => void; +} + +const CommentLikeComponent = ({ + dispatch, + organization, + event, + myId, + onRefresh, +}: CommentLikeComponentProps) => { + const [isLikeDisabled, setIsLikeDisabled] = useState(false); - onPressLikeIcon = async () => { - const { - // @ts-ignore - event: { organization, id, liked }, - // @ts-ignore - dispatch, - } = this.props; + const { id, liked, likesCount, commentsCount, subjectPerson } = event; + const onPressLikeIcon = async () => { try { - this.setState({ isLikeDisabled: true }); + setIsLikeDisabled(true); await dispatch(toggleLike(id, liked, organization && organization.id)); !liked && dispatch(trackActionWithoutData(ACTIONS.ITEM_LIKED)); } finally { - this.setState({ isLikeDisabled: false }); + setIsLikeDisabled(false); + onRefresh(); } }; - renderCommentIcon() { - const { - // @ts-ignore - event: { comments_count }, - } = this.props; - const displayCommentCount = comments_count > 0; + const renderCommentIcon = () => { + const displayCommentCount = commentsCount > 0; return ( <> - {displayCommentCount ? comments_count : null} + {displayCommentCount ? commentsCount : null} ); - } - - renderLikeIcon() { - // @ts-ignore - const { myId, event } = this.props; - const { subject_person, likes_count, liked } = event; - const { isLikeDisabled } = this.state; + }; + const renderLikeIcon = () => { const displayLikeCount = - likes_count > 0 && subject_person && subject_person.id === myId; + likesCount > 0 && subjectPerson && subjectPerson.id === myId; return ( <> - {displayLikeCount ? likes_count : null} + {displayLikeCount ? likesCount : null} ); - } - - render() { - // @ts-ignore - const { event } = this.props; - const { subject_person } = event; - - return ( - // @ts-ignore - - {subject_person && this.renderCommentIcon()} - {this.renderLikeIcon()} - - ); - } -} + }; -// @ts-ignore -CommentLikeComponent.propTypes = { - event: PropTypes.object.isRequired, - myId: PropTypes.string.isRequired, + return ( + + {subjectPerson && renderCommentIcon()} + {renderLikeIcon()} + + ); }; -// @ts-ignore -const mapStateToProps = ({ auth }) => ({ +const mapStateToProps = ({ auth }: { auth: AuthState }) => ({ myId: auth.person.id, }); + export default connect(mapStateToProps)(CommentLikeComponent); diff --git a/src/containers/CommentLikeComponent/styles.ts b/src/containers/CommentLikeComponent/styles.ts index 31917f3c4b..ff03d78600 100644 --- a/src/containers/CommentLikeComponent/styles.ts +++ b/src/containers/CommentLikeComponent/styles.ts @@ -3,6 +3,11 @@ import { StyleSheet } from 'react-native'; import theme from '../../theme'; export default StyleSheet.create({ + container: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'flex-end', + }, icon: { height: 24, width: 24, diff --git a/src/containers/CommentsList/__tests__/CommentsList.tsx b/src/containers/CommentsList/__tests__/CommentsList.tsx index e61ac24cdd..bad22d9ca5 100644 --- a/src/containers/CommentsList/__tests__/CommentsList.tsx +++ b/src/containers/CommentsList/__tests__/CommentsList.tsx @@ -1,10 +1,12 @@ +/* eslint max-lines: 0 */ import React from 'react'; -import { Alert } from 'react-native'; -import configureStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; +import { Alert, View, AlertButton } from 'react-native'; import i18n from 'i18next'; +import { fireEvent } from 'react-native-testing-library'; +import { ReactTestInstance } from 'react-test-renderer'; -import { renderShallow } from '../../../../testUtils'; +import { renderWithContext } from '../../../../testUtils'; +import { mockFragment } from '../../../../testUtils/apolloMockClient'; import { celebrateCommentsSelector } from '../../../selectors/celebrateComments'; import { orgPermissionSelector } from '../../../selectors/people'; import { @@ -17,7 +19,11 @@ import { import { reportComment } from '../../../actions/reportComments'; import { ORG_PERMISSIONS } from '../../../constants'; import { navigatePush } from '../../../actions/navigation'; -import Text from '../../../components/Text'; +import { Person } from '../../../reducers/people'; +import { Organization } from '../../../reducers/organizations'; +import { CelebrateComment } from '../../../reducers/celebrateComments'; +import { CELEBRATE_ITEM_FRAGMENT } from '../../../components/CelebrateItem/queries'; +import { GetCelebrateFeed_community_celebrationItems_nodes as CelebrateItem } from '../../../containers/CelebrateFeed/__generated__/GetCelebrateFeed'; import CommentsList from '..'; @@ -28,19 +34,35 @@ jest.mock('../../../selectors/celebration'); jest.mock('../../../selectors/people'); jest.mock('../../../selectors/celebrateComments'); -const mockStore = configureStore([thunk]); -// @ts-ignore -let store; - -const organizationId = '24234234'; -const event = { id: '90001', organization: { id: organizationId } }; -const celebrateComments = { - comments: [{ content: 'some comment' }, { content: 'another comment' }], +const me: Person = { id: '1', first_name: 'Matt', last_name: 'Smith' }; +const otherPerson: Person = { id: '2', first_name: 'Will', last_name: 'Smith' }; +const organization: Organization = { id: '24234234' }; +const event = mockFragment(CELEBRATE_ITEM_FRAGMENT); +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const comments: { comments: CelebrateComment[]; pagination: any } = { + comments: [ + { + id: '1', + created_at: '2004-04-04 00:00:00 UTC', + updated_at: '2004-04-04 00:00:00 UTC', + content: 'some comment', + person: otherPerson, + }, + { + id: '2', + created_at: '2004-04-04 00:00:00 UTC', + updated_at: '2004-04-04 00:00:00 UTC', + content: 'some comment', + person: otherPerson, + }, + ], pagination: {}, }; -const organizations = [event.organization]; -const celebrateCommentsState = [celebrateComments]; +const auth = { person: me }; +const organizations = { all: [organization] }; +const celebrateComments = { all: [comments] }; + const reloadCelebrateCommentsResult = { type: 'loaded comments' }; const getCelebrateCommentsNextPageResult = { type: 'got next page' }; const deleteCelebrateCommentResult = { type: 'delete comment' }; @@ -49,184 +71,206 @@ const setCelebrateEditingCommentResult = { type: 'set edit comment' }; const reportCommentResult = { type: 'report comment' }; const navigatePushResult = { type: 'navigate push' }; -// @ts-ignore -let screen; - -// @ts-ignore -reloadCelebrateComments.mockReturnValue(dispatch => - dispatch(reloadCelebrateCommentsResult), -); -// @ts-ignore -getCelebrateCommentsNextPage.mockReturnValue(dispatch => - dispatch(getCelebrateCommentsNextPageResult), -); -// @ts-ignore -deleteCelebrateComment.mockReturnValue(dispatch => - dispatch(deleteCelebrateCommentResult), -); -// @ts-ignore -resetCelebrateEditingComment.mockReturnValue(dispatch => - dispatch(resetCelebrateEditingCommentResult), -); -// @ts-ignore -setCelebrateEditingComment.mockReturnValue(dispatch => - dispatch(setCelebrateEditingCommentResult), -); -// @ts-ignore -navigatePush.mockReturnValue(dispatch => dispatch(navigatePushResult)); -// @ts-ignore -reportComment.mockReturnValue(dispatch => dispatch(reportCommentResult)); Alert.alert = jest.fn(); -const me = { id: '1' }; -const otherPerson = { id: '2' }; +const initialState = { auth, organizations, celebrateComments }; beforeEach(() => { - store = mockStore({ - auth: { person: me }, - organizations, - celebrateComments: celebrateCommentsState, - }); - - screen = renderShallow(, store); + (reloadCelebrateComments as jest.Mock).mockReturnValue( + reloadCelebrateCommentsResult, + ); + (getCelebrateCommentsNextPage as jest.Mock).mockReturnValue( + getCelebrateCommentsNextPageResult, + ); + (deleteCelebrateComment as jest.Mock).mockReturnValue( + deleteCelebrateCommentResult, + ); + (resetCelebrateEditingComment as jest.Mock).mockReturnValue( + resetCelebrateEditingCommentResult, + ); + (setCelebrateEditingComment as jest.Mock).mockReturnValue( + setCelebrateEditingCommentResult, + ); + (navigatePush as jest.Mock).mockReturnValue(navigatePushResult); + (reportComment as jest.Mock).mockReturnValue(reportCommentResult); + ((celebrateCommentsSelector as unknown) as jest.Mock).mockReturnValue( + comments, + ); }); describe('mounts with custom props', () => { it('passes in custom flatlist props', () => { - screen = renderShallow( + renderWithContext( Test }} + organization={organization} + listProps={{ listHeaderComponent: () => }} />, - // @ts-ignore - store, - ); - expect(screen).toMatchSnapshot(); + { + initialState, + }, + ).snapshot(); }); }); -describe('componentDidMount', () => { +describe('refreshes on mount', () => { it('refreshes items', () => { - expect(reloadCelebrateComments).toHaveBeenCalledWith(event); - // @ts-ignore - expect(store.getActions()).toEqual( - expect.arrayContaining([reloadCelebrateCommentsResult]), + const { store } = renderWithContext( + , + { + initialState, + }, ); + + expect(reloadCelebrateComments).toHaveBeenCalledWith( + event.id, + organization.id, + ); + expect(resetCelebrateEditingComment).toHaveBeenCalledWith(); + expect(store.getActions()).toEqual([ + reloadCelebrateCommentsResult, + resetCelebrateEditingCommentResult, + ]); }); }); describe('with no comments', () => { - // @ts-ignore - beforeAll(() => celebrateCommentsSelector.mockReturnValue(undefined)); + beforeEach(() => { + ((celebrateCommentsSelector as unknown) as jest.Mock).mockReturnValue( + undefined, + ); + }); it('renders correctly', () => { - // @ts-ignore - expect(screen).toMatchSnapshot(); + renderWithContext( + , + { + initialState, + }, + ).snapshot(); }); }); describe('with comments', () => { describe('with next page', () => { - beforeAll(() => - // @ts-ignore - celebrateCommentsSelector.mockReturnValue({ - ...celebrateComments, + beforeEach(() => + ((celebrateCommentsSelector as unknown) as jest.Mock).mockReturnValue({ + ...comments, pagination: { hasNextPage: true }, }), ); it('renders correctly', () => { - // @ts-ignore - expect(screen).toMatchSnapshot(); - }); - - it('renders item correctly', () => { - expect( - // @ts-ignore - screen.props().renderItem({ - item: { - content: 'hello roge', - person: { id: '1' }, - }, - }), - ).toMatchSnapshot(); + renderWithContext( + , + { + initialState, + }, + ).snapshot(); }); it('loads more comments', () => { - // @ts-ignore - screen.props().ListFooterComponent.props.onPress(); + const { store, getByTestId } = renderWithContext( + , + { + initialState, + }, + ); + + fireEvent.press(getByTestId('LoadMore')); - expect(getCelebrateCommentsNextPage).toHaveBeenCalledWith(event); - // @ts-ignore - expect(store.getActions()).toEqual( - expect.arrayContaining([getCelebrateCommentsNextPageResult]), + expect(getCelebrateCommentsNextPage).toHaveBeenCalledWith( + event.id, + organization.id, ); + expect(store.getActions()).toEqual([ + reloadCelebrateCommentsResult, + resetCelebrateEditingCommentResult, + getCelebrateCommentsNextPageResult, + ]); }); }); describe('without next page', () => { - beforeAll(() => - // @ts-ignore - celebrateCommentsSelector.mockReturnValue(celebrateComments), + beforeEach(() => + ((celebrateCommentsSelector as unknown) as jest.Mock).mockReturnValue( + comments, + ), ); it('renders correctly', () => { - // @ts-ignore - expect(screen).toMatchSnapshot(); + renderWithContext( + , + { + initialState, + }, + ).snapshot(); }); }); }); describe('determine comment menu actions', () => { - // @ts-ignore - let screen; - // @ts-ignore - let comment; - // @ts-ignore - let permission_id; - - const buildScreenWithComment = () => { - // @ts-ignore - orgPermissionSelector.mockReturnValue({ permission_id }); - - store = mockStore({ - auth: { person: me }, - organizations, - celebrateComments: { - // @ts-ignore - comments: [comment], - pagination: {}, - }, + let commentItem: ReactTestInstance; + let comment: CelebrateComment; + let permission_id: string; + + const buildScreen = () => { + ((orgPermissionSelector as unknown) as jest.Mock).mockReturnValue({ + permission_id, }); + ((celebrateCommentsSelector as unknown) as jest.Mock).mockReturnValue({ + comments: [comment], + pagination: {}, + }); + + const { getByTestId } = renderWithContext( + , + { + initialState, + }, + ); - screen = renderShallow(, store); + commentItem = getByTestId('CommentItem'); }; - // @ts-ignore - const testActionArray = expectedActions => { - expect( - // @ts-ignore - screen.props().renderItem({ item: comment }).props.menuActions, - ).toEqual(expectedActions); + const testActionArray = ( + expectedActions: { + text: string; + onPress: () => void; + destructive?: boolean; + }[], + ) => { + expect(commentItem.props.menuActions).toEqual(expectedActions); }; - // @ts-ignore - const testFireAction = actionIndex => { - // @ts-ignore - screen - .props() - // @ts-ignore - .renderItem({ item: comment }) - // @ts-ignore - .props.menuActions[actionIndex].onPress(comment); + const testFireAction = (actionIndex: number) => { + commentItem.props.menuActions[actionIndex].onPress(comment); }; describe('author actions', () => { beforeEach(() => { - comment = { id: 'comment1', person: me }; + comment = { + id: 'comment1', + created_at: '2004-04-04 00:00:00 UTC', + updated_at: '2004-04-04 00:00:00 UTC', + person: me, + content: 'comment 1', + }; permission_id = ORG_PERMISSIONS.ADMIN; - buildScreenWithComment(); + + buildScreen(); }); it('creates array', () => { @@ -245,21 +289,22 @@ describe('determine comment menu actions', () => { it('handleEdit', () => { testFireAction(0); - // @ts-ignore + expect(setCelebrateEditingComment).toHaveBeenCalledWith(comment.id); }); it('handleDelete', () => { - // @ts-ignore - Alert.alert = jest.fn((a, b, c) => c[1].onPress()); + Alert.alert = jest.fn( + (_, __, c: AlertButton[] | undefined) => + c && c[1] && c[1].onPress && c[1].onPress(), + ); testFireAction(1); expect(deleteCelebrateComment).toHaveBeenCalledWith( - organizationId, - event, - // @ts-ignore - comment, + organization.id, + event.id, + comment.id, ); expect(Alert.alert).toHaveBeenCalledWith( i18n.t('commentsList:deletePostHeader'), @@ -280,9 +325,16 @@ describe('determine comment menu actions', () => { describe('owner actions', () => { beforeEach(() => { - comment = { id: 'comment1', person: otherPerson }; + comment = { + id: 'comment1', + person: otherPerson, + created_at: '2004-04-04 00:00:00 UTC', + updated_at: '2004-04-04 00:00:00 UTC', + content: 'comment 1', + }; permission_id = ORG_PERMISSIONS.OWNER; - buildScreenWithComment(); + + buildScreen(); }); it('creates array', () => { @@ -296,16 +348,17 @@ describe('determine comment menu actions', () => { }); it('handleDelete', () => { - // @ts-ignore - Alert.alert = jest.fn((a, b, c) => c[1].onPress()); + Alert.alert = jest.fn( + (_, __, c: AlertButton[] | undefined) => + c && c[1] && c[1].onPress && c[1].onPress(), + ); testFireAction(0); expect(deleteCelebrateComment).toHaveBeenCalledWith( - organizationId, - event, - // @ts-ignore - comment, + organization.id, + event.id, + comment.id, ); expect(Alert.alert).toHaveBeenCalledWith( i18n.t('commentsList:deletePostHeader'), @@ -326,9 +379,16 @@ describe('determine comment menu actions', () => { describe('user actions', () => { beforeEach(() => { - comment = { id: 'comment1', person: otherPerson }; + comment = { + id: 'comment1', + person: otherPerson, + created_at: '2004-04-04 00:00:00 UTC', + updated_at: '2004-04-04 00:00:00 UTC', + content: 'comment 1', + }; permission_id = ORG_PERMISSIONS.USER; - buildScreenWithComment(); + + buildScreen(); }); it('creates array', () => { @@ -341,13 +401,14 @@ describe('determine comment menu actions', () => { }); it('handleReport', () => { - // @ts-ignore - Alert.alert = jest.fn((a, b, c) => c[1].onPress()); + Alert.alert = jest.fn( + (_, __, c: AlertButton[] | undefined) => + c && c[1] && c[1].onPress && c[1].onPress(), + ); testFireAction(0); - // @ts-ignore - expect(reportComment).toHaveBeenCalledWith(organizationId, comment); + expect(reportComment).toHaveBeenCalledWith(organization.id, comment); expect(Alert.alert).toHaveBeenCalledWith( i18n.t('commentsList:reportToOwnerHeader'), i18n.t('commentsList:reportAreYouSure'), diff --git a/src/containers/CommentsList/__tests__/__snapshots__/CommentsList.tsx.snap b/src/containers/CommentsList/__tests__/__snapshots__/CommentsList.tsx.snap index 10d1596225..7091d57673 100644 --- a/src/containers/CommentsList/__tests__/__snapshots__/CommentsList.tsx.snap +++ b/src/containers/CommentsList/__tests__/__snapshots__/CommentsList.tsx.snap @@ -1,39 +1,429 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`mounts with custom props passes in custom flatlist props 1`] = ` - +> + + + + + + + + Will Smith + + + + + April 4, 2004 @ 12:00 AM + + + + + + + some comment + + + + + + + + + + + + + + Will Smith + + + + + April 4, 2004 @ 12:00 AM + + + + + + + some comment + + + + + + + + + `; exports[`with comments with next page renders correctly 1`] = ` - } bounces={false} @@ -46,65 +436,485 @@ exports[`with comments with next page renders correctly 1`] = ` Array [ Object { "content": "some comment", + "created_at": "2004-04-04 00:00:00 UTC", + "id": "1", + "person": Object { + "first_name": "Will", + "id": "2", + "last_name": "Smith", + }, + "updated_at": "2004-04-04 00:00:00 UTC", }, Object { - "content": "another comment", + "content": "some comment", + "created_at": "2004-04-04 00:00:00 UTC", + "id": "2", + "person": Object { + "first_name": "Will", + "id": "2", + "last_name": "Smith", + }, + "updated_at": "2004-04-04 00:00:00 UTC", }, ] } disableVirtualization={false} + getItem={[Function]} + getItemCount={[Function]} horizontal={false} initialNumToRender={10} keyExtractor={[Function]} maxToRenderPerBatch={10} numColumns={1} + onContentSizeChange={[Function]} onEndReachedThreshold={2} + onLayout={[Function]} + onMomentumScrollEnd={[Function]} + onScroll={[Function]} + onScrollBeginDrag={[Function]} + onScrollEndDrag={[Function]} removeClippedSubviews={false} renderItem={[Function]} scrollEventThrottle={50} + stickyHeaderIndices={Array []} style={ Object { "flex": 1, } } updateCellsBatchingPeriod={50} + viewabilityConfigCallbackPairs={Array []} windowSize={21} -/> -`; - -exports[`with comments with next page renders item correctly 1`] = ` - +> + + + + + + + + Will Smith + + + + + April 4, 2004 @ 12:00 AM + + + + + + + some comment + + + + + + + + + + + + + + Will Smith + + + + + April 4, 2004 @ 12:00 AM + + + + + + + some comment + + + + + + + + + + + + LOAD MORE + + + + + + `; exports[`with comments without next page renders correctly 1`] = ` - +> + + + + + + + + Will Smith + + + + + April 4, 2004 @ 12:00 AM + + + + + + + some comment + + + + + + + + + + + + + + Will Smith + + + + + April 4, 2004 @ 12:00 AM + + + + + + + some comment + + + + + + + + + `; exports[`with no comments renders correctly 1`] = ` - +> + + `; diff --git a/src/containers/CommentsList/index.tsx b/src/containers/CommentsList/index.tsx index 3bce58fcd5..0cd7c019a4 100644 --- a/src/containers/CommentsList/index.tsx +++ b/src/containers/CommentsList/index.tsx @@ -1,8 +1,9 @@ -import React, { Component } from 'react'; +import React, { useEffect } from 'react'; import { connect } from 'react-redux-legacy'; -import PropTypes from 'prop-types'; import { Alert, FlatList } from 'react-native'; -import { withTranslation } from 'react-i18next'; +import { ThunkDispatch } from 'redux-thunk'; +import { AnyAction } from 'redux'; +import { useTranslation } from 'react-i18next'; import { celebrateCommentsSelector } from '../../selectors/celebrateComments'; import { @@ -17,37 +18,66 @@ import LoadMore from '../../components/LoadMore'; import { keyExtractorId, isOwner } from '../../utils/common'; import CommentItem from '../CommentItem'; import { orgPermissionSelector } from '../../selectors/people'; +import { AuthState } from '../../reducers/auth'; +import { + CelebrateCommentsState, + CelebrateComment, +} from '../../reducers/celebrateComments'; +import { Organization } from '../../reducers/organizations'; +import { Person } from '../../reducers/people'; +import { GetCelebrateFeed_community_celebrationItems_nodes } from '../CelebrateFeed/__generated__/GetCelebrateFeed'; import styles from './styles'; -// @ts-ignore -@withTranslation('commentsList') -class CommentsList extends Component { - componentDidMount() { - // @ts-ignore - const { dispatch, event } = this.props; +export interface CommentsListProps { + dispatch: ThunkDispatch< + { auth: AuthState; celebrateComments: CelebrateCommentsState }, + {}, + AnyAction + >; + event: GetCelebrateFeed_community_celebrationItems_nodes; + organization: Organization; + me: Person; + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + celebrateComments?: { comments: CelebrateComment[]; pagination: any }; + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + listProps: { [key: string]: any }; +} - dispatch(reloadCelebrateComments(event)); +const CommentsList = ({ + dispatch, + event, + organization, + me, + celebrateComments, + listProps, +}: CommentsListProps) => { + const { t } = useTranslation('commentsList'); + + useEffect(() => { + dispatch(reloadCelebrateComments(event.id, organization.id)); dispatch(resetCelebrateEditingComment()); - } - - handleLoadMore = () => { - // @ts-ignore - const { dispatch, event } = this.props; + }, []); - dispatch(getCelebrateCommentsNextPage(event)); + const handleLoadMore = () => { + dispatch(getCelebrateCommentsNextPage(event.id, organization.id)); }; - // @ts-ignore - handleEdit = item => { - // @ts-ignore - this.props.dispatch(setCelebrateEditingComment(item.id)); + const handleEdit = (item: CelebrateComment) => { + dispatch(setCelebrateEditingComment(item.id)); }; - // @ts-ignore - alert = ({ title, message, actionText, action }) => { - // @ts-ignore - const { t } = this.props; + const alert = ({ + title, + message, + actionText, + action, + }: { + title: string; + message: string; + actionText: string; + action: () => void; + }) => { Alert.alert(t(title), t(message), [ { text: t('cancel'), @@ -60,72 +90,62 @@ class CommentsList extends Component { ]); }; - // @ts-ignore - handleDelete = item => { - // @ts-ignore - const { dispatch, event } = this.props; - - this.alert({ + const handleDelete = (item: CelebrateComment) => { + alert({ title: 'deletePostHeader', message: 'deleteAreYouSure', actionText: 'deletePost', action: () => { - dispatch(deleteCelebrateComment(event.organization.id, event, item)); + dispatch(deleteCelebrateComment(organization.id, event.id, item.id)); }, }); }; - // @ts-ignore - handleReport = item => { - // @ts-ignore - const { dispatch, event } = this.props; - this.alert({ + const handleReport = (item: CelebrateComment) => { + alert({ title: 'reportToOwnerHeader', message: 'reportAreYouSure', actionText: 'reportPost', action: () => { - dispatch(reportComment(event.organization.id, item)); + dispatch(reportComment(organization.id, item)); }, }); }; - // @ts-ignore - menuActions = item => { - const { - // @ts-ignore - t, - // @ts-ignore - event: { organization }, - // @ts-ignore - me, - } = this.props; - - const actions = []; + const menuActions = (item: CelebrateComment) => { + const actions: { + text: string; + onPress: () => void; + destructive?: boolean; + }[] = []; + const deleteAction = { text: t('deletePost'), - onPress: () => this.handleDelete(item), + onPress: () => handleDelete(item), destructive: true, }; if (me.id === item.person.id) { actions.push({ text: t('editPost'), - onPress: () => this.handleEdit(item), + onPress: () => handleEdit(item), }); actions.push(deleteAction); } else { const orgPermission = - // @ts-ignore - orgPermissionSelector(null, { - person: me, - organization, - }) || {}; + orgPermissionSelector( + {}, + { + person: me, + organization, + }, + ) || {}; if (isOwner(orgPermission)) { actions.push(deleteAction); } else { actions.push({ text: t('reportToOwner'), - onPress: () => this.handleReport(item), + onPress: () => handleReport(item), }); } } @@ -133,51 +153,44 @@ class CommentsList extends Component { return actions; }; - // @ts-ignore - renderItem = ({ item }) => ( + const renderItem = ({ item }: { item: CelebrateComment }) => ( ); - render() { - const { - // @ts-ignore - listProps, - // @ts-ignore - celebrateComments: { comments, pagination } = {}, - } = this.props; - const { list, listContent } = styles; - - return ( - - } - bounces={false} - {...listProps} - /> - ); - } -} - -// @ts-ignore -CommentsList.propTypes = { - event: PropTypes.object.isRequired, + const { comments = [], pagination } = celebrateComments || {}; + const { list, listContent } = styles; + + return ( + + ) + } + bounces={false} + {...listProps} + /> + ); }; -// @ts-ignore -const mapStateToProps = ({ auth, celebrateComments }, { event }) => ({ +const mapStateToProps = ( + { + auth, + celebrateComments, + }: { auth: AuthState; celebrateComments: CelebrateCommentsState }, + { event }: { event: GetCelebrateFeed_community_celebrationItems_nodes }, +) => ({ me: auth.person, celebrateComments: celebrateCommentsSelector( { celebrateComments }, diff --git a/src/containers/Groups/EditStoryScreen/__tests__/EditStoryScreen.tsx b/src/containers/Groups/EditStoryScreen/__tests__/EditStoryScreen.tsx index 68dc62ea53..bd3b52caa0 100644 --- a/src/containers/Groups/EditStoryScreen/__tests__/EditStoryScreen.tsx +++ b/src/containers/Groups/EditStoryScreen/__tests__/EditStoryScreen.tsx @@ -3,17 +3,17 @@ import { fireEvent } from 'react-native-testing-library'; import { navigateBack } from '../../../../actions/navigation'; import { renderWithContext } from '../../../../../testUtils'; +import { mockFragment } from '../../../../../testUtils/apolloMockClient'; import { useAnalytics } from '../../../../utils/hooks/useAnalytics'; +import { CELEBRATE_ITEM_FRAGMENT } from '../../../../components/CelebrateItem/queries'; +import { GetCelebrateFeed_community_celebrationItems_nodes as CelebrateItem } from '../../../CelebrateFeed/__generated__/GetCelebrateFeed'; import EditStoryScreen from '..'; jest.mock('../../../../actions/navigation'); jest.mock('../../../../utils/hooks/useAnalytics'); -const celebrationItem = { - celebrateable_id: '111', - object_description: 'It was the best of times...', -}; +const celebrationItem = mockFragment(CELEBRATE_ITEM_FRAGMENT); const newText = 'It was the worst of times...'; @@ -38,7 +38,7 @@ it('renders correctly', () => { it('renders empty text correctly', () => { renderWithContext(, { navParams: { - celebrationItem: { ...celebrationItem, object_description: '' }, + celebrationItem: { ...celebrationItem, objectDescription: null }, onRefresh, }, }).snapshot(); diff --git a/src/containers/Groups/EditStoryScreen/__tests__/__snapshots__/EditStoryScreen.tsx.snap b/src/containers/Groups/EditStoryScreen/__tests__/__snapshots__/EditStoryScreen.tsx.snap index 20416f5634..931c3d14dd 100644 --- a/src/containers/Groups/EditStoryScreen/__tests__/__snapshots__/EditStoryScreen.tsx.snap +++ b/src/containers/Groups/EditStoryScreen/__tests__/__snapshots__/EditStoryScreen.tsx.snap @@ -190,7 +190,7 @@ exports[`renders correctly 1`] = ` } testID="EditInput" underlineColorAndroid="rgba(0,0,0,0)" - value="It was the best of times..." + value="qui recusandae ut" /> diff --git a/src/containers/Groups/EditStoryScreen/index.tsx b/src/containers/Groups/EditStoryScreen/index.tsx index 7d3be541ff..a8c5fd7577 100644 --- a/src/containers/Groups/EditStoryScreen/index.tsx +++ b/src/containers/Groups/EditStoryScreen/index.tsx @@ -14,8 +14,8 @@ import Header from '../../../components/Header'; import { navigateBack } from '../../../actions/navigation'; import BackButton from '../../BackButton'; import theme from '../../../theme'; -import { Event } from '../../../components/CelebrateItem'; import { useAnalytics } from '../../../utils/hooks/useAnalytics'; +import { GetCelebrateFeed_community_celebrationItems_nodes } from '../../CelebrateFeed/__generated__/GetCelebrateFeed'; import styles from './styles'; import { UpdateStory, UpdateStoryVariables } from './__generated__/UpdateStory'; @@ -39,13 +39,16 @@ const EditStoryScreen = ({ dispatch }: EditStoryProps) => { const { t } = useTranslation('editStoryScreen'); const { container, backButton, textInput } = styles; const onRefresh: () => Promise = useNavigationParam('onRefresh'); - const { object_description, celebrateable_id }: Event = useNavigationParam( + const { + objectDescription, + celebrateableId, + }: GetCelebrateFeed_community_celebrationItems_nodes = useNavigationParam( 'celebrationItem', ); const [updateStory] = useMutation( UPDATE_STORY, ); - const [story, changeStory] = useState(object_description); + const [story, changeStory] = useState(objectDescription || undefined); const saveStory = async () => { if (!story) { @@ -53,7 +56,7 @@ const EditStoryScreen = ({ dispatch }: EditStoryProps) => { } Keyboard.dismiss(); await updateStory({ - variables: { input: { id: celebrateable_id, content: story } }, + variables: { input: { id: celebrateableId, content: story } }, }); onRefresh(); diff --git a/src/containers/Groups/GroupCelebrate.tsx b/src/containers/Groups/GroupCelebrate.tsx index 7db3c7bf57..92d9ee7d6c 100644 --- a/src/containers/Groups/GroupCelebrate.tsx +++ b/src/containers/Groups/GroupCelebrate.tsx @@ -1,91 +1,54 @@ -import React, { Component } from 'react'; +import React from 'react'; import { connect } from 'react-redux-legacy'; -import { withTranslation } from 'react-i18next'; -import moment from 'moment'; +import { ThunkDispatch } from 'redux-thunk'; +import { AnyAction } from 'redux'; import CelebrateFeed from '../CelebrateFeed'; -import { - getGroupCelebrateFeed, - reloadGroupCelebrateFeed, -} from '../../actions/celebration'; import { refreshCommunity } from '../../actions/organizations'; import { organizationSelector } from '../../selectors/organizations'; -import { celebrationSelector } from '../../selectors/celebration'; -import { - momentUtc, - refresh, - orgIsGlobal, - shouldQueryReportedComments, -} from '../../utils/common'; +import { orgIsGlobal, shouldQueryReportedComments } from '../../utils/common'; import { getReportedComments } from '../../actions/reportComments'; import { orgPermissionSelector } from '../../selectors/people'; +import { AuthState } from '../../reducers/auth'; +import { Organization, OrganizationsState } from '../../reducers/organizations'; import Analytics from '../Analytics'; -// @ts-ignore -@withTranslation('groupsCelebrate') -class GroupCelebrate extends Component { - state = { refreshing: false }; - - componentDidMount() { - if (this.shouldLoadFeed()) { - this.loadItems(); - } - } - - shouldLoadFeed = () => { - // @ts-ignore - const { pagination, celebrateItems } = this.props; - - return ( - !celebrateItems || - celebrateItems.length === 0 || - pagination.page === 0 || - moment().diff(momentUtc(celebrateItems[0].date), 'days', true) > 1 - ); - }; - - loadItems = () => { - // @ts-ignore - const { dispatch, organization } = this.props; - dispatch(getGroupCelebrateFeed(organization.id)); - }; +export interface GroupCelebrateProps { + dispatch: ThunkDispatch<{ organizations: OrganizationsState }, {}, AnyAction>; + organization: Organization; + shouldQueryReport: boolean; +} - reloadItems = () => { - // @ts-ignore - const { dispatch, organization, shouldQueryReport } = this.props; +const GroupCelebrate = ({ + dispatch, + organization, + shouldQueryReport, +}: GroupCelebrateProps) => { + const handleRefetch = () => { dispatch(refreshCommunity(organization.id)); shouldQueryReport && dispatch(getReportedComments(organization.id)); - return dispatch(reloadGroupCelebrateFeed(organization.id)); }; - refreshItems = () => { - refresh(this, this.reloadItems); - }; - - render() { - const { refreshing } = this.state; - // @ts-ignore - const { celebrateItems, organization } = this.props; - - return ( - <> - - - - ); - } -} + return ( + <> + + + + ); +}; -// @ts-ignore -const mapStateToProps = ({ auth, organizations }, { orgId }) => { +const mapStateToProps = ( + { + auth, + organizations, + }: { auth: AuthState; organizations: OrganizationsState }, + { orgId }: { orgId: string }, +) => { const organization = organizationSelector({ organizations }, { orgId }); const myOrgPermission = orgPermissionSelector( {}, @@ -98,10 +61,6 @@ const mapStateToProps = ({ auth, organizations }, { orgId }) => { organization, myOrgPermission, ), - celebrateItems: celebrationSelector({ - celebrateItems: organization.celebrateItems || [], - }), - pagination: organization.celebratePagination || {}, }; }; diff --git a/src/containers/Groups/GroupUnreadFeed.tsx b/src/containers/Groups/GroupUnreadFeed.tsx index c61b7cef01..fee99eb93e 100644 --- a/src/containers/Groups/GroupUnreadFeed.tsx +++ b/src/containers/Groups/GroupUnreadFeed.tsx @@ -1,132 +1,107 @@ -import React, { Component } from 'react'; +import React from 'react'; import { StatusBar, View } from 'react-native'; import { connect } from 'react-redux-legacy'; -import { withTranslation } from 'react-i18next'; +import { useTranslation } from 'react-i18next'; +import { ThunkDispatch } from 'redux-thunk'; +import { AnyAction } from 'redux'; -import { refresh } from '../../utils/common'; import Header from '../../components/Header'; import { Button } from '../../components/common'; import BackButton from '../BackButton'; import { navigateBack } from '../../actions/navigation'; import { organizationSelector } from '../../selectors/organizations'; -import { getGroupCelebrateFeedUnread } from '../../actions/celebration'; import CelebrateFeed from '../CelebrateFeed'; import theme from '../../theme'; -import { celebrationSelector } from '../../selectors/celebration'; import { refreshCommunity } from '../../actions/organizations'; +import { OrganizationsState, Organization } from '../../reducers/organizations'; import { markCommentsRead, markCommentRead, } from '../../actions/unreadComments'; +import { GetCelebrateFeed_community_celebrationItems_nodes } from '../CelebrateFeed/__generated__/GetCelebrateFeed'; import Analytics from '../Analytics'; import styles from './styles'; -// @ts-ignore -@withTranslation('groupUnread') -class GroupUnreadFeed extends Component { - state = { refreshing: false, items: [] }; +export interface GroupUnreadFeedProps { + dispatch: ThunkDispatch<{ organizations: OrganizationsState }, {}, AnyAction>; + organization: Organization; + count: number; +} - componentDidMount() { - this.loadItems(); - } +const GroupUnreadFeed = ({ + dispatch, + organization, + count, +}: GroupUnreadFeedProps) => { + const { t } = useTranslation('groupUnread'); - loadItems = async () => { - // @ts-ignore - const { dispatch, organization } = this.props; - dispatch(refreshCommunity(organization.id)); - const { response } = await dispatch( - getGroupCelebrateFeedUnread(organization.id), - ); - const items = celebrationSelector({ - celebrateItems: response || [], - }); - this.setState({ items }); - return items; - }; + const back = () => dispatch(navigateBack()); - refreshItems = () => { - refresh(this, this.loadItems); - }; + const handleRefetch = () => dispatch(refreshCommunity(organization.id)); - markRead = async () => { - // @ts-ignore - const { dispatch, organization } = this.props; + const markAllAsRead = async () => { await dispatch(markCommentsRead(organization.id)); - this.back(); - }; - - // @ts-ignore - clearNotification = async event => { - // @ts-ignore - const { dispatch } = this.props; - await dispatch(markCommentRead(event.id)); - this.loadItems(); + back(); }; - // @ts-ignore - back = () => this.props.dispatch(navigateBack()); + const handleClearNotification = ( + event: GetCelebrateFeed_community_celebrationItems_nodes, + ) => dispatch(markCommentRead(event.id)); - render() { - // @ts-ignore - const { t, organization, count } = this.props; - const { refreshing, items } = this.state; - - return ( - - - -
} - right={ -