🐛 修复视频播放器无限循环渲染问题

## 主要修复
- 修复 FullscreenMediaModal 和 FullscreenVideoModal 中的 useEffect 循环依赖
- 重构 VideoPlayer 组件的视频属性管理逻辑
- 优化 useVideoPlayer 的初始化回调机制

## 新增功能
- 新增标签 API 支持 (lib/api/tags.ts)
- 新增内容骨架屏组件 (components/profile/content-skeleton.tsx)
- 新增返回按钮组件 (components/ui/back-button.tsx)

## 改进优化
- 优化视频播放器的性能,避免重复初始化
- 修复 useEffect 依赖项导致的无限循环更新
- 完善类型定义和 API 接口

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
imeepos
2025-11-10 11:58:56 +08:00
parent af64049c69
commit eee280d9b3
26 changed files with 1425 additions and 885 deletions

View File

@@ -1,3 +1,7 @@
import { Feather } from '@expo/vector-icons';
import * as ImagePicker from 'expo-image-picker';
import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
import { UploadCloud } from 'lucide-react';
import React, { useEffect, useState } from 'react';
import {
ActivityIndicator,
@@ -11,12 +15,13 @@ import {
View,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { ArrowLeft, UploadCloud } from 'lucide-react';
import { Feather } from '@expo/vector-icons';
import * as ImagePicker from 'expo-image-picker';
export const unstable_settings = {
headerShown: false,
};
import { DynamicForm } from '@/components/forms/dynamic-form';
import { BackButton } from '@/components/ui/back-button';
import { getTemplateById } from '@/lib/api/templates';
import { Template } from '@/lib/types/template';
import { RunFormSchema, RunTemplateData } from '@/lib/types/template-run';
@@ -28,7 +33,7 @@ interface Step1FormData {
script: string;
}
interface Step2FormData extends RunTemplateData {}
interface Step2FormData extends RunTemplateData { }
interface FormData {
step1: Step1FormData;
@@ -250,13 +255,7 @@ export default function TemplateFormScreen() {
showsVerticalScrollIndicator={false}
>
<View style={styles.topBar}>
<TouchableOpacity
activeOpacity={0.85}
onPress={() => router.back()}
style={styles.backButton}
>
<ArrowLeft size={22} color="#FFFFFF" strokeWidth={2.4} />
</TouchableOpacity>
<BackButton onPress={() => router.back()} />
</View>
<Text style={styles.heroTitle}>
@@ -329,13 +328,7 @@ export default function TemplateFormScreen() {
const renderStep2 = () => (
<View style={styles.step2Wrapper}>
<View style={styles.topBar}>
<TouchableOpacity
activeOpacity={0.85}
onPress={handleStep2Prev}
style={styles.backButton}
>
<ArrowLeft size={22} color="#FFFFFF" strokeWidth={2.4} />
</TouchableOpacity>
<BackButton onPress={handleStep2Prev} />
<Text style={styles.stepIndicator}>STEP 2 OF 2</Text>
</View>
@@ -406,24 +399,32 @@ export default function TemplateFormScreen() {
if (loading) {
return (
<View style={styles.loadingCanvas}>
<SafeAreaView style={styles.safeArea} edges={['top', 'left', 'right']}>
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color="#D1FF00" />
<Text style={styles.loadingText}>Loading...</Text>
</View>
</SafeAreaView>
</View>
<>
<Stack.Screen options={{ headerShown: false }} />
<View style={styles.loadingCanvas}>
<SafeAreaView style={styles.safeArea} edges={['top', 'left', 'right']}>
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color="#D1FF00" />
<Text style={styles.loadingText}>Loading...</Text>
</View>
</SafeAreaView>
</View>
</>
);
}
return (
<View style={styles.canvas}>
<SafeAreaView style={styles.safeArea} edges={['top', 'left', 'right']}>
{currentStep === 1 ? renderStep1() : renderStep2()}
</SafeAreaView>
{renderBottomActions()}
</View>
<>
<Stack.Screen options={{ headerShown: false }} />
<View style={styles.canvas}>
<SafeAreaView style={styles.safeArea} edges={['top', 'left', 'right']}>
{currentStep === 1 ? renderStep1() : renderStep2()}
</SafeAreaView>
{renderBottomActions()}
</View>
</>
);
}
@@ -461,17 +462,10 @@ const styles = StyleSheet.create({
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: 28,
},
backButton: {
width: 48,
height: 48,
borderRadius: 18,
borderWidth: 1,
borderColor: 'rgba(255, 255, 255, 0.1)',
backgroundColor: 'rgba(18, 18, 18, 0.78)',
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 0,
paddingTop: 8,
paddingBottom: 8,
gap: 12,
},
stepIndicator: {
flex: 1,
@@ -512,9 +506,6 @@ const styles = StyleSheet.create({
width: 74,
height: 74,
borderRadius: 24,
borderWidth: 2,
borderColor: '#050505',
backgroundColor: '#050505',
overflow: 'hidden',
},
previewInsetImage: {