diff --git a/native/components/nux-handler.react.js b/native/components/nux-handler.react.js --- a/native/components/nux-handler.react.js +++ b/native/components/nux-handler.react.js @@ -6,12 +6,7 @@ import { isLoggedIn } from 'lib/selectors/user-selectors.js'; -import { - firstNUXTipKey, - NUXTipsContext, - getNUXTipParams, -} from './nux-tips-context.react.js'; -import type { NUXTipRouteNames } from '../navigation/route-names.js'; +import { NUXTipsContext } from './nux-tips-context.react.js'; import { useSelector } from '../redux/redux-utils.js'; import { useOnFirstLaunchEffect } from '../utils/hooks.js'; @@ -33,16 +28,8 @@ const navigation = useNavigation(); const effect = React.useCallback(() => { - const { nextTip, tooltipLocation, nextRouteName } = - getNUXTipParams(firstNUXTipKey); - invariant(nextRouteName && nextTip, 'first nux tip should be defined'); - - navigation.navigate({ - name: nextRouteName, - params: { - tipKey: nextTip, - tooltipLocation, - }, + navigation.navigate<'NUXTipOverlayBackdrop'>({ + name: 'NUXTipOverlayBackdrop', }); }, [navigation]); diff --git a/native/components/nux-tips-context.react.js b/native/components/nux-tips-context.react.js --- a/native/components/nux-tips-context.react.js +++ b/native/components/nux-tips-context.react.js @@ -4,6 +4,7 @@ import { values } from 'lib/utils/objects.js'; +import type { AppNavigationProp } from '../navigation/app-navigator.react.js'; import { CommunityDrawerTipRouteName, MutedTabTipRouteName, @@ -22,6 +23,9 @@ +nextTip: ?NUXTip, +tooltipLocation: 'below' | 'above', +nextRouteName: ?NUXTipRouteNames, + +exitingCallback?: ( + navigation: AppNavigationProp, + ) => void, }; const firstNUXTipKey = 'firstTip'; @@ -42,6 +46,7 @@ nextTip: undefined, nextRouteName: undefined, tooltipLocation: 'below', + exitingCallback: navigation => navigation.goBack(), }, }; diff --git a/native/navigation/app-navigator.react.js b/native/navigation/app-navigator.react.js --- a/native/navigation/app-navigator.react.js +++ b/native/navigation/app-navigator.react.js @@ -8,6 +8,7 @@ import { CommunityDrawerNavigator } from './community-drawer-navigator.react.js'; import CommunityDrawerTip from './community-drawer-tip.react.js'; import MutedTabTip from './muted-tab-tip.react.js'; +import NUXTipOverlayBackdrop from './nux-tip-overlay-backdrop.react.js'; import { createOverlayNavigator } from './overlay-navigator.react.js'; import type { OverlayNavigationProp, @@ -17,6 +18,7 @@ import { CommunityDrawerTipRouteName, MutedTabTipRouteName, + NUXTipOverlayBackdropRouteName, } from './route-names.js'; import { UserAvatarCameraModalRouteName, @@ -164,6 +166,10 @@ component={CommunityDrawerTip} /> + {pushHandler} diff --git a/native/navigation/nux-tip-overlay-backdrop.react.js b/native/navigation/nux-tip-overlay-backdrop.react.js new file mode 100644 --- /dev/null +++ b/native/navigation/nux-tip-overlay-backdrop.react.js @@ -0,0 +1,105 @@ +// @flow + +import invariant from 'invariant'; +import * as React from 'react'; +import { withTiming } from 'react-native-reanimated'; + +import type { AppNavigationProp } from './app-navigator.react.js'; +import { OverlayContext } from './overlay-context.js'; +import type { NUXTipRouteNames, NavigationRoute } from './route-names'; +import { + firstNUXTipKey, + getNUXTipParams, +} from '../components/nux-tips-context.react.js'; +import { useStyles } from '../themes/colors.js'; +import { animationDuration } from '../tooltip/nux-tips-overlay.react.js'; +import { AnimatedView } from '../types/styles.js'; + +type Props = { + +navigation: AppNavigationProp<'NUXTipOverlayBackdrop'>, + +route: NavigationRoute<'NUXTipOverlayBackdrop'>, +}; + +function NUXTipOverlayBackdrop(props: Props): React.Node { + const overlayContext = React.useContext(OverlayContext); + invariant(overlayContext, 'NUXTipsOverlay should have OverlayContext'); + const { shouldRenderScreenContent } = overlayContext; + + return shouldRenderScreenContent ? ( + + ) : null; +} + +function opacityEnteringAnimation() { + 'worklet'; + + return { + animations: { + opacity: withTiming(0.7, { duration: animationDuration }), + }, + initialValues: { + opacity: 0, + }, + }; +} + +function NUXTipOverlayBackdropInner(props: Props): React.Node { + const overlayContext = React.useContext(OverlayContext); + invariant(overlayContext, 'NUXTipsOverlay should have OverlayContext'); + const { onExitFinish } = overlayContext; + + const styles = useStyles(unboundStyles); + + const opacityExitingAnimation = React.useCallback(() => { + 'worklet'; + + return { + animations: { + opacity: withTiming(0, { duration: animationDuration }), + }, + initialValues: { + opacity: 0.7, + }, + callback: onExitFinish, + }; + }, [onExitFinish]); + + const { nextTip, tooltipLocation, nextRouteName } = + getNUXTipParams(firstNUXTipKey); + invariant(nextRouteName && nextTip, 'first nux tip should be defined'); + + React.useEffect( + () => + props.navigation.navigate({ + name: nextRouteName, + params: { + tipKey: nextTip, + tooltipLocation, + }, + }), + // We want this effect to run exactly once, when this component is mounted + // eslint-disable-next-line react-hooks/exhaustive-deps + [], + ); + + return ( + + ); +} + +const unboundStyles = { + backdrop: { + backgroundColor: 'black', + bottom: 0, + left: 0, + position: 'absolute', + right: 0, + top: 0, + }, +}; + +export default NUXTipOverlayBackdrop; diff --git a/native/navigation/overlay-navigator.react.js b/native/navigation/overlay-navigator.react.js --- a/native/navigation/overlay-navigator.react.js +++ b/native/navigation/overlay-navigator.react.js @@ -41,12 +41,14 @@ TabNavigatorRouteName, CommunityDrawerTipRouteName, MutedTabTipRouteName, + NUXTipOverlayBackdropRouteName, } from './route-names.js'; import { isMessageTooltipKey } from '../chat/utils.js'; const newReanimatedRoutes = new Set([ CommunityDrawerTipRouteName, MutedTabTipRouteName, + NUXTipOverlayBackdropRouteName, ]); export type OverlayNavigationHelpers = diff --git a/native/navigation/route-names.js b/native/navigation/route-names.js --- a/native/navigation/route-names.js +++ b/native/navigation/route-names.js @@ -169,6 +169,7 @@ 'ThreadSettingsNotifications'; export const CommunityDrawerTipRouteName = 'CommunityDrawerTip'; export const MutedTabTipRouteName = 'MutedTabTip'; +export const NUXTipOverlayBackdropRouteName = 'NUXTipOverlayBackdrop'; export type RootParamList = { +LoggedOutModal: void, @@ -231,6 +232,7 @@ +TogglePinModal: TogglePinModalParams, +CommunityDrawerTip: NUXTipsOverlayParams, +MutedTabTip: NUXTipsOverlayParams, + +NUXTipOverlayBackdrop: void, ...TooltipModalParamList, }; diff --git a/native/tooltip/nux-tips-overlay.react.js b/native/tooltip/nux-tips-overlay.react.js --- a/native/tooltip/nux-tips-overlay.react.js +++ b/native/tooltip/nux-tips-overlay.react.js @@ -35,14 +35,6 @@ const animationDuration = 150; const unboundStyles = { - backdrop: { - backgroundColor: 'black', - bottom: 0, - left: 0, - position: 'absolute', - right: 0, - top: 0, - }, container: { flex: 1, }, @@ -120,19 +112,6 @@ const marginVertical: number = 20; const marginHorizontal: number = 10; -function opacityEnteringAnimation() { - 'worklet'; - - return { - animations: { - opacity: withTiming(0.7, { duration: animationDuration }), - }, - initialValues: { - opacity: 0, - }, - }; -} - function createNUXTipsOverlay( ButtonComponent: React.ComponentType>, tipText: string, @@ -268,20 +247,6 @@ } }, [dimensions.width, initialCoordinates]); - const opacityExitingAnimation = React.useCallback(() => { - 'worklet'; - - return { - animations: { - opacity: withTiming(0, { duration: animationDuration }), - }, - initialValues: { - opacity: 0.7, - }, - callback: onExitFinish, - }; - }, [onExitFinish]); - // prettier-ignore const tipContainerEnteringAnimation = React.useCallback( (values/*: EntryAnimationsValues*/) => { @@ -355,9 +320,10 @@ opacity: 1, transform: [{ translateX: 0 }, { translateY: 0 }, { scale: 1 }], }, + callback: onExitFinish, }; }, - [initialCoordinates.width, initialCoordinates.x, tooltipLocation], + [initialCoordinates.width, initialCoordinates.x, onExitFinish, tooltipLocation], ); let triangleDown = null; @@ -368,16 +334,21 @@ triangleUp = ; } - const callbackParams = getNUXTipParams(route.params.tipKey); - const onPressOk = React.useCallback(() => { + const callbackParams = getNUXTipParams(route.params.tipKey); + const { nextTip, tooltipLocation: nextLocation, nextRouteName, + exitingCallback, } = callbackParams; goBackOnce(); + if (exitingCallback) { + exitingCallback?.(navigation); + } + if (!nextTip || !nextRouteName) { return; } @@ -388,16 +359,11 @@ tooltipLocation: nextLocation, }, }); - }, [callbackParams, goBackOnce, navigation]); + }, [goBackOnce, navigation, route.params.tipKey]); return ( - >(NUXTipsOverlayWrapper); } -export { createNUXTipsOverlay }; +export { createNUXTipsOverlay, animationDuration };