Merge branch 'main' into hailey/79fab

This commit is contained in:
Hailey
2025-05-08 10:59:12 -07:00
66 changed files with 3034 additions and 1044 deletions
-21
View File
@@ -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'}
+30 -82
View File
@@ -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} />
</>
)
}