fix: bug
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { router } from 'expo-router';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { ScrollView, StyleSheet, View } from 'react-native';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { ScrollView, StyleSheet, View, type NativeScrollEvent, type NativeSyntheticEvent } from 'react-native';
|
||||
|
||||
import {
|
||||
CategoryTabs,
|
||||
@@ -69,16 +69,18 @@ function translateActivity(activity: Activity): FeatureItem | null {
|
||||
export default function ExploreScreen() {
|
||||
const { isAuthenticated } = useAuth();
|
||||
const { requireAuth } = useAuthGuard();
|
||||
const [activeCategory, setActiveCategory] = useState<string>();
|
||||
const [activeCategory, setActiveCategory] = useState<string>('');
|
||||
const [activityFeatures, setActivityFeatures] = useState<FeatureItem[]>([]);
|
||||
const [categoryCollection, setCategoryCollection] = useState<CategoryWithChildren[]>([]);
|
||||
|
||||
const scrollViewRef = useRef<ScrollView>(null);
|
||||
const categoryPositionsRef = useRef<Map<string, number>>(new Map());
|
||||
const isScrollingProgrammaticallyRef = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
const hydrateFeatureCarousel = async () => {
|
||||
try {
|
||||
const activities = await getActivities({ isActive: true });
|
||||
console.log({ activities })
|
||||
|
||||
const curatedFeatures = activities
|
||||
.map(translateActivity)
|
||||
.filter((feature): feature is FeatureItem => feature !== null);
|
||||
@@ -92,9 +94,6 @@ export default function ExploreScreen() {
|
||||
};
|
||||
|
||||
hydrateFeatureCarousel();
|
||||
|
||||
return () => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -157,65 +156,115 @@ export default function ExploreScreen() {
|
||||
return options.length > 0 ? options : [];
|
||||
}, [categoryCollection]);
|
||||
|
||||
const allCategoriesWithTemplates = useMemo(() => {
|
||||
return categoryCollection.map((category) => {
|
||||
const templates = (category.templates ?? [])
|
||||
.map(translateTemplateToCommunity)
|
||||
.filter((item): item is CommunityItem => item !== null);
|
||||
|
||||
const activeCategoryRecord = useMemo(() => {
|
||||
return categoryCollection.find((category) => category.id === activeCategory) ?? null;
|
||||
}, [categoryCollection, activeCategory]);
|
||||
return {
|
||||
id: category.id,
|
||||
name: coalesceText(category.nameEn, category.name),
|
||||
templates,
|
||||
};
|
||||
});
|
||||
}, [categoryCollection]);
|
||||
|
||||
const templateCommunityItems = useMemo(() => {
|
||||
if (!activeCategoryRecord) {
|
||||
return [];
|
||||
const handleScroll = useCallback((event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
||||
if (isScrollingProgrammaticallyRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (activeCategoryRecord.templates ?? [])
|
||||
.map(translateTemplateToCommunity)
|
||||
.filter((item): item is CommunityItem => item !== null);
|
||||
}, [activeCategoryRecord]);
|
||||
const scrollY = event.nativeEvent.contentOffset.y;
|
||||
const positions = Array.from(categoryPositionsRef.current.entries());
|
||||
|
||||
const featureItems = useMemo(() => {
|
||||
return activityFeatures.map((item, index) => ({
|
||||
...item,
|
||||
id: `${activeCategory}-${item.id || index}`,
|
||||
}));
|
||||
}, [activityFeatures]);
|
||||
let newActiveCategory = activeCategory;
|
||||
for (let i = positions.length - 1; i >= 0; i--) {
|
||||
const [categoryId, position] = positions[i];
|
||||
if (scrollY >= position - 100) {
|
||||
newActiveCategory = categoryId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const communityItems = useMemo(() => {
|
||||
const source: CommunityItem[] =
|
||||
templateCommunityItems.length > 0 ? templateCommunityItems : [];
|
||||
if (newActiveCategory !== activeCategory) {
|
||||
setActiveCategory(newActiveCategory);
|
||||
}
|
||||
}, [activeCategory]);
|
||||
|
||||
return source.map((item, index) => ({
|
||||
...item,
|
||||
id: `${activeCategory}-${item.id || index}`,
|
||||
}));
|
||||
}, [activeCategory, templateCommunityItems]);
|
||||
const handleCategoryChange = useCallback((categoryId: string) => {
|
||||
const targetPosition = categoryPositionsRef.current.get(categoryId);
|
||||
if (targetPosition === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handleGeneratePress = (item: CommunityItem) => {
|
||||
isScrollingProgrammaticallyRef.current = true;
|
||||
scrollViewRef.current?.scrollTo({
|
||||
y: targetPosition,
|
||||
animated: true,
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
isScrollingProgrammaticallyRef.current = false;
|
||||
}, 500);
|
||||
|
||||
setActiveCategory(categoryId);
|
||||
}, []);
|
||||
|
||||
const handleGeneratePress = useCallback((item: CommunityItem) => {
|
||||
requireAuth(() => {
|
||||
const templateId = item.id.split('-').pop() || item.id;
|
||||
router.push(`/templates/${templateId}`);
|
||||
});
|
||||
};
|
||||
}, [requireAuth]);
|
||||
|
||||
const handleFeaturePress = (item: FeatureItemType) => {
|
||||
const handleFeaturePress = useCallback((item: FeatureItemType) => {
|
||||
requireAuth(() => {
|
||||
console.log('用户已登录,使用功能:', item.title);
|
||||
// TODO: 实现功能逻辑
|
||||
});
|
||||
};
|
||||
}, [requireAuth]);
|
||||
|
||||
return (
|
||||
<PageLayout backgroundColor="#050505">
|
||||
<PageLayout backgroundColor="#050505" topInset="none">
|
||||
<StatusBarSpacer />
|
||||
<Header />
|
||||
<CategoryTabs
|
||||
categories={categoryOptions}
|
||||
activeId={activeCategory}
|
||||
onChange={handleCategoryChange}
|
||||
/>
|
||||
<ScrollView
|
||||
ref={scrollViewRef}
|
||||
style={styles.scroll}
|
||||
contentContainerStyle={styles.contentContainer}
|
||||
showsVerticalScrollIndicator={false}
|
||||
onScroll={handleScroll}
|
||||
scrollEventThrottle={16}
|
||||
>
|
||||
<StatusBarSpacer />
|
||||
<Header />
|
||||
<CategoryTabs categories={categoryOptions} activeId={activeCategory || ``} onChange={setActiveCategory} />
|
||||
{featureItems.length > 0 && <FeatureCarousel items={featureItems} onPress={handleFeaturePress} />}
|
||||
<SectionHeader title={activeCategoryRecord?.name || ``} />
|
||||
<CommunityGrid items={communityItems} onPressAction={handleGeneratePress} />
|
||||
<View style={styles.bottomSpacer} />
|
||||
{activityFeatures.length > 0 && (
|
||||
<FeatureCarousel
|
||||
items={activityFeatures}
|
||||
onPress={handleFeaturePress}
|
||||
/>
|
||||
)}
|
||||
{allCategoriesWithTemplates.map((category, index) => (
|
||||
<View
|
||||
key={category.id}
|
||||
onLayout={(event) => {
|
||||
const layout = event.nativeEvent.layout;
|
||||
categoryPositionsRef.current.set(category.id, layout.y);
|
||||
}}
|
||||
>
|
||||
<SectionHeader title={category.name} />
|
||||
<CommunityGrid
|
||||
items={category.templates.map((template, idx) => ({
|
||||
...template,
|
||||
id: `${category.id}-${template.id || idx}`,
|
||||
}))}
|
||||
onPressAction={handleGeneratePress}
|
||||
/>
|
||||
</View>
|
||||
))}
|
||||
</ScrollView>
|
||||
</PageLayout>
|
||||
);
|
||||
@@ -226,10 +275,6 @@ const styles = StyleSheet.create({
|
||||
flex: 1,
|
||||
},
|
||||
contentContainer: {
|
||||
paddingTop: 16,
|
||||
paddingBottom: 48,
|
||||
},
|
||||
bottomSpacer: {
|
||||
height: 32,
|
||||
paddingBottom: 16,
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user