mirror of
https://github.com/inaturalist/iNaturalistReactNative.git
synced 2026-05-05 14:15:27 -04:00
DQA voting status (#1757)
* Readability / cleanup updates for DQA * Moved from unnecessary use of useMutation to useQuery for fetching metrics * Log API errors * Avoid warnings in tests about updates outside of act * Update header in DQA after vote changes quality grade Closes #1504
This commit is contained in:
@@ -18,17 +18,20 @@ import { compact, groupBy } from "lodash";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import * as React from "react";
|
||||
import Observation from "realmModels/Observation";
|
||||
import { log } from "sharedHelpers/logger";
|
||||
import {
|
||||
useAuthenticatedMutation,
|
||||
useAuthenticatedQuery,
|
||||
useLocalObservation
|
||||
} from "sharedHooks";
|
||||
import useRemoteObservation from "sharedHooks/useRemoteObservation";
|
||||
|
||||
const logger = log.extend( "DQAContainer" );
|
||||
|
||||
const DQAContainer = ( ): React.Node => {
|
||||
const { isInternetReachable: isOnline } = useNetInfo( );
|
||||
const { params } = useRoute( );
|
||||
const { observationUUID } = params;
|
||||
const [qualityMetrics, setQualityMetrics] = useState( undefined );
|
||||
const [loadingAgree, setLoadingAgree] = useState( false );
|
||||
const [loadingDisagree, setLoadingDisagree] = useState( false );
|
||||
const [loadingMetric, setLoadingMetric] = useState( "none" );
|
||||
@@ -42,8 +45,9 @@ const DQAContainer = ( ): React.Node => {
|
||||
refetchRemoteObservation,
|
||||
isRefetching
|
||||
} = useRemoteObservation( observationUUID, fetchRemoteObservationEnabled );
|
||||
const observation
|
||||
= localObservation || Observation.mapApiToRealm( remoteObservation );
|
||||
const observation = remoteObservation
|
||||
? Observation.mapApiToRealm( remoteObservation )
|
||||
: localObservation;
|
||||
|
||||
const fetchMetricsParams = {
|
||||
id: observationUUID,
|
||||
@@ -70,21 +74,12 @@ const DQAContainer = ( ): React.Node => {
|
||||
}
|
||||
};
|
||||
|
||||
// destructured mutate to pass into useEffect to prevent infinite
|
||||
// rerender and disabling eslint useEffect dependency rule
|
||||
const { mutate } = useAuthenticatedMutation(
|
||||
( p, o ) => fetchQualityMetrics( p, o ),
|
||||
{
|
||||
onSuccess: response => {
|
||||
setNotLoading();
|
||||
setQualityMetrics( response );
|
||||
},
|
||||
onError: () => {
|
||||
if ( !isOnline ) {
|
||||
setHideOfflineSheet( false );
|
||||
}
|
||||
}
|
||||
}
|
||||
const {
|
||||
data: qualityMetrics,
|
||||
refetch: refetchQualityMetrics
|
||||
} = useAuthenticatedQuery(
|
||||
["fetchQualityMetrics", observationUUID],
|
||||
optsWithAuth => fetchQualityMetrics( fetchMetricsParams, optsWithAuth )
|
||||
);
|
||||
|
||||
const combinedQualityMetrics = {
|
||||
@@ -92,14 +87,6 @@ const DQAContainer = ( ): React.Node => {
|
||||
...groupBy( observation?.votes, "vote_scope" )
|
||||
};
|
||||
|
||||
useEffect( ( ) => {
|
||||
mutate( {
|
||||
id: params.observationUUID,
|
||||
fields: "metric,agree,user_id",
|
||||
ttl: -1
|
||||
} );
|
||||
}, [mutate, params] );
|
||||
|
||||
/**
|
||||
* After a success mutation of the needs_id vote we start the refetching of the remote
|
||||
* observation to update the metric status of the observation. So we need to wait until
|
||||
@@ -139,11 +126,13 @@ const DQAContainer = ( ): React.Node => {
|
||||
const createQualityMetricMutation = useAuthenticatedMutation(
|
||||
( qualityMetricParams, optsWithAuth ) => setQualityMetric( qualityMetricParams, optsWithAuth ),
|
||||
{
|
||||
onSuccess: () => {
|
||||
// fetch updated quality metrics with updated votes
|
||||
mutate( fetchMetricsParams );
|
||||
onSuccess: async ( ) => {
|
||||
await refetchQualityMetrics( );
|
||||
await refetchRemoteObservation( );
|
||||
setNotLoading( );
|
||||
},
|
||||
onError: () => {
|
||||
onError: error => {
|
||||
logger.error( "createQualityMetricMutation failure", error );
|
||||
setHideErrorSheet( false );
|
||||
}
|
||||
}
|
||||
@@ -176,14 +165,16 @@ const DQAContainer = ( ): React.Node => {
|
||||
faveMutation.mutate( faveParams );
|
||||
};
|
||||
|
||||
const createRemoveQualityMetricMutation = useAuthenticatedMutation(
|
||||
( p, o ) => deleteQualityMetric( p, o ),
|
||||
const removeQualityMetricMutation = useAuthenticatedMutation(
|
||||
( deleteParams, options ) => deleteQualityMetric( deleteParams, options ),
|
||||
{
|
||||
onSuccess: () => {
|
||||
// fetch updated quality metrics with updated votes
|
||||
mutate( fetchMetricsParams );
|
||||
onSuccess: async ( ) => {
|
||||
await refetchQualityMetrics( );
|
||||
await refetchRemoteObservation( );
|
||||
setNotLoading( );
|
||||
},
|
||||
onError: () => {
|
||||
onError: error => {
|
||||
logger.error( "removeQualityMetricMutation failed", error );
|
||||
setHideErrorSheet( false );
|
||||
}
|
||||
}
|
||||
@@ -197,7 +188,7 @@ const DQAContainer = ( ): React.Node => {
|
||||
metric,
|
||||
ttyl: -1
|
||||
};
|
||||
createRemoveQualityMetricMutation.mutate( qualityMetricParams );
|
||||
removeQualityMetricMutation.mutate( qualityMetricParams );
|
||||
};
|
||||
|
||||
// The quality metric "needs_id" uses a fave/unfave vote with vote_scope: "needs_id"
|
||||
|
||||
@@ -121,6 +121,8 @@ const DataQualityAssessment = ( {
|
||||
);
|
||||
}
|
||||
|
||||
// console.log( "[DEBUG DataQualityAssessment.js] qualityMetrics?.date: ", qualityMetrics?.date );
|
||||
|
||||
return (
|
||||
<ScrollViewWrapper testID="DataQualityAssessment">
|
||||
<View className="mx-[26px] my-[19px] space-y-[9px]">
|
||||
|
||||
@@ -32,6 +32,13 @@ jest.mock( "sharedHooks/useCurrentUser", ( ) => ( {
|
||||
} ) )
|
||||
} ) );
|
||||
|
||||
jest.mock( "sharedHooks/useAuthenticatedQuery", () => ( {
|
||||
__esModule: true,
|
||||
default: () => ( {
|
||||
data: []
|
||||
} )
|
||||
} ) );
|
||||
|
||||
const mockMutate = jest.fn();
|
||||
jest.mock( "sharedHooks/useAuthenticatedMutation", () => ( {
|
||||
__esModule: true,
|
||||
@@ -51,31 +58,32 @@ useRoute.mockImplementation( ( ) => ( {
|
||||
}
|
||||
} ) );
|
||||
|
||||
async function expectMutateToHaveBeenCalled() {
|
||||
expect( await mockMutate ).toHaveBeenCalled();
|
||||
// Since we mocked the mutate() method, we're expecting mutation not to
|
||||
// succeed. We want to wait for this so there are no extra things happening
|
||||
// outside of act()
|
||||
await screen.findByText( "ERROR LOADING IN DQA" );
|
||||
}
|
||||
|
||||
describe( "DQA Vote Buttons", ( ) => {
|
||||
test( "renders DQA vote buttons", async ( ) => {
|
||||
renderComponent( <DQAContainer /> );
|
||||
|
||||
const emptyDisagreeButtons = await screen.findAllByTestId( "DQAVoteButton.EmptyDisagree" );
|
||||
fireEvent.press( emptyDisagreeButtons[0] );
|
||||
|
||||
expect( await mockMutate ).toHaveBeenCalled();
|
||||
expect( emptyDisagreeButtons ).toBeTruthy( );
|
||||
} );
|
||||
|
||||
test( "calls api when DQA disagree button is pressed", async ( ) => {
|
||||
renderComponent( <DQAContainer /> );
|
||||
|
||||
const emptyDisagreeButtons = await screen.findAllByTestId( "DQAVoteButton.EmptyDisagree" );
|
||||
fireEvent.press( emptyDisagreeButtons[0] );
|
||||
|
||||
expect( await mockMutate ).toHaveBeenCalled();
|
||||
await expectMutateToHaveBeenCalled();
|
||||
} );
|
||||
|
||||
test( "calls api when DQA agree button is pressed", async ( ) => {
|
||||
renderComponent( <DQAContainer /> );
|
||||
|
||||
const emptyDisagreeButtons = await screen.findAllByTestId( "DQAVoteButton.EmptyAgree" );
|
||||
fireEvent.press( emptyDisagreeButtons[0] );
|
||||
|
||||
expect( await mockMutate ).toHaveBeenCalled();
|
||||
await expectMutateToHaveBeenCalled();
|
||||
} );
|
||||
} );
|
||||
|
||||
@@ -33,6 +33,13 @@ jest.mock( "sharedHooks/useCurrentUser", ( ) => ( {
|
||||
} ) )
|
||||
} ) );
|
||||
|
||||
jest.mock( "sharedHooks/useAuthenticatedQuery", () => ( {
|
||||
__esModule: true,
|
||||
default: () => ( {
|
||||
data: []
|
||||
} )
|
||||
} ) );
|
||||
|
||||
const mockMutate = jest.fn();
|
||||
jest.mock( "sharedHooks/useAuthenticatedMutation", () => ( {
|
||||
__esModule: true,
|
||||
@@ -46,6 +53,15 @@ jest.mock( "sharedHooks/useLocalObservation", () => ( {
|
||||
default: jest.fn( ( ) => mockObservation )
|
||||
} ) );
|
||||
|
||||
jest.mock( "sharedHooks/useRemoteObservation", ( ) => ( {
|
||||
__esModule: true,
|
||||
default: ( _uuid, _fetchRemoteEnabled ) => ( {
|
||||
remoteObservation: mockObservation,
|
||||
refetchRemoteObservation: jest.fn( ),
|
||||
isRefetching: false
|
||||
} )
|
||||
} ) );
|
||||
|
||||
useRoute.mockImplementation( ( ) => ( {
|
||||
params: {
|
||||
observationUUID: mockObservation.uuid
|
||||
|
||||
Reference in New Issue
Block a user