diff --git a/apps/user-ui/app/(drawer)/(tabs)/home/index.tsx b/apps/user-ui/app/(drawer)/(tabs)/home/index.tsx
index 39fe454..65c7a83 100755
--- a/apps/user-ui/app/(drawer)/(tabs)/home/index.tsx
+++ b/apps/user-ui/app/(drawer)/(tabs)/home/index.tsx
@@ -1,5 +1,5 @@
-import React, { useState, useCallback, useMemo, memo, useRef } from "react";
-import { View, Dimensions, Image, RefreshControl, ScrollView } from "react-native";
+import React, { useState, useCallback, useMemo, memo } from "react";
+import { View, Dimensions, Image, RefreshControl } from "react-native";
import { TabView, TabBar } from "react-native-tab-view";
import { LinearGradient } from "expo-linear-gradient";
import { useRouter } from "expo-router";
@@ -39,7 +39,7 @@ const gridItemWidth = (screenWidth - 48) / 2;
// Hero product card geometry (used to size the tab scene heights)
const heroCardImageHeight = heroItemWidth * 0.82;
-const heroCardTextBlock = 86;
+const heroCardTextBlock = 92;
const heroCardHeight = heroCardImageHeight + heroCardTextBlock;
const heroRowHeight = heroCardHeight + 12; // + mb-3
const TAG_GRID_COLUMNS = 3;
@@ -60,9 +60,9 @@ const formatTimeRange = (deliveryTime: string) => {
const staticStyles = {
flatListContent: { gap: 16 },
- columnWrapper: { gap: 16, paddingHorizontal: 16 },
+ columnWrapper: { justifyContent: 'space-between', paddingHorizontal: 16 },
slotsListContent: { paddingBottom: 24 },
-};
+} as const;
const TAG_COLORS = [
{ pageBg: '#FFF8F9', bg: '#FFF1F2', border: '#FECDD3', text: '#9F1239', dot: '#E11D48' }, // rose
@@ -195,45 +195,6 @@ const SlotCard = memo(({ slot }: SlotCardProps) => {
);
});
-interface ExploreTabProps {
- tag: any;
- isSelected: boolean;
- onPress: () => void;
-}
-
-const ExploreTab = memo(({ tag, isSelected, onPress }: ExploreTabProps) => {
- return (
-
-
-
- {tag.tagName}
-
-
-
-
- );
-});
-
interface TagTabViewProps {
dashboardTags: any[];
activeTagId: number | null;
@@ -261,17 +222,18 @@ const TagTabView = memo(({
return idx < 0 ? 0 : idx;
}, [dashboardTags, activeTagId]);
- // Height: fit the max visible product rows across all tags (default 6 per tag).
+ // Height: fit the active tag's visible product rows (default 6 per tag),
+ // plus room for the "Show More" button when it has more products.
+ const activeTagKey = routes[index]?.key;
const sceneHeight = useMemo(() => {
- let maxRows = 1;
- for (const tag of dashboardTags) {
- const count = (productsByTagId[tag.id] || []).length;
- const visible = expandedByTagId[tag.id] ? count : Math.min(count, 6);
- const rows = Math.max(1, Math.ceil(visible / TAG_GRID_COLUMNS));
- maxRows = Math.max(maxRows, rows);
- }
- return maxRows * heroRowHeight + 16;
- }, [dashboardTags, productsByTagId, expandedByTagId]);
+ const tagId = Number(activeTagKey);
+ const count = (productsByTagId[tagId]?.length) || 0;
+ const expanded = expandedByTagId[tagId];
+ const visible = expanded ? count : Math.min(count, 6);
+ const rows = Math.max(1, Math.ceil(visible / TAG_GRID_COLUMNS));
+ const showMore = count > 6 && !expanded;
+ return rows * heroRowHeight + 24 + (showMore ? 52 : 0);
+ }, [activeTagKey, productsByTagId, expandedByTagId]);
const renderTabBar = useCallback((props: any) => (
void;
-}
-
-const StickyTabsRow = memo(({ dashboardTags, activeTagId, onSelectTag }: StickyTabsRowProps) => {
- const scrollRef = useRef(null);
-
- const handleSelect = (id: number) => {
- onSelectTag(id);
- const idx = dashboardTags.findIndex((tag) => tag.id === id);
- if (idx >= 0) {
- scrollRef.current?.scrollTo({ x: idx * 96, animated: true });
- }
- };
-
- return (
-
- {dashboardTags.map((tag) => (
- handleSelect(tag.id)} />
- ))}
-
- );
-});
-
interface ExploreProductItemProps {
item: any;
onPress: (id: number) => void;
@@ -408,14 +339,17 @@ const ProductItem = memo(({ item, onPress }: ProductItemProps) => {
const handlePress = useCallback(() => onPress(item.id), [item.id, onPress]);
return (
-
+
+
+
);
});
@@ -429,7 +363,6 @@ interface ListHeaderProps {
activeTagId: number | null;
productsByTagId: Record;
onSelectTag: (id: number) => void;
- onTabsSectionLayout: (layout: { y: number; height: number }) => void;
}
const ListHeader = memo(({
@@ -442,7 +375,6 @@ const ListHeader = memo(({
activeTagId,
productsByTagId,
onSelectTag,
- onTabsSectionLayout,
}: ListHeaderProps) => {
const handleLayout = useCallback((event: any) => {
const { y, height } = event.nativeEvent.layout;
@@ -472,10 +404,6 @@ const ListHeader = memo(({
{dashboardTags.length > 0 && (
{
- const { y, height } = event.nativeEvent.layout;
- onTabsSectionLayout({ y, height });
- }}
style={[
tw`mx-4 mb-2 rounded-[28px] px-3 pt-3 pb-[30px]`,
{ backgroundColor: theme.colors.brand25 },
@@ -549,8 +477,8 @@ const ListHeader = memo(({
)}
-
-
+
+
@@ -572,11 +500,8 @@ export default function Dashboard() {
const [isLoadingDialogOpen, setIsLoadingDialogOpen] = useState(false);
const [gradientHeight, setGradientHeight] = useState(0);
const [displayedProducts, setDisplayedProducts] = useState([]);
+ const [visibleCount, setVisibleCount] = useState(21);
const [hasMore, setHasMore] = useState(true);
- const [isLoadingMore, setIsLoadingMore] = useState(false);
- const [searchBarHeight, setSearchBarHeight] = useState(0);
- const [tabsSectionLayout, setTabsSectionLayout] = useState({ y: 0, height: 0 });
- const [showStickyTabs, setShowStickyTabs] = useState(false);
const { getQuickestSlot } = useProductSlotIdentifier();
const productSlotsMap = useCentralSlotStore((state) => state.productSlotsMap);
const refetchProducts = useCentralProductStore((state) => state.refetchProducts);
@@ -608,7 +533,7 @@ export default function Dashboard() {
return
}
- const initialBatch = products
+ const allSorted = products
.filter(p => typeof p.id === "number")
.sort((a, b) => {
const slotA = getQuickestSlot(a.id)
@@ -622,10 +547,19 @@ export default function Dashboard() {
return 0
})
- setDisplayedProducts(initialBatch)
- setHasMore(products.length > 10)
+ setDisplayedProducts(allSorted)
+ setVisibleCount(21)
+ setHasMore(allSorted.length > 21)
}, [productsData, productSlotsMap]);
+ const handleShowMore = useCallback(() => {
+ setVisibleCount((prev) => {
+ const next = prev + 21;
+ setHasMore(next < displayedProducts.length);
+ return next;
+ });
+ }, [displayedProducts.length]);
+
const sortedSlots = useMemo(() => {
if (!slotsData?.slots) return [];
const now = dayjs();
@@ -716,19 +650,6 @@ export default function Dashboard() {
router.push("/(drawer)/(tabs)/home/search-results");
}, [router]);
- const handleTabsSectionLayout = useCallback((layout: { y: number; height: number }) => {
- setTabsSectionLayout(layout);
- }, []);
-
- const handleListScroll = useCallback((event: any) => {
- const scrollY = event.nativeEvent.contentOffset.y;
- const stickyStart = tabsSectionLayout.y;
- const stickyEnd = tabsSectionLayout.y + tabsSectionLayout.height - 56;
- const shouldShowStickyTabs = tabsSectionLayout.height > 0 && scrollY > stickyStart && scrollY < stickyEnd;
-
- setShowStickyTabs((current) => current === shouldShowStickyTabs ? current : shouldShowStickyTabs);
- }, [tabsSectionLayout]);
-
const renderProductItem = useCallback(({ item }: { item: any }) => (
//
@@ -745,9 +666,8 @@ export default function Dashboard() {
activeTagId={activeTagId}
productsByTagId={productsByTagId}
onSelectTag={setSelectedTagId}
- onTabsSectionLayout={handleTabsSectionLayout}
/>
- ), [gradientHeight, handleGradientLayout, storesData, sortedSlots, handleProductPress, dashboardTags, activeTagId, productsByTagId, handleTabsSectionLayout]);
+ ), [gradientHeight, handleGradientLayout, storesData, sortedSlots, handleProductPress, dashboardTags, activeTagId, productsByTagId]);
const pageTint = activeTagId == null ? '#FFFFFF' : getTagColor(activeTagId).pageBg;
const pageTintStyle = useMemo(() => [
@@ -791,7 +711,6 @@ export default function Dashboard() {
setSearchBarHeight(event.nativeEvent.layout.height)}
>
item.id.toString()}
- numColumns={2}
+ numColumns={3}
style={{ backgroundColor: '#FFFFFF' }}
- onScroll={handleListScroll}
- scrollEventThrottle={16}
contentContainerStyle={listContentContainerStyle}
columnWrapperStyle={staticStyles.columnWrapper}
renderItem={renderProductItem}
ListHeaderComponent={listHeader}
+ ListFooterComponent={
+ hasMore ? (
+
+
+ Show More
+
+
+ ) : null
+ }
refreshControl={
- {showStickyTabs && dashboardTags.length > 0 && (
-
-
-
-
-
- )}
-