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)