feat: integrate UI components into Home tab
Replace custom loading/error states with reusable components (RefreshControl, LoadingState, ErrorState). Add pull-to-refresh functionality and remove duplicate styles. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
9703bb8fce
commit
141ccabe06
|
|
@ -19,7 +19,9 @@ import {
|
||||||
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
|
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
import { DownArrowIcon, PointsIcon, SearchIcon } from '@/components/icon';
|
import { DownArrowIcon, PointsIcon, SearchIcon } from '@/components/icon';
|
||||||
import { HomeSkeleton } from '@/components/skeleton/HomeSkeleton';
|
import ErrorState from '@/components/ErrorState';
|
||||||
|
import LoadingState from '@/components/LoadingState';
|
||||||
|
import RefreshControl from '@/components/RefreshControl';
|
||||||
import { useActivates } from '@/hooks/use-activates';
|
import { useActivates } from '@/hooks/use-activates';
|
||||||
import { useCategories } from '@/hooks/use-categories';
|
import { useCategories } from '@/hooks/use-categories';
|
||||||
import { CategoryTemplate } from '@repo/sdk';
|
import { CategoryTemplate } from '@repo/sdk';
|
||||||
|
|
@ -82,6 +84,7 @@ export default function HomeScreen() {
|
||||||
const params = useLocalSearchParams()
|
const params = useLocalSearchParams()
|
||||||
const [activeTab, setActiveTab] = useState(0)
|
const [activeTab, setActiveTab] = useState(0)
|
||||||
const [selectedCategoryId, setSelectedCategoryId] = useState<string | null>(null)
|
const [selectedCategoryId, setSelectedCategoryId] = useState<string | null>(null)
|
||||||
|
const [refreshing, setRefreshing] = useState(false)
|
||||||
|
|
||||||
const { load: loadCategories, data: categoriesData, loading: categoriesLoading, error: categoriesError } = useCategories()
|
const { load: loadCategories, data: categoriesData, loading: categoriesLoading, error: categoriesError } = useCategories()
|
||||||
const { load, data: activatesData, error } = useActivates()
|
const { load, data: activatesData, error } = useActivates()
|
||||||
|
|
@ -91,6 +94,12 @@ export default function HomeScreen() {
|
||||||
loadCategories()
|
loadCategories()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
const handleRefresh = async () => {
|
||||||
|
setRefreshing(true)
|
||||||
|
await Promise.all([load(), loadCategories()])
|
||||||
|
setRefreshing(false)
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 当分类数据加载完成后,默认选中第一个分类
|
// 当分类数据加载完成后,默认选中第一个分类
|
||||||
if (categoriesData?.categories && categoriesData.categories.length > 0 && !selectedCategoryId) {
|
if (categoriesData?.categories && categoriesData.categories.length > 0 && !selectedCategoryId) {
|
||||||
|
|
@ -280,6 +289,9 @@ export default function HomeScreen() {
|
||||||
style={styles.scrollView}
|
style={styles.scrollView}
|
||||||
contentContainerStyle={styles.scrollContent}
|
contentContainerStyle={styles.scrollContent}
|
||||||
showsVerticalScrollIndicator={false}
|
showsVerticalScrollIndicator={false}
|
||||||
|
refreshControl={
|
||||||
|
<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />
|
||||||
|
}
|
||||||
onScroll={(event) => {
|
onScroll={(event) => {
|
||||||
const scrollY = event.nativeEvent.contentOffset.y
|
const scrollY = event.nativeEvent.contentOffset.y
|
||||||
if (scrollY >= tabsPositionRef.current) {
|
if (scrollY >= tabsPositionRef.current) {
|
||||||
|
|
@ -330,26 +342,21 @@ export default function HomeScreen() {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 加载状态 */}
|
{/* 加载状态 */}
|
||||||
{showLoading && <HomeSkeleton />}
|
{showLoading && <LoadingState />}
|
||||||
|
|
||||||
|
{/* 错误状态 - 分类加载失败 */}
|
||||||
|
{categoriesError && (
|
||||||
|
<ErrorState message="加载分类失败" onRetry={() => loadCategories()} />
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 空状态 - 分类数据为空 */}
|
{/* 空状态 - 分类数据为空 */}
|
||||||
{showEmptyState && (
|
{showEmptyState && !categoriesError && (
|
||||||
<View style={styles.emptyState}>
|
<ErrorState message="暂无分类数据" onRetry={() => loadCategories()} />
|
||||||
<Text style={styles.emptyStateText}>暂无分类数据</Text>
|
|
||||||
<Pressable style={styles.retryButton} onPress={() => loadCategories()}>
|
|
||||||
<Text style={styles.retryButtonText}>重新加载</Text>
|
|
||||||
</Pressable>
|
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 空状态 - 当前分类下没有模板 */}
|
{/* 空状态 - 当前分类下没有模板 */}
|
||||||
{showEmptyTemplates && (
|
{showEmptyTemplates && (
|
||||||
<View style={styles.emptyState}>
|
<ErrorState message="该分类暂无模板" onRetry={() => loadCategories()} />
|
||||||
<Text style={styles.emptyStateText}>该分类暂无模板</Text>
|
|
||||||
<Pressable style={styles.retryButton} onPress={() => loadCategories()}>
|
|
||||||
<Text style={styles.retryButtonText}>刷新</Text>
|
|
||||||
</Pressable>
|
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 内容网格 - 使用 FlashList 优化性能 */}
|
{/* 内容网格 - 使用 FlashList 优化性能 */}
|
||||||
|
|
@ -659,26 +666,4 @@ const styles = StyleSheet.create({
|
||||||
color: '#F5F5F5',
|
color: '#F5F5F5',
|
||||||
lineHeight: 20,
|
lineHeight: 20,
|
||||||
},
|
},
|
||||||
emptyState: {
|
|
||||||
flex: 1,
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
paddingVertical: 60,
|
|
||||||
gap: 16,
|
|
||||||
},
|
|
||||||
emptyStateText: {
|
|
||||||
color: '#ABABAB',
|
|
||||||
fontSize: 14,
|
|
||||||
},
|
|
||||||
retryButton: {
|
|
||||||
backgroundColor: '#FF6699',
|
|
||||||
paddingHorizontal: 24,
|
|
||||||
paddingVertical: 12,
|
|
||||||
borderRadius: 8,
|
|
||||||
},
|
|
||||||
retryButtonText: {
|
|
||||||
color: '#FFFFFF',
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: '600',
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue