Make explore view sticky; default to species view (#1647)

* Remove tab bar when sharing photos into app; closes #1645

* Make explore view sticky with species as default view; closes #1614

* Fix Explore integration test; use default species view
This commit is contained in:
Amanda Bullington
2024-06-04 15:07:15 -07:00
committed by GitHub
parent 716efc1a78
commit 829fa70f34
9 changed files with 96 additions and 62 deletions

View File

@@ -23,7 +23,6 @@ const RootExploreContainerWithContext = ( ): Node => {
const currentUser = useCurrentUser( );
const storedParams = useStore( state => state.storedParams );
const setStoredParams = useStore( state => state.setStoredParams );
const setExploreView = useStore( state => state.setExploreView );
const worldwidePlaceText = t( "Worldwide" );
@@ -33,10 +32,6 @@ const RootExploreContainerWithContext = ( ): Node => {
const [showFiltersModal, setShowFiltersModal] = useState( false );
useEffect( ( ) => {
setExploreView( "species" );
}, [setExploreView] );
const updateTaxon = ( taxon: Object ) => {
dispatch( {
type: EXPLORE_ACTION.CHANGE_TAXON,
@@ -167,10 +162,10 @@ const RootExploreContainerWithContext = ( ): Node => {
);
};
const ExploreContainer = (): Node => (
const RootExploreContainer = (): Node => (
<ExploreProvider>
<RootExploreContainerWithContext />
</ExploreProvider>
);
export default ExploreContainer;
export default RootExploreContainer;

View File

@@ -14,15 +14,10 @@ const useParams = ( ): Object => {
const { params } = useRoute( );
const { dispatch, setExploreLocation } = useExplore( );
const storedParams = useStore( state => state.storedParams );
const setExploreView = useStore( state => state.setExploreView );
const worldwidePlaceText = t( "Worldwide" );
const updateContextWithParams = useCallback( async ( storedState = { } ) => {
if ( params?.viewSpecies ) {
setExploreView( "species" );
}
const setWorldwide = ( ) => {
dispatch( {
type: EXPLORE_ACTION.SET_PLACE,
@@ -81,7 +76,6 @@ const useParams = ( ): Object => {
dispatch,
params,
setExploreLocation,
setExploreView,
worldwidePlaceText
] );

View File

@@ -28,6 +28,7 @@ const ToolbarContainer = ( {
syncInProgress,
toggleLayout
}: Props ): Node => {
const setExploreView = useStore( state => state.setExploreView );
const currentUser = useCurrentUser( );
const navigation = useNavigation( );
const deletions = useStore( state => state.deletions );
@@ -69,12 +70,15 @@ const ToolbarContainer = ( {
] );
const navToExplore = useCallback(
( ) => navigation.navigate( "Explore", {
user: currentUser,
worldwide: true,
resetStoredParams: true
} ),
[navigation, currentUser]
( ) => {
setExploreView( "observations" );
navigation.navigate( "Explore", {
user: currentUser,
worldwide: true,
resetStoredParams: true
} );
},
[navigation, currentUser, setExploreView]
);
const { t } = useTranslation( );

View File

@@ -13,6 +13,7 @@ import {
import type { Node } from "react";
import React, { useCallback } from "react";
import { useTranslation } from "sharedHooks";
import useStore from "stores/useStore";
import AboutProjectType from "./AboutProjectType";
@@ -26,6 +27,8 @@ type Props = {
const ProjectDetails = ( {
project, joinProject, leaveProject, loadingProjectMembership
}: Props ): Node => {
const setExploreView = useStore( state => state.setExploreView );
const { t } = useTranslation( );
const navigation = useNavigation( );
@@ -39,13 +42,15 @@ const ProjectDetails = ( {
);
const onSpeciesPressed = useCallback(
( ) => navigation.navigate( "Explore", {
project,
worldwide: true,
viewSpecies: true,
resetStoredParams: true
} ),
[navigation, project]
( ) => {
setExploreView( "species" );
navigation.navigate( "Explore", {
project,
worldwide: true,
resetStoredParams: true
} );
},
[navigation, project, setExploreView]
);
if ( !project ) {

View File

@@ -30,6 +30,7 @@ import DeviceInfo from "react-native-device-info";
import { useTheme } from "react-native-paper";
import { log } from "sharedHelpers/logger";
import { useAuthenticatedQuery, useTranslation, useUserMe } from "sharedHooks";
import useStore from "stores/useStore";
import EstablishmentMeans from "./EstablishmentMeans";
import TaxonDetailsMediaViewerHeader from "./TaxonDetailsMediaViewerHeader";
@@ -48,6 +49,7 @@ const TAXON_URL = "https://www.inaturalist.org/taxa";
const isTablet = DeviceInfo.isTablet();
const TaxonDetails = ( ): Node => {
const setExploreView = useStore( state => state.setExploreView );
const theme = useTheme( );
const navigation = useNavigation( );
const { params } = useRoute( );
@@ -229,17 +231,20 @@ const TaxonDetails = ( ): Node => {
<View className="ml-5">
<INatIconButton
icon="compass-rose-outline"
onPress={( ) => navigation.navigate( "TabNavigator", {
screen: "TabStackNavigator",
params: {
screen: "Explore",
onPress={( ) => {
setExploreView( "observations" );
navigation.navigate( "TabNavigator", {
screen: "TabStackNavigator",
params: {
taxon,
worldwide: true,
resetStoredParams: true
screen: "Explore",
params: {
taxon,
worldwide: true,
resetStoredParams: true
}
}
}
} )}
} );
}}
accessibilityLabel={t( "See-observations-of-this-taxon-in-explore" )}
accessibilityHint={t( "Navigates-to-explore" )}
size={30}

View File

@@ -25,11 +25,13 @@ import {
useCurrentUser,
useIsConnected
} from "sharedHooks";
import useStore from "stores/useStore";
import FollowButtonContainer from "./FollowButtonContainer";
import UnfollowSheet from "./UnfollowSheet";
const UserProfile = ( ): Node => {
const setExploreView = useStore( state => state.setExploreView );
const navigation = useNavigation( );
const currentUser = useCurrentUser( );
const { params } = useRoute( );
@@ -79,22 +81,27 @@ const UserProfile = ( ): Node => {
// }, [navigation, user, currentUser] );
const onObservationPressed = useCallback(
( ) => navigation.navigate( "Explore", {
user,
worldwide: true,
resetStoredParams: true
} ),
[navigation, user]
( ) => {
setExploreView( "observations" );
navigation.navigate( "Explore", {
user,
worldwide: true,
resetStoredParams: true
} );
},
[navigation, user, setExploreView]
);
const onSpeciesPressed = useCallback(
( ) => navigation.navigate( "Explore", {
user,
worldwide: true,
viewSpecies: true,
resetStoredParams: true
} ),
[navigation, user]
( ) => {
setExploreView( "species" );
navigation.navigate( "Explore", {
user,
worldwide: true,
resetStoredParams: true
} );
},
[navigation, user, setExploreView]
);
if ( !user ) {

View File

@@ -9,6 +9,12 @@ export const initialMapRegion = {
longitudeDelta: DELTA
};
const DEFAULT_STATE = {
storedParams: {},
exploreView: "species",
mapRegion: initialMapRegion
};
interface MapRegion {
latitude: number,
longitude: number,
@@ -26,11 +32,9 @@ interface ExploreSlice {
}
const createExploreSlice: StateCreator<ExploreSlice> = set => ( {
storedParams: {},
...DEFAULT_STATE,
setStoredParams: params => set( ( ) => ( { storedParams: params } ) ),
exploreView: "observations",
setExploreView: exploreView => set( ( ) => ( { exploreView } ) ),
mapRegion: initialMapRegion,
setMapRegion: region => set( ( ) => ( { mapRegion: region } ) )
} );

View File

@@ -1,4 +1,4 @@
import { fireEvent, screen } from "@testing-library/react-native";
import { fireEvent, screen, userEvent } from "@testing-library/react-native";
import ExploreContainer from "components/Explore/ExploreContainer";
import inatjs from "inaturalistjs";
import React from "react";
@@ -17,12 +17,34 @@ const mockRemoteObservation = factory( "RemoteObservation", {
taxon: factory.states( "genus" )( "RemoteTaxon" )
} );
const mockTaxon = factory( "LocalTaxon" );
const actor = userEvent.setup( );
beforeAll( ( ) => {
inatjs.observations.speciesCounts.mockResolvedValue( makeResponse( [{
count: 1,
taxon: mockTaxon
}] ) );
inatjs.observations.search.mockResolvedValue( makeResponse( [mockRemoteObservation] ) );
} );
const switchToObservationsView = async ( ) => {
const speciesViewIcon = await screen.findByLabelText( /Species View/ );
expect( speciesViewIcon ).toBeVisible( );
await actor.press( speciesViewIcon );
const observationsRadioButton = await screen.findByText( "Observations" );
await actor.press( observationsRadioButton );
const confirmButton = await screen.findByText( /EXPLORE OBSERVATIONS/ );
await actor.press( confirmButton );
const obsTaxonNameElt = await screen.findByText( mockRemoteObservation.taxon.name );
expect( obsTaxonNameElt ).toBeTruthy( );
};
describe( "Explore", ( ) => {
it( "should render", async ( ) => {
inatjs.observations.search.mockResolvedValue( makeResponse( [mockRemoteObservation] ) );
it( "should render species view and switch to observations view list correctly", async ( ) => {
renderAppWithComponent( <ExploreContainer /> );
const obsTaxonNameElt = await screen.findByText( mockRemoteObservation.taxon.name );
expect( obsTaxonNameElt ).toBeTruthy( );
await switchToObservationsView( );
expect(
await screen.findByTestId( `ObsStatus.${mockRemoteObservation.uuid}` )
).toBeTruthy( );
@@ -31,11 +53,9 @@ describe( "Explore", ( ) => {
).toBeFalsy( );
} );
it( "should display grid item correctly", async ( ) => {
inatjs.observations.search.mockResolvedValue( makeResponse( [mockRemoteObservation] ) );
it( "should display observations view grid correctly", async ( ) => {
renderAppWithComponent( <ExploreContainer /> );
const obsTaxonNameElt = await screen.findByText( mockRemoteObservation.taxon.name );
expect( obsTaxonNameElt ).toBeTruthy( );
await switchToObservationsView( );
expect(
await screen.findByTestId( "SegmentedButton.grid" )
).toBeTruthy( );

View File

@@ -233,8 +233,8 @@ describe( "logged in", ( ) => {
await actor.press( taxonDetailsExploreButton );
const defaultGlobalLocation = await screen.findByText( /Worldwide/ );
expect( defaultGlobalLocation ).toBeVisible( );
const speciesIcon = await screen.findByLabelText( /Species View/ );
expect( speciesIcon ).toBeVisible( );
const observationsIcon = await screen.findByLabelText( /Observations View/ );
expect( observationsIcon ).toBeVisible( );
const backButton = screen.queryByTestId( "Explore.BackButton" );
await actor.press( backButton );
expect( taxonDetailsExploreButton ).toBeVisible( );