diff --git a/native/chat/chat.react.js b/native/chat/chat.react.js --- a/native/chat/chat.react.js +++ b/native/chat/chat.react.js @@ -55,6 +55,7 @@ import ThreadSettingsButton from './thread-settings-button.react.js'; import ThreadSettingsHeaderTitle from './thread-settings-header-title.react.js'; import KeyboardAvoidingView from '../components/keyboard-avoiding-view.react.js'; +import { NUXHandler } from '../components/nux-handler.react.js'; import { nuxTip, NUXTipsContext, @@ -536,6 +537,7 @@ + {draftUpdater} diff --git a/native/components/nux-handler.react.js b/native/components/nux-handler.react.js new file mode 100644 --- /dev/null +++ b/native/components/nux-handler.react.js @@ -0,0 +1,52 @@ +// @flow + +import { useNavigation } from '@react-navigation/core'; +import invariant from 'invariant'; +import * as React from 'react'; + +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 { useSelector } from '../redux/redux-utils.js'; +import { useOnFirstLaunchEffect } from '../utils/hooks.js'; + +function NUXHandler(): React.Node { + const nuxTipsContext = React.useContext(NUXTipsContext); + invariant(nuxTipsContext, 'nuxTipsContext should be defined'); + const { tipsProps } = nuxTipsContext; + + const loggedIn = useSelector(isLoggedIn); + + if (!tipsProps || !loggedIn) { + return null; + } + + return ; +} + +function NUXHandlerInner(): React.Node { + 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]); + + useOnFirstLaunchEffect('NUX_HANDLER', effect); +} + +export { NUXHandler }; 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,8 +4,11 @@ import { values } from 'lib/utils/objects.js'; +import { + CommunityDrawerTipRouteName, + MutedTabTipRouteName, +} from '../navigation/route-names.js'; import type { NUXTipRouteNames } from '../navigation/route-names.js'; -import { MutedTabTipRouteName } from '../navigation/route-names.js'; const nuxTip = Object.freeze({ COMMUNITY_DRAWER: 'community_drawer', @@ -21,7 +24,15 @@ +nextRouteName: ?NUXTipRouteNames, }; -const nuxTipParams: { [NUXTip]: NUXTipParams } = { +const firstNUXTipKey = 'firstTip'; +type NUXTipParamsKeys = NUXTip | 'firstTip'; + +const nuxTipParams: { +[NUXTipParamsKeys]: NUXTipParams } = { + [firstNUXTipKey]: { + nextTip: nuxTip.COMMUNITY_DRAWER, + tooltipLocation: 'below', + nextRouteName: CommunityDrawerTipRouteName, + }, [nuxTip.COMMUNITY_DRAWER]: { nextTip: nuxTip.MUTED, tooltipLocation: 'below', @@ -34,7 +45,7 @@ }, }; -function getNUXTipParams(currentTipKey: NUXTip): NUXTipParams { +function getNUXTipParams(currentTipKey: NUXTipParamsKeys): NUXTipParams { return nuxTipParams[currentTipKey]; } @@ -49,7 +60,7 @@ export type NUXTipsContextType = { +registerTipButton: (type: NUXTip, tipProps: ?TipProps) => void, - +getTipsProps: () => ?{ +[type: NUXTip]: TipProps }, + +tipsProps: ?{ +[type: NUXTip]: TipProps }, }; const NUXTipsContext: React.Context = @@ -62,33 +73,39 @@ function NUXTipsContextProvider(props: Props): React.Node { const { children } = props; - const tipsProps = React.useRef<{ [tip: NUXTip]: ?TipProps }>({}); + const [tipsPropsState, setTipsPropsState] = React.useState<{ + +[tip: NUXTip]: ?TipProps, + }>(() => ({})); const registerTipButton = React.useCallback( (type: NUXTip, tipProps: ?TipProps) => { - tipsProps.current[type] = tipProps; + setTipsPropsState(currenttipsPropsState => { + const newtipsPropsState = { ...currenttipsPropsState }; + newtipsPropsState[type] = tipProps; + return newtipsPropsState; + }); }, [], ); - const getTipsProps = React.useCallback(() => { + const tipsProps = React.useMemo(() => { const result: { [tip: NUXTip]: TipProps } = {}; for (const type of values(nuxTip)) { - if (!tipsProps.current[type]) { + if (!tipsPropsState[type]) { return null; } - result[type] = tipsProps.current[type]; + result[type] = tipsPropsState[type]; } return result; - }, []); + }, [tipsPropsState]); const value = React.useMemo( () => ({ registerTipButton, - getTipsProps, + tipsProps, }), - [getTipsProps, registerTipButton], + [tipsProps, registerTipButton], ); return ( @@ -96,4 +113,10 @@ ); } -export { NUXTipsContext, NUXTipsContextProvider, nuxTip, getNUXTipParams }; +export { + NUXTipsContext, + NUXTipsContextProvider, + nuxTip, + getNUXTipParams, + firstNUXTipKey, +}; 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 @@ -129,7 +129,7 @@ const { navigation, route } = props; const { initialCoordinates, verticalBounds } = React.useMemo(() => { - const tipsProps = nuxTipContext?.getTipsProps(); + const tipsProps = nuxTipContext?.tipsProps; invariant(tipsProps, 'tips props should be defined in nux tips overlay'); const { pageX, pageY, width, height } = tipsProps[route.params.tipKey];