feat: 优化 VideoBox 组件,支持本地视频播放

This commit is contained in:
康猛 2026-01-07 14:45:49 +08:00
parent 50b401b342
commit 159ebbae45
4 changed files with 30 additions and 22 deletions

View File

@ -80,7 +80,8 @@ const VideoBox = ({ url, needWeb = true, width = 256, style, ...videoProps }: Pr
const isLocal = !url.startsWith('http://') && !url.startsWith('https://') const isLocal = !url.startsWith('http://') && !url.startsWith('https://')
const isImageFile = isImg(url) const isImageFile = isImg(url)
if (isLocal && !isImageFile) { if (isLocal) {
if (!isImageFile) {
return ( return (
<Video <Video
key={url ?? 'no-url'} key={url ?? 'no-url'}
@ -98,6 +99,15 @@ const VideoBox = ({ url, needWeb = true, width = 256, style, ...videoProps }: Pr
/> />
) )
} }
return (
<Image
cachePolicy="memory-disk"
source={{ uri: url }}
style={style as any}
transition={{ duration: 200, effect: 'cross-dissolve' }}
/>
)
}
return ( return (
// 移除 key 避免组件重建导致闪烁,使用 transition 实现平滑切换 // 移除 key 避免组件重建导致闪烁,使用 transition 实现平滑切换

View File

@ -1,7 +1,6 @@
import * as Sentry from '@sentry/react-native' import * as Sentry from '@sentry/react-native'
import { type Directory, Paths } from 'expo-file-system' import { type Directory, Paths } from 'expo-file-system'
import * as FileSystem from 'expo-file-system/legacy' import * as FileSystem from 'expo-file-system/legacy'
import { Image } from 'expo-image'
import * as ImagePicker from 'expo-image-picker' import * as ImagePicker from 'expo-image-picker'
import * as MediaLibrary from 'expo-media-library' import * as MediaLibrary from 'expo-media-library'
import { observer } from 'mobx-react-lite' import { observer } from 'mobx-react-lite'
@ -214,11 +213,7 @@ export default observer(function TabTwoScreen() {
return ( return (
<Block style={{ width: BLE_UUIDS.SCREEN_SIZE, height: BLE_UUIDS.SCREEN_SIZE }} onClick={downloadFile}> <Block style={{ width: BLE_UUIDS.SCREEN_SIZE, height: BLE_UUIDS.SCREEN_SIZE }} onClick={downloadFile}>
<Image <VideoBox url={imageUri} style={{ width: BLE_UUIDS.SCREEN_SIZE, height: BLE_UUIDS.SCREEN_SIZE }} />
contentFit="cover"
source={{ uri: imageUri }}
style={{ width: BLE_UUIDS.SCREEN_SIZE, height: BLE_UUIDS.SCREEN_SIZE }}
/>
</Block> </Block>
) )
} }

View File

@ -302,6 +302,8 @@ export const useBleExplorer = () => {
const onBindStatus = (status: BindingResponse) => { const onBindStatus = (status: BindingResponse) => {
const activated = status.success === 1 const activated = status.success === 1
const contents = status.contents || [] const contents = status.contents || []
console.log('onBindStatus-----', status)
setState((prev) => ({ ...prev, isActivated: activated, contents })) setState((prev) => ({ ...prev, isActivated: activated, contents }))
} }
@ -607,7 +609,7 @@ export const useBleExplorer = () => {
const timeoutId = setTimeout(() => { const timeoutId = setTimeout(() => {
pendingPrepareTransfersRef.current.delete(key) pendingPrepareTransfersRef.current.delete(key)
reject(new Error('Prepare transfer timeout')) reject(new Error('Prepare transfer timeout'))
}, 10000) // 10秒超时 }, 10e3) // 10秒超时
// 先注册 Promise再发送请求 // 先注册 Promise再发送请求
pendingPrepareTransfersRef.current.set(key, { resolve, reject, timeoutId }) pendingPrepareTransfersRef.current.set(key, { resolve, reject, timeoutId })
@ -944,6 +946,8 @@ export const useBleExplorer = () => {
return Promise.reject('Invalid uriOrUrl key') return Promise.reject('Invalid uriOrUrl key')
} }
const prepareResp = await prepareTransfer(key, fileSizeByte) const prepareResp = await prepareTransfer(key, fileSizeByte)
console.log('prepareResp-----', prepareResp)
if (prepareResp.status !== 'ready') { if (prepareResp.status !== 'ready') {
setState((prev) => ({ ...prev, loading: { ...prev.loading, converting: false } })) setState((prev) => ({ ...prev, loading: { ...prev.loading, converting: false } }))
console.log('prepareResp not ready-----', prepareResp) console.log('prepareResp not ready-----', prepareResp)
@ -967,8 +971,8 @@ export const useBleExplorer = () => {
return Promise.resolve() return Promise.resolve()
} catch (error: any) { } catch (error: any) {
setState((prev) => ({ ...prev, loading: { ...prev.loading, transferring: false } })) setState((prev) => ({ ...prev, loading: { ...prev.loading, transferring: false } }))
console.log(`Transfer failed: ${error.message}`) console.log(`Transfer failed: ${error?.message}`)
return Promise.reject(error.message) return Promise.reject(error?.message)
} }
}, },
[state.connectedDevice, fileTransferService, convertImgToANIAsBuffer, setError], [state.connectedDevice, fileTransferService, convertImgToANIAsBuffer, setError],

View File

@ -71,7 +71,6 @@
"react-native-svg": "^15.15.1", "react-native-svg": "^15.15.1",
"react-native-video": "^6.18.0", "react-native-video": "^6.18.0",
"react-native-web": "~0.21.0", "react-native-web": "~0.21.0",
"react-native-webview": "^13.16.0",
"react-native-worklets": "0.5.1", "react-native-worklets": "0.5.1",
"tailwind-variants": "^0.2.1", "tailwind-variants": "^0.2.1",
"twrnc": "^4.11.1", "twrnc": "^4.11.1",