From 9fdde1cdf3c06ecefd606e5907a0c1adeadce498 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Fri, 2 May 2025 15:06:28 -0500 Subject: [PATCH] [APP-1158] Gate features by email verification state (#8305) * Use new hook in all locations * Format * Seems to work, not great duplication * Wrap all open composer calls * Remove unneeded spans * Missed one * Fix handler on Conversation * Gate new chat in header * Add comment * Remove whoopsie * Format * add hackfix for dialog not showing * add prompt to accept chat btn * navigation not necessary * send back one screen, rather than home * Update comment --------- Co-authored-by: Samuel Newman --- .../StarterPack/ProfileStarterPacks.tsx | 50 ++++---- src/components/dms/MessageProfileButton.tsx | 43 +++---- src/components/dms/dialogs/NewChatDialog.tsx | 35 +++--- src/lib/hooks/useIntentHandler.ts | 4 +- src/lib/hooks/useOpenComposer.tsx | 21 ++++ src/lib/hooks/useRequireEmailVerification.tsx | 53 +++++++++ src/screens/Messages/ChatList.tsx | 15 ++- src/screens/Messages/Conversation.tsx | 52 +++++--- .../Messages/components/RequestButtons.tsx | 34 +++++- .../Messages/components/RequestListItem.tsx | 2 +- src/screens/Profile/ProfileFeed/index.tsx | 21 ++-- src/screens/VideoFeed/index.tsx | 40 ++++--- src/state/shell/composer/index.tsx | 14 ++- .../composer/useComposerKeyboardShortcut.tsx | 4 +- src/state/shell/index.tsx | 3 +- src/view/com/composer/Composer.tsx | 24 ---- .../com/composer/videos/SelectVideoBtn.tsx | 112 +++++------------- src/view/com/feeds/FeedPage.tsx | 16 +-- src/view/com/post-thread/PostThread.tsx | 18 +-- src/view/com/post-thread/PostThreadItem.tsx | 4 +- src/view/com/post/Post.tsx | 16 ++- src/view/com/posts/PostFeedItem.tsx | 20 ++-- src/view/com/util/post-ctrls/PostCtrls.tsx | 14 +-- src/view/screens/Feeds.tsx | 15 ++- src/view/screens/Lists.tsx | 37 +++--- src/view/screens/ModerationModlists.tsx | 37 +++--- src/view/screens/Notifications.tsx | 10 +- src/view/screens/Profile.tsx | 15 ++- src/view/screens/ProfileList.tsx | 4 +- src/view/shell/desktop/LeftNav.tsx | 4 +- 30 files changed, 403 insertions(+), 334 deletions(-) create mode 100644 src/lib/hooks/useOpenComposer.tsx create mode 100644 src/lib/hooks/useRequireEmailVerification.tsx diff --git a/src/components/StarterPack/ProfileStarterPacks.tsx b/src/components/StarterPack/ProfileStarterPacks.tsx index d567895066..374f3fb75d 100644 --- a/src/components/StarterPack/ProfileStarterPacks.tsx +++ b/src/components/StarterPack/ProfileStarterPacks.tsx @@ -18,7 +18,7 @@ import {useNavigation} from '@react-navigation/native' import {useGenerateStarterPackMutation} from '#/lib/generate-starterpack' import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset' -import {useEmail} from '#/lib/hooks/useEmail' +import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {type NavigationProp} from '#/lib/routes/types' import {parseStarterPackUri} from '#/lib/strings/starter-pack' @@ -29,7 +29,6 @@ import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' import {atoms as a, ios, useTheme} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {useDialogControl} from '#/components/Dialog' -import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog' import {PlusSmall_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' import {LinearGradientBackground} from '#/components/LinearGradientBackground' import {Loader} from '#/components/Loader' @@ -196,9 +195,7 @@ function Empty() { const confirmDialogControl = useDialogControl() const followersDialogControl = useDialogControl() const errorDialogControl = useDialogControl() - - const {needsEmailVerification} = useEmail() - const verifyEmailControl = useDialogControl() + const requireEmailVerification = useRequireEmailVerification() const [isGenerating, setIsGenerating] = useState(false) @@ -229,6 +226,27 @@ function Empty() { generateStarterPack() } + const openConfirmDialog = useCallback(() => { + confirmDialogControl.open() + }, [confirmDialogControl]) + const wrappedOpenConfirmDialog = requireEmailVerification(openConfirmDialog, { + instructions: [ + + Before creating a starter pack, you must first verify your email. + , + ], + }) + const navToWizard = useCallback(() => { + navigation.navigate('StarterPackWizard') + }, [navigation]) + const wrappedNavToWizard = requireEmailVerification(navToWizard, { + instructions: [ + + Before creating a starter pack, you must first verify your email. + , + ], + }) + return ( { - if (needsEmailVerification) { - verifyEmailControl.open() - } else { - confirmDialogControl.open() - } - }} + onPress={wrappedOpenConfirmDialog} style={{backgroundColor: 'transparent'}}> Make one for me @@ -276,13 +288,7 @@ function Empty() { color="primary" size="small" disabled={isGenerating} - onPress={() => { - if (needsEmailVerification) { - verifyEmailControl.open() - } else { - navigation.navigate('StarterPackWizard') - } - }} + onPress={wrappedNavToWizard} style={{ backgroundColor: 'white', borderColor: 'white', @@ -338,12 +344,6 @@ function Empty() { onConfirm={generate} confirmButtonCta={_(msg`Retry`)} /> - ) } diff --git a/src/components/dms/MessageProfileButton.tsx b/src/components/dms/MessageProfileButton.tsx index 7f31f550ca..ba082153c1 100644 --- a/src/components/dms/MessageProfileButton.tsx +++ b/src/components/dms/MessageProfileButton.tsx @@ -1,20 +1,18 @@ import React from 'react' import {View} from 'react-native' -import {AppBskyActorDefs} from '@atproto/api' -import {msg} from '@lingui/macro' +import {type AppBskyActorDefs} from '@atproto/api' +import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useNavigation} from '@react-navigation/native' -import {useEmail} from '#/lib/hooks/useEmail' -import {NavigationProp} from '#/lib/routes/types' +import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' +import {type NavigationProp} from '#/lib/routes/types' import {logEvent} from '#/lib/statsig/statsig' import {useGetConvoAvailabilityQuery} from '#/state/queries/messages/get-convo-availability' import {useGetConvoForMembers} from '#/state/queries/messages/get-convo-for-members' import * as Toast from '#/view/com/util/Toast' import {atoms as a, useTheme} from '#/alf' import {Button, ButtonIcon} from '#/components/Button' -import {useDialogControl} from '#/components/Dialog' -import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog' import {canBeMessaged} from '#/components/dms/util' import {Message_Stroke2_Corner0_Rounded as Message} from '#/components/icons/Message' @@ -26,8 +24,7 @@ export function MessageProfileButton({ const {_} = useLingui() const t = useTheme() const navigation = useNavigation() - const {needsEmailVerification} = useEmail() - const verifyEmailControl = useDialogControl() + const requireEmailVerification = useRequireEmailVerification() const {data: convoAvailability} = useGetConvoAvailabilityQuery(profile.did) const {mutate: initiateConvo} = useGetConvoForMembers({ @@ -45,11 +42,6 @@ export function MessageProfileButton({ return } - if (needsEmailVerification) { - verifyEmailControl.open() - return - } - if (convoAvailability.convo) { logEvent('chat:open', {logContext: 'ProfileHeader'}) navigation.navigate('MessagesConversation', { @@ -59,14 +51,15 @@ export function MessageProfileButton({ logEvent('chat:create', {logContext: 'ProfileHeader'}) initiateConvo([profile.did]) } - }, [ - needsEmailVerification, - verifyEmailControl, - navigation, - profile.did, - initiateConvo, - convoAvailability, - ]) + }, [navigation, profile.did, initiateConvo, convoAvailability]) + + const wrappedOnPress = requireEmailVerification(onPress, { + instructions: [ + + Before you may message another user, you must first verify your email. + , + ], + }) if (!convoAvailability) { // show pending state based on declaration @@ -102,15 +95,9 @@ export function MessageProfileButton({ shape="round" label={_(msg`Message ${profile.handle}`)} style={[a.justify_center]} - onPress={onPress}> + onPress={wrappedOnPress}> - ) } else { diff --git a/src/components/dms/dialogs/NewChatDialog.tsx b/src/components/dms/dialogs/NewChatDialog.tsx index a5ba793fb3..74a143c135 100644 --- a/src/components/dms/dialogs/NewChatDialog.tsx +++ b/src/components/dms/dialogs/NewChatDialog.tsx @@ -1,8 +1,8 @@ import {useCallback} from 'react' -import {msg} from '@lingui/macro' +import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {useEmail} from '#/lib/hooks/useEmail' +import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' import {logEvent} from '#/lib/statsig/statsig' import {logger} from '#/logger' import {useGetConvoForMembers} from '#/state/queries/messages/get-convo-for-members' @@ -10,9 +10,7 @@ import {FAB} from '#/view/com/util/fab/FAB' import * as Toast from '#/view/com/util/Toast' import {useTheme} from '#/alf' import * as Dialog from '#/components/Dialog' -import {useDialogControl} from '#/components/Dialog' import {SearchablePeopleList} from '#/components/dialogs/SearchablePeopleList' -import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog' import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' export function NewChat({ @@ -24,8 +22,7 @@ export function NewChat({ }) { const t = useTheme() const {_} = useLingui() - const {needsEmailVerification} = useEmail() - const verifyEmailControl = useDialogControl() + const requireEmailVerification = useRequireEmailVerification() const {mutate: createChat} = useGetConvoForMembers({ onSuccess: data => { @@ -49,17 +46,22 @@ export function NewChat({ [control, createChat], ) + const onPress = useCallback(() => { + control.open() + }, [control]) + const wrappedOnPress = requireEmailVerification(onPress, { + instructions: [ + + Before you may message another user, you must first verify your email. + , + ], + }) + return ( <> { - if (needsEmailVerification) { - verifyEmailControl.open() - } else { - control.open() - } - }} + onPress={wrappedOnPress} icon={} accessibilityRole="button" accessibilityLabel={_(msg`New chat`)} @@ -74,13 +76,6 @@ export function NewChat({ sortByMessageDeclaration /> - - ) } diff --git a/src/lib/hooks/useIntentHandler.ts b/src/lib/hooks/useIntentHandler.ts index a33aff2371..4a56537500 100644 --- a/src/lib/hooks/useIntentHandler.ts +++ b/src/lib/hooks/useIntentHandler.ts @@ -1,10 +1,10 @@ import React from 'react' import * as Linking from 'expo-linking' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {logEvent} from '#/lib/statsig/statsig' import {isNative} from '#/platform/detection' import {useSession} from '#/state/session' -import {useComposerControls} from '#/state/shell' import {useCloseAllActiveElements} from '#/state/util' import {useIntentDialogs} from '#/components/intents/IntentDialogs' import {Referrer} from '../../../modules/expo-bluesky-swiss-army' @@ -83,7 +83,7 @@ export function useIntentHandler() { export function useComposeIntent() { const closeAllActiveElements = useCloseAllActiveElements() - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const {hasSession} = useSession() return React.useCallback( diff --git a/src/lib/hooks/useOpenComposer.tsx b/src/lib/hooks/useOpenComposer.tsx new file mode 100644 index 0000000000..bc4d3e1b16 --- /dev/null +++ b/src/lib/hooks/useOpenComposer.tsx @@ -0,0 +1,21 @@ +import {useMemo} from 'react' +import {Trans} from '@lingui/macro' + +import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' +import {useOpenComposer as rootUseOpenComposer} from '#/state/shell/composer' + +export function useOpenComposer() { + const {openComposer} = rootUseOpenComposer() + const requireEmailVerification = useRequireEmailVerification() + return useMemo(() => { + return { + openComposer: requireEmailVerification(openComposer, { + instructions: [ + + Before creating a post, you must first verify your email. + , + ], + }), + } + }, [openComposer, requireEmailVerification]) +} diff --git a/src/lib/hooks/useRequireEmailVerification.tsx b/src/lib/hooks/useRequireEmailVerification.tsx new file mode 100644 index 0000000000..26045847e0 --- /dev/null +++ b/src/lib/hooks/useRequireEmailVerification.tsx @@ -0,0 +1,53 @@ +import {useCallback} from 'react' +import {Keyboard} from 'react-native' + +import {useEmail} from '#/lib/hooks/useEmail' +import {useRequireAuth, useSession} from '#/state/session' +import {useCloseAllActiveElements} from '#/state/util' +import { + EmailDialogScreenID, + type Screen, + useEmailDialogControl, +} from '#/components/dialogs/EmailDialog' + +export function useRequireEmailVerification() { + const {currentAccount} = useSession() + const {needsEmailVerification} = useEmail() + const requireAuth = useRequireAuth() + const emailDialogControl = useEmailDialogControl() + const closeAll = useCloseAllActiveElements() + + return useCallback( + any>( + cb: T, + config: Omit< + Extract, + 'id' + > = {}, + ): ((...args: Parameters) => ReturnType) => { + return (...args: Parameters): ReturnType => { + if (!currentAccount) { + return requireAuth(() => cb(...args)) as ReturnType + } + if (needsEmailVerification) { + Keyboard.dismiss() + closeAll() + emailDialogControl.open({ + id: EmailDialogScreenID.Verify, + ...config, + }) + return undefined as ReturnType + } else { + return cb(...args) + } + } + }, + [ + needsEmailVerification, + currentAccount, + emailDialogControl, + closeAll, + requireAuth, + ], + ) +} diff --git a/src/screens/Messages/ChatList.tsx b/src/screens/Messages/ChatList.tsx index 2846ed828f..8e77c79c7a 100644 --- a/src/screens/Messages/ChatList.tsx +++ b/src/screens/Messages/ChatList.tsx @@ -9,6 +9,7 @@ import {type NativeStackScreenProps} from '@react-navigation/native-stack' import {useAppState} from '#/lib/hooks/useAppState' import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' +import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' import {type MessagesTabNavigatorParams} from '#/lib/routes/types' import {cleanError} from '#/lib/strings/errors' import {logger} from '#/logger' @@ -321,6 +322,18 @@ export function MessagesScreen({navigation, route}: Props) { function Header({newChatControl}: {newChatControl: DialogControlProps}) { const {_} = useLingui() const {gtMobile} = useBreakpoints() + const requireEmailVerification = useRequireEmailVerification() + + const openChatControl = useCallback(() => { + newChatControl.open() + }, [newChatControl]) + const wrappedOpenChatControl = requireEmailVerification(openChatControl, { + instructions: [ + + Before you may message another user, you must first verify your email. + , + ], + }) const settingsLink = ( + onPress={wrappedOpenChatControl}> New chat diff --git a/src/screens/Messages/Conversation.tsx b/src/screens/Messages/Conversation.tsx index e02a6c86d8..e845227f02 100644 --- a/src/screens/Messages/Conversation.tsx +++ b/src/screens/Messages/Conversation.tsx @@ -1,4 +1,4 @@ -import React, {useCallback} from 'react' +import React, {useCallback, useEffect} from 'react' import {View} from 'react-native' import { type AppBskyActorDefs, @@ -17,6 +17,7 @@ import {type NativeStackScreenProps} from '@react-navigation/native-stack' import {useEmail} from '#/lib/hooks/useEmail' import {useEnableKeyboardControllerScreen} from '#/lib/hooks/useEnableKeyboardController' +import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import { type CommonNavigatorParams, type NavigationProp, @@ -192,22 +193,43 @@ function InnerReady({ const {needsEmailVerification} = useEmail() const emailDialogControl = useEmailDialogControl() - React.useEffect(() => { + /** + * Must be non-reactive, otherwise the update to open the global dialog will + * cause a re-render loop. + */ + const maybeBlockForEmailVerification = useNonReactiveCallback(() => { if (needsEmailVerification) { - emailDialogControl.open({ - id: EmailDialogScreenID.Verify, - instructions: [ - - Before you may message another user, you must first verify your - email. - , - ], - onCloseWithoutVerifying: () => { - navigation.navigate('Home') - }, - }) + /* + * HACKFIX + * + * Load bearing timeout, to bump this state update until the after the + * `navigator.addListener('state')` handler closes elements from + * `shell/index.*.tsx` - sfn & esb + */ + setTimeout(() => + emailDialogControl.open({ + id: EmailDialogScreenID.Verify, + instructions: [ + + Before you may message another user, you must first verify your + email. + , + ], + onCloseWithoutVerifying: () => { + if (navigation.canGoBack()) { + navigation.goBack() + } else { + navigation.navigate('Messages', {animation: 'pop'}) + } + }, + }), + ) } - }, [needsEmailVerification, emailDialogControl, navigation]) + }) + + useEffect(() => { + maybeBlockForEmailVerification() + }, [maybeBlockForEmailVerification]) return ( <> diff --git a/src/screens/Messages/components/RequestButtons.tsx b/src/screens/Messages/components/RequestButtons.tsx index 62db096009..3490bec0da 100644 --- a/src/screens/Messages/components/RequestButtons.tsx +++ b/src/screens/Messages/components/RequestButtons.tsx @@ -1,11 +1,12 @@ import {useCallback} from 'react' -import {ChatBskyActorDefs, ChatBskyConvoDefs} from '@atproto/api' +import {type ChatBskyActorDefs, ChatBskyConvoDefs} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {StackActions, useNavigation} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' -import {NavigationProp} from '#/lib/routes/types' +import {useEmail} from '#/lib/hooks/useEmail' +import {type NavigationProp} from '#/lib/routes/types' import {useProfileShadow} from '#/state/cache/profile-shadow' import {useAcceptConversation} from '#/state/queries/messages/accept-conversation' import {precacheConvoQuery} from '#/state/queries/messages/conversation' @@ -13,8 +14,17 @@ import {useLeaveConvo} from '#/state/queries/messages/leave-conversation' import {useProfileBlockMutationQueue} from '#/state/queries/profile' import * as Toast from '#/view/com/util/Toast' import {atoms as a} from '#/alf' -import {Button, ButtonIcon, ButtonProps, ButtonText} from '#/components/Button' +import { + Button, + ButtonIcon, + type ButtonProps, + ButtonText, +} from '#/components/Button' import {useDialogControl} from '#/components/Dialog' +import { + EmailDialogScreenID, + useEmailDialogControl, +} from '#/components/dialogs/EmailDialog' import {ReportDialog} from '#/components/dms/ReportDialog' import {CircleX_Stroke2_Corner0_Rounded} from '#/components/icons/CircleX' import {Flag_Stroke2_Corner0_Rounded as FlagIcon} from '#/components/icons/Flag' @@ -186,6 +196,8 @@ export function AcceptChatButton({ const {_} = useLingui() const queryClient = useQueryClient() const navigation = useNavigation() + const {needsEmailVerification} = useEmail() + const emailDialogControl = useEmailDialogControl() const {mutate: acceptConvo, isPending} = useAcceptConversation(convo.id, { onMutate: () => { @@ -216,8 +228,20 @@ export function AcceptChatButton({ }) const onPressAccept = useCallback(() => { - acceptConvo() - }, [acceptConvo]) + if (needsEmailVerification) { + emailDialogControl.open({ + id: EmailDialogScreenID.Verify, + instructions: [ + + Before you can accept this chat request, you must first verify your + email. + , + ], + }) + } else { + acceptConvo() + } + }, [acceptConvo, needsEmailVerification, emailDialogControl]) return ( - - - ) -} - -function VerifyEmailPrompt({control}: {control: Prompt.PromptControlProps}) { - const {_} = useLingui() - const verifyEmailDialogControl = useDialogControl() - - return ( - <> - { - verifyEmailDialogControl.open() - }} - /> - ) } diff --git a/src/view/com/feeds/FeedPage.tsx b/src/view/com/feeds/FeedPage.tsx index f643adaf9f..604533b0f1 100644 --- a/src/view/com/feeds/FeedPage.tsx +++ b/src/view/com/feeds/FeedPage.tsx @@ -1,32 +1,32 @@ import React from 'react' import {View} from 'react-native' -import {AppBskyActorDefs, AppBskyFeedDefs} from '@atproto/api' +import {type AppBskyActorDefs, AppBskyFeedDefs} from '@atproto/api' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {NavigationProp, useNavigation} from '@react-navigation/native' +import {type NavigationProp, useNavigation} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' import {VIDEO_FEED_URIS} from '#/lib/constants' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {ComposeIcon2} from '#/lib/icons' import {getRootNavigation, getTabState, TabState} from '#/lib/routes/helpers' -import {AllNavigatorParams} from '#/lib/routes/types' +import {type AllNavigatorParams} from '#/lib/routes/types' import {logEvent} from '#/lib/statsig/statsig' import {s} from '#/lib/styles' import {isNative} from '#/platform/detection' import {listenSoftReset} from '#/state/events' import {FeedFeedbackProvider, useFeedFeedback} from '#/state/feed-feedback' import {useSetHomeBadge} from '#/state/home-badge' -import {SavedFeedSourceInfo} from '#/state/queries/feed' +import {type SavedFeedSourceInfo} from '#/state/queries/feed' import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed' -import {FeedDescriptor, FeedParams} from '#/state/queries/post-feed' +import {type FeedDescriptor, type FeedParams} from '#/state/queries/post-feed' import {truncateAndInvalidate} from '#/state/queries/util' import {useSession} from '#/state/session' import {useSetMinimalShellMode} from '#/state/shell' -import {useComposerControls} from '#/state/shell/composer' import {useHeaderOffset} from '#/components/hooks/useHeaderOffset' import {PostFeed} from '../posts/PostFeed' import {FAB} from '../util/fab/FAB' -import {ListMethods} from '../util/List' +import {type ListMethods} from '../util/List' import {LoadLatestBtn} from '../util/load-latest/LoadLatestBtn' import {MainScrollProvider} from '../util/MainScrollProvider' @@ -57,7 +57,7 @@ export function FeedPage({ const {_} = useLingui() const navigation = useNavigation>() const queryClient = useQueryClient() - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const [isScrolledDown, setIsScrolledDown] = React.useState(false) const setMinimalShellMode = useSetMinimalShellMode() const headerOffset = useHeaderOffset() diff --git a/src/view/com/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx index 83dbdb5537..d974ce6b53 100644 --- a/src/view/com/post-thread/PostThread.tsx +++ b/src/view/com/post-thread/PostThread.tsx @@ -5,7 +5,7 @@ import Animated from 'react-native-reanimated' import {useSafeAreaInsets} from 'react-native-safe-area-context' import { AppBskyFeedDefs, - AppBskyFeedThreadgate, + type AppBskyFeedThreadgate, moderatePost, } from '@atproto/api' import {msg, Trans} from '@lingui/macro' @@ -14,6 +14,7 @@ import {useLingui} from '@lingui/react' import {HITSLOP_10} from '#/lib/constants' import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' import {useMinimalShellFabTransform} from '#/lib/hooks/useMinimalShellTransform' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {useSetTitle} from '#/lib/hooks/useSetTitle' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {clamp} from '#/lib/numbers' @@ -25,19 +26,18 @@ import {useModerationOpts} from '#/state/preferences/moderation-opts' import { fillThreadModerationCache, sortThread, - ThreadBlocked, - ThreadModerationCache, - ThreadNode, - ThreadNotFound, - ThreadPost, + type ThreadBlocked, + type ThreadModerationCache, + type ThreadNode, + type ThreadNotFound, + type ThreadPost, usePostThreadQuery, } from '#/state/queries/post-thread' import {useSetThreadViewPreferencesMutation} from '#/state/queries/preferences' import {usePreferencesQuery} from '#/state/queries/preferences' import {useSession} from '#/state/session' -import {useComposerControls} from '#/state/shell' import {useMergedThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies' -import {List, ListMethods} from '#/view/com/util/List' +import {List, type ListMethods} from '#/view/com/util/List' import {atoms as a, useTheme} from '#/alf' import {Button, ButtonIcon} from '#/components/Button' import {SettingsSliderVertical_Stroke2_Corner0_Rounded as SettingsSlider} from '#/components/icons/SettingsSlider' @@ -394,7 +394,7 @@ export function PostThread({uri}: {uri: string | undefined}) { [refetch], ) - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const onPressReply = React.useCallback(() => { if (thread?.type !== 'post') { return diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index dfd641f666..10c3e6b4d1 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -17,6 +17,7 @@ import {msg, Plural, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {MAX_POST_LINES} from '#/lib/constants' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {useOpenLink} from '#/lib/hooks/useOpenLink' import {usePalette} from '#/lib/hooks/usePalette' import {makeProfileLink} from '#/lib/routes/links' @@ -36,7 +37,6 @@ import {useProfileShadow} from '#/state/cache/profile-shadow' import {useLanguagePrefs} from '#/state/preferences' import {type ThreadPost} from '#/state/queries/post-thread' import {useSession} from '#/state/session' -import {useComposerControls} from '#/state/shell/composer' import {useMergedThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies' import {PostThreadFollowBtn} from '#/view/com/post-thread/PostThreadFollowBtn' import {ErrorMessage} from '#/view/com/util/error/ErrorMessage' @@ -204,7 +204,7 @@ let PostThreadItemLoaded = ({ const pal = usePalette('default') const {_, i18n} = useLingui() const langPrefs = useLanguagePrefs() - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const [limitLines, setLimitLines] = React.useState( () => countLines(richText?.text) >= MAX_POST_LINES, ) diff --git a/src/view/com/post/Post.tsx b/src/view/com/post/Post.tsx index 600cee4289..c6cf254f34 100644 --- a/src/view/com/post/Post.tsx +++ b/src/view/com/post/Post.tsx @@ -1,11 +1,11 @@ import React, {useMemo, useState} from 'react' -import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native' +import {type StyleProp, StyleSheet, View, type ViewStyle} from 'react-native' import { - AppBskyFeedDefs, + type AppBskyFeedDefs, AppBskyFeedPost, AtUri, moderatePost, - ModerationDecision, + type ModerationDecision, RichText as RichTextAPI, } from '@atproto/api' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' @@ -14,15 +14,19 @@ import {useLingui} from '@lingui/react' import {useQueryClient} from '@tanstack/react-query' import {MAX_POST_LINES} from '#/lib/constants' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {usePalette} from '#/lib/hooks/usePalette' import {makeProfileLink} from '#/lib/routes/links' import {countLines} from '#/lib/strings/helpers' import {colors, s} from '#/lib/styles' -import {POST_TOMBSTONE, Shadow, usePostShadow} from '#/state/cache/post-shadow' +import { + POST_TOMBSTONE, + type Shadow, + usePostShadow, +} from '#/state/cache/post-shadow' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {precacheProfile} from '#/state/queries/profile' import {useSession} from '#/state/session' -import {useComposerControls} from '#/state/shell/composer' import {AviFollowButton} from '#/view/com/posts/AviFollowButton' import {atoms as a} from '#/alf' import {ProfileHoverCard} from '#/components/ProfileHoverCard' @@ -113,7 +117,7 @@ function PostInner({ const queryClient = useQueryClient() const pal = usePalette('default') const {_} = useLingui() - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const [limitLines, setLimitLines] = useState( () => countLines(richText?.text) >= MAX_POST_LINES, ) diff --git a/src/view/com/posts/PostFeedItem.tsx b/src/view/com/posts/PostFeedItem.tsx index 499b9ccd56..180a5b1d29 100644 --- a/src/view/com/posts/PostFeedItem.tsx +++ b/src/view/com/posts/PostFeedItem.tsx @@ -1,35 +1,39 @@ import React, {memo, useMemo, useState} from 'react' import {StyleSheet, View} from 'react-native' import { - AppBskyActorDefs, + type AppBskyActorDefs, AppBskyFeedDefs, AppBskyFeedPost, AppBskyFeedThreadgate, AtUri, - ModerationDecision, + type ModerationDecision, RichText as RichTextAPI, } from '@atproto/api' import { FontAwesomeIcon, - FontAwesomeIconStyle, + type FontAwesomeIconStyle, } from '@fortawesome/react-native-fontawesome' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useQueryClient} from '@tanstack/react-query' -import {isReasonFeedSource, ReasonFeedSource} from '#/lib/api/feed/types' +import {isReasonFeedSource, type ReasonFeedSource} from '#/lib/api/feed/types' import {MAX_POST_LINES} from '#/lib/constants' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {usePalette} from '#/lib/hooks/usePalette' import {makeProfileLink} from '#/lib/routes/links' import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeHandle} from '#/lib/strings/handles' import {countLines} from '#/lib/strings/helpers' import {s} from '#/lib/styles' -import {POST_TOMBSTONE, Shadow, usePostShadow} from '#/state/cache/post-shadow' +import { + POST_TOMBSTONE, + type Shadow, + usePostShadow, +} from '#/state/cache/post-shadow' import {useFeedFeedbackContext} from '#/state/feed-feedback' import {precacheProfile} from '#/state/queries/profile' import {useSession} from '#/state/session' -import {useComposerControls} from '#/state/shell/composer' import {useMergedThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies' import {FeedNameText} from '#/view/com/util/FeedInfoText' import {PostCtrls} from '#/view/com/util/post-ctrls/PostCtrls' @@ -43,7 +47,7 @@ import {Repost_Stroke2_Corner2_Rounded as RepostIcon} from '#/components/icons/R import {ContentHider} from '#/components/moderation/ContentHider' import {LabelsOnMyPost} from '#/components/moderation/LabelsOnMe' import {PostAlerts} from '#/components/moderation/PostAlerts' -import {AppModerationCause} from '#/components/Pills' +import {type AppModerationCause} from '#/components/Pills' import {ProfileHoverCard} from '#/components/ProfileHoverCard' import {RichText} from '#/components/RichText' import {SubtleWebHover} from '#/components/SubtleWebHover' @@ -150,7 +154,7 @@ let FeedItemInner = ({ rootPost: AppBskyFeedDefs.PostView }): React.ReactNode => { const queryClient = useQueryClient() - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const pal = usePalette('default') const {_} = useLingui() diff --git a/src/view/com/util/post-ctrls/PostCtrls.tsx b/src/view/com/util/post-ctrls/PostCtrls.tsx index fe583e8017..fae9f081d6 100644 --- a/src/view/com/util/post-ctrls/PostCtrls.tsx +++ b/src/view/com/util/post-ctrls/PostCtrls.tsx @@ -8,11 +8,11 @@ import { } from 'react-native' import * as Clipboard from 'expo-clipboard' import { - AppBskyFeedDefs, - AppBskyFeedPost, - AppBskyFeedThreadgate, + type AppBskyFeedDefs, + type AppBskyFeedPost, + type AppBskyFeedThreadgate, AtUri, - RichText as RichTextAPI, + type RichText as RichTextAPI, } from '@atproto/api' import {msg, plural} from '@lingui/macro' import {useLingui} from '@lingui/react' @@ -22,18 +22,18 @@ import {DISCOVER_DEBUG_DIDS, POST_CTRL_HITSLOP} from '#/lib/constants' import {CountWheel} from '#/lib/custom-animations/CountWheel' import {AnimatedLikeIcon} from '#/lib/custom-animations/LikeIcon' import {useHaptics} from '#/lib/haptics' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {makeProfileLink} from '#/lib/routes/links' import {shareUrl} from '#/lib/sharing' import {useGate} from '#/lib/statsig/statsig' import {toShareUrl} from '#/lib/strings/url-helpers' -import {Shadow} from '#/state/cache/types' +import {type Shadow} from '#/state/cache/types' import {useFeedFeedbackContext} from '#/state/feed-feedback' import { usePostLikeMutationQueue, usePostRepostMutationQueue, } from '#/state/queries/post' import {useRequireAuth, useSession} from '#/state/session' -import {useComposerControls} from '#/state/shell/composer' import { ProgressGuideAction, useProgressGuideControls, @@ -74,7 +74,7 @@ let PostCtrls = ({ }): React.ReactNode => { const t = useTheme() const {_, i18n} = useLingui() - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const {currentAccount} = useSession() const [queueLike, queueUnlike] = usePostLikeMutationQueue(post, logContext) const [queueRepost, queueUnrepost] = usePostRepostMutationQueue( diff --git a/src/view/screens/Feeds.tsx b/src/view/screens/Feeds.tsx index 520400fd17..ca0280116d 100644 --- a/src/view/screens/Feeds.tsx +++ b/src/view/screens/Feeds.tsx @@ -1,30 +1,33 @@ import React from 'react' import {ActivityIndicator, StyleSheet, View} from 'react-native' -import {AppBskyFeedDefs} from '@atproto/api' +import {type AppBskyFeedDefs} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useFocusEffect} from '@react-navigation/native' import debounce from 'lodash.debounce' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {usePalette} from '#/lib/hooks/usePalette' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {ComposeIcon2} from '#/lib/icons' -import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types' +import { + type CommonNavigatorParams, + type NativeStackScreenProps, +} from '#/lib/routes/types' import {cleanError} from '#/lib/strings/errors' import {s} from '#/lib/styles' import {isNative, isWeb} from '#/platform/detection' import { - SavedFeedItem, + type SavedFeedItem, useGetPopularFeedsQuery, useSavedFeeds, useSearchPopularFeedsMutation, } from '#/state/queries/feed' import {useSession} from '#/state/session' import {useSetMinimalShellMode} from '#/state/shell' -import {useComposerControls} from '#/state/shell/composer' import {ErrorMessage} from '#/view/com/util/error/ErrorMessage' import {FAB} from '#/view/com/util/fab/FAB' -import {List, ListMethods} from '#/view/com/util/List' +import {List, type ListMethods} from '#/view/com/util/List' import {FeedFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' import {Text} from '#/view/com/util/text/Text' import {NoFollowingFeed} from '#/screens/Feeds/NoFollowingFeed' @@ -102,7 +105,7 @@ type FlatlistSlice = export function FeedsScreen(_props: Props) { const pal = usePalette('default') - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const {isMobile} = useWebMediaQueries() const [query, setQuery] = React.useState('') const [isPTR, setIsPTR] = React.useState(false) diff --git a/src/view/screens/Lists.tsx b/src/view/screens/Lists.tsx index 300153966d..bcda97dc5a 100644 --- a/src/view/screens/Lists.tsx +++ b/src/view/screens/Lists.tsx @@ -4,16 +4,17 @@ import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useFocusEffect, useNavigation} from '@react-navigation/native' -import {useEmail} from '#/lib/hooks/useEmail' -import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types' -import {NavigationProp} from '#/lib/routes/types' +import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' +import { + type CommonNavigatorParams, + type NativeStackScreenProps, +} from '#/lib/routes/types' +import {type NavigationProp} from '#/lib/routes/types' import {useModalControls} from '#/state/modals' import {useSetMinimalShellMode} from '#/state/shell' import {MyLists} from '#/view/com/lists/MyLists' import {atoms as a} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' -import {useDialogControl} from '#/components/Dialog' -import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog' import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus' import * as Layout from '#/components/Layout' @@ -23,8 +24,7 @@ export function ListsScreen({}: Props) { const setMinimalShellMode = useSetMinimalShellMode() const navigation = useNavigation() const {openModal} = useModalControls() - const {needsEmailVerification} = useEmail() - const control = useDialogControl() + const requireEmailVerification = useRequireEmailVerification() useFocusEffect( React.useCallback(() => { @@ -33,11 +33,6 @@ export function ListsScreen({}: Props) { ) const onPressNewList = React.useCallback(() => { - if (needsEmailVerification) { - control.open() - return - } - openModal({ name: 'create-or-edit-list', purpose: 'app.bsky.graph.defs#curatelist', @@ -51,7 +46,15 @@ export function ListsScreen({}: Props) { } catch {} }, }) - }, [needsEmailVerification, control, openModal, navigation]) + }, [openModal, navigation]) + + const wrappedOnPressNewList = requireEmailVerification(onPressNewList, { + instructions: [ + + Before creating a list, you must first verify your email. + , + ], + }) return ( @@ -68,7 +71,7 @@ export function ListsScreen({}: Props) { color="secondary" variant="solid" size="small" - onPress={onPressNewList}> + onPress={wrappedOnPressNewList}> New @@ -76,12 +79,6 @@ export function ListsScreen({}: Props) { - ) } diff --git a/src/view/screens/ModerationModlists.tsx b/src/view/screens/ModerationModlists.tsx index 0b5090e3d4..23ed492f64 100644 --- a/src/view/screens/ModerationModlists.tsx +++ b/src/view/screens/ModerationModlists.tsx @@ -4,16 +4,17 @@ import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useFocusEffect, useNavigation} from '@react-navigation/native' -import {useEmail} from '#/lib/hooks/useEmail' -import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types' -import {NavigationProp} from '#/lib/routes/types' +import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' +import { + type CommonNavigatorParams, + type NativeStackScreenProps, +} from '#/lib/routes/types' +import {type NavigationProp} from '#/lib/routes/types' import {useModalControls} from '#/state/modals' import {useSetMinimalShellMode} from '#/state/shell' import {MyLists} from '#/view/com/lists/MyLists' import {atoms as a} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' -import {useDialogControl} from '#/components/Dialog' -import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog' import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus' import * as Layout from '#/components/Layout' @@ -23,8 +24,7 @@ export function ModerationModlistsScreen({}: Props) { const setMinimalShellMode = useSetMinimalShellMode() const navigation = useNavigation() const {openModal} = useModalControls() - const {needsEmailVerification} = useEmail() - const control = useDialogControl() + const requireEmailVerification = useRequireEmailVerification() useFocusEffect( React.useCallback(() => { @@ -33,11 +33,6 @@ export function ModerationModlistsScreen({}: Props) { ) const onPressNewList = React.useCallback(() => { - if (needsEmailVerification) { - control.open() - return - } - openModal({ name: 'create-or-edit-list', purpose: 'app.bsky.graph.defs#modlist', @@ -51,7 +46,15 @@ export function ModerationModlistsScreen({}: Props) { } catch {} }, }) - }, [needsEmailVerification, control, openModal, navigation]) + }, [openModal, navigation]) + + const wrappedOnPressNewList = requireEmailVerification(onPressNewList, { + instructions: [ + + Before creating a list, you must first verify your email. + , + ], + }) return ( @@ -68,7 +71,7 @@ export function ModerationModlistsScreen({}: Props) { color="secondary" variant="solid" size="small" - onPress={onPressNewList}> + onPress={wrappedOnPressNewList}> New @@ -76,12 +79,6 @@ export function ModerationModlistsScreen({}: Props) { - ) } diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx index 1880fb816a..ace0de2aed 100644 --- a/src/view/screens/Notifications.tsx +++ b/src/view/screens/Notifications.tsx @@ -6,10 +6,11 @@ import {useFocusEffect, useIsFocused} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {ComposeIcon2} from '#/lib/icons' import { - NativeStackScreenProps, - NotificationsTabNavigatorParams, + type NativeStackScreenProps, + type NotificationsTabNavigatorParams, } from '#/lib/routes/types' import {s} from '#/lib/styles' import {logger} from '#/logger' @@ -22,12 +23,11 @@ import { } from '#/state/queries/notifications/unread' import {truncateAndInvalidate} from '#/state/queries/util' import {useSetMinimalShellMode} from '#/state/shell' -import {useComposerControls} from '#/state/shell/composer' import {NotificationFeed} from '#/view/com/notifications/NotificationFeed' import {Pager} from '#/view/com/pager/Pager' import {TabBar} from '#/view/com/pager/TabBar' import {FAB} from '#/view/com/util/fab/FAB' -import {ListMethods} from '#/view/com/util/List' +import {type ListMethods} from '#/view/com/util/List' import {LoadLatestBtn} from '#/view/com/util/load-latest/LoadLatestBtn' import {MainScrollProvider} from '#/view/com/util/MainScrollProvider' import {atoms as a} from '#/alf' @@ -49,7 +49,7 @@ type Props = NativeStackScreenProps< > export function NotificationsScreen({}: Props) { const {_} = useLingui() - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const unreadNotifs = useUnreadNotifications() const hasNew = !!unreadNotifs const {checkUnread: checkUnreadAll} = useUnreadNotificationsApi() diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index 425d55656c..cc339bb03d 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -2,9 +2,9 @@ import React, {useCallback, useMemo} from 'react' import {StyleSheet} from 'react-native' import {SafeAreaView} from 'react-native-safe-area-context' import { - AppBskyActorDefs, + type AppBskyActorDefs, moderateProfile, - ModerationOpts, + type ModerationOpts, RichText as RichTextAPI, } from '@atproto/api' import {msg} from '@lingui/macro' @@ -12,9 +12,13 @@ import {useLingui} from '@lingui/react' import {useFocusEffect} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {useSetTitle} from '#/lib/hooks/useSetTitle' import {ComposeIcon2} from '#/lib/icons' -import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types' +import { + type CommonNavigatorParams, + type NativeStackScreenProps, +} from '#/lib/routes/types' import {combinedDisplayName} from '#/lib/strings/display-names' import {cleanError} from '#/lib/strings/errors' import {isInvalidHandle} from '#/lib/strings/handles' @@ -28,13 +32,12 @@ import {useProfileQuery} from '#/state/queries/profile' import {useResolveDidQuery} from '#/state/queries/resolve-uri' import {useAgent, useSession} from '#/state/session' import {useSetMinimalShellMode} from '#/state/shell' -import {useComposerControls} from '#/state/shell/composer' import {ProfileFeedgens} from '#/view/com/feeds/ProfileFeedgens' import {ProfileLists} from '#/view/com/lists/ProfileLists' import {PagerWithHeader} from '#/view/com/pager/PagerWithHeader' import {ErrorScreen} from '#/view/com/util/error/ErrorScreen' import {FAB} from '#/view/com/util/fab/FAB' -import {ListRef} from '#/view/com/util/List' +import {type ListRef} from '#/view/com/util/List' import {ProfileHeader, ProfileHeaderLoading} from '#/screens/Profile/Header' import {ProfileFeedSection} from '#/screens/Profile/Sections/Feed' import {ProfileLabelsSection} from '#/screens/Profile/Sections/Labels' @@ -165,7 +168,7 @@ function ProfileScreenLoaded({ const profile = useProfileShadow(profileUnshadowed) const {hasSession, currentAccount} = useSession() const setMinimalShellMode = useSetMinimalShellMode() - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const { data: labelerInfo, error: labelerError, diff --git a/src/view/screens/ProfileList.tsx b/src/view/screens/ProfileList.tsx index 61f1eb745a..78cf5d11e7 100644 --- a/src/view/screens/ProfileList.tsx +++ b/src/view/screens/ProfileList.tsx @@ -16,6 +16,7 @@ import {useNavigation} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' import {useHaptics} from '#/lib/haptics' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {usePalette} from '#/lib/hooks/usePalette' import {useSetTitle} from '#/lib/hooks/useSetTitle' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' @@ -54,7 +55,6 @@ import {useResolveUriQuery} from '#/state/queries/resolve-uri' import {truncateAndInvalidate} from '#/state/queries/util' import {useSession} from '#/state/session' import {useSetMinimalShellMode} from '#/state/shell' -import {useComposerControls} from '#/state/shell/composer' import {ListMembers} from '#/view/com/lists/ListMembers' import {PagerWithHeader} from '#/view/com/pager/PagerWithHeader' import {PostFeed} from '#/view/com/posts/PostFeed' @@ -155,7 +155,7 @@ function ProfileListScreenLoaded({ }) { const {_} = useLingui() const queryClient = useQueryClient() - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const setMinimalShellMode = useSetMinimalShellMode() const {currentAccount} = useSession() const {rkey} = route.params diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx index 7d7c0ac8d7..526e6ce1ce 100644 --- a/src/view/shell/desktop/LeftNav.tsx +++ b/src/view/shell/desktop/LeftNav.tsx @@ -10,6 +10,7 @@ import { } from '@react-navigation/native' import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher' +import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {usePalette} from '#/lib/hooks/usePalette' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {getCurrentRoute, isTab} from '#/lib/routes/helpers' @@ -25,7 +26,6 @@ import {useUnreadMessageCount} from '#/state/queries/messages/list-conversations import {useUnreadNotifications} from '#/state/queries/notifications/unread' import {useProfilesQuery} from '#/state/queries/profile' import {type SessionAccount, useSession, useSessionApi} from '#/state/session' -import {useComposerControls} from '#/state/shell/composer' import {useLoggedOutViewControls} from '#/state/shell/logged-out' import {useCloseAllActiveElements} from '#/state/util' import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' @@ -447,7 +447,7 @@ function NavItem({count, hasNew, href, icon, iconFilled, label}: NavItemProps) { function ComposeBtn() { const {currentAccount} = useSession() const {getState} = useNavigation() - const {openComposer} = useComposerControls() + const {openComposer} = useOpenComposer() const {_} = useLingui() const {leftNavMinimal} = useLayoutBreakpoints() const [isFetchingHandle, setIsFetchingHandle] = React.useState(false)