Add functionality for advanced iNaturalist mode (#2591)

* Add functionality for advanced iNaturalist mode

* Fix language settings test by toggling advanced mode on

* Fix e2e tests by adding advanced user toggle

* Fix more tests in Settings with advanced toggle
This commit is contained in:
Amanda Bullington
2024-12-26 21:35:27 -08:00
committed by GitHub
parent 31ce8994d9
commit 1c59f89a14
8 changed files with 116 additions and 82 deletions

View File

@@ -10,6 +10,10 @@ export default async function switchPowerMode() {
const settingsDrawerMenuItem = element( by.id( "settings" ) );
await waitFor( settingsDrawerMenuItem ).toBeVisible().withTimeout( 10000 );
await settingsDrawerMenuItem.tap();
// Tap the settings radio button for advanced interface mode
const advancedInterfaceRadioButton = element( by.id( "advanced-interface-option" ) );
await waitFor( advancedInterfaceRadioButton ).toBeVisible().withTimeout( 10000 );
await advancedInterfaceRadioButton.tap();
// Tap the settings radio button for power user mode
const powerUserRadioButton = element( by.id( "all-observation-option" ) );
await waitFor( powerUserRadioButton ).toBeVisible().withTimeout( 10000 );

View File

@@ -4,6 +4,7 @@ import {
Heading4,
PickerSheet
} from "components/SharedComponents";
import { View } from "components/styledComponents";
import _ from "lodash";
import React, { useEffect, useState } from "react";
import changeLanguage from "sharedHelpers/changeLanguage.ts";
@@ -51,8 +52,8 @@ const LanguageSetting = ( { onChange }: Props ) => {
}
return (
<>
<Heading4 className="mt-7">{t( "APP-LANGUAGE" )}</Heading4>
<View className="mb-9">
<Heading4>{t( "APP-LANGUAGE" )}</Heading4>
<Button
className="mt-4"
text={t( "CHANGE-APP-LANGUAGE" )}
@@ -75,7 +76,7 @@ const LanguageSetting = ( { onChange }: Props ) => {
pickerValues={webLocalesOptions}
/>
)}
</>
</View>
);
};

View File

@@ -145,16 +145,9 @@ const Settings = ( ) => {
updateUserMutation.mutate( payload );
}, [settings?.id, updateUserMutation] );
const renderLoggedIn = ( ) => (
<View>
{( isSaving || isLoading ) && (
<View className="absolute z-10 bg-white/80
w-full h-full flex items-center justify-center"
>
<ActivityIndicator size={50} />
</View>
)}
<Heading4 className="mt-7">{t( "TAXON-NAMES-DISPLAY" )}</Heading4>
const renderTaxonNamesSection = ( ) => (
<View className="mb-9">
<Heading4>{t( "TAXON-NAMES-DISPLAY" )}</Heading4>
<Body2 className="mt-3">{t( "This-is-how-taxon-names-will-be-displayed" )}</Body2>
<View className="mt-[22px]">
<RadioButtonRow
@@ -180,6 +173,19 @@ const Settings = ( ) => {
label={t( "Scientific-Name" )}
/>
</View>
</View>
);
const renderLoggedIn = ( ) => (
<View>
{( isSaving || isLoading ) && (
<View className="absolute z-10 bg-white/80
w-full h-full flex items-center justify-center"
>
<ActivityIndicator size={50} />
</View>
)}
{!isDefaultMode && renderTaxonNamesSection( )}
<LanguageSetting
onChange={newLocale => {
QueueItem.enqueue(
@@ -194,46 +200,48 @@ const Settings = ( ) => {
);
}}
/>
<Heading4 className="mt-7">{t( "INATURALIST-ACCOUNT-SETTINGS" )}</Heading4>
<Body2 className="mt-2">{t( "Edit-your-profile-change-your-settings" )}</Body2>
<Button
className="mt-4"
text={t( "ACCOUNT-SETTINGS" )}
onPress={() => {
confirmInternetConnection( );
if ( !isConnected ) { return; }
setShowingWebViewSettings( true );
<View>
<Heading4>{t( "INATURALIST-ACCOUNT-SETTINGS" )}</Heading4>
<Body2 className="mt-2">{t( "Edit-your-profile-change-your-settings" )}</Body2>
<Button
className="mt-4"
text={t( "ACCOUNT-SETTINGS" )}
onPress={() => {
confirmInternetConnection( );
if ( !isConnected ) { return; }
setShowingWebViewSettings( true );
navigation.navigate( "FullPageWebView", {
title: t( "ACCOUNT-SETTINGS" ),
loggedIn: true,
initialUrl: SETTINGS_URL,
blurEvent: FINISHED_WEB_SETTINGS,
clickablePathnames: ["/users/delete"],
skipSetSourceInShouldStartLoadWithRequest: true,
shouldLoadUrl: url => {
async function signOutGoHome() {
Alert.alert(
t( "Account-Deleted" ),
t( "It-may-take-up-to-an-hour-to-remove-content" )
);
// sign out
await signOut( { realm, clearRealm: true, queryClient } );
// navigate to My Obs
navigation.navigate( "ObsList" );
navigation.navigate( "FullPageWebView", {
title: t( "ACCOUNT-SETTINGS" ),
loggedIn: true,
initialUrl: SETTINGS_URL,
blurEvent: FINISHED_WEB_SETTINGS,
clickablePathnames: ["/users/delete"],
skipSetSourceInShouldStartLoadWithRequest: true,
shouldLoadUrl: url => {
async function signOutGoHome() {
Alert.alert(
t( "Account-Deleted" ),
t( "It-may-take-up-to-an-hour-to-remove-content" )
);
// sign out
await signOut( { realm, clearRealm: true, queryClient } );
// navigate to My Obs
navigation.navigate( "ObsList" );
}
// If the webview navigates to a URL that indicates the account
// was deleted, sign the current user out of the app
if ( url === `${Config.OAUTH_API_URL}/?account_deleted=true` ) {
signOutGoHome( );
return false;
}
return true;
}
// If the webview navigates to a URL that indicates the account
// was deleted, sign the current user out of the app
if ( url === `${Config.OAUTH_API_URL}/?account_deleted=true` ) {
signOutGoHome( );
return false;
}
return true;
}
} );
}}
accessibilityLabel={t( "INATURALIST-SETTINGS" )}
/>
} );
}}
accessibilityLabel={t( "INATURALIST-SETTINGS" )}
/>
</View>
</View>
);
@@ -241,9 +249,9 @@ const Settings = ( ) => {
<ScrollViewWrapper>
<StatusBar barStyle="dark-content" />
<View className="p-5">
<View className="mb-5">
<Heading4>{t( "INATURALIST-INTERFACE-MODE" )}</Heading4>
<View className="mt-[22px] pr-5">
<View className="mb-9">
<Heading4>{t( "INATURALIST-MODE" )}</Heading4>
<View className="mt-[22px]">
<RadioButtonRow
smallLabel
checked={isDefaultMode}
@@ -251,36 +259,39 @@ const Settings = ( ) => {
label={t( "Default--interface-mode" )}
/>
</View>
<View className="mt-4 pr-5">
<View className="mt-4">
<RadioButtonRow
testID="advanced-interface-option"
smallLabel
checked={!isDefaultMode}
onPress={( ) => setIsDefaultMode( false )}
label={t( "Advanced--interface-mode" )}
label={t( "Advanced--interface-mode-with-explainer" )}
/>
</View>
</View>
<View className="mb-5">
<Heading4>{t( "OBSERVATION-BUTTON" )}</Heading4>
<Body2 className="mt-3">{t( "When-tapping-the-green-observation-button" )}</Body2>
<View className="mt-[22px] pr-5">
<RadioButtonRow
smallLabel
checked={!isAllAddObsOptionsMode}
onPress={() => setIsAllAddObsOptionsMode( false )}
label={t( "iNaturalist-AI-Camera" )}
/>
{!isDefaultMode && (
<View className="mb-9">
<Heading4>{t( "OBSERVATION-BUTTON" )}</Heading4>
<Body2 className="mt-3">{t( "When-tapping-the-green-observation-button" )}</Body2>
<View className="mt-[22px] pr-5">
<RadioButtonRow
smallLabel
checked={!isAllAddObsOptionsMode}
onPress={() => setIsAllAddObsOptionsMode( false )}
label={t( "iNaturalist-AI-Camera" )}
/>
</View>
<View className="mt-4 pr-5">
<RadioButtonRow
testID="all-observation-option"
smallLabel
checked={isAllAddObsOptionsMode}
onPress={() => setIsAllAddObsOptionsMode( true )}
label={t( "All-observation-option" )}
/>
</View>
</View>
<View className="mt-4 pr-5">
<RadioButtonRow
testID="all-observation-option"
smallLabel
checked={isAllAddObsOptionsMode}
onPress={() => setIsAllAddObsOptionsMode( true )}
label={t( "All-observation-option" )}
/>
</View>
</View>
)}
{currentUser && renderLoggedIn( )}
</View>
</ScrollViewWrapper>

View File

@@ -64,7 +64,7 @@ Add-optional-notes = Add optional notes
Adds-your-vote-of-agreement = Adds your vote of agreement
# Hint for a button that adds a vote of disagreement
Adds-your-vote-of-disagreement = Adds your vote of disagreement
Advanced--interface-mode = Advanced
Advanced--interface-mode-with-explainer = Advanced (Upload multiple photos and sounds)
Affiliation = Affiliation: { $site }
# Label for button that adds an identification of the same taxon as another identification
Agree = Agree
@@ -581,7 +581,6 @@ iNaturalist-has-no-ID-suggestions-for-this-photo = iNaturalist has no ID suggest
INATURALIST-HELP-PAGE = INATURALIST HELP PAGE
iNaturalist-helps-you-identify = iNaturalist helps you identify the plants and animals around you while generating data for science and conservation. Get connected with a community of millions scientists and naturalists who can help you learn more about nature!
iNaturalist-identification-suggestions-are-based-on = iNaturalist's identification suggestions are based on observations and identifications made by the iNaturalist community, including { $user1 }, { $user2 }, { $user3 }, and many others.
INATURALIST-INTERFACE-MODE = INATURALIST INTERFACE MODE
iNaturalist-is-a-501 = iNaturalist is a 501(c)(3) non-profit in the United States of America (Tax ID/EIN 92-1296468).
iNaturalist-is-a-community-of-naturalists = iNaturalist is a community of naturalists that works together to create and identify wild biodiversity observations.
iNaturalist-is-loading-ID-suggestions = iNaturalist is loading ID suggestions...
@@ -589,6 +588,7 @@ iNaturalist-is-supported-by = iNaturalist is supported by an independent, 501(c)
iNaturalist-is-supported-by-our-community = iNaturalist is supported by our amazing community. From everyday naturalists who add observations and identifications, to curators who manage our taxonomy and help with moderation, to the volunteer translators who make iNaturalist more accessible to worldwide audiences, to our community-based donors, we are extraordinarily grateful to all the people in our community who make iNaturalist the platform it is.
iNaturalist-mission-is-to-connect = iNaturalist's mission is to connect people to nature and advance biodiversity science and conservation.
INATURALIST-MISSION-VISION = INATURALIST'S MISSION & VISION
INATURALIST-MODE = INATURALIST MODE
INATURALIST-NETWORK = INATURALIST NETWORK
INATURALIST-SETTINGS = INATURALIST SETTINGS
# Label for the role a user plays on iNaturalist, e.g. "INATURALIST STAFF"

View File

@@ -26,7 +26,7 @@
"Add-optional-notes": "Add optional notes",
"Adds-your-vote-of-agreement": "Adds your vote of agreement",
"Adds-your-vote-of-disagreement": "Adds your vote of disagreement",
"Advanced--interface-mode": "Advanced",
"Advanced--interface-mode-with-explainer": "Advanced (Upload multiple photos and sounds)",
"Affiliation": "Affiliation: { $site }",
"Agree": "Agree",
"AGREE": "AGREE",
@@ -337,7 +337,6 @@
"INATURALIST-HELP-PAGE": "INATURALIST HELP PAGE",
"iNaturalist-helps-you-identify": "iNaturalist helps you identify the plants and animals around you while generating data for science and conservation. Get connected with a community of millions scientists and naturalists who can help you learn more about nature!",
"iNaturalist-identification-suggestions-are-based-on": "iNaturalist's identification suggestions are based on observations and identifications made by the iNaturalist community, including { $user1 }, { $user2 }, { $user3 }, and many others.",
"INATURALIST-INTERFACE-MODE": "INATURALIST INTERFACE MODE",
"iNaturalist-is-a-501": "iNaturalist is a 501(c)(3) non-profit in the United States of America (Tax ID/EIN 92-1296468).",
"iNaturalist-is-a-community-of-naturalists": "iNaturalist is a community of naturalists that works together to create and identify wild biodiversity observations.",
"iNaturalist-is-loading-ID-suggestions": "iNaturalist is loading ID suggestions...",
@@ -345,6 +344,7 @@
"iNaturalist-is-supported-by-our-community": "iNaturalist is supported by our amazing community. From everyday naturalists who add observations and identifications, to curators who manage our taxonomy and help with moderation, to the volunteer translators who make iNaturalist more accessible to worldwide audiences, to our community-based donors, we are extraordinarily grateful to all the people in our community who make iNaturalist the platform it is.",
"iNaturalist-mission-is-to-connect": "iNaturalist's mission is to connect people to nature and advance biodiversity science and conservation.",
"INATURALIST-MISSION-VISION": "INATURALIST'S MISSION & VISION",
"INATURALIST-MODE": "INATURALIST MODE",
"INATURALIST-NETWORK": "INATURALIST NETWORK",
"INATURALIST-SETTINGS": "INATURALIST SETTINGS",
"INATURALIST-STAFF": "{ $inaturalist } STAFF",

View File

@@ -64,7 +64,7 @@ Add-optional-notes = Add optional notes
Adds-your-vote-of-agreement = Adds your vote of agreement
# Hint for a button that adds a vote of disagreement
Adds-your-vote-of-disagreement = Adds your vote of disagreement
Advanced--interface-mode = Advanced
Advanced--interface-mode-with-explainer = Advanced (Upload multiple photos and sounds)
Affiliation = Affiliation: { $site }
# Label for button that adds an identification of the same taxon as another identification
Agree = Agree
@@ -581,7 +581,6 @@ iNaturalist-has-no-ID-suggestions-for-this-photo = iNaturalist has no ID suggest
INATURALIST-HELP-PAGE = INATURALIST HELP PAGE
iNaturalist-helps-you-identify = iNaturalist helps you identify the plants and animals around you while generating data for science and conservation. Get connected with a community of millions scientists and naturalists who can help you learn more about nature!
iNaturalist-identification-suggestions-are-based-on = iNaturalist's identification suggestions are based on observations and identifications made by the iNaturalist community, including { $user1 }, { $user2 }, { $user3 }, and many others.
INATURALIST-INTERFACE-MODE = INATURALIST INTERFACE MODE
iNaturalist-is-a-501 = iNaturalist is a 501(c)(3) non-profit in the United States of America (Tax ID/EIN 92-1296468).
iNaturalist-is-a-community-of-naturalists = iNaturalist is a community of naturalists that works together to create and identify wild biodiversity observations.
iNaturalist-is-loading-ID-suggestions = iNaturalist is loading ID suggestions...
@@ -589,6 +588,7 @@ iNaturalist-is-supported-by = iNaturalist is supported by an independent, 501(c)
iNaturalist-is-supported-by-our-community = iNaturalist is supported by our amazing community. From everyday naturalists who add observations and identifications, to curators who manage our taxonomy and help with moderation, to the volunteer translators who make iNaturalist more accessible to worldwide audiences, to our community-based donors, we are extraordinarily grateful to all the people in our community who make iNaturalist the platform it is.
iNaturalist-mission-is-to-connect = iNaturalist's mission is to connect people to nature and advance biodiversity science and conservation.
INATURALIST-MISSION-VISION = INATURALIST'S MISSION & VISION
INATURALIST-MODE = INATURALIST MODE
INATURALIST-NETWORK = INATURALIST NETWORK
INATURALIST-SETTINGS = INATURALIST SETTINGS
# Label for the role a user plays on iNaturalist, e.g. "INATURALIST STAFF"

View File

@@ -35,6 +35,12 @@ beforeAll( uniqueRealmBeforeAll );
afterAll( uniqueRealmAfterAll );
// /UNIQUE REALM SETUP
const toggleAdvancedMode = async ( ) => {
const advancedRadioButton = await screen
.findByText( /Advanced/ );
fireEvent.press( advancedRadioButton );
};
describe( "LanguageSettings", ( ) => {
test( "uses locale preference of the local device", ( ) => {
renderAppWithComponent( <Settings /> );
@@ -63,12 +69,14 @@ describe( "LanguageSettings", ( ) => {
test( "uses locale preference from server", async ( ) => {
renderAppWithComponent( <Settings /> );
await toggleAdvancedMode( );
const sciNameText = await screen.findByText( /Научное название/ );
expect( sciNameText ).toBeVisible( );
} );
test( "changes locales and updates server with new locale", async ( ) => {
renderAppWithComponent( <Settings /> );
await toggleAdvancedMode( );
const changeLocaleButton = await screen.findByText( /CHANGE APP LANGUAGE/ );
fireEvent.press( changeLocaleButton );
const picker = await screen.findByTestId( "ReactNativePicker" );

View File

@@ -26,6 +26,12 @@ jest.mock( "@react-navigation/native", ( ) => {
const initialStoreState = useStore.getState( );
const toggleAdvancedMode = async ( ) => {
const advancedRadioButton = await screen
.findByText( /Advanced/ );
fireEvent.press( advancedRadioButton );
};
beforeAll( async ( ) => {
useStore.setState( initialStoreState, true );
// userEvent recommends fake timers
@@ -48,6 +54,7 @@ beforeEach( ( ) => {
describe( "Settings", ( ) => {
it( "should toggle the green observation button", async ( ) => {
renderComponent( <Settings /> );
await toggleAdvancedMode( );
const aiCameraRow = await screen.findByLabelText( "iNaturalist AI Camera" );
expect( aiCameraRow ).toHaveProp( "accessibilityState", expect.objectContaining( {
checked: true
@@ -73,6 +80,7 @@ describe( "Settings", ( ) => {
it( "should toggle taxon names display", async ( ) => {
renderComponent( <Settings /> );
await toggleAdvancedMode( );
const sciNameFirst = await screen.findByLabelText( "Scientific Name (Common Name)" );
expect( sciNameFirst ).toHaveProp( "accessibilityState", expect.objectContaining( {
checked: false
@@ -110,6 +118,7 @@ describe( "Settings", ( ) => {
it( "should not change state if taxon names toggled with no internet", async ( ) => {
renderComponent( <Settings /> );
await toggleAdvancedMode( );
const sciNameFirst = await screen.findByLabelText( "Scientific Name (Common Name)" );
expect( sciNameFirst ).toHaveProp( "accessibilityState", expect.objectContaining( {
checked: false
@@ -135,6 +144,7 @@ describe( "Settings", ( ) => {
test( "should change language immediately via language picker via online results", async ( ) => {
renderComponent( <Settings /> );
await toggleAdvancedMode( );
const changeLanguageButton = await screen.findByText( /CHANGE APP LANGUAGE/ );
fireEvent.press( changeLanguageButton );
const picker = await screen.findByTestId( "ReactNativePicker" );