diff --git a/app.config.js b/app.config.js
index 1467f762fd..4a44912289 100644
--- a/app.config.js
+++ b/app.config.js
@@ -211,8 +211,6 @@ module.exports = function (config) {
sounds: PLATFORM === 'ios' ? ['assets/dm.aiff'] : ['assets/dm.mp3'],
},
],
- 'expo-video',
- 'react-native-compressor',
'./plugins/starterPackAppClipExtension/withStarterPackAppClip.js',
'./plugins/withAndroidManifestPlugin.js',
'./plugins/withAndroidManifestFCMIconPlugin.js',
diff --git a/assets/icons/videoClip_stroke2_corner0_rounded.svg b/assets/icons/videoClip_stroke2_corner0_rounded.svg
deleted file mode 100644
index fd4c08d478..0000000000
--- a/assets/icons/videoClip_stroke2_corner0_rounded.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/package.json b/package.json
index 0ea23a2749..5f9f03457a 100644
--- a/package.json
+++ b/package.json
@@ -136,7 +136,6 @@
"expo-system-ui": "~3.0.4",
"expo-task-manager": "~11.8.1",
"expo-updates": "~0.25.14",
- "expo-video": "^1.1.10",
"expo-web-browser": "~13.0.3",
"fast-text-encoding": "^1.0.6",
"history": "^5.3.0",
@@ -167,7 +166,6 @@
"react-dom": "^18.2.0",
"react-keyed-flatten-children": "^3.0.0",
"react-native": "0.74.1",
- "react-native-compressor": "^1.8.24",
"react-native-date-picker": "^4.4.2",
"react-native-drawer-layout": "^4.0.0-alpha.3",
"react-native-fs": "^2.20.0",
diff --git a/src/components/icons/VideoClip.tsx b/src/components/icons/VideoClip.tsx
deleted file mode 100644
index c2c13c4913..0000000000
--- a/src/components/icons/VideoClip.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import {createSinglePathSVG} from './TEMPLATE'
-
-export const VideoClip_Stroke2_Corner0_Rounded = createSinglePathSVG({
- path: 'M3 4a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4Zm2 1v2h2V5H5Zm4 0v6h6V5H9Zm8 0v2h2V5h-2Zm2 4h-2v2h2V9Zm0 4h-2v2.444h2V13Zm0 4.444h-2V19h2v-1.556ZM15 19v-6H9v6h6Zm-8 0v-2H5v2h2Zm-2-4h2v-2H5v2Zm0-4h2V9H5v2Z',
-})
diff --git a/src/lib/hooks/usePermissions.ts b/src/lib/hooks/usePermissions.ts
index d248e19759..9f1f8fb6f7 100644
--- a/src/lib/hooks/usePermissions.ts
+++ b/src/lib/hooks/usePermissions.ts
@@ -48,35 +48,6 @@ export function usePhotoLibraryPermission() {
return {requestPhotoAccessIfNeeded}
}
-export function useVideoLibraryPermission() {
- const [res, requestPermission] = MediaLibrary.usePermissions({
- granularPermissions: ['video'],
- })
- const requestVideoAccessIfNeeded = async () => {
- // On the, we use to produce a filepicker
- // This does not need any permission granting.
- if (isWeb) {
- return true
- }
-
- if (res?.granted) {
- return true
- } else if (!res || res.status === 'undetermined' || res?.canAskAgain) {
- const {canAskAgain, granted, status} = await requestPermission()
-
- if (!canAskAgain && status === 'undetermined') {
- openPermissionAlert('video library')
- }
-
- return granted
- } else {
- openPermissionAlert('video library')
- return false
- }
- }
- return {requestVideoAccessIfNeeded}
-}
-
export function useCameraPermission() {
const [res, requestPermission] = Camera.useCameraPermissions()
diff --git a/src/lib/hooks/usePermissions.web.ts b/src/lib/hooks/usePermissions.web.ts
index b65bbc4141..c550a7d6df 100644
--- a/src/lib/hooks/usePermissions.web.ts
+++ b/src/lib/hooks/usePermissions.web.ts
@@ -14,11 +14,3 @@ export function useCameraPermission() {
return {requestCameraAccessIfNeeded}
}
-
-export function useVideoLibraryPermission() {
- const requestVideoAccessIfNeeded = async () => {
- return true
- }
-
- return {requestVideoAccessIfNeeded}
-}
diff --git a/src/lib/media/video/compress.ts b/src/lib/media/video/compress.ts
deleted file mode 100644
index 60e5e94a00..0000000000
--- a/src/lib/media/video/compress.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import {getVideoMetaData, Video} from 'react-native-compressor'
-
-export type CompressedVideo = {
- uri: string
- size: number
-}
-
-export async function compressVideo(
- file: string,
- opts?: {
- getCancellationId?: (id: string) => void
- onProgress?: (progress: number) => void
- },
-): Promise {
- const {onProgress, getCancellationId} = opts || {}
-
- const compressed = await Video.compress(
- file,
- {
- getCancellationId,
- compressionMethod: 'manual',
- bitrate: 3_000_000, // 3mbps
- maxSize: 1920,
- },
- onProgress,
- )
-
- const info = await getVideoMetaData(compressed)
- return {uri: compressed, size: info.size}
-}
diff --git a/src/lib/media/video/compress.web.ts b/src/lib/media/video/compress.web.ts
deleted file mode 100644
index 968f2b157a..0000000000
--- a/src/lib/media/video/compress.web.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import {VideoTooLargeError} from 'lib/media/video/errors'
-
-const MAX_VIDEO_SIZE = 1024 * 1024 * 100 // 100MB
-
-export type CompressedVideo = {
- uri: string
- size: number
-}
-
-// doesn't actually compress, but throws if >100MB
-export async function compressVideo(
- file: string,
- _callbacks?: {
- onProgress: (progress: number) => void
- },
-): Promise {
- const blob = await fetch(file).then(res => res.blob())
- const video = URL.createObjectURL(blob)
-
- if (blob.size > MAX_VIDEO_SIZE) {
- throw new VideoTooLargeError()
- }
-
- return {
- size: blob.size,
- uri: video,
- }
-}
diff --git a/src/lib/media/video/errors.ts b/src/lib/media/video/errors.ts
deleted file mode 100644
index 701a7e2355..0000000000
--- a/src/lib/media/video/errors.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export class VideoTooLargeError extends Error {
- constructor() {
- super('Videos cannot be larger than 100MB')
- this.name = 'VideoTooLargeError'
- }
-}
diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts
index 378b273494..6a4081185f 100644
--- a/src/lib/statsig/gates.ts
+++ b/src/lib/statsig/gates.ts
@@ -11,4 +11,3 @@ export type Gate =
| 'suggested_feeds_interstitial'
| 'suggested_follows_interstitial'
| 'ungroup_follow_backs'
- | 'videos'
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx
index c8a77385ea..9e2f77d4df 100644
--- a/src/view/com/composer/Composer.tsx
+++ b/src/view/com/composer/Composer.tsx
@@ -1,5 +1,4 @@
import React, {
- Suspense,
useCallback,
useEffect,
useImperativeHandle,
@@ -43,7 +42,7 @@ import {
} from '#/lib/gif-alt-text'
import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED'
import {LikelyType} from '#/lib/link-meta/link-meta'
-import {logEvent, useGate} from '#/lib/statsig/statsig'
+import {logEvent} from '#/lib/statsig/statsig'
import {logger} from '#/logger'
import {emitPostCreated} from '#/state/events'
import {useModalControls} from '#/state/modals'
@@ -97,10 +96,6 @@ import {SuggestedLanguage} from './select-language/SuggestedLanguage'
import {TextInput, TextInputRef} from './text-input/TextInput'
import {ThreadgateBtn} from './threadgate/ThreadgateBtn'
import {useExternalLinkFetch} from './useExternalLinkFetch'
-import {SelectVideoBtn} from './videos/SelectVideoBtn'
-import {useVideoState} from './videos/state'
-import {VideoPreview} from './videos/VideoPreview'
-import {VideoTranscodeProgress} from './videos/VideoTranscodeProgress'
import hairlineWidth = StyleSheet.hairlineWidth
type CancelRef = {
@@ -120,7 +115,6 @@ export const ComposePost = observer(function ComposePost({
}: Props & {
cancelRef?: React.RefObject
}) {
- const gate = useGate()
const {currentAccount} = useSession()
const agent = useAgent()
const {data: currentProfile} = useProfileQuery({did: currentAccount!.did})
@@ -162,14 +156,6 @@ export const ComposePost = observer(function ComposePost({
const [quote, setQuote] = useState(
initQuote,
)
- const {
- video,
- onSelectVideo,
- videoPending,
- videoProcessingData,
- clearVideo,
- videoProcessingProgress,
- } = useVideoState({setError})
const {extLink, setExtLink} = useExternalLinkFetch({setQuote})
const [extGif, setExtGif] = useState()
const [labels, setLabels] = useState([])
@@ -389,9 +375,8 @@ export const ComposePost = observer(function ComposePost({
? _(msg`Write your reply`)
: _(msg`What's up?`)
- const canSelectImages =
- gallery.size < 4 && !extLink && !video && !videoPending
- const hasMedia = gallery.size > 0 || Boolean(extLink) || Boolean(video)
+ const canSelectImages = gallery.size < 4 && !extLink
+ const hasMedia = gallery.size > 0 || Boolean(extLink)
const onEmojiButtonPress = useCallback(() => {
openPicker?.(textInput.current?.getCursorPosition())
@@ -615,20 +600,7 @@ export const ComposePost = observer(function ComposePost({
setQuote(undefined)} />
)}
- ) : null}
- {videoPending && videoProcessingData ? (
-
- ) : (
- video && (
- // remove suspense when we get rid of lazy
-
-
-
- )
- )}
+ ) : undefined}
@@ -647,12 +619,6 @@ export const ComposePost = observer(function ComposePost({
]}>
- {gate('videos') && (
-
- )}
{
const t = useTheme()
+ const {_} = useLingui()
const linkInfo = React.useMemo(
() =>
@@ -66,7 +70,25 @@ export const ExternalEmbed = ({
) : null}
-
+
+
+
)
}
diff --git a/src/view/com/composer/ExternalEmbedRemoveBtn.tsx b/src/view/com/composer/ExternalEmbedRemoveBtn.tsx
deleted file mode 100644
index 7742900a83..0000000000
--- a/src/view/com/composer/ExternalEmbedRemoveBtn.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import React from 'react'
-import {TouchableOpacity} from 'react-native'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {msg} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-
-import {s} from 'lib/styles'
-
-export function ExternalEmbedRemoveBtn({onRemove}: {onRemove: () => void}) {
- const {_} = useLingui()
-
- return (
-
-
-
- )
-}
diff --git a/src/view/com/composer/char-progress/CharProgress.tsx b/src/view/com/composer/char-progress/CharProgress.tsx
index a205fe0963..a3fa78a59a 100644
--- a/src/view/com/composer/char-progress/CharProgress.tsx
+++ b/src/view/com/composer/char-progress/CharProgress.tsx
@@ -1,14 +1,13 @@
import React from 'react'
import {View} from 'react-native'
+import {Text} from '../../util/text/Text'
// @ts-ignore no type definition -prf
import ProgressCircle from 'react-native-progress/Circle'
// @ts-ignore no type definition -prf
import ProgressPie from 'react-native-progress/Pie'
-
-import {MAX_GRAPHEME_LENGTH} from 'lib/constants'
-import {usePalette} from 'lib/hooks/usePalette'
import {s} from 'lib/styles'
-import {Text} from '../../util/text/Text'
+import {usePalette} from 'lib/hooks/usePalette'
+import {MAX_GRAPHEME_LENGTH} from 'lib/constants'
const DANGER_LENGTH = MAX_GRAPHEME_LENGTH
diff --git a/src/view/com/composer/videos/SelectVideoBtn.tsx b/src/view/com/composer/videos/SelectVideoBtn.tsx
deleted file mode 100644
index 9c528a92e2..0000000000
--- a/src/view/com/composer/videos/SelectVideoBtn.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-import React, {useCallback} from 'react'
-import {
- ImagePickerAsset,
- launchImageLibraryAsync,
- MediaTypeOptions,
- UIImagePickerPreferredAssetRepresentationMode,
-} from 'expo-image-picker'
-import {msg} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-
-import {useVideoLibraryPermission} from '#/lib/hooks/usePermissions'
-import {isNative} from '#/platform/detection'
-import {atoms as a, useTheme} from '#/alf'
-import {Button} from '#/components/Button'
-import {VideoClip_Stroke2_Corner0_Rounded as VideoClipIcon} from '#/components/icons/VideoClip'
-
-const VIDEO_MAX_DURATION = 90
-
-type Props = {
- onSelectVideo: (video: ImagePickerAsset) => void
- disabled?: boolean
-}
-
-export function SelectVideoBtn({onSelectVideo, disabled}: Props) {
- const {_} = useLingui()
- const t = useTheme()
- const {requestVideoAccessIfNeeded} = useVideoLibraryPermission()
-
- const onPressSelectVideo = useCallback(async () => {
- if (isNative && !(await requestVideoAccessIfNeeded())) {
- return
- }
-
- const response = await launchImageLibraryAsync({
- exif: false,
- mediaTypes: MediaTypeOptions.Videos,
- videoMaxDuration: VIDEO_MAX_DURATION,
- quality: 1,
- legacy: true,
- preferredAssetRepresentationMode:
- UIImagePickerPreferredAssetRepresentationMode.Current,
- })
- if (response.assets && response.assets.length > 0) {
- onSelectVideo(response.assets[0])
- }
- }, [onSelectVideo, requestVideoAccessIfNeeded])
-
- return (
- <>
-
- >
- )
-}
diff --git a/src/view/com/composer/videos/VideoPreview.tsx b/src/view/com/composer/videos/VideoPreview.tsx
deleted file mode 100644
index b04cdf1c8b..0000000000
--- a/src/view/com/composer/videos/VideoPreview.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-/* eslint-disable @typescript-eslint/no-shadow */
-import React from 'react'
-import {View} from 'react-native'
-import {useVideoPlayer, VideoView} from 'expo-video'
-
-import {CompressedVideo} from '#/lib/media/video/compress'
-import {ExternalEmbedRemoveBtn} from 'view/com/composer/ExternalEmbedRemoveBtn'
-import {atoms as a} from '#/alf'
-
-export function VideoPreview({
- video,
- clear,
-}: {
- video: CompressedVideo
- clear: () => void
-}) {
- const player = useVideoPlayer(video.uri, player => {
- player.loop = true
- player.play()
- })
-
- return (
-
-
-
-
- )
-}
diff --git a/src/view/com/composer/videos/VideoPreview.web.tsx b/src/view/com/composer/videos/VideoPreview.web.tsx
deleted file mode 100644
index 223dbd4244..0000000000
--- a/src/view/com/composer/videos/VideoPreview.web.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import React from 'react'
-import {View} from 'react-native'
-
-import {CompressedVideo} from '#/lib/media/video/compress'
-import {ExternalEmbedRemoveBtn} from 'view/com/composer/ExternalEmbedRemoveBtn'
-import {atoms as a} from '#/alf'
-
-export function VideoPreview({
- video,
- clear,
-}: {
- video: CompressedVideo
- clear: () => void
-}) {
- return (
-
-
-
-
- )
-}
diff --git a/src/view/com/composer/videos/VideoTranscodeBackdrop.tsx b/src/view/com/composer/videos/VideoTranscodeBackdrop.tsx
deleted file mode 100644
index 1f41736420..0000000000
--- a/src/view/com/composer/videos/VideoTranscodeBackdrop.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import React, {useEffect} from 'react'
-import {clearCache, createVideoThumbnail} from 'react-native-compressor'
-import Animated, {FadeIn} from 'react-native-reanimated'
-import {Image} from 'expo-image'
-import {useQuery} from '@tanstack/react-query'
-
-import {atoms as a} from '#/alf'
-
-export function VideoTranscodeBackdrop({uri}: {uri: string}) {
- const {data: thumbnail} = useQuery({
- queryKey: ['thumbnail', uri],
- queryFn: async () => {
- return await createVideoThumbnail(uri)
- },
- })
-
- useEffect(() => {
- return () => {
- clearCache()
- }
- }, [])
-
- return (
-
- {thumbnail && (
-
- )}
-
- )
-}
diff --git a/src/view/com/composer/videos/VideoTranscodeBackdrop.web.tsx b/src/view/com/composer/videos/VideoTranscodeBackdrop.web.tsx
deleted file mode 100644
index 9b580fdf2a..0000000000
--- a/src/view/com/composer/videos/VideoTranscodeBackdrop.web.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-import React from 'react'
-
-export function VideoTranscodeBackdrop({uri}: {uri: string}) {
- return (
-
- )
-}
diff --git a/src/view/com/composer/videos/VideoTranscodeProgress.tsx b/src/view/com/composer/videos/VideoTranscodeProgress.tsx
deleted file mode 100644
index 79407cd3ef..0000000000
--- a/src/view/com/composer/videos/VideoTranscodeProgress.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import React from 'react'
-import {View} from 'react-native'
-// @ts-expect-error no type definition
-import ProgressPie from 'react-native-progress/Pie'
-import {ImagePickerAsset} from 'expo-image-picker'
-
-import {atoms as a, useTheme} from '#/alf'
-import {Text} from '#/components/Typography'
-import {VideoTranscodeBackdrop} from './VideoTranscodeBackdrop'
-
-export function VideoTranscodeProgress({
- input,
- progress,
-}: {
- input: ImagePickerAsset
- progress: number
-}) {
- const t = useTheme()
-
- const aspectRatio = input.width / input.height
-
- return (
-
-
-
-
- Compressing...
-
-
- )
-}
diff --git a/src/view/com/composer/videos/state.ts b/src/view/com/composer/videos/state.ts
deleted file mode 100644
index 0d47dd0562..0000000000
--- a/src/view/com/composer/videos/state.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import {useState} from 'react'
-import {ImagePickerAsset} from 'expo-image-picker'
-import {msg} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-import {useMutation} from '@tanstack/react-query'
-
-import {compressVideo} from '#/lib/media/video/compress'
-import {logger} from '#/logger'
-import {VideoTooLargeError} from 'lib/media/video/errors'
-import * as Toast from 'view/com/util/Toast'
-
-export function useVideoState({setError}: {setError: (error: string) => void}) {
- const {_} = useLingui()
- const [progress, setProgress] = useState(0)
-
- const {mutate, data, isPending, isError, reset, variables} = useMutation({
- mutationFn: async (asset: ImagePickerAsset) => {
- const compressed = await compressVideo(asset.uri, {
- onProgress: num => setProgress(trunc2dp(num)),
- })
-
- return compressed
- },
- onError: (e: any) => {
- // Don't log these errors in sentry, just let the user know
- if (e instanceof VideoTooLargeError) {
- Toast.show(_(msg`Videos cannot be larger than 100MB`))
- return
- }
- logger.error('Failed to compress video', {safeError: e})
- setError(_(msg`Could not compress video`))
- },
- onMutate: () => {
- setProgress(0)
- },
- })
-
- return {
- video: data,
- onSelectVideo: mutate,
- videoPending: isPending,
- videoProcessingData: variables,
- videoError: isError,
- clearVideo: reset,
- videoProcessingProgress: progress,
- }
-}
-
-function trunc2dp(num: number) {
- return Math.trunc(num * 100) / 100
-}
diff --git a/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx b/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx
index cc742c8c01..7429fa267d 100644
--- a/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx
+++ b/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx
@@ -57,7 +57,7 @@ export const ExternalLinkEmbed = ({
}
return (
-
+
{imageUri && !embedPlayerParams ? (