fix: 修复 VideoBox 组件中 imageRef 的类型定义,避免潜在的空引用错误;优化 Sync 组件中的 itemWidth 计算,提升性能
This commit is contained in:
parent
f4ef24b28f
commit
79a8dae49f
|
|
@ -16,7 +16,7 @@ type Props = {
|
|||
// 默认宽度256半屏宽度
|
||||
const VideoBox = ({ url, needWeb = true, width = 256, style, autoplay = true, ...videoProps }: Props) => {
|
||||
const [urlFinal, setUrlFinal] = useState('')
|
||||
const imageRef = useRef<ImageRef>(null)
|
||||
const imageRef = useRef<ImageRef | null>(null)
|
||||
|
||||
const createUrl = (url: string) => {
|
||||
return `https://modal-dev.bowong.cc/api/custom/video/converter/v2?media_url=${encodeURI(url)}&options=compression_level=3,quality=70,loop=true,resolution=${width}x${width},fps=24`
|
||||
|
|
|
|||
|
|
@ -38,3 +38,7 @@ Use `bun` as the package manager (`package.json` `packageManager`).
|
|||
## Configuration & Environment
|
||||
- Environment files live in `.env.*` and are loaded with `dotenv` in scripts.
|
||||
- Build profiles and OTA channels live in `eas.json`; do not commit secrets.
|
||||
|
||||
|
||||
## rule
|
||||
Always use Context7 MCP when I need library/API documentation, code generation, setup or configuration steps without me having to explicitly ask.
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { Ionicons } from '@expo/vector-icons'
|
||||
import { useIsFocused } from '@react-navigation/native'
|
||||
import { Block, ConfirmModal, Img, ListEmpty, Text, Toast, VideoBox } from '@share/components'
|
||||
import { Block, ConfirmModal, Img, ListEmpty, Text, Toast } from '@share/components'
|
||||
import { FlashList } from '@shopify/flash-list'
|
||||
import { Image } from 'expo-image'
|
||||
import { LinearGradient } from 'expo-linear-gradient'
|
||||
|
|
@ -385,10 +385,10 @@ const Index = observer(function Index() {
|
|||
|
||||
if (first !== null && last !== null) {
|
||||
// 每列3个,所以前后各加3行(9个item)
|
||||
for (let i = Math.max(0, first - 9); i < first; i++) {
|
||||
for (let i = Math.max(0, first - 6); i < first; i++) {
|
||||
if (allItems[i]) currentVisible.add(allItems[i].id)
|
||||
}
|
||||
for (let i = last + 1; i < Math.min(allItems.length, last + 10); i++) {
|
||||
for (let i = last + 1; i < Math.min(allItems.length, last - 6); i++) {
|
||||
currentVisible.add(allItems[i].id)
|
||||
}
|
||||
}
|
||||
|
|
@ -447,11 +447,12 @@ const Index = observer(function Index() {
|
|||
}
|
||||
numColumns={3}
|
||||
onEndReached={handleLoadMore}
|
||||
// 设置合理的回收池大小,避免内存无限增长
|
||||
maxItemsInRecyclePool={12}
|
||||
// 设置合理的回收池大小,复用组件实例以提高性能
|
||||
maxItemsInRecyclePool={0}
|
||||
// 自动移除不可见的原生视图,停止渲染和解码
|
||||
removeClippedSubviews={true}
|
||||
// 减小预渲染距离,降低内存占用
|
||||
drawDistance={150}
|
||||
drawDistance={300}
|
||||
onEndReachedThreshold={0.3}
|
||||
refreshControl={<RefreshControl colors={['#FFE500']} refreshing={refreshing} onRefresh={handleRefresh} />}
|
||||
renderItem={renderItem}
|
||||
|
|
@ -461,7 +462,7 @@ const Index = observer(function Index() {
|
|||
viewabilityConfig={{
|
||||
itemVisiblePercentThreshold: 10,
|
||||
minimumViewTime: 50,
|
||||
waitForInteraction: false,
|
||||
waitForInteraction: true,
|
||||
}}
|
||||
showsVerticalScrollIndicator={false}
|
||||
data={allItems}
|
||||
|
|
@ -583,26 +584,16 @@ const HeroCircle = observer<HeroCircleProps>(function HeroCircle({ selectedItem,
|
|||
const { balance } = userBalanceStore
|
||||
|
||||
const Width = 216
|
||||
const previewUrl = selectedItem?.webpHighPreviewUrl || selectedItem?.url || ''
|
||||
|
||||
const existItem = !!selectedItem?.url
|
||||
const previewUrl = selectedItem?.webpHighPreviewUrl || selectedItem?.webpPreviewUrl || ''
|
||||
|
||||
return (
|
||||
<Block className="relative z-10 items-center">
|
||||
<Block className="relative mx-[12px] flex-row justify-between">
|
||||
<Block className="flex-1">
|
||||
<Block className="relative z-10 mt-[20px] flex size-[224px] rounded-full border-4 border-black shadow-deep-black">
|
||||
{existItem && (
|
||||
<Block className="relative size-full overflow-hidden rounded-full">
|
||||
<VideoBox style={{ height: Width, width: Width, borderRadius: Width }} width={256} url={previewUrl} />
|
||||
</Block>
|
||||
)}
|
||||
|
||||
{!existItem && (
|
||||
<Block className="relative size-full overflow-hidden rounded-full bg-transparent">
|
||||
<Text className="text-[14px] font-bold text-white/60">加载中...</Text>
|
||||
</Block>
|
||||
)}
|
||||
<Block className="relative size-full overflow-hidden rounded-full">
|
||||
<Img style={{ height: Width, width: Width, borderRadius: Width }} width={256} src={previewUrl} />
|
||||
</Block>
|
||||
|
||||
<Block className="pointer-events-none absolute inset-0 rounded-full border-2 border-black/10" />
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ import { screenWidth, uploadFile } from '@/utils'
|
|||
import { cn } from '@/utils/cn'
|
||||
import { extractCdnKey } from '@/utils/getCDNKey'
|
||||
|
||||
const ITEM_WIDTH = Math.floor((screenWidth - 12 * 2 - 12 * 2) / 3)
|
||||
|
||||
// ============ 主组件 ============
|
||||
const Sync = observer(() => {
|
||||
// 从MobX Store获取用户信息
|
||||
|
|
@ -53,8 +55,6 @@ const Sync = observer(() => {
|
|||
|
||||
const { connectedDevice, isScanning, transferProgress } = bleStore.state
|
||||
|
||||
const itemWidth = Math.floor((screenWidth - 12 * 2 - 12 * 2) / 3)
|
||||
|
||||
useFocusEffect(() => {
|
||||
if (!isAuthenticated) {
|
||||
router.replace('/')
|
||||
|
|
@ -382,16 +382,15 @@ const Sync = observer(() => {
|
|||
<GridItem
|
||||
isSelected={isSelected}
|
||||
isSelectionMode={isSelectionMode}
|
||||
itemWidth={itemWidth}
|
||||
itemWidth={ITEM_WIDTH}
|
||||
post={post}
|
||||
// 页面失焦时不渲染,减少内存占用
|
||||
isVisible={isFocused}
|
||||
onSelect={handleItemSelect}
|
||||
key={post?.id}
|
||||
/>
|
||||
)
|
||||
},
|
||||
[isSelectionMode, selectedIds, selectedItem, itemWidth, handleItemSelect, isFocused],
|
||||
[isSelectionMode, selectedIds, selectedItem, handleItemSelect, isFocused],
|
||||
)
|
||||
|
||||
return (
|
||||
|
|
@ -403,8 +402,8 @@ const Sync = observer(() => {
|
|||
contentContainerStyle={{ paddingHorizontal: 12, paddingBottom: 200 }}
|
||||
data={posts}
|
||||
drawDistance={300}
|
||||
maxItemsInRecyclePool={0}
|
||||
removeClippedSubviews={true}
|
||||
getItemType={() => 'row'}
|
||||
ItemSeparatorComponent={() => <Block style={{ height: 6 }} />}
|
||||
keyExtractor={(item: any) => item?.id || `fallback-${Math.random()}`}
|
||||
ListFooterComponent={ListFooter}
|
||||
|
|
|
|||
Loading…
Reference in New Issue