diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 9b92086..c9244ce 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1840,6 +1840,65 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga + - react-native-bottom-tabs (1.0.0): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - react-native-bottom-tabs/common (= 1.0.0) + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - SwiftUIIntrospect (~> 1.0) + - Yoga + - react-native-bottom-tabs/common (1.0.0): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - SwiftUIIntrospect (~> 1.0) + - Yoga - react-native-cameraroll (7.10.2): - boost - DoubleConversion @@ -3269,6 +3328,7 @@ PODS: - StripeUICore (= 24.23.3) - StripeUICore (24.23.3): - StripeCore (= 24.23.3) + - SwiftUIIntrospect (1.3.0) - Yoga (0.0.0) - ZIPFoundation (0.9.20) @@ -3317,6 +3377,7 @@ DEPENDENCIES: - React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`) - "react-native-blur (from `../node_modules/@react-native-community/blur`)" - react-native-blurhash (from `../node_modules/react-native-blurhash`) + - react-native-bottom-tabs (from `../node_modules/react-native-bottom-tabs`) - "react-native-cameraroll (from `../node_modules/@react-native-camera-roll/camera-roll`)" - "react-native-compat (from `../node_modules/@walletconnect/react-native-compat`)" - react-native-get-random-values (from `../node_modules/react-native-get-random-values`) @@ -3390,6 +3451,7 @@ SPEC REPOS: - StripePaymentSheet - StripePaymentsUI - StripeUICore + - SwiftUIIntrospect - ZIPFoundation EXTERNAL SOURCES: @@ -3480,6 +3542,8 @@ EXTERNAL SOURCES: :path: "../node_modules/@react-native-community/blur" react-native-blurhash: :path: "../node_modules/react-native-blurhash" + react-native-bottom-tabs: + :path: "../node_modules/react-native-bottom-tabs" react-native-cameraroll: :path: "../node_modules/@react-native-camera-roll/camera-roll" react-native-compat: @@ -3643,6 +3707,7 @@ SPEC CHECKSUMS: React-microtasksnativemodule: f0238469cb9894fd18c419137d312665b8fc05b3 react-native-blur: 6af83e7e3c4c1446a188d9b2c493600fc4beb173 react-native-blurhash: 634dbcab9df4f325a67d4e8cff7388ef7b8b11ae + react-native-bottom-tabs: 43a03d0189648390a1117463d4a30e173258e1a7 react-native-cameraroll: bb98380ee21115d5fe1ae0f8b80c86e044613746 react-native-compat: 67bb1df21b252406f83fe8c0302c8479b53c03b0 react-native-get-random-values: d16467cf726c618e9c7a8c3c39c31faa2244bbba @@ -3711,6 +3776,7 @@ SPEC CHECKSUMS: StripePaymentSheet: 7bc20a0ab10d91acc06dc449ab61bb2cf221b0f4 StripePaymentsUI: 1590647b3f635a58fb0e7058d86d3e9ff89250d4 StripeUICore: a63d95d604e70a5895f9d6b09c49a46757e86353 + SwiftUIIntrospect: fee9aa07293ee280373a591e1824e8ddc869ba5d Yoga: edeb9900b9e5bb5b27b9a6a2d5914e4fe4033c1b ZIPFoundation: dfd3d681c4053ff7e2f7350bc4e53b5dba3f5351 diff --git a/ios/tanjong.xcodeproj/project.pbxproj b/ios/tanjong.xcodeproj/project.pbxproj index 5cab08d..bfbc3d0 100644 --- a/ios/tanjong.xcodeproj/project.pbxproj +++ b/ios/tanjong.xcodeproj/project.pbxproj @@ -408,14 +408,10 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-tanjong/Pods-tanjong-resources-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-tanjong/Pods-tanjong-resources-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-tanjong/Pods-tanjong-resources.sh\"\n"; @@ -465,14 +461,10 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-tanjong/Pods-tanjong-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-tanjong/Pods-tanjong-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-tanjong/Pods-tanjong-frameworks.sh\"\n"; diff --git a/package.json b/package.json index 21649bb..132d15b 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ }, "dependencies": { "@apollo/client": "^3.14.0", + "@bottom-tabs/react-navigation": "^1.0.0", "@formatjs/intl-pluralrules": "^5.4.6", "@georstat/react-native-image-cache": "^3.1.0", "@gluestack-style/react": "^1.0.57", @@ -62,6 +63,7 @@ "react-native-actions-sheet": "^0.9.7", "react-native-blurhash": "^2.1.2", "react-native-bootsplash": "^6.3.11", + "react-native-bottom-tabs": "^1.0.0", "react-native-device-info": "^14.1.1", "react-native-file-access": "^3.2.0", "react-native-gesture-handler": "^2.28.0", diff --git a/src/pages/home/home.page.tsx b/src/pages/home/home.page.tsx index 808cdbc..9597369 100644 --- a/src/pages/home/home.page.tsx +++ b/src/pages/home/home.page.tsx @@ -1,4 +1,4 @@ -import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs' +import { useBottomTabBarHeight } from 'react-native-bottom-tabs' import { FlashList } from '@shopify/flash-list' import { useAtomValue } from 'jotai' import { View } from '@gluestack-ui/themed' @@ -21,173 +21,173 @@ import { useHeaderHeight } from '@react-navigation/elements' type HomePageProps = {} function HomePage(props: HomePageProps) { - const uid = useAtomValue(uidAtom) - useHomeLoad() - - const bs = useBooksQuery({ - variables: { - id: uid!, - pagination: { - limit: 10, - offset: 0, - }, - }, - skip: !uid, - }) - - const [atEnd, setAtEnd] = useState(false) - - const onReachedEnd = useCallback(() => { - if (atEnd) { - return - } - if (!uid) { - return - } - const allLength = bs.data?.books.length ?? 0 - return bs - .fetchMore({ - variables: { - id: uid, - pagination: { - limit: 10, - offset: allLength, - }, - }, - }) - .then((res) => { - if (res.data.books.length < 10) { - setAtEnd(true) - } - }) - }, [uid, bs.data?.books.length, atEnd]) - - const bh = useBottomTabBarHeight(); - const colorScheme = useColorScheme(); - const isDarkMode = colorScheme === 'dark'; - const insets = useSafeAreaInsets(); - const headerHeight = useHeaderHeight(); - - const theReadingBook = useMemo(() => { - const lbs = bs.data?.books ?? [] - if (lbs.length === 0) { - return null - } - return lbs[0].doubanId - }, [bs.data?.books]) - - const listedBook = useMemo(() => { - let lbs = [...(bs.data?.books ?? [])] - - if (theReadingBook) { - lbs = lbs.filter((x) => x.doubanId !== theReadingBook) - } - - return lbs - }, [bs.data?.books, theReadingBook]) - - if (!uid) { - return - } - - if (bs.error) { - return ( - - bs.refetch({ - id: uid, - pagination: { - limit: 10, - offset: 0, - }, - }) - } - /> - ) - } - - if (bs.loading) { - return ( - - - - ) - } - if ((bs.data?.books.length ?? 0) === 0) { - return - } - - return ( - - - ( - - {/* Currently Reading Section */} - - - - 📚 - - - Currently Reading - {bs.data?.books.length ?? 0} books in progress - - - - {theReadingBook && ( - - - - )} - {listedBook.length > 0 && ( - - - - 📖 - - - Your Library - {listedBook.length} books collected - - - - )} - - )} - refreshControl={ - bs.refetch()} - tintColor={isDarkMode ? '#818CF8' : '#6366F1'} - /> - } - numColumns={2} - data={listedBook} - renderItem={({ item }) => ( - - - - )} - estimatedItemSize={250} - onEndReached={onReachedEnd} - onEndReachedThreshold={1} - ListFooterComponent={} - ItemSeparatorComponent={() => } - /> - - - ) + const uid = useAtomValue(uidAtom) + useHomeLoad() + + const bs = useBooksQuery({ + variables: { + id: uid!, + pagination: { + limit: 10, + offset: 0, + }, + }, + skip: !uid, + }) + + const [atEnd, setAtEnd] = useState(false) + + const onReachedEnd = useCallback(() => { + if (atEnd) { + return + } + if (!uid) { + return + } + const allLength = bs.data?.books.length ?? 0 + return bs + .fetchMore({ + variables: { + id: uid, + pagination: { + limit: 10, + offset: allLength, + }, + }, + }) + .then((res) => { + if (res.data.books.length < 10) { + setAtEnd(true) + } + }) + }, [uid, bs.data?.books.length, atEnd]) + + const bh = useBottomTabBarHeight(); + const colorScheme = useColorScheme(); + const isDarkMode = colorScheme === 'dark'; + const insets = useSafeAreaInsets(); + const headerHeight = useHeaderHeight(); + + const theReadingBook = useMemo(() => { + const lbs = bs.data?.books ?? [] + if (lbs.length === 0) { + return null + } + return lbs[0].doubanId + }, [bs.data?.books]) + + const listedBook = useMemo(() => { + let lbs = [...(bs.data?.books ?? [])] + + if (theReadingBook) { + lbs = lbs.filter((x) => x.doubanId !== theReadingBook) + } + + return lbs + }, [bs.data?.books, theReadingBook]) + + if (!uid) { + return + } + + if (bs.error) { + return ( + + bs.refetch({ + id: uid, + pagination: { + limit: 10, + offset: 0, + }, + }) + } + /> + ) + } + + if (bs.loading) { + return ( + + + + ) + } + if ((bs.data?.books.length ?? 0) === 0) { + return + } + + return ( + + + ( + + {/* Currently Reading Section */} + + + + 📚 + + + Currently Reading + {bs.data?.books.length ?? 0} books in progress + + + + {theReadingBook && ( + + + + )} + {listedBook.length > 0 && ( + + + + 📖 + + + Your Library + {listedBook.length} books collected + + + + )} + + )} + refreshControl={ + bs.refetch()} + tintColor={isDarkMode ? '#818CF8' : '#6366F1'} + /> + } + numColumns={2} + data={listedBook} + renderItem={({ item }) => ( + + + + )} + estimatedItemSize={250} + onEndReached={onReachedEnd} + onEndReachedThreshold={1} + ListFooterComponent={} + ItemSeparatorComponent={() => } + /> + + + ) } const styles = StyleSheet.create({ flexOne: { flex: 1 }, - listContent: { + listContent: { paddingHorizontal: 20, }, separator: { height: 16 }, diff --git a/src/pages/profile/profile.page.tsx b/src/pages/profile/profile.page.tsx index 66bc9c4..4646f81 100644 --- a/src/pages/profile/profile.page.tsx +++ b/src/pages/profile/profile.page.tsx @@ -1,7 +1,6 @@ import { - BottomTabScreenProps, useBottomTabBarHeight -} from '@react-navigation/bottom-tabs' +} from 'react-native-bottom-tabs' import { useHeaderHeight } from '@react-navigation/elements' import { Link, useNavigation } from '@react-navigation/native' import { FlashList } from '@shopify/flash-list' diff --git a/src/pages/square/index.tsx b/src/pages/square/index.tsx index b6feafd..fcd0264 100644 --- a/src/pages/square/index.tsx +++ b/src/pages/square/index.tsx @@ -5,17 +5,15 @@ import { FlashList } from '@shopify/flash-list'; import { useAtomValue } from 'jotai'; import { uidAtom } from '../../atomic'; import ClippingCell from '../../components/clipping/cell'; -import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs'; import { useClippingCellAvgHeight } from '../../hooks/clipping'; import EmptyBox from '../../components/empty/empty'; import SkeletonClippingList from '../../components/skeleton/clippings'; import { GradientBackground } from '../../components/ui'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'; import { useHeaderHeight } from '@react-navigation/elements'; function SquarePage() { const uid = useAtomValue(uidAtom); - const bh = useBottomTabBarHeight(); const colorScheme = useColorScheme(); const isDarkMode = colorScheme === 'dark'; const insets = useSafeAreaInsets(); @@ -110,8 +108,7 @@ function SquarePage() { )} ItemSeparatorComponent={() => } - ListFooterComponent={} - estimatedItemSize={itemSizeCellHeight} + ListFooterComponent={} onEndReached={onReachedEnd} onEndReachedThreshold={1} /> diff --git a/src/routes/root-tabs.tsx b/src/routes/root-tabs.tsx index b0e4c7b..dab1897 100644 --- a/src/routes/root-tabs.tsx +++ b/src/routes/root-tabs.tsx @@ -1,77 +1,18 @@ -import {RouteKeys, RouteParamList, TabRouteParamList} from '../routes' -import {BlurView} from '@react-native-community/blur' -import {useTextColor} from '../hooks/color' +import { RouteKeys } from '../routes' import HomePage from '../pages/home/home.page' import ProfilePage from '../pages/profile/profile.page' import SquarePage from '../pages/square' -import { - HomeIcon, - UserIcon, - GlobeAmericasIcon -} from 'react-native-heroicons/outline' -import {useColorScheme} from 'react-native' -import {createBottomTabNavigator} from '@react-navigation/bottom-tabs' -import {createStaticNavigation} from '@react-navigation/native' +import { createNativeBottomTabNavigator } from '@bottom-tabs/react-navigation' -const TabStack = createBottomTabNavigator({ - screenOptions: props => { - const c = useColorScheme() - const textColor = useTextColor() - return { - headerTransparent: true, - headerShown: true, - headerTitleStyle: { - color: c === 'dark' ? '#E0E7FF' : '#1E293B', - fontSize: 18, - fontWeight: '400', - letterSpacing: -0.3 - }, - headerBackground: () => ( - - ), - headerShadowVisible: false, - headerLargeTitleStyle: { - color: c === 'dark' ? '#ffffff' : '#000000' - }, - tabBarStyle: { - position: 'absolute', - bottom: 0, - left: 0, - right: 0 - }, - tabBarBackground: () => ( - - ) - } - }, +const TabStack = createNativeBottomTabNavigator({ screens: { [RouteKeys.TabHome]: { screen: HomePage, options: { title: 'Library', tabBarLabel: 'Books', - tabBarIcon: ({color, size}) => + // tabBarIcon: ({ color, size }) => + tabBarIcon: () => ({ sfSymbol: 'book.fill' }) } }, [RouteKeys.TabSquare]: { @@ -79,9 +20,10 @@ const TabStack = createBottomTabNavigator({ options: { title: 'Discovery', tabBarLabel: 'Discover', - tabBarIcon: ({color, size}) => ( - - ) + // tabBarIcon: ({ color, size }) => ( + // + // ) + tabBarIcon: () => ({ sfSymbol: 'safari.fill' }) } }, [RouteKeys.TabProfile]: { @@ -89,19 +31,11 @@ const TabStack = createBottomTabNavigator({ options: { title: 'Profile', tabBarLabel: 'Profile', - tabBarIcon: ({color, size}) => + // tabBarIcon: ({ color, size }) => + tabBarIcon: () => ({ sfSymbol: 'person.crop.circle.fill' }) } } } }) -// type HomeTabPagesProps = { -// } - -// const Navigation = createStaticNavigation(TabStack) - -// function RootTabs(props: HomeTabPagesProps) { -// return -// } - export default TabStack diff --git a/yarn.lock b/yarn.lock index cbafb1a..7389586 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1828,6 +1828,20 @@ __metadata: languageName: node linkType: hard +"@bottom-tabs/react-navigation@npm:^1.0.0": + version: 1.0.0 + resolution: "@bottom-tabs/react-navigation@npm:1.0.0" + dependencies: + color: ^5.0.0 + peerDependencies: + "@react-navigation/native": ">=7" + react: "*" + react-native: "*" + react-native-bottom-tabs: "*" + checksum: 902bead6dcb6bde0a218808eb83ab1d17c06e460f6768d9498963c1ee2e02a7b2ef62b72a8240ec684601b6fd43e79cc83d83f3ac76b7784fb5e54d4d8eec761 + languageName: node + linkType: hard + "@callstack/react-theme-provider@npm:3.0.3": version: 3.0.3 resolution: "@callstack/react-theme-provider@npm:3.0.3" @@ -10247,6 +10261,15 @@ __metadata: languageName: node linkType: hard +"color-convert@npm:^3.0.1": + version: 3.1.2 + resolution: "color-convert@npm:3.1.2" + dependencies: + color-name: ^2.0.0 + checksum: 6817f4de27dd847129d84aa3695978c93a8eac8b67c496b9be1de78b33e1c96318edb4b814da25d7fe15fcedc62ede48a940ce24e258cde71e56eb3bb6e077de + languageName: node + linkType: hard + "color-name@npm:1.1.3": version: 1.1.3 resolution: "color-name@npm:1.1.3" @@ -10261,6 +10284,13 @@ __metadata: languageName: node linkType: hard +"color-name@npm:^2.0.0": + version: 2.0.2 + resolution: "color-name@npm:2.0.2" + checksum: 58e5fa3853a0dac813179e75a1fe07ff362abacb9fd456fcaae702b74d4ed5f6de2cbaee07ff2660f3495c7a6ceabc4ef0420165db0018e7150a6d4045f6539e + languageName: node + linkType: hard + "color-string@npm:^1.9.0": version: 1.9.1 resolution: "color-string@npm:1.9.1" @@ -10271,6 +10301,15 @@ __metadata: languageName: node linkType: hard +"color-string@npm:^2.0.0": + version: 2.1.2 + resolution: "color-string@npm:2.1.2" + dependencies: + color-name: ^2.0.0 + checksum: 8468bf79a56da7b4d75628d7fd36b6c214f2013bcafd618c1d92194960f202e656add3395eb82e208ac6038707b882c01dea9bbe182e325ca090f5ea1695d48e + languageName: node + linkType: hard + "color@npm:^4.2.3": version: 4.2.3 resolution: "color@npm:4.2.3" @@ -10281,6 +10320,16 @@ __metadata: languageName: node linkType: hard +"color@npm:^5.0.0": + version: 5.0.2 + resolution: "color@npm:5.0.2" + dependencies: + color-convert: ^3.0.1 + color-string: ^2.0.0 + checksum: 59f343e54652c4589e3f6517cda56fb53637cbb2e468d85024ea2634ce530f6b9cf772e069beae680d716bbbee4c980bfd013a47baa2a75d75552176fc5a3529 + languageName: node + linkType: hard + "colorette@npm:^1.0.7": version: 1.4.0 resolution: "colorette@npm:1.4.0" @@ -18011,6 +18060,20 @@ __metadata: languageName: node linkType: hard +"react-native-bottom-tabs@npm:^1.0.0": + version: 1.0.0 + resolution: "react-native-bottom-tabs@npm:1.0.0" + dependencies: + react-freeze: ^1.0.0 + sf-symbols-typescript: ^2.0.0 + use-latest-callback: ^0.2.1 + peerDependencies: + react: "*" + react-native: "*" + checksum: cc8c9f032b5b3be05c5a5af92646d8c8984b54282098916cfaff00c96922372a7f5cf1db9e8b4cd394d6b6853d57e9031b5dde913b22f5a835e1839424cd0a44 + languageName: node + linkType: hard + "react-native-country-picker-modal@npm:^2.0.0": version: 2.0.0 resolution: "react-native-country-picker-modal@npm:2.0.0" @@ -19170,6 +19233,13 @@ __metadata: languageName: node linkType: hard +"sf-symbols-typescript@npm:^2.0.0": + version: 2.1.0 + resolution: "sf-symbols-typescript@npm:2.1.0" + checksum: 63ddd79f660268e82889618bcf73d111e013b11d8795c6c89232c21b6770e58a37c7e103590caa4d2a6077b1e211fbaeaf39b665cfaf25f5507e03365e0faebe + languageName: node + linkType: hard + "sharp@npm:^0.32.6": version: 0.32.6 resolution: "sharp@npm:0.32.6" @@ -20140,6 +20210,7 @@ __metadata: "@babel/plugin-transform-template-literals": ^7.27.1 "@babel/runtime": ^7.28.4 "@biomejs/biome": 2.2.6 + "@bottom-tabs/react-navigation": ^1.0.0 "@formatjs/intl-pluralrules": ^5.4.6 "@georstat/react-native-image-cache": ^3.1.0 "@gluestack-style/react": ^1.0.57 @@ -20218,6 +20289,7 @@ __metadata: react-native-actions-sheet: ^0.9.7 react-native-blurhash: ^2.1.2 react-native-bootsplash: ^6.3.11 + react-native-bottom-tabs: ^1.0.0 react-native-device-info: ^14.1.1 react-native-file-access: ^3.2.0 react-native-gesture-handler: ^2.28.0 @@ -21229,7 +21301,7 @@ __metadata: languageName: node linkType: hard -"use-latest-callback@npm:^0.2.4": +"use-latest-callback@npm:^0.2.1, use-latest-callback@npm:^0.2.4": version: 0.2.6 resolution: "use-latest-callback@npm:0.2.6" peerDependencies: