diff options
Diffstat (limited to 'src/view')
-rw-r--r-- | src/view/com/composer/Composer.tsx | 21 | ||||
-rw-r--r-- | src/view/com/composer/videos/SelectVideoBtn.tsx | 112 | ||||
-rw-r--r-- | src/view/com/feeds/FeedPage.tsx | 16 | ||||
-rw-r--r-- | src/view/com/post-thread/PostThread.tsx | 18 | ||||
-rw-r--r-- | src/view/com/post-thread/PostThreadItem.tsx | 4 | ||||
-rw-r--r-- | src/view/com/post/Post.tsx | 16 | ||||
-rw-r--r-- | src/view/com/posts/PostFeedItem.tsx | 4 | ||||
-rw-r--r-- | src/view/com/util/post-ctrls/PostCtrls.tsx | 4 | ||||
-rw-r--r-- | src/view/screens/Feeds.tsx | 15 | ||||
-rw-r--r-- | src/view/screens/Lists.tsx | 37 | ||||
-rw-r--r-- | src/view/screens/ModerationModlists.tsx | 37 | ||||
-rw-r--r-- | src/view/screens/Notifications.tsx | 10 | ||||
-rw-r--r-- | src/view/screens/Profile.tsx | 15 | ||||
-rw-r--r-- | src/view/screens/ProfileList.tsx | 4 | ||||
-rw-r--r-- | src/view/shell/desktop/LeftNav.tsx | 4 | ||||
-rw-r--r-- | src/view/shell/index.tsx | 2 | ||||
-rw-r--r-- | src/view/shell/index.web.tsx | 2 |
17 files changed, 128 insertions, 193 deletions
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index b6d269d28..e690e7256 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -62,7 +62,6 @@ import { type SupportedMimeTypes, } from '#/lib/constants' import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED' -import {useEmail} from '#/lib/hooks/useEmail' import {useIsKeyboardVisible} from '#/lib/hooks/useIsKeyboardVisible' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import {usePalette} from '#/lib/hooks/usePalette' @@ -120,8 +119,6 @@ import * as Toast from '#/view/com/util/Toast' import {UserAvatar} from '#/view/com/util/UserAvatar' import {atoms as a, native, useTheme, web} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' -import {useDialogControl} from '#/components/Dialog' -import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog' import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmile} from '#/components/icons/Emoji' import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' @@ -331,15 +328,6 @@ export const ComposePost = ({ } }, [onPressCancel, closeAllDialogs, closeAllModals]) - const {needsEmailVerification} = useEmail() - const emailVerificationControl = useDialogControl() - - useEffect(() => { - if (needsEmailVerification) { - emailVerificationControl.open() - } - }, [needsEmailVerification, emailVerificationControl]) - const missingAltError = useMemo(() => { if (!requireAltTextEnabled) { return @@ -620,15 +608,6 @@ export const ComposePost = ({ const isWebFooterSticky = !isNative && thread.posts.length > 1 return ( <BottomSheetPortalProvider> - <VerifyEmailDialog - control={emailVerificationControl} - onCloseWithoutVerifying={() => { - onClose() - }} - reasonText={_( - msg`Before creating a post, you must first verify your email.`, - )} - /> <KeyboardAvoidingView testID="composePostView" behavior={isIOS ? 'padding' : 'height'} diff --git a/src/view/com/composer/videos/SelectVideoBtn.tsx b/src/view/com/composer/videos/SelectVideoBtn.tsx index 8d9371f0d..96715955f 100644 --- a/src/view/com/composer/videos/SelectVideoBtn.tsx +++ b/src/view/com/composer/videos/SelectVideoBtn.tsx @@ -1,26 +1,19 @@ import {useCallback} from 'react' -import {Keyboard} from 'react-native' -import {ImagePickerAsset} from 'expo-image-picker' +import {type ImagePickerAsset} from 'expo-image-picker' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import { SUPPORTED_MIME_TYPES, - SupportedMimeTypes, + type SupportedMimeTypes, VIDEO_MAX_DURATION_MS, } from '#/lib/constants' -import {BSKY_SERVICE} from '#/lib/constants' import {useVideoLibraryPermission} from '#/lib/hooks/usePermissions' -import {getHostnameFromUrl} from '#/lib/strings/url-helpers' import {isWeb} from '#/platform/detection' import {isNative} from '#/platform/detection' -import {useSession} from '#/state/session' import {atoms as a, useTheme} from '#/alf' import {Button} from '#/components/Button' -import {useDialogControl} from '#/components/Dialog' -import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog' import {VideoClip_Stroke2_Corner0_Rounded as VideoClipIcon} from '#/components/icons/VideoClip' -import * as Prompt from '#/components/Prompt' import {pickVideo} from './pickVideo' type Props = { @@ -33,66 +26,45 @@ export function SelectVideoBtn({onSelectVideo, disabled, setError}: Props) { const {_} = useLingui() const t = useTheme() const {requestVideoAccessIfNeeded} = useVideoLibraryPermission() - const control = Prompt.usePromptControl() - const {currentAccount} = useSession() const onPressSelectVideo = useCallback(async () => { if (isNative && !(await requestVideoAccessIfNeeded())) { return } - if ( - currentAccount && - !currentAccount.emailConfirmed && - getHostnameFromUrl(currentAccount.service) === - getHostnameFromUrl(BSKY_SERVICE) - ) { - Keyboard.dismiss() - control.open() - } else { - const response = await pickVideo() - if (response.assets && response.assets.length > 0) { - const asset = response.assets[0] - try { - if (isWeb) { - // asset.duration is null for gifs (see the TODO in pickVideo.web.ts) - if (asset.duration && asset.duration > VIDEO_MAX_DURATION_MS) { - throw Error(_(msg`Videos must be less than 3 minutes long`)) - } - // compression step on native converts to mp4, so no need to check there - if ( - !SUPPORTED_MIME_TYPES.includes( - asset.mimeType as SupportedMimeTypes, - ) - ) { - throw Error(_(msg`Unsupported video type: ${asset.mimeType}`)) - } - } else { - if (typeof asset.duration !== 'number') { - throw Error('Asset is not a video') - } - if (asset.duration > VIDEO_MAX_DURATION_MS) { - throw Error(_(msg`Videos must be less than 3 minutes long`)) - } + const response = await pickVideo() + if (response.assets && response.assets.length > 0) { + const asset = response.assets[0] + try { + if (isWeb) { + // asset.duration is null for gifs (see the TODO in pickVideo.web.ts) + if (asset.duration && asset.duration > VIDEO_MAX_DURATION_MS) { + throw Error(_(msg`Videos must be less than 3 minutes long`)) } - onSelectVideo(asset) - } catch (err) { - if (err instanceof Error) { - setError(err.message) - } else { - setError(_(msg`An error occurred while selecting the video`)) + // compression step on native converts to mp4, so no need to check there + if ( + !SUPPORTED_MIME_TYPES.includes(asset.mimeType as SupportedMimeTypes) + ) { + throw Error(_(msg`Unsupported video type: ${asset.mimeType}`)) } + } else { + if (typeof asset.duration !== 'number') { + throw Error('Asset is not a video') + } + if (asset.duration > VIDEO_MAX_DURATION_MS) { + throw Error(_(msg`Videos must be less than 3 minutes long`)) + } + } + onSelectVideo(asset) + } catch (err) { + if (err instanceof Error) { + setError(err.message) + } else { + setError(_(msg`An error occurred while selecting the video`)) } } } - }, [ - requestVideoAccessIfNeeded, - currentAccount, - control, - setError, - _, - onSelectVideo, - ]) + }, [requestVideoAccessIfNeeded, setError, _, onSelectVideo]) return ( <> @@ -111,30 +83,6 @@ export function SelectVideoBtn({onSelectVideo, disabled, setError}: Props) { style={disabled && t.atoms.text_contrast_low} /> </Button> - <VerifyEmailPrompt control={control} /> - </> - ) -} - -function VerifyEmailPrompt({control}: {control: Prompt.PromptControlProps}) { - const {_} = useLingui() - const verifyEmailDialogControl = useDialogControl() - - return ( - <> - <Prompt.Basic - control={control} - title={_(msg`Verified email required`)} - description={_( - msg`To upload videos to Bluesky, you must first verify your email.`, - )} - confirmButtonCta={_(msg`Verify now`)} - confirmButtonColor="primary" - onConfirm={() => { - verifyEmailDialogControl.open() - }} - /> - <VerifyEmailDialog control={verifyEmailDialogControl} /> </> ) } diff --git a/src/view/com/feeds/FeedPage.tsx b/src/view/com/feeds/FeedPage.tsx index f643adaf9..604533b0f 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<NavigationProp<AllNavigatorParams>>() 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 83dbdb553..d974ce6b5 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 dfd641f66..10c3e6b4d 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 600cee428..c6cf254f3 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 facd31e5f..123a8b0c2 100644 --- a/src/view/com/posts/PostFeedItem.tsx +++ b/src/view/com/posts/PostFeedItem.tsx @@ -19,6 +19,7 @@ import {useQueryClient} from '@tanstack/react-query' 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' @@ -33,7 +34,6 @@ import { 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' @@ -159,7 +159,7 @@ let FeedItemInner = ({ onShowLess?: (interaction: AppBskyFeedDefs.Interaction) => void }): 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 d97654a63..a9cae8886 100644 --- a/src/view/com/util/post-ctrls/PostCtrls.tsx +++ b/src/view/com/util/post-ctrls/PostCtrls.tsx @@ -22,6 +22,7 @@ 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' @@ -33,7 +34,6 @@ import { usePostRepostMutationQueue, } from '#/state/queries/post' import {useRequireAuth, useSession} from '#/state/session' -import {useComposerControls} from '#/state/shell/composer' import { ProgressGuideAction, useProgressGuideControls, @@ -76,7 +76,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 520400fd1..ca0280116 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 300153966..bcda97dc5 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<NavigationProp>() 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: [ + <Trans key="lists"> + Before creating a list, you must first verify your email. + </Trans>, + ], + }) return ( <Layout.Screen testID="listsScreen"> @@ -68,7 +71,7 @@ export function ListsScreen({}: Props) { color="secondary" variant="solid" size="small" - onPress={onPressNewList}> + onPress={wrappedOnPressNewList}> <ButtonIcon icon={PlusIcon} /> <ButtonText> <Trans context="action">New</Trans> @@ -76,12 +79,6 @@ export function ListsScreen({}: Props) { </Button> </Layout.Header.Outer> <MyLists filter="curate" style={a.flex_grow} /> - <VerifyEmailDialog - reasonText={_( - msg`Before creating a list, you must first verify your email.`, - )} - control={control} - /> </Layout.Screen> ) } diff --git a/src/view/screens/ModerationModlists.tsx b/src/view/screens/ModerationModlists.tsx index 0b5090e3d..23ed492f6 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<NavigationProp>() 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: [ + <Trans key="modlist"> + Before creating a list, you must first verify your email. + </Trans>, + ], + }) return ( <Layout.Screen testID="moderationModlistsScreen"> @@ -68,7 +71,7 @@ export function ModerationModlistsScreen({}: Props) { color="secondary" variant="solid" size="small" - onPress={onPressNewList}> + onPress={wrappedOnPressNewList}> <ButtonIcon icon={PlusIcon} /> <ButtonText> <Trans context="action">New</Trans> @@ -76,12 +79,6 @@ export function ModerationModlistsScreen({}: Props) { </Button> </Layout.Header.Outer> <MyLists filter="mod" style={a.flex_grow} /> - <VerifyEmailDialog - reasonText={_( - msg`Before creating a list, you must first verify your email.`, - )} - control={control} - /> </Layout.Screen> ) } diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx index 1880fb816..ace0de2ae 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 425d55656..cc339bb03 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 61f1eb745..78cf5d11e 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 0688fb5dc..7d34a3d14 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) diff --git a/src/view/shell/index.tsx b/src/view/shell/index.tsx index 1e34f6da5..cd328c457 100644 --- a/src/view/shell/index.tsx +++ b/src/view/shell/index.tsx @@ -25,6 +25,7 @@ import {ModalsContainer} from '#/view/com/modals/Modal' import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' import {atoms as a, select, useTheme} from '#/alf' import {setSystemUITheme} from '#/alf/util/systemUI' +import {EmailDialog} from '#/components/dialogs/EmailDialog' import {InAppBrowserConsentDialog} from '#/components/dialogs/InAppBrowserConsent' import {MutedWordsDialog} from '#/components/dialogs/MutedWords' import {SigninDialog} from '#/components/dialogs/Signin' @@ -152,6 +153,7 @@ function ShellInner() { <ModalsContainer /> <MutedWordsDialog /> <SigninDialog /> + <EmailDialog /> <InAppBrowserConsentDialog /> <Lightbox /> <PortalOutlet /> diff --git a/src/view/shell/index.web.tsx b/src/view/shell/index.web.tsx index 898ff8fa8..a7ff76d61 100644 --- a/src/view/shell/index.web.tsx +++ b/src/view/shell/index.web.tsx @@ -17,6 +17,7 @@ import {Lightbox} from '#/view/com/lightbox/Lightbox' import {ModalsContainer} from '#/view/com/modals/Modal' import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' import {atoms as a, select, useTheme} from '#/alf' +import {EmailDialog} from '#/components/dialogs/EmailDialog' import {MutedWordsDialog} from '#/components/dialogs/MutedWords' import {SigninDialog} from '#/components/dialogs/Signin' import {Outlet as PortalOutlet} from '#/components/Portal' @@ -67,6 +68,7 @@ function ShellInner() { <ModalsContainer /> <MutedWordsDialog /> <SigninDialog /> + <EmailDialog /> <Lightbox /> <PortalOutlet /> |