mirror of
https://github.com/inaturalist/iNaturalistReactNative.git
synced 2026-02-18 14:56:26 -05:00
Show notifications obs photos for remote obs (#1519)
--------- Co-authored-by: Angie Ta <angie@inaturalist.org>
This commit is contained in:
@@ -65,6 +65,28 @@ const fetchRemoteObservation = async (
|
||||
}
|
||||
};
|
||||
|
||||
const fetchRemoteObservations = async (
|
||||
uuids: Array<string>,
|
||||
params: Object = {},
|
||||
opts: Object = {}
|
||||
): Promise<?number> => {
|
||||
try {
|
||||
const response = await inatjs.observations.fetch(
|
||||
uuids,
|
||||
params,
|
||||
opts
|
||||
);
|
||||
if ( !response ) { return null; }
|
||||
const { results } = response;
|
||||
if ( results?.length > 0 ) {
|
||||
return results.map( mapToLocalSchema );
|
||||
}
|
||||
return null;
|
||||
} catch ( e ) {
|
||||
return handleError( e );
|
||||
}
|
||||
};
|
||||
|
||||
const markAsReviewed = async ( params: Object = {}, opts: Object = {} ): Promise<?number> => {
|
||||
try {
|
||||
return await inatjs.observations.review( params, opts );
|
||||
@@ -205,6 +227,7 @@ export {
|
||||
fetchObservationUpdates,
|
||||
fetchObservers,
|
||||
fetchRemoteObservation,
|
||||
fetchRemoteObservations,
|
||||
fetchSpeciesCounts,
|
||||
fetchUnviewedObservationUpdatesCount,
|
||||
markAsReviewed,
|
||||
|
||||
@@ -22,8 +22,9 @@ import {
|
||||
useObservationsUpdates,
|
||||
useTranslation
|
||||
} from "sharedHooks";
|
||||
import useRemoteObservation,
|
||||
{ fetchRemoteObservationKey } from "sharedHooks/useRemoteObservation";
|
||||
import useRemoteObservation, {
|
||||
fetchRemoteObservationKey
|
||||
} from "sharedHooks/useRemoteObservation";
|
||||
import { ACTIVITY_TAB_ID, DETAILS_TAB_ID } from "stores/createLayoutSlice";
|
||||
import useStore from "stores/useStore";
|
||||
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
// @flow
|
||||
|
||||
import { useInfiniteQuery } from "@tanstack/react-query";
|
||||
import { fetchObservationUpdates } from "api/observations";
|
||||
import { fetchObservationUpdates, fetchRemoteObservations } from "api/observations";
|
||||
import { getJWT } from "components/LoginSignUp/AuthenticationService";
|
||||
import { flatten } from "lodash";
|
||||
import { RealmContext } from "providers/contexts";
|
||||
import { useCallback } from "react";
|
||||
import Observation from "realmModels/Observation";
|
||||
import { reactQueryRetry } from "sharedHelpers/logging";
|
||||
import { useCurrentUser } from "sharedHooks";
|
||||
|
||||
const { useRealm } = RealmContext;
|
||||
|
||||
const BASE_PARAMS = {
|
||||
observations_by: "owner",
|
||||
fields: "all",
|
||||
@@ -17,6 +22,16 @@ const BASE_PARAMS = {
|
||||
|
||||
const useInfiniteNotificationsScroll = ( ): Object => {
|
||||
const currentUser = useCurrentUser( );
|
||||
const realm = useRealm( );
|
||||
|
||||
const fetchObsByUUIDs = useCallback( async ( uuids, authOptions ) => {
|
||||
const observations = await fetchRemoteObservations(
|
||||
uuids,
|
||||
{ fields: Observation.FIELDS },
|
||||
authOptions
|
||||
);
|
||||
Observation.upsertRemoteObservations( observations, realm );
|
||||
}, [realm] );
|
||||
|
||||
const infQueryResult = useInfiniteQuery( {
|
||||
queryKey: ["useInfiniteNotificationsScroll"],
|
||||
@@ -35,6 +50,10 @@ const useInfiniteNotificationsScroll = ( ): Object => {
|
||||
}
|
||||
|
||||
const response = await fetchObservationUpdates( params, options );
|
||||
const obsUUIDs = response?.map( obsUpdate => obsUpdate.resource_uuid ) || [];
|
||||
if ( obsUUIDs.length > 0 ) {
|
||||
await fetchObsByUUIDs( obsUUIDs, options );
|
||||
}
|
||||
|
||||
return response;
|
||||
},
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
import { define } from "factoria";
|
||||
|
||||
import userFactory from "./RemoteUser";
|
||||
|
||||
export default define( "RemoteComment", faker => ( {
|
||||
body: faker.lorem.paragraph( ),
|
||||
created_at: faker.date.past( ).toISOString( ),
|
||||
id: faker.number.int( ),
|
||||
parent_id: faker.number.int( ),
|
||||
parent_type: "Observation",
|
||||
updated_at: faker.date.past( ).toISOString( ),
|
||||
user: userFactory( "RemoteUser" ),
|
||||
user_id: faker.number.int( ),
|
||||
uuid: faker.string.uuid( )
|
||||
} ) );
|
||||
|
||||
@@ -121,6 +121,7 @@ function renderHook( renderCallback, options = {} ) {
|
||||
}
|
||||
|
||||
export {
|
||||
queryClient,
|
||||
renderApp,
|
||||
renderAppWithComponent,
|
||||
renderAppWithObservations,
|
||||
|
||||
109
tests/integration/Notifications.test.tsx
Normal file
109
tests/integration/Notifications.test.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
import { screen, waitFor } from "@testing-library/react-native";
|
||||
import NotificationsContainer from "components/Notifications/NotificationsContainer";
|
||||
import inatjs from "inaturalistjs";
|
||||
import React from "react";
|
||||
import factory, { makeResponse } from "tests/factory";
|
||||
import { queryClient, renderAppWithComponent } from "tests/helpers/render";
|
||||
import setupUniqueRealm from "tests/helpers/uniqueRealm";
|
||||
import { signIn, signOut } from "tests/helpers/user";
|
||||
|
||||
// UNIQUE REALM SETUP
|
||||
const mockRealmIdentifier = __filename;
|
||||
const { mockRealmModelsIndex, uniqueRealmBeforeAll, uniqueRealmAfterAll } = setupUniqueRealm(
|
||||
mockRealmIdentifier
|
||||
);
|
||||
jest.mock( "realmModels/index", ( ) => mockRealmModelsIndex );
|
||||
jest.mock( "providers/contexts", ( ) => {
|
||||
const originalModule = jest.requireActual( "providers/contexts" );
|
||||
return {
|
||||
__esModule: true,
|
||||
...originalModule,
|
||||
RealmContext: {
|
||||
...originalModule.RealmContext,
|
||||
useRealm: ( ) => global.mockRealms[mockRealmIdentifier]
|
||||
}
|
||||
};
|
||||
} );
|
||||
beforeAll( uniqueRealmBeforeAll );
|
||||
afterAll( uniqueRealmAfterAll );
|
||||
// /UNIQUE REALM SETUP
|
||||
|
||||
const mockUser = factory( "LocalUser" );
|
||||
|
||||
function makeMockObsUpdatesResponse( mockObs ) {
|
||||
const mockObservation = mockObs || factory( "RemoteObservation", {
|
||||
user: mockUser
|
||||
} );
|
||||
const mockComment = factory( "RemoteComment", { parent_id: mockObservation.id } );
|
||||
const mockUpdate = {
|
||||
id: 123,
|
||||
created_at: mockComment.created_at,
|
||||
comment: mockComment,
|
||||
comment_id: mockComment.id,
|
||||
notifier_id: mockComment.id,
|
||||
notifier_type: "Comment",
|
||||
notification: "activity",
|
||||
resource_owner_id: mockUser.id,
|
||||
resource_type: "Observation",
|
||||
resource_id: mockObservation.id,
|
||||
resource_uuid: mockObservation.uuid,
|
||||
viewed: false
|
||||
};
|
||||
const obsUpdatesResponse = makeResponse( [mockUpdate] );
|
||||
inatjs.observations.updates.mockResolvedValue( obsUpdatesResponse );
|
||||
inatjs.observations.fetch.mockResolvedValue( makeResponse( [mockObservation] ) );
|
||||
return obsUpdatesResponse;
|
||||
}
|
||||
|
||||
describe( "Notifications", () => {
|
||||
beforeEach( async () => {
|
||||
jest.useFakeTimers( );
|
||||
signIn( mockUser, { realm: global.mockRealms[__filename] } );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
jest.clearAllMocks();
|
||||
signOut( { realm: global.mockRealms[__filename] } );
|
||||
queryClient.clear( );
|
||||
} );
|
||||
|
||||
it( "should show a notification", async ( ) => {
|
||||
makeMockObsUpdatesResponse( );
|
||||
renderAppWithComponent( <NotificationsContainer /> );
|
||||
await waitFor( ( ) => {
|
||||
expect( inatjs.observations.updates ).toHaveBeenCalled( );
|
||||
} );
|
||||
expect(
|
||||
await screen.findByText( /added a comment to an observation by you/ )
|
||||
).toBeVisible( );
|
||||
} );
|
||||
|
||||
it( "should show a photo for an observation not in the local database", async ( ) => {
|
||||
const mockObservation = factory( "RemoteObservation", {
|
||||
observation_photos: [
|
||||
factory( "RemoteObservationPhoto" )
|
||||
]
|
||||
} );
|
||||
const localObservation = global.mockRealms[__filename].objectForPrimaryKey(
|
||||
"Observation",
|
||||
mockObservation.uuid
|
||||
);
|
||||
expect( localObservation ).toBeFalsy( );
|
||||
const photoUrl = mockObservation.observation_photos[0].photo.url;
|
||||
expect( photoUrl ).toBeTruthy( );
|
||||
const response = makeMockObsUpdatesResponse( mockObservation );
|
||||
const { comment } = response.results[0];
|
||||
expect( response.results[0].resource_uuid ).toEqual( mockObservation.uuid );
|
||||
renderAppWithComponent( <NotificationsContainer /> );
|
||||
expect( await screen.findByText( comment.user.login ) ).toBeVisible( );
|
||||
const localObservationAfter = global.mockRealms[__filename].objectForPrimaryKey(
|
||||
"Observation",
|
||||
mockObservation.uuid
|
||||
);
|
||||
expect( localObservationAfter ).toBeTruthy( );
|
||||
const image = await screen.findByTestId( "ObservationIcon.photo" );
|
||||
await waitFor( () => {
|
||||
expect( image.props.source ).toStrictEqual( { uri: photoUrl } );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
Reference in New Issue
Block a user