Fix missing map tiles when taxon chosen on Explore (#1722)

* IconicTaxonChooser no longer takes a taxon or manages its own state
* Setting iconic taxon filter to Unknown is now its own action
* Unsetting a taxon can be done with the CHOOSE_TAXON action by setting taxon
  to null or undefined
* Omitted some extraneous params from tile requests
This commit is contained in:
Ken-ichi
2024-06-28 10:13:51 -07:00
committed by GitHub
parent cc8b610f2f
commit ae2f9eeddc
14 changed files with 251 additions and 205 deletions

View File

@@ -1477,12 +1477,12 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
boost: d3f49c53809116a5d38da093a8aa78bf551aed09
BVLinearGradient: 880f91a7854faff2df62518f0281afb1c60d49a3
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
DoubleConversion: fea03f2699887d960129cc54bba7e52542b6f953
FasterImage: 60d0750ddbcefff0070c4c17309c2d1d6cc650f0
FBLazyVector: 9f533d5a4c75ca77c8ed774aced1a91a0701781e
FBReactNativeSpec: 40b791f4a1df779e7e4aa12c000319f4f216d40a
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2
hermes-engine: 39589e9c297d024e90fe68f6830ff86c4e01498a
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
MMKV: 506311d0494023c2f7e0b62cc1f31b7370fa3cfb

View File

@@ -344,11 +344,7 @@ const Misc = (): Node => {
<ConfidenceInterval confidence={3} activeColor="bg-inatGreen" />
<Heading2 className="my-2">Iconic Taxon Chooser</Heading2>
<IconicTaxonChooser
taxon={{
name: "Aves",
id: 3,
iconic_taxon_name: "Aves"
}}
chosen={["aves"]}
before={<Button text={t( "ADD-AN-ID" )} className="rounded-full" />}
onTaxonChosen={taxon => console.log( "taxon selected:", taxon )}
/>

View File

@@ -45,6 +45,7 @@ const exploreViewIcon = {
type Props = {
closeFiltersModal: Function,
count: Object,
filterByIconicTaxonUnknown: Function,
hideBackButton: boolean,
isOnline: boolean,
loadingStatus: boolean,
@@ -61,6 +62,7 @@ type Props = {
const Explore = ( {
closeFiltersModal,
count,
filterByIconicTaxonUnknown,
hideBackButton,
isOnline,
loadingStatus,
@@ -256,6 +258,7 @@ const Explore = ( {
<ExploreFiltersModal
showModal={showFiltersModal}
closeModal={closeFiltersModal}
filterByIconicTaxonUnknown={filterByIconicTaxonUnknown}
updateTaxon={updateTaxon}
updateLocation={updateLocation}
updateUser={updateUser}

View File

@@ -32,22 +32,6 @@ const ExploreContainerWithContext = ( ): Node => {
useParams( );
const updateTaxon = ( taxon: Object ) => {
if ( !taxon ) {
dispatch( {
type: EXPLORE_ACTION.CHANGE_TAXON_NONE,
taxon: null
} );
} else {
dispatch( {
type: EXPLORE_ACTION.CHANGE_TAXON,
taxon,
taxonId: taxon?.id,
taxonName: taxon?.preferred_common_name || taxon?.name
} );
}
};
const updateLocation = ( place: Object ) => {
if ( place === "worldwide" ) {
dispatch( {
@@ -113,13 +97,16 @@ const ExploreContainerWithContext = ( ): Node => {
closeFiltersModal={closeFiltersModal}
count={count}
hideBackButton={false}
filterByIconicTaxonUnknown={
() => dispatch( { type: EXPLORE_ACTION.FILTER_BY_ICONIC_TAXON_UNKNOWN } )
}
isOnline={isOnline}
loadingStatus={loadingStatus}
openFiltersModal={openFiltersModal}
queryParams={queryParams}
showFiltersModal={showFiltersModal}
updateCount={updateCount}
updateTaxon={updateTaxon}
updateTaxon={taxon => dispatch( { type: EXPLORE_ACTION.CHANGE_TAXON, taxon } )}
updateLocation={updateLocation}
updateUser={updateUser}
updateProject={updateProject}

View File

@@ -48,6 +48,7 @@ const Header = ( {
const theme = useTheme( );
const { state, numberOfFilters } = useExplore( );
const { taxon } = state;
const iconicTaxonNames = state.iconic_taxa || [];
const placeGuess = state.place_guess;
const [showTaxonSearch, setShowTaxonSearch] = useState( false );
const [showLocationSearch, setShowLocationSearch] = useState( false );
@@ -71,11 +72,11 @@ const Header = ( {
/>
) }
<View className="flex-1">
{taxon
{( taxon || iconicTaxonNames.indexOf( "unknown" ) >= 0 )
? (
<DisplayTaxon
accessibilityLabel={t( "Change-taxon-filter" )}
taxon={taxon}
taxon={taxon || "unknown"}
showInfoButton={false}
showCheckmark={false}
handlePress={() => setShowTaxonSearch( true )}

View File

@@ -28,7 +28,7 @@ type Props = {
const MapView = ( {
observations,
queryParams: tileMapParams
queryParams
}: Props ): Node => {
const { t } = useTranslation( );
const { isDebug } = useDebugMode( );
@@ -48,6 +48,14 @@ const MapView = ( {
updateMapBoundaries
} = useMapLocation( );
const tileMapParams = {
...queryParams
};
// Tile queries never need these params
delete tileMapParams.return_bounds;
delete tileMapParams.order;
delete tileMapParams.orderBy;
return (
<View className="flex-1 overflow-hidden h-full">
<View className="z-10">

View File

@@ -9,6 +9,7 @@ import FilterModal from "./FilterModal";
type Props = {
showModal: boolean,
closeModal: Function,
filterByIconicTaxonUnknown: Function,
updateTaxon: Function,
updateLocation: Function,
updateUser: Function,
@@ -18,6 +19,7 @@ type Props = {
const ExploreFiltersModal = ( {
showModal,
closeModal,
filterByIconicTaxonUnknown,
updateTaxon,
updateLocation,
updateUser,
@@ -31,6 +33,7 @@ const ExploreFiltersModal = ( {
modal={(
<FilterModal
closeModal={closeModal}
filterByIconicTaxonUnknown={filterByIconicTaxonUnknown}
updateTaxon={updateTaxon}
updateLocation={updateLocation}
updateUser={updateUser}

View File

@@ -58,6 +58,7 @@ const { useRealm } = RealmContext;
interface Props {
closeModal: Function,
filterByIconicTaxonUnknown: Function
updateTaxon: Function,
updateLocation: Function,
updateUser: Function,
@@ -66,6 +67,7 @@ interface Props {
const FilterModal = ( {
closeModal,
filterByIconicTaxonUnknown,
updateTaxon,
updateLocation,
updateUser,
@@ -85,30 +87,31 @@ const FilterModal = ( {
defaultExploreLocation
} = useExplore();
const {
taxon,
place_guess: placeGuess,
user,
project,
sortBy,
researchGrade,
needsID,
casual,
hrank,
lrank,
dateObserved,
observed_on: observedOn,
d1,
d2,
months,
dateUploaded,
created_on: createdOn,
created_d1: createdD1,
created_d2: createdD2,
media,
created_on: createdOn,
d1,
d2,
dateObserved,
dateUploaded,
establishmentMean,
wildStatus,
hrank,
iconic_taxa: iconicTaxonNames,
lrank,
media,
months,
needsID,
observed_on: observedOn,
photoLicense,
place_guess: placeGuess,
project,
researchGrade,
reviewedFilter,
photoLicense
sortBy,
taxon,
user,
wildStatus
} = state;
const NONE = "NONE";
@@ -678,7 +681,7 @@ const FilterModal = ( {
<View className="mb-7">
<Heading4 className="px-4 mb-5">{t( "TAXON" )}</Heading4>
<View className="px-4 mb-5">
{taxon
{( taxon || ( iconicTaxonNames || [] ).indexOf( "unknown" ) >= 0 )
? (
<Pressable
className="flex-row justify-between items-center"
@@ -688,7 +691,7 @@ const FilterModal = ( {
setShowTaxonSearchModal( true );
}}
>
<DisplayTaxon taxon={taxon} />
<DisplayTaxon taxon={taxon || "unknown"} />
<INatIcon name="edit" size={22} />
</Pressable>
)
@@ -704,16 +707,21 @@ const FilterModal = ( {
</View>
<IconicTaxonChooser
before
taxon={taxon}
chosen={iconicTaxonNames || [taxon?.name?.toLowerCase()]}
onTaxonChosen={( taxonName: string ) => {
if ( taxonName === "unknown" ) {
updateTaxon( );
if ( ( iconicTaxonNames || [] ).indexOf( taxonName ) >= 0 ) {
updateTaxon( null );
} else {
filterByIconicTaxonUnknown();
}
} else if ( taxon?.name?.toLowerCase() === taxonName ) {
updateTaxon( null );
} else {
const selectedTaxon = realm
?.objects( "Taxon" )
.filtered( "name CONTAINS[c] $0", taxonName );
const iconicTaxon
= selectedTaxon.length > 0
const iconicTaxon = selectedTaxon.length > 0
? selectedTaxon[0]
: null;
updateTaxon( iconicTaxon );

View File

@@ -36,22 +36,6 @@ const RootExploreContainerWithContext = ( ): Node => {
const [showFiltersModal, setShowFiltersModal] = useState( false );
const updateTaxon = ( taxon: Object ) => {
if ( !taxon ) {
dispatch( {
type: EXPLORE_ACTION.CHANGE_TAXON_NONE,
taxon: null
} );
} else {
dispatch( {
type: EXPLORE_ACTION.CHANGE_TAXON,
taxon,
taxonId: taxon?.id,
taxonName: taxon?.preferred_common_name || taxon?.name
} );
}
};
const updateLocation = ( place: Object ) => {
if ( place === "worldwide" ) {
dispatch( {
@@ -144,13 +128,16 @@ const RootExploreContainerWithContext = ( ): Node => {
closeFiltersModal={closeFiltersModal}
count={count}
hideBackButton
filterByIconicTaxonUnknown={
() => dispatch( { type: EXPLORE_ACTION.FILTER_BY_ICONIC_TAXON_UNKNOWN } )
}
isOnline={isOnline}
loadingStatus={loadingStatus}
openFiltersModal={openFiltersModal}
queryParams={queryParams}
showFiltersModal={showFiltersModal}
updateCount={updateCount}
updateTaxon={updateTaxon}
updateTaxon={taxon => dispatch( { type: EXPLORE_ACTION.CHANGE_TAXON, taxon } )}
updateLocation={updateLocation}
updateUser={updateUser}
updateProject={updateProject}

View File

@@ -10,6 +10,7 @@ import {
TaxonResult
} from "components/SharedComponents";
import { View } from "components/styledComponents";
import { capitalize } from "lodash";
import { RealmContext } from "providers/contexts";
import type { Node } from "react";
import React, { useCallback, useEffect } from "react";
@@ -40,19 +41,15 @@ const IdentificationSection = ( {
const navigation = useNavigation( );
const realm = useRealm( );
const identification = currentObservation?.taxon;
const identTaxon = currentObservation?.taxon;
const hasPhotos = currentObservation?.observationPhotos?.length > 0;
const hasIdentification = identification && identification.rank_level !== 100;
const hasIdentification = identTaxon && identTaxon.rank_level !== 100;
const showIconicTaxonChooser = !identification
|| identification.name === identification.iconic_taxon_name
|| identification.isIconic
|| identification.name === "Life";
const onTaxonChosen = taxonName => updateObservationKeys( {
taxon: realm?.objects( "Taxon" ).filtered( "name CONTAINS[c] $0", taxonName )[0]
} );
const showIconicTaxonChooser = !identTaxon
|| identTaxon.name === identTaxon.iconic_taxon_name
|| identTaxon.isIconic
|| identTaxon.name === "Life";
const navToSuggestions = useCallback( ( ) => {
if ( hasPhotos ) {
@@ -83,20 +80,20 @@ const IdentificationSection = ( {
<IconicTaxonChooser
before={(
<Button
level={identification
level={identTaxon
? "neutral"
: "focus"}
onPress={navToSuggestions}
text={t( "ADD-AN-ID" )}
className={classnames( "rounded-full py-1 h-[36px] ml-4", {
"border border-darkGray border-[2px]": identification
"border border-darkGray border-[2px]": identTaxon
} )}
testID="ObsEdit.Suggestions"
icon={(
<INatIcon
name="sparkly-label"
size={24}
color={identification
color={identTaxon
? theme.colors.primary
: theme.colors.onPrimary}
/>
@@ -104,8 +101,26 @@ const IdentificationSection = ( {
accessibilityLabel={t( "View-suggestions" )}
/>
)}
taxon={identification}
onTaxonChosen={onTaxonChosen}
chosen={
identTaxon
? [identTaxon.name.toLowerCase()]
: []
}
onTaxonChosen={taxonName => {
const capitalizedTaxonName = capitalize( taxonName );
if (
// user chose unknown
taxonName === "unknown"
// user tapped the selected iconic taxon to unselect
|| identTaxon?.name === capitalizedTaxonName
) {
updateObservationKeys( { taxon: undefined } );
} else {
const newTaxon = realm?.objects( "Taxon" )
.filtered( "name = $0", capitalizedTaxonName )[0];
updateObservationKeys( { taxon: newTaxon } );
}
}}
/>
</View>
);
@@ -120,14 +135,14 @@ const IdentificationSection = ( {
</View>
)}
</View>
{identification && (
{identTaxon && (
<View className="mt-5 mx-5">
<TaxonResult
accessibilityLabel={t( "Edits-this-observations-taxon" )}
asListItem={false}
handlePress={navToSuggestions}
hideNavButtons
taxon={identification}
taxon={identTaxon}
showInfoButton={false}
showCheckmark={false}
showEditButton

View File

@@ -3,21 +3,15 @@ import classnames from "classnames";
import { INatIconButton } from "components/SharedComponents";
import { View } from "components/styledComponents";
import type { Node } from "react";
import React, {
useCallback, useEffect, useState
} from "react";
import React, { useCallback } from "react";
import { FlatList } from "react-native";
import { useIconicTaxa, useTranslation } from "sharedHooks";
import { useTranslation } from "sharedHooks";
import colors from "styles/tailwindColors";
type Props = {
// $FlowIgnore
before: unknown,
before?: Node,
chosen: string[],
onTaxonChosen: Function,
taxon: {
id: number,
name: string
},
testID?: string
};
@@ -44,30 +38,13 @@ const iconicTaxonIcons = [
const IconicTaxonChooser = ( {
before,
chosen = [],
onTaxonChosen,
taxon,
testID
}: Props ): Node => {
const { t } = useTranslation( );
const [selectedIcon, setSelectedIcon] = useState( null );
const iconicTaxa = useIconicTaxa( { reload: false } );
const isIconic = taxon?.id && iconicTaxa.filtered( `id = ${taxon?.id}` );
useEffect( ( ) => {
if ( !isIconic ) { return; }
setSelectedIcon( taxon.name.toLowerCase( ) );
}, [isIconic, taxon] );
useEffect( ( ) => {
// reset selectedIcon state when navigating multiple observations
// on ObsEdit screen
if ( !taxon && selectedIcon ) {
setSelectedIcon( null );
}
}, [taxon, selectedIcon] );
const renderIcon = useCallback( ( { item } ) => {
const isSelected = selectedIcon === item;
const renderIcon = useCallback( ( { item: iconicTaxonName } ) => {
const isSelected = chosen.indexOf( iconicTaxonName ) >= 0;
return (
<View
className={
@@ -82,26 +59,30 @@ const IconicTaxonChooser = ( {
accessibilityState={{
selected: isSelected
}}
testID={`IconicTaxonButton.${item}`}
testID={`IconicTaxonButton.${iconicTaxonName}`}
>
<INatIconButton
icon={`iconic-${item}`}
icon={`iconic-${iconicTaxonName}`}
size={22}
onPress={( ) => {
setSelectedIcon( item );
onTaxonChosen( item );
onTaxonChosen( iconicTaxonName );
}}
color={isSelected && colors.white}
accessibilityLabel={
t( "Iconic-taxon-name", { iconicTaxon: item } )
t( "Iconic-taxon-name", { iconicTaxon: iconicTaxonName } )
}
accessibilityHint={
t( "Selects-iconic-taxon-X-for-identification", { iconicTaxon: item } )
t( "Selects-iconic-taxon-X-for-identification", { iconicTaxon: iconicTaxonName } )
}
/>
</View>
);
}, [onTaxonChosen, t, selectedIcon] );
}, [
chosen,
onTaxonChosen,
t
// selectedIcon
] );
const renderHeader = useCallback( ( ) => {
if ( before ) {

View File

@@ -1,6 +1,7 @@
/* eslint-disable no-unused-vars */
/* eslint-disable no-shadow */
import { t } from "i18next";
import { isEqual } from "lodash";
import * as React from "react";
import { LatLng } from "react-native-maps";
@@ -12,6 +13,7 @@ export enum EXPLORE_ACTION {
CHANGE_SORT_BY = "CHANGE_SORT_BY",
CHANGE_TAXON = "CHANGE_TAXON",
DISCARD = "DISCARD",
FILTER_BY_ICONIC_TAXON_UNKNOWN = "FILTER_BY_ICONIC_TAXON_UNKNOWN",
RESET = "RESET",
SET_DATE_OBSERVED_ALL = "SET_DATE_OBSERVED_ALL",
SET_DATE_OBSERVED_EXACT = "SET_DATE_OBSERVED_EXACT",
@@ -160,65 +162,68 @@ interface PLACE {
type ExploreProviderProps = {children: React.ReactNode}
type State = {
verifiable: boolean,
casual: boolean,
created_d1: string | null | undefined,
created_d2: string | null | undefined,
created_on: string | null | undefined,
d1: string | null | undefined,
d2: string | null | undefined,
dateObserved: DATE_OBSERVED,
dateUploaded: DATE_UPLOADED,
establishmentMean: ESTABLISHMENT_MEAN,
hrank: TAXONOMIC_RANK | undefined | null,
iconic_taxa: string[] | undefined,
lat?: number,
lng?: number,
lrank: TAXONOMIC_RANK | undefined | null,
mapBoundaries: MapBoundaries | undefined,
media: MEDIA,
months: number[] | null | undefined,
needsID: boolean,
nelat?: number,
nelng?: number,
observed_on: string | null | undefined,
photoLicense: PHOTO_LICENSE,
place: PLACE | null | undefined,
place_guess: string,
place_id: number | null | undefined,
// TODO: technically this is not any Object but a "Project"
// and should be typed as such (e.g., in realm model)
project: Object | undefined,
project_id: number | undefined,
radius?: number,
researchGrade: boolean,
return_bounds: boolean,
reviewedFilter: REVIEWED,
sortBy: SORT_BY,
swlat?: number,
swlng?: number,
// TODO: technically this is not any Object but a "Taxon"
// and should be typed as such (e.g., in realm model)
taxon: Object | undefined,
taxon_id: number | undefined,
place: PLACE | null | undefined,
place_id: number | null | undefined,
place_guess: string,
user_id: number | undefined,
// TODO: technically this is not any Object but a "User"
// and should be typed as such (e.g., in realm model)
user: Object | undefined,
project_id: number | undefined,
// TODO: technically this is not any Object but a "Project"
// and should be typed as such (e.g., in realm model)
project: Object | undefined,
sortBy: SORT_BY,
researchGrade: boolean,
needsID: boolean,
casual: boolean,
hrank: TAXONOMIC_RANK | undefined | null,
lrank: TAXONOMIC_RANK | undefined | null,
dateObserved: DATE_OBSERVED,
observed_on: string | null | undefined,
d1: string | null | undefined,
d2: string | null | undefined,
months: number[] | null | undefined,
dateUploaded: DATE_UPLOADED,
created_on: string | null | undefined,
created_d1: string | null | undefined,
created_d2: string | null | undefined,
media: MEDIA,
establishmentMean: ESTABLISHMENT_MEAN,
wildStatus: WILD_STATUS,
reviewedFilter: REVIEWED,
photoLicense: PHOTO_LICENSE,
mapBoundaries: MapBoundaries
user_id: number | undefined,
verifiable: boolean,
wildStatus: WILD_STATUS
}
type Action = {type: EXPLORE_ACTION.RESET}
| {type: EXPLORE_ACTION.DISCARD, snapshot: State}
| {type: EXPLORE_ACTION.SET_USER, user: Object, userId: number, storedState: State}
| {
type: EXPLORE_ACTION.CHANGE_TAXON,
taxon: Object,
taxonId: number,
taxonName: string,
taxon: { id: number },
storedState: State
}
| {
type: EXPLORE_ACTION.CHANGE_TAXON_NONE,
iconic_taxa: Object
}
| { type: EXPLORE_ACTION.FILTER_BY_ICONIC_TAXON_UNKNOWN }
| {type: EXPLORE_ACTION.SET_EXPLORE_LOCATION, exploreLocation: Object}
| {
type: EXPLORE_ACTION.SET_PLACE,
place: PLACE,
placeId: number,
placeName: string,
placeGuess: string,
lat: number,
lng: number,
radius: number,
@@ -272,26 +277,29 @@ const calculatedFilters = {
// Sort by: is NOT a filter criteria, but should return to default state when reset is pressed
const defaultFilters = {
...calculatedFilters,
user: undefined,
project: undefined,
sortBy: SORT_BY.DATE_UPLOADED_NEWEST,
observed_on: undefined,
created_d1: undefined,
created_d2: undefined,
created_on: undefined,
d1: undefined,
d2: undefined,
iconic_taxa: undefined,
months: undefined,
created_on: undefined,
created_d1: undefined,
created_d2: undefined
observed_on: undefined,
project: undefined,
sortBy: SORT_BY.DATE_UPLOADED_NEWEST,
user: undefined
};
const initialState = {
const initialState: State = {
...defaultFilters,
mapBoundaries: undefined,
place: undefined,
place_guess: "",
place_id: undefined,
return_bounds: true,
taxon: undefined,
taxon_id: undefined,
place_id: undefined,
place_guess: "",
verifiable: true,
return_bounds: true
verifiable: true
};
// Checks if the date is in the format XXXX-XX-XX
@@ -315,6 +323,9 @@ async function defaultExploreLocation( ) {
};
}
// Note: if an action needs to remove a value from state, do not `delete` it.
// Instead, set it to undefined. This helps us detect changes to the default
// state
function exploreReducer( state: State, action: Action ) {
switch ( action.type ) {
case EXPLORE_ACTION.RESET:
@@ -324,43 +335,56 @@ function exploreReducer( state: State, action: Action ) {
};
case EXPLORE_ACTION.DISCARD:
return action.snapshot;
case EXPLORE_ACTION.CHANGE_TAXON:
return {
case EXPLORE_ACTION.CHANGE_TAXON: {
const newState = {
...state,
...action.storedState,
taxon: action.taxon,
taxon_id: action.taxonId,
iconic_taxa: []
iconic_taxa: undefined
};
case EXPLORE_ACTION.CHANGE_TAXON_NONE:
return {
if ( action.taxon ) {
newState.taxon = action.taxon;
newState.taxon_id = action.taxon.id;
} else {
newState.taxon = undefined;
newState.taxon_id = undefined;
}
return newState;
}
// Every iconic taxon filter is essentially a taxon filter... except
// "unknown", which is a search for observations not associated with an
// iconic taxon (either they have no taxon or their taxon is not a
// descendant of an iconic taxon), so it needs its own special action.
// We could also redo this so all iconic taxon filters remove the taxon
// and add iconic_taxa.
case EXPLORE_ACTION.FILTER_BY_ICONIC_TAXON_UNKNOWN: {
const newState = {
...state,
taxon: "Unknown",
taxon_id: null,
iconic_taxa: ["unknown"]
iconic_taxa: ["unknown"],
taxon: undefined,
taxon_id: undefined
};
return newState;
}
case EXPLORE_ACTION.SET_EXPLORE_LOCATION:
return {
...state,
...action.exploreLocation
};
case EXPLORE_ACTION.SET_PLACE:
// eslint-disable-next-line no-case-declarations
const placeState = {
return {
...state,
...action.storedState,
place: action.place,
place_id: action.placeId,
place_guess: action.placeGuess,
lat: action.lat,
lng: action.lng,
radius: action.radius
nelat: undefined,
nelng: undefined,
place: action.place,
place_guess: action.placeGuess,
place_id: action.placeId,
radius: action.radius,
swlat: undefined,
swlng: undefined
};
delete placeState.swlat;
delete placeState.swlng;
delete placeState.nelat;
delete placeState.nelng;
return placeState;
case EXPLORE_ACTION.SET_USER:
return {
...state,
@@ -512,15 +536,14 @@ function exploreReducer( state: State, action: Action ) {
reviewedFilter: action.reviewedFilter
};
case EXPLORE_ACTION.SET_MAP_BOUNDARIES: {
const newState = {
return {
...state,
...action.mapBoundaries
...action.mapBoundaries,
lat: undefined,
lng: undefined,
place_id: undefined,
radius: undefined
};
delete newState.place_id;
delete newState.lat;
delete newState.lng;
delete newState.radius;
return newState;
}
case EXPLORE_ACTION.USE_STORED_STATE:
return {
@@ -544,9 +567,12 @@ const ExploreProvider = ( { children }: ExploreProviderProps ) => {
if ( !snapshot ) {
return false;
}
return Object.keys( snapshot ).some( key => snapshot[key] !== state[key] );
return Object.keys( snapshot ).some( key => !isEqual( snapshot[key], state[key] ) );
};
const differsFromSnapshot: boolean = checkSnapshot();
const differsFromSnapshot: boolean = React.useMemo(
checkSnapshot,
[state, snapshot]
);
const discardChanges = () => {
if ( !snapshot ) {

View File

@@ -18,7 +18,7 @@ describe( "IconicTaxonChooser", () => {
name: "Aves"
} );
expect(
<IconicTaxonChooser taxon={mockTaxon} />
<IconicTaxonChooser chosen={[mockTaxon.name.toLowerCase()]} />
).toBeAccessible( );
} );
@@ -28,7 +28,7 @@ describe( "IconicTaxonChooser", () => {
iconic_taxon_name: "Plantae"
} );
render( <IconicTaxonChooser taxon={mockTaxon} /> );
render( <IconicTaxonChooser chosen={[mockTaxon.name.toLowerCase()]} /> );
const plantButton = await screen.findByTestId(
`IconicTaxonButton.${mockTaxon.name.toLowerCase( )}`

View File

@@ -40,5 +40,36 @@ describe( "ExploreContext", ( ) => {
expect( reducedState.radius ).toBeUndefined( );
} );
} );
describe( EXPLORE_ACTION.CHANGE_TAXON, ( ) => {
it( "should remove iconic_taxa", ( ) => {
const taxon = factory( "RemoteTaxon" );
const initialState = { iconic_taxa: ["Animalia"] };
const reducedState = exploreReducer( initialState, {
type: EXPLORE_ACTION.CHANGE_TAXON,
taxon,
taxonId: taxon.id
} );
expect( reducedState.iconic_taxa ).toBeUndefined( );
} );
it( "should extract an id from a taxon", ( ) => {
const taxon = factory( "RemoteTaxon" );
const initialState = { };
const reducedState = exploreReducer( initialState, {
type: EXPLORE_ACTION.CHANGE_TAXON,
taxon
} );
expect( reducedState.taxon_id ).toEqual( taxon.id );
} );
it( "should remove an id from a blank taxon", ( ) => {
const taxon = factory( "RemoteTaxon" );
const initialState = { taxon, taxon_id: taxon.id };
const reducedState = exploreReducer( initialState, {
type: EXPLORE_ACTION.CHANGE_TAXON,
taxon: null
} );
// expect( reducedState ).not.toHaveProperty( "taxon_id" );
expect( reducedState.taxon_id ).toBeUndefined( );
} );
} );
} );
} );