From ad66f78dc9ccd2c6c8f0e67ac8a6c33519db5ce7 Mon Sep 17 00:00:00 2001 From: Mohamed Bassem Date: Sun, 23 Nov 2025 11:07:43 +0000 Subject: feat(mobile): Add tags screen to mobile app (#2163) * feat: Add tags screen to mobile app Add a new Tags tab to the mobile app that displays all tags sorted by usage. The screen includes: - Paginated tag list with infinite scroll - Display of tag names and bookmark counts - Pull-to-refresh functionality - Navigation to individual tag detail screens - Empty state and loading indicators This brings tag browsing functionality to the mobile app, similar to the existing Lists tab. * feat: Add search functionality to mobile tags screen Add a search input to the tags screen that allows users to filter tags by name. The search includes: - Debounced search input (300ms delay) to reduce API calls - Real-time filtering as the user types - Sort by relevance when searching, by usage when not searching - Smooth animated clear button This enhances the tags browsing experience by making it easy to find specific tags in a large collection. * format --------- Co-authored-by: Claude --- apps/mobile/app/dashboard/(tabs)/_layout.tsx | 9 +- apps/mobile/app/dashboard/(tabs)/tags.tsx | 140 +++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 apps/mobile/app/dashboard/(tabs)/tags.tsx (limited to 'apps') diff --git a/apps/mobile/app/dashboard/(tabs)/_layout.tsx b/apps/mobile/app/dashboard/(tabs)/_layout.tsx index 7419c348..316eddcf 100644 --- a/apps/mobile/app/dashboard/(tabs)/_layout.tsx +++ b/apps/mobile/app/dashboard/(tabs)/_layout.tsx @@ -2,7 +2,7 @@ import React, { useLayoutEffect } from "react"; import { Tabs, useNavigation } from "expo-router"; import { StyledTabs } from "@/components/navigation/tabs"; import { useColorScheme } from "@/lib/useColorScheme"; -import { ClipboardList, Home, Settings } from "lucide-react-native"; +import { ClipboardList, Home, Settings, Tag } from "lucide-react-native"; export default function TabLayout() { const { colors } = useColorScheme(); @@ -37,6 +37,13 @@ export default function TabLayout() { tabBarIcon: ({ color }) => , }} /> + , + }} + /> { + setRefreshing(isPending); + }, [isPending]); + + if (error) { + return refetch()} />; + } + + if (!data) { + return ; + } + + const onRefresh = () => { + apiUtils.tags.list.invalidate(); + }; + + const tags: TagItem[] = data.tags.map((tag) => ({ + id: tag.id, + name: tag.name, + numBookmarks: tag.numBookmarks, + href: `/dashboard/tags/${tag.id}`, + })); + + const handleLoadMore = () => { + if (hasNextPage && !isFetchingNextPage) { + fetchNextPage(); + } + }; + + return ( + + + + + + } + contentContainerStyle={{ + gap: 5, + }} + renderItem={(item) => ( + + + + + {item.item.name} + + {item.item.numBookmarks}{" "} + {item.item.numBookmarks === 1 ? "bookmark" : "bookmarks"} + + + + + + + )} + data={tags} + refreshing={refreshing} + onRefresh={onRefresh} + onEndReached={handleLoadMore} + onEndReachedThreshold={0.5} + ListFooterComponent={ + isFetchingNextPage ? ( + + + Loading more... + + + ) : null + } + ListEmptyComponent={ + !isPending ? ( + + + No tags yet + + + ) : null + } + /> + + ); +} -- cgit v1.2.3-70-g09d2