mirror of
https://github.com/inaturalist/iNaturalistReactNative.git
synced 2026-06-18 12:41:54 -04:00
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:
@@ -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
|
||||
|
||||
@@ -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 )}
|
||||
/>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 )}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ) {
|
||||
|
||||
@@ -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 ) {
|
||||
|
||||
@@ -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( )}`
|
||||
|
||||
@@ -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( );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
Reference in New Issue
Block a user