✨ feat: 完整应用重构 - 优化界面架构与用户体验
主要变更: - 重构应用界面:优化首页、登录、认证流程 - 新增用户档案模块:包含完整的档案管理组件 - 优化路由结构:重新组织页面布局和导航 - 改进API集成:新增活动、内容分类等API模块 - 删除冗余组件:清理不必要的文件和依赖 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
228
components/profile/content-gallery.tsx
Normal file
228
components/profile/content-gallery.tsx
Normal file
@@ -0,0 +1,228 @@
|
||||
import React from 'react';
|
||||
import { View, FlatList, ActivityIndicator, RefreshControl, StyleSheet, Image } from 'react-native';
|
||||
import { VideoPlayer } from '@/components/video/video-player';
|
||||
import { ThemedText } from '@/components/themed-text';
|
||||
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
|
||||
import { useColorScheme } from '@/hooks/use-color-scheme';
|
||||
import type { TemplateGeneration } from '@/lib/api/template-generations';
|
||||
|
||||
export function ContentGallery({
|
||||
generations,
|
||||
isRefreshing,
|
||||
onRefresh,
|
||||
isLoadingMore,
|
||||
hasMore,
|
||||
onLoadMore,
|
||||
}: {
|
||||
generations: TemplateGeneration[];
|
||||
isRefreshing: boolean;
|
||||
onRefresh: () => void;
|
||||
isLoadingMore: boolean;
|
||||
hasMore: boolean;
|
||||
onLoadMore: () => void;
|
||||
}) {
|
||||
const colorScheme = useColorScheme();
|
||||
const palette = colorScheme === 'dark' ? darkPalette : lightPalette;
|
||||
|
||||
const renderItem = ({ item }: { item: TemplateGeneration }) => (
|
||||
<ContentItem palette={palette} generation={item} />
|
||||
);
|
||||
|
||||
const renderFooter = () => {
|
||||
if (isLoadingMore) {
|
||||
return (
|
||||
<View style={[styles.loadingMoreContainer, { backgroundColor: palette.background }]}>
|
||||
<ActivityIndicator size="small" color={palette.accent} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
data={generations}
|
||||
renderItem={renderItem}
|
||||
ListFooterComponent={renderFooter}
|
||||
numColumns={2}
|
||||
keyExtractor={(item) => item.id}
|
||||
columnWrapperStyle={styles.columnWrapper}
|
||||
showsVerticalScrollIndicator={false}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={isRefreshing}
|
||||
onRefresh={onRefresh}
|
||||
tintColor={palette.accent}
|
||||
colors={[palette.accent]}
|
||||
/>
|
||||
}
|
||||
onEndReached={onLoadMore}
|
||||
onEndReachedThreshold={0.5}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function ContentItem({
|
||||
palette,
|
||||
generation,
|
||||
}: {
|
||||
palette: any;
|
||||
generation: TemplateGeneration;
|
||||
}) {
|
||||
const formatDate = (dateString: string) => {
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
});
|
||||
};
|
||||
|
||||
const renderMedia = () => {
|
||||
const mediaUrl = generation.resultUrl[0];
|
||||
if (generation.type === 'IMAGE') {
|
||||
return (
|
||||
<View style={styles.mediaContainer}>
|
||||
<Image
|
||||
source={{ uri: mediaUrl }}
|
||||
style={styles.mediaImage}
|
||||
resizeMode="cover"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
} else if (generation.type === 'VIDEO') {
|
||||
return (
|
||||
<View style={styles.mediaContainer}>
|
||||
<VideoPlayer
|
||||
source={{ uri: mediaUrl }}
|
||||
style={styles.mediaVideo}
|
||||
shouldPlay={false}
|
||||
isLooping={true}
|
||||
isMuted={true}
|
||||
useNativeControls={false}
|
||||
showPoster={true}
|
||||
maxHeight={300}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
styles.contentItem,
|
||||
{
|
||||
backgroundColor: palette.surface,
|
||||
borderColor: palette.border,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{renderMedia()}
|
||||
<View style={styles.contentInfo}>
|
||||
<ThemedText
|
||||
style={[styles.contentTitle, { color: palette.textPrimary }]}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{generation.template.title}
|
||||
</ThemedText>
|
||||
<View style={styles.contentMeta}>
|
||||
<View style={styles.metaItem}>
|
||||
<MaterialIcons name="schedule" size={14} color={palette.textSecondary} />
|
||||
<ThemedText style={[styles.metaText, { color: palette.textSecondary }]}>
|
||||
{formatDate(generation.createdAt)}
|
||||
</ThemedText>
|
||||
</View>
|
||||
{generation.status !== 'completed' && (
|
||||
<View style={styles.statusBadge}>
|
||||
<ThemedText style={[styles.statusText, { color: palette.textSecondary }]}>
|
||||
{generation.status}
|
||||
</ThemedText>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const darkPalette = {
|
||||
background: '#050505',
|
||||
surface: '#121216',
|
||||
border: '#1D1E24',
|
||||
textPrimary: '#F6F7FA',
|
||||
textSecondary: '#8E9098',
|
||||
accent: '#B7FF2F',
|
||||
};
|
||||
|
||||
const lightPalette = {
|
||||
background: '#F7F8FB',
|
||||
surface: '#FFFFFF',
|
||||
border: '#E2E5ED',
|
||||
textPrimary: '#0F1320',
|
||||
textSecondary: '#5E6474',
|
||||
accent: '#405CFF',
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
columnWrapper: {
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
contentItem: {
|
||||
flex: 1,
|
||||
borderRadius: 16,
|
||||
borderWidth: 1,
|
||||
marginBottom: 12,
|
||||
overflow: 'hidden',
|
||||
marginHorizontal: 6,
|
||||
},
|
||||
mediaContainer: {
|
||||
width: '100%',
|
||||
aspectRatio: 1,
|
||||
backgroundColor: '#000',
|
||||
},
|
||||
mediaImage: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
mediaVideo: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
contentInfo: {
|
||||
padding: 12,
|
||||
},
|
||||
contentTitle: {
|
||||
fontSize: 15,
|
||||
fontWeight: '600',
|
||||
marginBottom: 8,
|
||||
},
|
||||
contentMeta: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
metaItem: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginRight: 12,
|
||||
},
|
||||
metaText: {
|
||||
fontSize: 12,
|
||||
marginLeft: 4,
|
||||
},
|
||||
statusBadge: {
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 4,
|
||||
borderRadius: 4,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.1)',
|
||||
},
|
||||
statusText: {
|
||||
fontSize: 11,
|
||||
fontWeight: '600',
|
||||
textTransform: 'capitalize' as const,
|
||||
},
|
||||
loadingMoreContainer: {
|
||||
paddingVertical: 20,
|
||||
alignItems: 'center',
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user