mirror of
https://github.com/inaturalist/iNaturalistReactNative.git
synced 2025-12-23 22:18:36 -05:00
add back tooltip
This commit is contained in:
@@ -10,8 +10,8 @@ import useStore from "stores/useStore";
|
||||
import colors from "styles/tailwindColors";
|
||||
|
||||
interface Props {
|
||||
closeModal: ( ) => void;
|
||||
navAndCloseModal: ( screen: string, params?: {
|
||||
closeBottomSheet: ( ) => void;
|
||||
navAndCloseBottomSheet: ( screen: string, params?: {
|
||||
camera?: string
|
||||
} ) => void;
|
||||
hidden: boolean;
|
||||
@@ -34,7 +34,7 @@ const GREEN_CIRCLE_CLASS = "bg-inatGreen rounded-full h-[36px] w-[36px] mb-2";
|
||||
const ROW_CLASS = "flex-row justify-center space-x-4 w-full flex-1";
|
||||
|
||||
const AddObsBottomSheet = ( {
|
||||
closeModal, navAndCloseModal, hidden
|
||||
closeBottomSheet, navAndCloseBottomSheet, hidden
|
||||
}: Props ) => {
|
||||
const { t } = useTranslation( );
|
||||
|
||||
@@ -44,7 +44,7 @@ const AddObsBottomSheet = ( {
|
||||
aiCamera: {
|
||||
text: t( "ID-with-AI-Camera" ),
|
||||
icon: "aicamera",
|
||||
onPress: ( ) => navAndCloseModal( "Camera", { camera: "AI" } ),
|
||||
onPress: ( ) => navAndCloseBottomSheet( "Camera", { camera: "AI" } ),
|
||||
testID: "aicamera-button",
|
||||
accessibilityLabel: t( "AI-Camera" ),
|
||||
accessibilityHint: t( "Navigates-to-AI-camera" )
|
||||
@@ -52,7 +52,7 @@ const AddObsBottomSheet = ( {
|
||||
standardCamera: {
|
||||
text: t( "Take-photos" ),
|
||||
icon: "camera",
|
||||
onPress: ( ) => navAndCloseModal( "Camera", { camera: "Standard" } ),
|
||||
onPress: ( ) => navAndCloseBottomSheet( "Camera", { camera: "Standard" } ),
|
||||
testID: "camera-button",
|
||||
accessibilityLabel: t( "Camera" ),
|
||||
accessibilityHint: t( "Navigates-to-camera" )
|
||||
@@ -60,7 +60,7 @@ const AddObsBottomSheet = ( {
|
||||
photoLibrary: {
|
||||
text: t( "Upload-photos" ),
|
||||
icon: "photo-library",
|
||||
onPress: ( ) => navAndCloseModal( "PhotoLibrary" ),
|
||||
onPress: ( ) => navAndCloseBottomSheet( "PhotoLibrary" ),
|
||||
testID: "import-media-button",
|
||||
accessibilityLabel: t( "Photo-importer" ),
|
||||
accessibilityHint: t( "Navigates-to-photo-importer" )
|
||||
@@ -68,7 +68,7 @@ const AddObsBottomSheet = ( {
|
||||
soundRecorder: {
|
||||
text: t( "Record-a-sound" ),
|
||||
icon: "microphone",
|
||||
onPress: ( ) => navAndCloseModal( "SoundRecorder" ),
|
||||
onPress: ( ) => navAndCloseBottomSheet( "SoundRecorder" ),
|
||||
testID: "record-sound-button",
|
||||
accessibilityLabel: t( "Sound-recorder" ),
|
||||
accessibilityHint: t( "Navigates-to-sound-recorder" )
|
||||
@@ -79,14 +79,14 @@ const AddObsBottomSheet = ( {
|
||||
onPress: async ( ) => {
|
||||
const newObservation = await Observation.new( );
|
||||
prepareObsEdit( newObservation );
|
||||
navAndCloseModal( "ObsEdit" );
|
||||
navAndCloseBottomSheet( "ObsEdit" );
|
||||
},
|
||||
testID: "observe-without-evidence-button",
|
||||
accessibilityLabel: t( "Observation-with-no-evidence" ),
|
||||
accessibilityHint: t( "Navigates-to-observation-edit-screen" )
|
||||
}
|
||||
} ), [
|
||||
navAndCloseModal,
|
||||
navAndCloseBottomSheet,
|
||||
prepareObsEdit,
|
||||
t
|
||||
] );
|
||||
@@ -124,7 +124,7 @@ const AddObsBottomSheet = ( {
|
||||
|
||||
return (
|
||||
<BottomSheet
|
||||
onPressClose={closeModal}
|
||||
onPressClose={closeBottomSheet}
|
||||
hidden={hidden}
|
||||
insideModal={false}
|
||||
hideCloseButton
|
||||
|
||||
@@ -1,36 +1,46 @@
|
||||
// @flow
|
||||
|
||||
import { CommonActions, useNavigation } from "@react-navigation/native";
|
||||
import classNames from "classnames";
|
||||
import AddObsBottomSheet from "components/AddObsBottomSheet/AddObsBottomSheet";
|
||||
import { Body2 } from "components/SharedComponents";
|
||||
import GradientButton from "components/SharedComponents/Buttons/GradientButton";
|
||||
import { t } from "i18next";
|
||||
import { getCurrentRoute } from "navigation/navigationUtils";
|
||||
import * as React from "react";
|
||||
import { Modal, View } from "react-native";
|
||||
import { log } from "sharedHelpers/logger";
|
||||
import { useCurrentUser, useLayoutPrefs } from "sharedHooks";
|
||||
import { useCurrentUser, useLayoutPrefs, useTranslation } from "sharedHooks";
|
||||
import useStore, { zustandStorage } from "stores/useStore";
|
||||
|
||||
const logger = log.extend( "AddObsButton" );
|
||||
|
||||
const AddObsButton = ( ): React.Node => {
|
||||
const [showModal, setModal] = React.useState( false );
|
||||
const [showBottomSheet, setShowBottomSheet] = React.useState( false );
|
||||
const [showTooltip, setShowTooltip] = React.useState( false );
|
||||
|
||||
const openModal = React.useCallback( () => setModal( true ), [] );
|
||||
const closeModal = React.useCallback( () => setModal( false ), [] );
|
||||
const { t } = useTranslation();
|
||||
|
||||
const openBottomSheet = React.useCallback( () => setShowBottomSheet( true ), [] );
|
||||
const closeBottomSheet = React.useCallback( () => setShowBottomSheet( false ), [] );
|
||||
|
||||
const { isAllAddObsOptionsMode } = useLayoutPrefs( );
|
||||
const currentRoute = getCurrentRoute( );
|
||||
const currentUser = useCurrentUser( );
|
||||
|
||||
// #region Tooltip logic
|
||||
|
||||
// Controls whether to show the tooltip, and to show it only once to the user
|
||||
const showKey = "AddObsButtonTooltip";
|
||||
const shownOnce = useStore( state => state.layout.shownOnce );
|
||||
const setShownOnce = useStore( state => state.layout.setShownOnce );
|
||||
const justFinishedSignup = useStore( state => state.layout.justFinishedSignup );
|
||||
const numOfUserObservations = zustandStorage.getItem( "numOfUserObservations" );
|
||||
|
||||
// Base trigger condition in all cases:
|
||||
// Only show the tooltip if the user has only AI camera as an option in this button.
|
||||
// Only show the tooltip on MyObservations screen.
|
||||
let triggerCondition = !isAllAddObsOptionsMode && currentRoute?.name === "ObsList";
|
||||
|
||||
if ( justFinishedSignup ) {
|
||||
// If a user creates a new account, they should see the tooltip right after
|
||||
// dismissing the account creation pivot card and landing on My Obs.
|
||||
@@ -44,7 +54,6 @@ const AddObsButton = ( ): React.Node => {
|
||||
triggerCondition = false;
|
||||
} else if ( !currentUser ) {
|
||||
// If logged out, user should see the tooltip after making their second observation
|
||||
// If a user is logged out, they should see the tooltip after making their second observation.
|
||||
triggerCondition = triggerCondition && numOfUserObservations > 1;
|
||||
} else if ( numOfUserObservations > 50 ) {
|
||||
// If a user logs in to an existing account with <=50 observations,
|
||||
@@ -61,24 +70,21 @@ const AddObsButton = ( ): React.Node => {
|
||||
triggerCondition = triggerCondition && !!shownOnce["fifty-observation"];
|
||||
}
|
||||
|
||||
// The tooltip should only appear once per app download.
|
||||
const tooltipIsVisible = !shownOnce[showKey] && triggerCondition;
|
||||
// The tooltip should only appear once per app download. // delete
|
||||
// const tooltipIsVisible = !shownOnce[showKey] && triggerCondition; // delete?
|
||||
|
||||
React.useEffect( () => {
|
||||
// If the tooltip visibility condition changes from false to true,
|
||||
// we set the showModal state to true because the tooltip is in the modal.
|
||||
// We have a lot of modals in the app, so we use a timeout to avoid opening two modals
|
||||
// at the same time, like the PivotCards for example that in some cases were just closed
|
||||
// by the user.
|
||||
let timeoutId;
|
||||
if ( tooltipIsVisible ) {
|
||||
timeoutId = setTimeout( () => {
|
||||
openModal();
|
||||
}, 400 );
|
||||
}
|
||||
return () => {
|
||||
clearTimeout( timeoutId );
|
||||
};
|
||||
}, [tooltipIsVisible, openModal] );
|
||||
// The tooltip should only appear once per app download.
|
||||
if ( !shownOnce[showKey] && triggerCondition ) setShowTooltip( true );
|
||||
}, [shownOnce, triggerCondition] );
|
||||
|
||||
const dismissTooltip = () => {
|
||||
setShowTooltip( false );
|
||||
setShownOnce( showKey );
|
||||
openBottomSheet();
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
const resetObservationFlowSlice = useStore( state => state.resetObservationFlowSlice );
|
||||
const navigation = useNavigation( );
|
||||
@@ -89,7 +95,7 @@ const AddObsButton = ( ): React.Node => {
|
||||
logger.info( `isAdvancedUser: ${isAllAddObsOptionsMode}` );
|
||||
}, [isAllAddObsOptionsMode] );
|
||||
|
||||
const navAndCloseModal = ( screen, params ) => {
|
||||
const navAndCloseBottomSheet = ( screen, params ) => {
|
||||
if ( screen !== "ObsEdit" ) {
|
||||
resetObservationFlowSlice( );
|
||||
}
|
||||
@@ -116,26 +122,54 @@ const AddObsButton = ( ): React.Node => {
|
||||
} )
|
||||
);
|
||||
|
||||
closeModal( );
|
||||
closeBottomSheet( );
|
||||
};
|
||||
const navToARCamera = ( ) => { navAndCloseModal( "Camera", { camera: "AI" } ); };
|
||||
const navToARCamera = ( ) => { navAndCloseBottomSheet( "Camera", { camera: "AI" } ); };
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
visible={showTooltip}
|
||||
animationType="fade"
|
||||
transparent
|
||||
onRequestClose={dismissTooltip}
|
||||
>
|
||||
<View className="flex-1 bg-black/50 items-center justify-end">
|
||||
{/* Tooltip */}
|
||||
<View className="relative bottom-[24px] items-center">
|
||||
<View className="bg-white rounded-2xl px-5 py-4">
|
||||
<Body2>{t( "Press-and-hold-to-view-more-options" )}</Body2>
|
||||
</View>
|
||||
<View
|
||||
className={classNames(
|
||||
"border-l-[10px] border-r-[10px] border-x-[#00000000]",
|
||||
"border-t-[16px] border-t-white mb-2"
|
||||
)}
|
||||
/>
|
||||
<GradientButton
|
||||
sizeClassName="w-[69px] h-[69px]"
|
||||
onPress={() => {}}
|
||||
onLongPress={() => dismissTooltip()}
|
||||
accessibilityLabel={t( "Add-observations" )}
|
||||
accessibilityHint={t( "Shows-observation-creation-options" )}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
{/* match the animation timing on FadeInView.tsx */}
|
||||
<AddObsBottomSheet
|
||||
closeModal={closeModal}
|
||||
hidden={!showModal}
|
||||
navAndCloseModal={navAndCloseModal}
|
||||
closeBottomSheet={closeBottomSheet}
|
||||
hidden={!showBottomSheet}
|
||||
navAndCloseBottomSheet={navAndCloseBottomSheet}
|
||||
/>
|
||||
<GradientButton
|
||||
sizeClassName="w-[69px] h-[69px] mb-[5px]"
|
||||
onLongPress={() => {
|
||||
if ( !isAllAddObsOptionsMode ) openModal();
|
||||
if ( !isAllAddObsOptionsMode ) openBottomSheet();
|
||||
}}
|
||||
onPress={() => {
|
||||
if ( isAllAddObsOptionsMode ) {
|
||||
openModal();
|
||||
openBottomSheet();
|
||||
} else {
|
||||
navToARCamera();
|
||||
}
|
||||
|
||||
@@ -922,6 +922,7 @@ POTENTIAL-DISAGREEMENT = POTENTIAL DISAGREEMENT
|
||||
Potential-disagreement-description = <0>Is the evidence enough to confirm this is </0><1></1><0>?<0>
|
||||
Potential-disagreement-disagree = <0>No, but this is a member of </0><1></1>
|
||||
Potential-disagreement-unsure = <0>I don't know but I am sure this is </0><1></1>
|
||||
Press-and-hold-to-view-more-options = Press and hold to view more options
|
||||
Previous-observation = Previous observation
|
||||
# Accessibility label for a button that goes to the previous slide on onboarding cards
|
||||
Previous-slide = Previous slide
|
||||
|
||||
@@ -544,6 +544,7 @@
|
||||
"Potential-disagreement-description": "<0>Is the evidence enough to confirm this is </0><1></1><0>?<0>",
|
||||
"Potential-disagreement-disagree": "<0>No, but this is a member of </0><1></1>",
|
||||
"Potential-disagreement-unsure": "<0>I don't know but I am sure this is </0><1></1>",
|
||||
"Press-and-hold-to-view-more-options": "Press and hold to view more options",
|
||||
"Previous-observation": "Previous observation",
|
||||
"Previous-slide": "Previous slide",
|
||||
"Privacy-Policy": "Privacy Policy",
|
||||
|
||||
@@ -922,6 +922,7 @@ POTENTIAL-DISAGREEMENT = POTENTIAL DISAGREEMENT
|
||||
Potential-disagreement-description = <0>Is the evidence enough to confirm this is </0><1></1><0>?<0>
|
||||
Potential-disagreement-disagree = <0>No, but this is a member of </0><1></1>
|
||||
Potential-disagreement-unsure = <0>I don't know but I am sure this is </0><1></1>
|
||||
Press-and-hold-to-view-more-options = Press and hold to view more options
|
||||
Previous-observation = Previous observation
|
||||
# Accessibility label for a button that goes to the previous slide on onboarding cards
|
||||
Previous-slide = Previous slide
|
||||
|
||||
Reference in New Issue
Block a user