From 22788a16d6e2e243905ba17f246c48b28b37f118 Mon Sep 17 00:00:00 2001 From: yungblud Date: Fri, 20 Dec 2024 21:10:18 +0900 Subject: [PATCH 01/12] feat(billets-app): :art: v1.6.2 --- apps/billets-app/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/billets-app/package.json b/apps/billets-app/package.json index 0093ff33..dca96a87 100644 --- a/apps/billets-app/package.json +++ b/apps/billets-app/package.json @@ -1,6 +1,6 @@ { "name": "@coldsurfers/billets-app", - "version": "1.6.1", + "version": "1.6.2", "private": true, "scripts": { "android": "react-native run-android", From e13b43ec7075ce3a054c2e81ade8490bca94b9e6 Mon Sep 17 00:00:00 2001 From: yungblud Date: Sat, 21 Dec 2024 14:34:35 +0900 Subject: [PATCH 02/12] refactor(billets-app): :art: refactored App --- apps/billets-app/App.tsx | 16 +++++++------- apps/billets-app/src/lib/contexts/index.ts | 1 - .../contexts/tab-bar-visible-context/index.ts | 1 - .../tab-bar-visible-context.tsx | 22 ------------------- 4 files changed, 8 insertions(+), 32 deletions(-) delete mode 100644 apps/billets-app/src/lib/contexts/tab-bar-visible-context/index.ts delete mode 100644 apps/billets-app/src/lib/contexts/tab-bar-visible-context/tab-bar-visible-context.tsx diff --git a/apps/billets-app/App.tsx b/apps/billets-app/App.tsx index 27af2d5d..25c62cdd 100644 --- a/apps/billets-app/App.tsx +++ b/apps/billets-app/App.tsx @@ -1,9 +1,9 @@ -import { AuthContextProvider, TabBarVisibleContextProvider, useFirebaseAnalytics, useFirebaseCrashlytics } from '@/lib' +import { AuthContextProvider, useFirebaseAnalytics, useFirebaseCrashlytics } from '@/lib' import { CommonScreenLayout } from '@/ui' import { colors } from '@coldsurfers/ocean-road' -import { Text } from '@coldsurfers/ocean-road/native' +import { Spinner, Text } from '@coldsurfers/ocean-road/native' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' -import React, { PropsWithChildren, useEffect, useState } from 'react' +import React, { PropsWithChildren, Suspense, useEffect, useState } from 'react' import { Platform, StatusBar, View } from 'react-native' import BootSplash from 'react-native-bootsplash' import codePush, { DownloadProgress, RemotePackage } from 'react-native-code-push' @@ -112,16 +112,16 @@ const BootSplashAwaiter = ({ children }: PropsWithChildren) => { const App = () => { return ( - - - + + + }> - - + + ) } diff --git a/apps/billets-app/src/lib/contexts/index.ts b/apps/billets-app/src/lib/contexts/index.ts index f4775796..94b92959 100644 --- a/apps/billets-app/src/lib/contexts/index.ts +++ b/apps/billets-app/src/lib/contexts/index.ts @@ -1,4 +1,3 @@ export * from './auth-context' export * from './constants' -export * from './tab-bar-visible-context' export * from './toast-visible-context' diff --git a/apps/billets-app/src/lib/contexts/tab-bar-visible-context/index.ts b/apps/billets-app/src/lib/contexts/tab-bar-visible-context/index.ts deleted file mode 100644 index c0402550..00000000 --- a/apps/billets-app/src/lib/contexts/tab-bar-visible-context/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './tab-bar-visible-context' diff --git a/apps/billets-app/src/lib/contexts/tab-bar-visible-context/tab-bar-visible-context.tsx b/apps/billets-app/src/lib/contexts/tab-bar-visible-context/tab-bar-visible-context.tsx deleted file mode 100644 index 597cf0fa..00000000 --- a/apps/billets-app/src/lib/contexts/tab-bar-visible-context/tab-bar-visible-context.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React, { createContext, PropsWithChildren, useCallback, useState } from 'react' - -export const TabBarVisibleContext = createContext<{ - tabBarVisible: boolean - show: () => void - hide: () => void -}>({ - tabBarVisible: true, - show: () => {}, - hide: () => {}, -}) - -export const TabBarVisibleContextProvider = ({ children }: PropsWithChildren) => { - const [tabBarVisible, setTabBarVisible] = useState(true) - const show = useCallback(() => { - setTabBarVisible(true) - }, []) - const hide = useCallback(() => { - setTabBarVisible(false) - }, []) - return {children} -} From f926fc976281d83addc42ed12373f511e8d796dd Mon Sep 17 00:00:00 2001 From: yungblud Date: Sat, 21 Dec 2024 14:55:40 +0900 Subject: [PATCH 03/12] feat(billets-app): :art: implemented suspense with concert query --- .../concert-list-item/concert-list-item.tsx | 86 +++++---- .../concert-list-item.utils.ts | 24 +++ .../ui/concert-list/concert-list-skeleton.tsx | 20 ++ .../ui/concert-list/concert-list.styles.ts | 15 ++ .../concert/ui/concert-list/concert-list.tsx | 181 ++++++++---------- .../features/concert/ui/concert-list/index.ts | 2 + .../search-bottom-list/search-bottom-list.tsx | 5 +- .../search-default-bottom-result-list.tsx | 13 +- .../queries/useConcertListQuery.ts | 6 +- .../src/screens/home-screen/home-screen.tsx | 105 ++++------ 10 files changed, 233 insertions(+), 224 deletions(-) create mode 100644 apps/billets-app/src/features/concert/ui/concert-list-item/concert-list-item.utils.ts create mode 100644 apps/billets-app/src/features/concert/ui/concert-list/concert-list-skeleton.tsx create mode 100644 apps/billets-app/src/features/concert/ui/concert-list/concert-list.styles.ts diff --git a/apps/billets-app/src/features/concert/ui/concert-list-item/concert-list-item.tsx b/apps/billets-app/src/features/concert/ui/concert-list-item/concert-list-item.tsx index 70b266f5..8bcb6242 100644 --- a/apps/billets-app/src/features/concert/ui/concert-list-item/concert-list-item.tsx +++ b/apps/billets-app/src/features/concert/ui/concert-list-item/concert-list-item.tsx @@ -4,8 +4,12 @@ import { Text } from '@coldsurfers/ocean-road/native' import { useCallback } from 'react' import { Pressable, StyleSheet, View } from 'react-native' import FastImage from 'react-native-fast-image' -import palettes from '../../../../lib/palettes' import useSubscribedConcertQuery from '../../../../lib/react-query/queries/useSubscribedConcertQuery' +import { + getConcertListBottomWrapperDynamicStyles, + getConcertListItemWrapperDynamicStyles, + getConcertListThumbnailWrapperDynamicStyles, +} from './concert-list-item.utils' export const ConcertListItem = ({ concertId, @@ -40,25 +44,10 @@ export const ConcertListItem = ({ }, [onPressSubscribe, subscribedConcertData, concertId]) return ( - + {onPressSubscribe && ( @@ -66,17 +55,7 @@ export const ConcertListItem = ({ )} - + { + return ( + + + + + + + + + + ) +} + const styles = StyleSheet.create({ concertListItem: { width: '100%', @@ -151,20 +153,24 @@ const styles = StyleSheet.create({ flexGrow: 1, }, concertInfoWrapper: {}, - concertSaveButton: { - marginLeft: 'auto', - borderWidth: 1, - borderRadius: 18, - borderColor: palettes.gray['300'], - width: 36, - height: 36, - alignItems: 'center', - justifyContent: 'center', - }, - concertSaveButtonIcon: { fontSize: 24 }, bottom: { flexDirection: 'row', alignItems: 'center', }, subscribeBtnWrapper: { position: 'absolute', right: 12, bottom: 12 }, + skeletonBackground: { + backgroundColor: colors.oc.gray[3].value, + }, + skeletonTitle: { + width: '80%', + backgroundColor: colors.oc.gray[3].value, + height: 24, + borderRadius: 8, + }, + skeletonSubtitle: { + width: '20%', + backgroundColor: colors.oc.gray[3].value, + height: 24, + borderRadius: 8, + }, }) diff --git a/apps/billets-app/src/features/concert/ui/concert-list-item/concert-list-item.utils.ts b/apps/billets-app/src/features/concert/ui/concert-list-item/concert-list-item.utils.ts new file mode 100644 index 00000000..b9d290fd --- /dev/null +++ b/apps/billets-app/src/features/concert/ui/concert-list-item/concert-list-item.utils.ts @@ -0,0 +1,24 @@ +import { StyleProp, ViewStyle } from 'react-native' + +export const getConcertListItemWrapperDynamicStyles = (size: 'small' | 'large'): StyleProp => { + return { + width: size === 'small' ? 140 : '100%', + padding: size === 'small' ? 0 : 12, + } +} + +export const getConcertListThumbnailWrapperDynamicStyles = (size: 'small' | 'large') => { + return { + borderBottomLeftRadius: size === 'small' ? 0 : 8, + borderBottomRightRadius: size === 'small' ? 0 : 8, + } +} + +export const getConcertListBottomWrapperDynamicStyles = (size: 'small' | 'large') => { + return { + paddingLeft: size === 'small' ? 8 : 0, + paddingRight: size === 'small' ? 8 : 0, + marginTop: size === 'small' ? 8 : 16, + marginBottom: size === 'small' ? 8 : 4, + } +} diff --git a/apps/billets-app/src/features/concert/ui/concert-list/concert-list-skeleton.tsx b/apps/billets-app/src/features/concert/ui/concert-list/concert-list-skeleton.tsx new file mode 100644 index 00000000..95bc92fc --- /dev/null +++ b/apps/billets-app/src/features/concert/ui/concert-list/concert-list-skeleton.tsx @@ -0,0 +1,20 @@ +import { memo } from 'react' +import { FlatList } from 'react-native' +import { ConcertListItem } from '../concert-list-item' +import { concertListStyles } from './concert-list.styles' + +const renderItem = () => { + return +} + +const data = Array.from({ length: 5 }).map((_, index) => index) + +export const ConcertListSkeleton = memo(() => { + return ( + + ) +}) diff --git a/apps/billets-app/src/features/concert/ui/concert-list/concert-list.styles.ts b/apps/billets-app/src/features/concert/ui/concert-list/concert-list.styles.ts new file mode 100644 index 00000000..4586d054 --- /dev/null +++ b/apps/billets-app/src/features/concert/ui/concert-list/concert-list.styles.ts @@ -0,0 +1,15 @@ +import { StyleSheet } from 'react-native' + +export const concertListStyles = StyleSheet.create({ + concertListContentContainer: { + paddingHorizontal: 16, + marginTop: 12, + paddingBottom: 24, + flexGrow: 1, + }, + loadingIndicatorWrapper: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, +}) diff --git a/apps/billets-app/src/features/concert/ui/concert-list/concert-list.tsx b/apps/billets-app/src/features/concert/ui/concert-list/concert-list.tsx index f156bd8b..c463345a 100644 --- a/apps/billets-app/src/features/concert/ui/concert-list/concert-list.tsx +++ b/apps/billets-app/src/features/concert/ui/concert-list/concert-list.tsx @@ -1,117 +1,100 @@ -import { useUserCurrentLocationStore } from '@/features/location/stores' import useConcertListQuery from '@/lib/react-query/queries/useConcertListQuery' import { CommonListEmpty } from '@/ui' import { format } from 'date-fns' import { forwardRef, useCallback, useMemo, useState } from 'react' -import { ActivityIndicator, FlatList, ListRenderItem, StyleSheet, View } from 'react-native' -import { useShallow } from 'zustand/shallow' +import { ActivityIndicator, FlatList, ListRenderItem, View } from 'react-native' import { ConcertListItem } from '../concert-list-item' +import { concertListStyles } from './concert-list.styles' import { ConcertListItemT } from './concert-list.types' type ConcertListProps = { onPressItem?: (item: ConcertListItemT) => void onPressSubscribe?: (item: ConcertListItemT, options: { isSubscribed: boolean }) => void + latitude: number + longitude: number } -export const ConcertList = forwardRef(({ onPressItem, onPressSubscribe }, ref) => { - const [isRefreshing, setIsRefreshing] = useState(false) - const { latitude, longitude } = useUserCurrentLocationStore( - useShallow((state) => ({ - latitude: state.latitude ? +`${state.latitude}`.substring(0, 7) : null, - longitude: state.longitude ? +`${state.longitude}`.substring(0, 8) : null, - })), - ) - const { - data: concertListData, - isPending: isPendingConcertList, - fetchNextPage: fetchNextConcertList, - isFetchingNextPage: isFetchingNextConcertList, - hasNextPage: hasNextConcertListPage, - refetch: refetchConcertList, - } = useConcertListQuery( - { - latLng: { - latitude: latitude!, - longitude: longitude!, - }, - }, - { - enabled: !!latitude && !!longitude, - refetchOnWindowFocus: false, - }, - ) - const concertList = useMemo(() => { - return concertListData?.pages.flat() ?? [] - }, [concertListData]) - - const renderItem: ListRenderItem = useCallback( - (info) => { - return ( - onPressItem?.(info.item)} - onPressSubscribe={({ isSubscribed }) => onPressSubscribe?.(info.item, { isSubscribed })} - /> - ) - }, - [onPressItem, onPressSubscribe], - ) - - const onEndReached = useCallback(async () => { - if (isPendingConcertList || isFetchingNextConcertList) { - return - } - if (!hasNextConcertListPage) { - return - } - await fetchNextConcertList() - }, [fetchNextConcertList, hasNextConcertListPage, isFetchingNextConcertList, isPendingConcertList]) +export const ConcertList = forwardRef( + ({ onPressItem, onPressSubscribe, latitude, longitude }, ref) => { + const [isRefreshing, setIsRefreshing] = useState(false) - const onRefresh = useCallback(async () => { - setIsRefreshing(true) - await refetchConcertList() - setIsRefreshing(false) - }, [refetchConcertList]) + const { + data: concertListData, + isPending: isPendingConcertList, + fetchNextPage: fetchNextConcertList, + isFetchingNextPage: isFetchingNextConcertList, + hasNextPage: hasNextConcertListPage, + refetch: refetchConcertList, + } = useConcertListQuery( + { + latLng: { + latitude, + longitude, + }, + }, + { + refetchOnWindowFocus: false, + }, + ) + const concertList = useMemo(() => { + return concertListData?.pages.flat() ?? [] + }, [concertListData]) - return ( - item.id} - renderItem={renderItem} - showsVerticalScrollIndicator={false} - contentContainerStyle={styles.concertListContentContainer} - ListEmptyComponent={ - isPendingConcertList ? ( - - - - ) : ( - + const renderItem: ListRenderItem = useCallback( + (info) => { + return ( + onPressItem?.(info.item)} + onPressSubscribe={({ isSubscribed }) => onPressSubscribe?.(info.item, { isSubscribed })} + /> ) + }, + [onPressItem, onPressSubscribe], + ) + + const onEndReached = useCallback(async () => { + if (isPendingConcertList || isFetchingNextConcertList) { + return + } + if (!hasNextConcertListPage) { + return } - onEndReached={onEndReached} - refreshing={isRefreshing} - onRefresh={onRefresh} - /> - ) -}) + await fetchNextConcertList() + }, [fetchNextConcertList, hasNextConcertListPage, isFetchingNextConcertList, isPendingConcertList]) -const styles = StyleSheet.create({ - concertListContentContainer: { - paddingHorizontal: 16, - marginTop: 12, - paddingBottom: 24, - flexGrow: 1, - }, - loadingIndicatorWrapper: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', + const onRefresh = useCallback(async () => { + setIsRefreshing(true) + await refetchConcertList() + setIsRefreshing(false) + }, [refetchConcertList]) + + return ( + item.id} + renderItem={renderItem} + showsVerticalScrollIndicator={false} + contentContainerStyle={concertListStyles.concertListContentContainer} + ListEmptyComponent={ + isPendingConcertList ? ( + + + + ) : ( + + ) + } + onEndReached={onEndReached} + refreshing={isRefreshing} + onRefresh={onRefresh} + /> + ) }, -}) +) diff --git a/apps/billets-app/src/features/concert/ui/concert-list/index.ts b/apps/billets-app/src/features/concert/ui/concert-list/index.ts index c0b026ce..a24e52d5 100644 --- a/apps/billets-app/src/features/concert/ui/concert-list/index.ts +++ b/apps/billets-app/src/features/concert/ui/concert-list/index.ts @@ -1 +1,3 @@ export * from './concert-list' +export * from './concert-list-skeleton' +export * from './concert-list.types' diff --git a/apps/billets-app/src/features/search/ui/search-bottom-list/search-bottom-list.tsx b/apps/billets-app/src/features/search/ui/search-bottom-list/search-bottom-list.tsx index b794b641..779b5982 100644 --- a/apps/billets-app/src/features/search/ui/search-bottom-list/search-bottom-list.tsx +++ b/apps/billets-app/src/features/search/ui/search-bottom-list/search-bottom-list.tsx @@ -22,6 +22,9 @@ export const SearchBottomList = memo( if (locationConcerts.length > 0) { return } - return + if (latitude !== null && longitude !== null) { + return + } + return null }, ) diff --git a/apps/billets-app/src/features/search/ui/search-default-bottom-result-list/search-default-bottom-result-list.tsx b/apps/billets-app/src/features/search/ui/search-default-bottom-result-list/search-default-bottom-result-list.tsx index 035272bc..be18f82d 100644 --- a/apps/billets-app/src/features/search/ui/search-default-bottom-result-list/search-default-bottom-result-list.tsx +++ b/apps/billets-app/src/features/search/ui/search-default-bottom-result-list/search-default-bottom-result-list.tsx @@ -10,23 +10,16 @@ import { ListRenderItem, StyleSheet } from 'react-native' import { SearchItem } from '../search-item' import { SearchItemThumbnail } from '../search-item-thumbnail' -export const SearchDefaultBottomResultList = ({ - latitude, - longitude, -}: { - latitude: number | null - longitude: number | null -}) => { +export const SearchDefaultBottomResultList = ({ latitude, longitude }: { latitude: number; longitude: number }) => { const navigation = useSearchScreenNavigation() const { data: concertList } = useConcertListQuery( { latLng: { - latitude: latitude!, - longitude: longitude!, + latitude, + longitude, }, }, { - enabled: !!latitude && !!longitude, refetchOnWindowFocus: false, }, ) diff --git a/apps/billets-app/src/lib/react-query/queries/useConcertListQuery.ts b/apps/billets-app/src/lib/react-query/queries/useConcertListQuery.ts index 70042279..a1a595ae 100644 --- a/apps/billets-app/src/lib/react-query/queries/useConcertListQuery.ts +++ b/apps/billets-app/src/lib/react-query/queries/useConcertListQuery.ts @@ -1,4 +1,4 @@ -import { InfiniteData, UseInfiniteQueryOptions, useInfiniteQuery } from '@tanstack/react-query' +import { InfiniteData, useSuspenseInfiniteQuery, UseSuspenseInfiniteQueryOptions } from '@tanstack/react-query' import { LatLng } from '../../../types/LatLng' import { fetchClient } from '../../api/openapi-client' import { v1QueryKeyFactory } from '../../query-key-factory' @@ -28,7 +28,7 @@ type TPageParam = number function useConcertListQuery( params: TQueryParams, options?: Partial< - UseInfiniteQueryOptions< + UseSuspenseInfiniteQueryOptions< TQueryData, TError, InfiniteData, @@ -38,7 +38,7 @@ function useConcertListQuery( > >, ) { - return useInfiniteQuery, TQueryKey, TPageParam>({ + return useSuspenseInfiniteQuery, TQueryKey, TPageParam>({ ...options, initialPageParam: 0, queryKey: v1QueryKeyFactory.concerts.list(params).queryKey, diff --git a/apps/billets-app/src/screens/home-screen/home-screen.tsx b/apps/billets-app/src/screens/home-screen/home-screen.tsx index 74312866..243171a5 100644 --- a/apps/billets-app/src/screens/home-screen/home-screen.tsx +++ b/apps/billets-app/src/screens/home-screen/home-screen.tsx @@ -6,20 +6,20 @@ import { useUserCurrentLocationStore, } from '@/features' import { ConcertListItemT } from '@/features/concert/ui/concert-list/concert-list.types' -import { AnimatePresence } from '@/ui' +import { AnimatePresence, CommonScreenLayout } from '@/ui' import { useScrollToTop } from '@react-navigation/native' -import { useCallback, useRef, useState } from 'react' -import { FlatList, StyleSheet } from 'react-native' -import { SafeAreaView } from 'react-native-safe-area-context' +import { Suspense, useCallback, useRef, useState } from 'react' +import { FlatList } from 'react-native' import { useShallow } from 'zustand/shallow' -import { ConcertList } from '../../features/concert' -import palettes from '../../lib/palettes' +import { ConcertList, ConcertListSkeleton } from '../../features/concert' import useGetMeQuery from '../../lib/react-query/queries/useGetMeQuery' import { useHomeScreenNavigation } from './home-screen.hooks' -export const HomeScreen = () => { +const SuspenseHomeScreen = () => { const navigation = useHomeScreenNavigation() const listRef = useRef(null) + useScrollToTop(listRef) + const [locationModalVisible, setLocationModalVisible] = useState(false) const { latitude, longitude } = useUserCurrentLocationStore( useShallow((state) => ({ @@ -33,10 +33,10 @@ export const HomeScreen = () => { const toggleSubscribeConcert = useToggleSubscribeConcert() const onPressConcertListItem = useCallback( - (concertId: string) => { + (item: ConcertListItemT) => { navigation.navigate('ConcertStackNavigation', { screen: 'ConcertDetailScreen', - params: { concertId }, + params: { concertId: item.id }, }) }, [navigation], @@ -60,8 +60,6 @@ export const HomeScreen = () => { [meData, navigation, toggleSubscribeConcert], ) - useScrollToTop(listRef) - const onPressSubscribe = useCallback( ( item: ConcertListItemT, @@ -76,15 +74,25 @@ export const HomeScreen = () => { [onPressSubscribeConcertListItem], ) + const showLocationModal = useCallback(() => { + setLocationModalVisible(true) + }, []) + return ( - + {latitude === null && longitude === null && } - setLocationModalVisible(true)} /> - onPressConcertListItem(item.id)} - onPressSubscribe={onPressSubscribe} - /> + + {latitude !== null && longitude !== null && ( + }> + + + )} {locationModalVisible && ( { /> )} - + ) } -const styles = StyleSheet.create({ - wrapper: { flex: 1, backgroundColor: palettes.gray['100'] }, - concertListItem: { - width: '100%', - backgroundColor: palettes.white, - marginBottom: 12, - padding: 12, - borderRadius: 8, - }, - concertThumbnail: { - width: '100%', - height: 250, - borderRadius: 8, - marginBottom: 12, - }, - concertTitle: { fontWeight: 'bold', fontSize: 18 }, - concertFormattedDate: { marginTop: 8 }, - concertVenue: { marginTop: 8 }, - concertListContentContainer: { - paddingHorizontal: 12, - marginTop: 12, - paddingBottom: 24, - flexGrow: 1, - }, - loadingIndicatorWrapper: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - }, - emptyWrapper: { flex: 1, alignItems: 'center', justifyContent: 'center' }, - emptyEmoji: { fontSize: 28 }, - emptyDesc: { - fontSize: 28, - fontWeight: 'bold', - textAlign: 'center', - marginTop: 12, - }, - concertInfoWrapper: { - flexDirection: 'row', - alignItems: 'center', - }, - concertSaveButton: { - marginLeft: 'auto', - borderWidth: 1, - borderRadius: 18, - borderColor: palettes.gray['300'], - width: 36, - height: 36, - alignItems: 'center', - justifyContent: 'center', - }, -}) +export const HomeScreen = () => { + return ( + }> + + + ) +} From 1419a2285cb9f24e00038434b015e52b28ad6785 Mon Sep 17 00:00:00 2001 From: yungblud Date: Sat, 21 Dec 2024 15:22:48 +0900 Subject: [PATCH 04/12] feat(billets-app): :art: implemented MyScreenLandingLayout --- .../src/screens/my-screen/my-screen.tsx | 29 +------------- apps/billets-app/src/ui/index.ts | 1 + .../src/ui/my-screen-landing-layout/index.ts | 1 + .../my-screen-landing-layout.tsx | 38 +++++++++++++++++++ 4 files changed, 42 insertions(+), 27 deletions(-) create mode 100644 apps/billets-app/src/ui/my-screen-landing-layout/index.ts create mode 100644 apps/billets-app/src/ui/my-screen-landing-layout/my-screen-landing-layout.tsx diff --git a/apps/billets-app/src/screens/my-screen/my-screen.tsx b/apps/billets-app/src/screens/my-screen/my-screen.tsx index 2873a69f..e4d44cde 100644 --- a/apps/billets-app/src/screens/my-screen/my-screen.tsx +++ b/apps/billets-app/src/screens/my-screen/my-screen.tsx @@ -1,13 +1,12 @@ import { SubscribedConcertList } from '@/features' import { $api } from '@/lib/api/openapi-client' -import { CommonScreenLayout } from '@/ui' +import { CommonScreenLayout, MyScreenLandingLayout } from '@/ui' import { colors } from '@coldsurfers/ocean-road' import { Button, ProfileThumbnail, Spinner, Text } from '@coldsurfers/ocean-road/native' import React, { useCallback, useContext, useEffect, useState } from 'react' import { Alert, Pressable, SectionList, SectionListRenderItem, StyleSheet, View } from 'react-native' import { match } from 'ts-pattern' import { AuthContext } from '../../lib/contexts/auth-context/auth-context' -import palettes from '../../lib/palettes' import { useMyScreenNavigation } from './my-screen.hooks' import { MyScreenSettingSectionListData, MyScreenSettingSectionListSectionT } from './my-screen.types' @@ -196,15 +195,7 @@ export const MyScreen = () => { /> ) : ( - - - {`๐ŸŽ‰\n์˜ˆ์ •๋œ ๋งŽ์€\n๊ณต์—ฐ์„\n๋†“์น˜์ง€ ๋งˆ์„ธ์š”`} - - {`๋กœ๊ทธ์ธ ํ›„ ์ฐœํ•˜๊ธฐ๋ฅผ ์‚ฌ์šฉํ•ด๋ณด์„ธ์š”`} - - + ) } @@ -215,11 +206,6 @@ const styles = StyleSheet.create({ alignItems: 'center', justifyContent: 'center', }, - loginButtonTitle: { - color: colors.oc.white.value, - fontWeight: '700', - fontSize: 14, - }, sectionListContentContainer: { backgroundColor: colors.oc.gray[1].value, flexGrow: 1, @@ -234,17 +220,6 @@ const styles = StyleSheet.create({ }, itemText: { fontWeight: '700', fontSize: 18 }, sectionList: { backgroundColor: colors.oc.gray[1].value }, - loginButton: { - backgroundColor: palettes.lightblue[500], - marginTop: 16, - }, - loginText: { fontSize: 24, textAlign: 'center' }, - loginSubText: { - fontSize: 16, - textAlign: 'center', - color: palettes.gray[500], - marginTop: 8, - }, profileItem: { flexDirection: 'row', alignItems: 'center', diff --git a/apps/billets-app/src/ui/index.ts b/apps/billets-app/src/ui/index.ts index 0338e6cb..a7d0720c 100644 --- a/apps/billets-app/src/ui/index.ts +++ b/apps/billets-app/src/ui/index.ts @@ -3,5 +3,6 @@ export * from './common-back-icon-button' export * from './common-image-viewer' export * from './common-list-empty' export * from './common-screen-layout' +export * from './my-screen-landing-layout' export * from './navigation-header' export * from './tab-bar' diff --git a/apps/billets-app/src/ui/my-screen-landing-layout/index.ts b/apps/billets-app/src/ui/my-screen-landing-layout/index.ts new file mode 100644 index 00000000..de1392ba --- /dev/null +++ b/apps/billets-app/src/ui/my-screen-landing-layout/index.ts @@ -0,0 +1 @@ +export * from './my-screen-landing-layout' diff --git a/apps/billets-app/src/ui/my-screen-landing-layout/my-screen-landing-layout.tsx b/apps/billets-app/src/ui/my-screen-landing-layout/my-screen-landing-layout.tsx new file mode 100644 index 00000000..888afc82 --- /dev/null +++ b/apps/billets-app/src/ui/my-screen-landing-layout/my-screen-landing-layout.tsx @@ -0,0 +1,38 @@ +import { colors } from '@coldsurfers/ocean-road' +import { Button, Text } from '@coldsurfers/ocean-road/native' +import { memo } from 'react' +import { StyleSheet } from 'react-native' +import { CommonScreenLayout } from '../common-screen-layout' + +export const MyScreenLandingLayout = memo(({ onPressLoginButton }: { onPressLoginButton: () => void }) => { + return ( + + + {`๐ŸŽ‰\n์˜ˆ์ •๋œ ๋งŽ์€\n๊ณต์—ฐ์„\n๋†“์น˜์ง€ ๋งˆ์„ธ์š”`} + + {`๋กœ๊ทธ์ธ ํ›„ ์ฐœํ•˜๊ธฐ๋ฅผ ์‚ฌ์šฉํ•ด๋ณด์„ธ์š”`} + + + ) +}) + +const styles = StyleSheet.create({ + wrapper: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + loginButton: { + backgroundColor: colors.oc.cyan[8].value, + marginTop: 16, + }, + loginText: { fontSize: 24, textAlign: 'center' }, + loginSubText: { + fontSize: 16, + textAlign: 'center', + color: colors.oc.gray[7].value, + marginTop: 8, + }, +}) From a76bdfe0784fd0ee28018dfa546d641670f3e660 Mon Sep 17 00:00:00 2001 From: yungblud Date: Sat, 21 Dec 2024 15:25:20 +0900 Subject: [PATCH 05/12] feat(billets-app): :art: suspense my screen --- .../src/screens/my-screen/my-screen.tsx | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/apps/billets-app/src/screens/my-screen/my-screen.tsx b/apps/billets-app/src/screens/my-screen/my-screen.tsx index e4d44cde..90d198d4 100644 --- a/apps/billets-app/src/screens/my-screen/my-screen.tsx +++ b/apps/billets-app/src/screens/my-screen/my-screen.tsx @@ -1,9 +1,10 @@ import { SubscribedConcertList } from '@/features' import { $api } from '@/lib/api/openapi-client' +import useGetMeQuery from '@/lib/react-query/queries/useGetMeQuery' import { CommonScreenLayout, MyScreenLandingLayout } from '@/ui' import { colors } from '@coldsurfers/ocean-road' import { Button, ProfileThumbnail, Spinner, Text } from '@coldsurfers/ocean-road/native' -import React, { useCallback, useContext, useEffect, useState } from 'react' +import React, { Suspense, useCallback, useContext, useEffect, useState } from 'react' import { Alert, Pressable, SectionList, SectionListRenderItem, StyleSheet, View } from 'react-native' import { match } from 'ts-pattern' import { AuthContext } from '../../lib/contexts/auth-context/auth-context' @@ -51,9 +52,10 @@ const ListFooterComponent = () => { ) } -export const MyScreen = () => { +const SuspenseMyScreen = () => { const navigation = useMyScreenNavigation() - const { user, logout, isLoading } = useContext(AuthContext) + const { logout } = useContext(AuthContext) + const { data: user } = useGetMeQuery() const [settingSections, setSettingSections] = useState>([]) const onPressLoginButton = useCallback(() => { navigation.navigate('LoginStackNavigation', { @@ -174,14 +176,6 @@ export const MyScreen = () => { ]) }, [logout, navigation, user]) - if (isLoading) { - return ( - - - - ) - } - return user ? ( { ) } +export const MyScreen = () => { + return ( + }> + + + ) +} + const styles = StyleSheet.create({ wrapper: { flex: 1, From 3a5da6701ddba69e4267beb3a6a28f4c6b05ea77 Mon Sep 17 00:00:00 2001 From: yungblud Date: Sat, 21 Dec 2024 15:30:13 +0900 Subject: [PATCH 06/12] refactor(billets-app): :art: refactored my screen sections --- .../src/screens/my-screen/my-screen.tsx | 23 ++++++++++--------- .../src/screens/my-screen/my-screen.types.ts | 9 ++++---- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/apps/billets-app/src/screens/my-screen/my-screen.tsx b/apps/billets-app/src/screens/my-screen/my-screen.tsx index 90d198d4..f576ebfc 100644 --- a/apps/billets-app/src/screens/my-screen/my-screen.tsx +++ b/apps/billets-app/src/screens/my-screen/my-screen.tsx @@ -4,12 +4,16 @@ import useGetMeQuery from '@/lib/react-query/queries/useGetMeQuery' import { CommonScreenLayout, MyScreenLandingLayout } from '@/ui' import { colors } from '@coldsurfers/ocean-road' import { Button, ProfileThumbnail, Spinner, Text } from '@coldsurfers/ocean-road/native' -import React, { Suspense, useCallback, useContext, useEffect, useState } from 'react' +import React, { Suspense, useCallback, useContext, useMemo } from 'react' import { Alert, Pressable, SectionList, SectionListRenderItem, StyleSheet, View } from 'react-native' import { match } from 'ts-pattern' import { AuthContext } from '../../lib/contexts/auth-context/auth-context' import { useMyScreenNavigation } from './my-screen.hooks' -import { MyScreenSettingSectionListData, MyScreenSettingSectionListSectionT } from './my-screen.types' +import { + MyScreenSettingSectionListData, + MyScreenSettingSectionListSectionDataT, + MyScreenSettingSectionListSectionT, +} from './my-screen.types' const ListFooterComponent = () => { const { logout } = useContext(AuthContext) @@ -56,7 +60,6 @@ const SuspenseMyScreen = () => { const navigation = useMyScreenNavigation() const { logout } = useContext(AuthContext) const { data: user } = useGetMeQuery() - const [settingSections, setSettingSections] = useState>([]) const onPressLoginButton = useCallback(() => { navigation.navigate('LoginStackNavigation', { screen: 'LoginSelectionScreen', @@ -120,12 +123,11 @@ const SuspenseMyScreen = () => { [onPressSubscribedConcertListItem], ) - useEffect(() => { + const sections = useMemo(() => { if (!user) { - return + return [] } - - setSettingSections([ + return [ { title: 'profile', uiTitle: '๐Ÿ™‚ ๋งˆ์ด ํ”„๋กœํ•„', @@ -165,7 +167,6 @@ const SuspenseMyScreen = () => { screen: 'HomeScreen', params: {}, }) - setSettingSections([]) }, }, ]) @@ -173,15 +174,15 @@ const SuspenseMyScreen = () => { }, ], }, - ]) + ] }, [logout, navigation, user]) return user ? ( - contentContainerStyle={styles.sectionListContentContainer} style={styles.sectionList} - sections={settingSections} + sections={sections} stickySectionHeadersEnabled={false} ListFooterComponent={ListFooterComponent} renderSectionHeader={renderSectionHeader} diff --git a/apps/billets-app/src/screens/my-screen/my-screen.types.ts b/apps/billets-app/src/screens/my-screen/my-screen.types.ts index 60de6b17..083467c0 100644 --- a/apps/billets-app/src/screens/my-screen/my-screen.types.ts +++ b/apps/billets-app/src/screens/my-screen/my-screen.types.ts @@ -13,10 +13,11 @@ export type MyScreenSettingSectionListSectionT = { onPress: () => void } } +export type MyScreenSettingSectionListSectionDataT = { + title: string + onPress: () => void +} export type MyScreenSettingSectionListData = SectionListData< - { - title: string - onPress: () => void - }, + MyScreenSettingSectionListSectionDataT, MyScreenSettingSectionListSectionT > From 1855fb56d1cdb6d6189f49840b61580d529465a5 Mon Sep 17 00:00:00 2001 From: yungblud Date: Sat, 21 Dec 2024 16:34:35 +0900 Subject: [PATCH 07/12] feat(billets-app): :art: implemented suspense on my screen --- .../concert-list-item/concert-list-item.tsx | 1 + .../ui/subscribed-concert-list/index.ts | 1 + .../subscribed-concert-list-skeleton.tsx | 24 ++++++++++++ .../subscribed-concert-list.styles.ts | 11 ++++++ .../subscribed-concert-list.tsx | 38 +++++++------------ .../react-query/queries/useConcertQuery.ts | 6 +-- .../queries/useSubscribedConcertListQuery.ts | 13 +++++-- .../src/screens/my-screen/my-screen.tsx | 8 +++- 8 files changed, 70 insertions(+), 32 deletions(-) create mode 100644 apps/billets-app/src/features/subscribed-concert/ui/subscribed-concert-list/subscribed-concert-list-skeleton.tsx create mode 100644 apps/billets-app/src/features/subscribed-concert/ui/subscribed-concert-list/subscribed-concert-list.styles.ts diff --git a/apps/billets-app/src/features/concert/ui/concert-list-item/concert-list-item.tsx b/apps/billets-app/src/features/concert/ui/concert-list-item/concert-list-item.tsx index 8bcb6242..c01c7e3b 100644 --- a/apps/billets-app/src/features/concert/ui/concert-list-item/concert-list-item.tsx +++ b/apps/billets-app/src/features/concert/ui/concert-list-item/concert-list-item.tsx @@ -108,6 +108,7 @@ ConcertListItem.Skeleton = ({ size = 'large' }: { size?: 'small' | 'large' }) => + {size === 'small' && } { + return +} + +const data = Array.from({ length: 5 }) + +const ItemSeparator = () => + +export const SubscribedConcertListSkeleton = memo(() => { + return ( + + ) +}) diff --git a/apps/billets-app/src/features/subscribed-concert/ui/subscribed-concert-list/subscribed-concert-list.styles.ts b/apps/billets-app/src/features/subscribed-concert/ui/subscribed-concert-list/subscribed-concert-list.styles.ts new file mode 100644 index 00000000..20c1d72a --- /dev/null +++ b/apps/billets-app/src/features/subscribed-concert/ui/subscribed-concert-list/subscribed-concert-list.styles.ts @@ -0,0 +1,11 @@ +import { StyleSheet } from 'react-native' + +export const subscribedConcertListStyles = StyleSheet.create({ + contentContainer: { + paddingHorizontal: 16, + marginTop: 12, + }, + itemSeparator: { + width: 10, + }, +}) diff --git a/apps/billets-app/src/features/subscribed-concert/ui/subscribed-concert-list/subscribed-concert-list.tsx b/apps/billets-app/src/features/subscribed-concert/ui/subscribed-concert-list/subscribed-concert-list.tsx index 32c82fda..b7bb938d 100644 --- a/apps/billets-app/src/features/subscribed-concert/ui/subscribed-concert-list/subscribed-concert-list.tsx +++ b/apps/billets-app/src/features/subscribed-concert/ui/subscribed-concert-list/subscribed-concert-list.tsx @@ -1,10 +1,11 @@ -import { useCallback, useMemo, useState } from 'react' -import { FlatList, ListRenderItem, StyleSheet, View } from 'react-native' -import useGetMeQuery from '../../../../lib/react-query/queries/useGetMeQuery' +import { ConcertListItem } from '@/features/concert' +import { Suspense, useCallback, useMemo, useState } from 'react' +import { FlatList, ListRenderItem, View } from 'react-native' import useSubscribedConcertListQuery from '../../../../lib/react-query/queries/useSubscribedConcertListQuery' import { SubscribedConcertListItem } from '../subscribed-concert-list-item' +import { subscribedConcertListStyles } from './subscribed-concert-list.styles' -const ItemSeparator = () => +const ItemSeparator = () => export function SubscribedConcertList({ onPressItem, @@ -16,7 +17,6 @@ export function SubscribedConcertList({ listHeaderComponent?: React.ComponentType }) { const [isRefreshing, setIsRefreshing] = useState(false) - const { data: meData } = useGetMeQuery() const { data: concertListData, fetchNextPage, @@ -25,20 +25,20 @@ export function SubscribedConcertList({ hasNextPage, isPending, refetch, - } = useSubscribedConcertListQuery({ - enabled: !!meData, - }) + } = useSubscribedConcertListQuery() const listData = useMemo(() => { return concertListData?.pages.flatMap((page) => page).filter((v) => !!v) ?? [] }, [concertListData]) const renderItem = useCallback>( (info) => { return ( - + }> + + ) }, [horizontal, onPressItem], @@ -67,7 +67,7 @@ export function SubscribedConcertList({ keyExtractor={(item, index) => `${item.concertId}-${index}`} renderItem={renderItem} ItemSeparatorComponent={ItemSeparator} - contentContainerStyle={styles.contentContainer} + contentContainerStyle={subscribedConcertListStyles.contentContainer} ListHeaderComponent={listHeaderComponent} onEndReached={horizontal ? undefined : onEndReached} onRefresh={horizontal ? undefined : onRefresh} @@ -78,13 +78,3 @@ export function SubscribedConcertList({ /> ) } - -const styles = StyleSheet.create({ - contentContainer: { - paddingHorizontal: 16, - marginTop: 12, - }, - itemSeparator: { - width: 10, - }, -}) diff --git a/apps/billets-app/src/lib/react-query/queries/useConcertQuery.ts b/apps/billets-app/src/lib/react-query/queries/useConcertQuery.ts index fc98c207..5b067b1f 100644 --- a/apps/billets-app/src/lib/react-query/queries/useConcertQuery.ts +++ b/apps/billets-app/src/lib/react-query/queries/useConcertQuery.ts @@ -1,4 +1,4 @@ -import { UseQueryOptions, useQuery } from '@tanstack/react-query' +import { UseSuspenseQueryOptions, useSuspenseQuery } from '@tanstack/react-query' import { fetchClient } from '../../api/openapi-client' import { v1QueryKeyFactory } from '../../query-key-factory' @@ -20,9 +20,9 @@ type TQueryKey = ReturnType<(typeof v1QueryKeyFactory)['concerts']['detail']>['q function useConcertQuery( variables: { concertId: string }, - options?: UseQueryOptions, + options?: UseSuspenseQueryOptions, ) { - return useQuery({ + return useSuspenseQuery({ ...options, queryFn: () => queryFn(variables), queryKey: v1QueryKeyFactory.concerts.detail(variables).queryKey, diff --git a/apps/billets-app/src/lib/react-query/queries/useSubscribedConcertListQuery.ts b/apps/billets-app/src/lib/react-query/queries/useSubscribedConcertListQuery.ts index ad927efd..19e6e3df 100644 --- a/apps/billets-app/src/lib/react-query/queries/useSubscribedConcertListQuery.ts +++ b/apps/billets-app/src/lib/react-query/queries/useSubscribedConcertListQuery.ts @@ -1,6 +1,6 @@ import { fetchClient } from '@/lib/api/openapi-client' import { v1QueryKeyFactory } from '@/lib/query-key-factory' -import { InfiniteData, useInfiniteQuery, UseInfiniteQueryOptions } from '@tanstack/react-query' +import { InfiniteData, useSuspenseInfiniteQuery, UseSuspenseInfiniteQueryOptions } from '@tanstack/react-query' const DEFAULT_SIZE = 20 @@ -23,11 +23,18 @@ type TQueryKey = (typeof v1QueryKeyFactory)['concerts']['subscribedList']['query type TPageParam = number type Options = Partial< - UseInfiniteQueryOptions, TQueryData, TQueryKey, TPageParam> + UseSuspenseInfiniteQueryOptions< + TQueryData, + TError, + InfiniteData, + TQueryData, + TQueryKey, + TPageParam + > > const useSubscribedConcertListQuery = (options?: Options) => { - return useInfiniteQuery({ + return useSuspenseInfiniteQuery({ ...options, initialPageParam: 0, queryKey: v1QueryKeyFactory.concerts.subscribedList.queryKey, diff --git a/apps/billets-app/src/screens/my-screen/my-screen.tsx b/apps/billets-app/src/screens/my-screen/my-screen.tsx index f576ebfc..a3137786 100644 --- a/apps/billets-app/src/screens/my-screen/my-screen.tsx +++ b/apps/billets-app/src/screens/my-screen/my-screen.tsx @@ -1,4 +1,4 @@ -import { SubscribedConcertList } from '@/features' +import { SubscribedConcertList, SubscribedConcertListSkeleton } from '@/features' import { $api } from '@/lib/api/openapi-client' import useGetMeQuery from '@/lib/react-query/queries/useGetMeQuery' import { CommonScreenLayout, MyScreenLandingLayout } from '@/ui' @@ -116,7 +116,11 @@ const SuspenseMyScreen = () => { ) }) .with('saved', () => { - return + return ( + }> + + + ) }) .exhaustive() }, From 2b8a1138043cb580a8e7c1c1f6788de87a2bdc23 Mon Sep 17 00:00:00 2001 From: yungblud Date: Sat, 21 Dec 2024 16:36:25 +0900 Subject: [PATCH 08/12] feat(billets-app): :art: suspense query useGetMeQuery --- .../billets-app/src/lib/react-query/queries/useGetMeQuery.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/billets-app/src/lib/react-query/queries/useGetMeQuery.ts b/apps/billets-app/src/lib/react-query/queries/useGetMeQuery.ts index 33079c02..f70c5c9d 100644 --- a/apps/billets-app/src/lib/react-query/queries/useGetMeQuery.ts +++ b/apps/billets-app/src/lib/react-query/queries/useGetMeQuery.ts @@ -1,4 +1,4 @@ -import { useQuery } from '@tanstack/react-query' +import { useSuspenseQuery } from '@tanstack/react-query' import { fetchClient } from '../../api/openapi-client' import { v1QueryKeyFactory } from '../../query-key-factory' @@ -8,10 +8,9 @@ const queryFn = async () => { } function useGetMeQuery() { - return useQuery({ + return useSuspenseQuery({ queryKey: useGetMeQuery.extractKey(), queryFn, - retry: false, }) } From 4347de6955120c0c9c11c34e104a9e5c584909d6 Mon Sep 17 00:00:00 2001 From: yungblud Date: Mon, 23 Dec 2024 09:26:59 +0900 Subject: [PATCH 09/12] feat(billets-app): :art: enhanced map region by maintaining region value --- .../hooks/use-map-region-with-zoom-level.ts | 14 ++++++- .../ui/concert-map-view/concert-map-view.tsx | 32 +++------------- .../screens/search-screen/search-screen.tsx | 38 ++++++++++++++++++- 3 files changed, 55 insertions(+), 29 deletions(-) diff --git a/apps/billets-app/src/features/map/hooks/use-map-region-with-zoom-level.ts b/apps/billets-app/src/features/map/hooks/use-map-region-with-zoom-level.ts index a1ed5628..f8caf78f 100644 --- a/apps/billets-app/src/features/map/hooks/use-map-region-with-zoom-level.ts +++ b/apps/billets-app/src/features/map/hooks/use-map-region-with-zoom-level.ts @@ -1,4 +1,5 @@ -import { useState } from 'react' +import { useCallback, useState } from 'react' +import { LatLng } from 'react-native-maps' import { getZoomLevel } from '../utils' import { MapRegionWithZoomLevel, UseMapRegionWithZoomLevelParams } from './use-map-region-with-zoom-level.types' @@ -11,8 +12,19 @@ export const useMapRegionWithZoomLevel = ({ latitude, longitude }: UseMapRegionW zoomLevel: getZoomLevel(0.0922), }) + const initialize = useCallback((params: LatLng) => { + setMapRegionWithZoomLevel({ + latitude: params.latitude, + longitude: params.longitude, + latitudeDelta: 0.0922, + longitudeDelta: 0.0421, + zoomLevel: getZoomLevel(0.0922), + }) + }, []) + return { mapRegionWithZoomLevel, setMapRegionWithZoomLevel, + initialize, } } diff --git a/apps/billets-app/src/features/map/ui/concert-map-view/concert-map-view.tsx b/apps/billets-app/src/features/map/ui/concert-map-view/concert-map-view.tsx index 48c893db..448e78e2 100644 --- a/apps/billets-app/src/features/map/ui/concert-map-view/concert-map-view.tsx +++ b/apps/billets-app/src/features/map/ui/concert-map-view/concert-map-view.tsx @@ -1,21 +1,22 @@ -import { useUserCurrentLocationStore } from '@/features/location' -import { memo, useCallback, useEffect, useMemo } from 'react' +import { memo, useEffect, useMemo } from 'react' import { StyleSheet } from 'react-native' import MapView, { Camera, Region } from 'react-native-maps' import { z } from 'zod' -import { useShallow } from 'zustand/shallow' -import { useMapPoints, useMapRegionWithZoomLevel, useSuperCluster } from '../../hooks' +import { MapRegionWithZoomLevel, useMapPoints, useSuperCluster } from '../../hooks' import { mapPointSchema } from '../../map.types' -import { getZoomLevel } from '../../utils' import { ConcertMapMarker } from '../concert-map-marker' export const ConcertMapView = memo( ({ + mapRegionWithZoomLevel, onChangeClusters, onChangePoints, onChangeVisiblePoints, onChangeLocationConcerts, + onRegionChangeComplete, }: { + mapRegionWithZoomLevel: MapRegionWithZoomLevel + onRegionChangeComplete: (region: Region) => void onChangeClusters?: (clusters: z.infer[]) => void onChangePoints?: (points: z.infer[]) => void onChangeVisiblePoints?: (visiblePoints: z.infer[]) => void @@ -28,17 +29,6 @@ export const ConcertMapView = memo( }[], ) => void }) => { - const { lat, lng } = useUserCurrentLocationStore( - useShallow((state) => ({ - lat: state.latitude, - lng: state.longitude, - })), - ) - const { mapRegionWithZoomLevel, setMapRegionWithZoomLevel } = useMapRegionWithZoomLevel({ - latitude: lat ?? 37.78825, - longitude: lng ?? -122.4324, - }) - const { points, visiblePoints, locationConcerts } = useMapPoints({ mapRegionWithZoomLevel, }) @@ -48,16 +38,6 @@ export const ConcertMapView = memo( points, }) - const onRegionChangeComplete = useCallback( - (region: Region) => { - setMapRegionWithZoomLevel({ - ...region, - zoomLevel: getZoomLevel(region.latitudeDelta), - }) - }, - [setMapRegionWithZoomLevel], - ) - const initialCamera = useMemo(() => { return { center: { diff --git a/apps/billets-app/src/screens/search-screen/search-screen.tsx b/apps/billets-app/src/screens/search-screen/search-screen.tsx index 0f64b2ed..0f200b87 100644 --- a/apps/billets-app/src/screens/search-screen/search-screen.tsx +++ b/apps/billets-app/src/screens/search-screen/search-screen.tsx @@ -1,4 +1,10 @@ -import { ConcertMapView, mapPointSchema, useUserCurrentLocationStore } from '@/features' +import { + ConcertMapView, + getZoomLevel, + mapPointSchema, + useMapRegionWithZoomLevel, + useUserCurrentLocationStore, +} from '@/features' import { getViewMode, SearchStoreLocationConcert, SearchStoreSnapIndex, useSearchStore } from '@/features/search/store' import { FULLY_EXPANDED_SNAP_INDEX } from '@/features/search/store/search-store.constants' import { SearchBottomList } from '@/features/search/ui' @@ -12,6 +18,7 @@ import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs' import { useDebounce } from '@uidotdev/usehooks' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { Keyboard, KeyboardAvoidingView, KeyboardAvoidingViewProps, Platform, StyleSheet, View } from 'react-native' +import { Region } from 'react-native-maps' import Animated, { runOnJS, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated' import { z } from 'zod' import { useShallow } from 'zustand/shallow' @@ -43,9 +50,10 @@ export const SearchScreen = () => { keyword: state.keyword, })), ) - const { setSelectedLocationFilter } = useSearchStore( + const { setSelectedLocationFilter, selectedLocationFilter } = useSearchStore( useShallow((state) => ({ setSelectedLocationFilter: state.setSelectedLocationFilter, + selectedLocationFilter: state.selectedLocationFilter, })), ) const { viewMode, setViewMode, snapIndex, setSnapIndex } = useSearchStore( @@ -80,6 +88,11 @@ export const SearchScreen = () => { })), ) + const { mapRegionWithZoomLevel, setMapRegionWithZoomLevel, initialize } = useMapRegionWithZoomLevel({ + latitude: latitude ?? 37.78825, + longitude: longitude ?? -122.4324, + }) + useEffect(() => { const keyboardWillShowEmitterSubscription = Keyboard.addListener('keyboardWillShow', (e) => { bottomBtnBottomValue.value = withTiming(e.endCoordinates.height - bottomTabBarHeight, { duration: 250 }) @@ -149,14 +162,35 @@ export const SearchScreen = () => { [setLocationConcerts], ) + const onRegionChangeComplete = useCallback( + (region: Region) => { + setMapRegionWithZoomLevel({ + ...region, + zoomLevel: getZoomLevel(region.latitudeDelta), + }) + }, + [setMapRegionWithZoomLevel], + ) + + useEffect(() => { + if (selectedLocationFilter === 'current-location' || selectedLocationFilter === null) { + initialize({ + latitude: latitude ?? 37.78825, + longitude: longitude ?? -122.4324, + }) + } + }, [initialize, latitude, longitude, selectedLocationFilter]) + return ( {viewMode === 'map' && ( )} Date: Mon, 23 Dec 2024 09:30:54 +0900 Subject: [PATCH 10/12] feat(billets-app): :art: implemented useShowBottomTabBar --- apps/billets-app/src/lib/hooks/index.ts | 1 + .../lib/hooks/use-show-bottom-tab-bar/index.ts | 1 + .../use-show-bottom-tab-bar.tsx | 15 +++++++++++++++ .../src/screens/home-screen/home-screen.tsx | 2 ++ .../src/screens/my-screen/my-screen.tsx | 4 ++++ 5 files changed, 23 insertions(+) create mode 100644 apps/billets-app/src/lib/hooks/use-show-bottom-tab-bar/index.ts create mode 100644 apps/billets-app/src/lib/hooks/use-show-bottom-tab-bar/use-show-bottom-tab-bar.tsx diff --git a/apps/billets-app/src/lib/hooks/index.ts b/apps/billets-app/src/lib/hooks/index.ts index 5b987222..a2d83e29 100644 --- a/apps/billets-app/src/lib/hooks/index.ts +++ b/apps/billets-app/src/lib/hooks/index.ts @@ -4,4 +4,5 @@ export * from './use-firebase-analytics' export * from './use-firebase-crashlytics' export * from './use-firebase-messaging' export * from './use-is-tablet' +export * from './use-show-bottom-tab-bar' export * from './use-store-review' diff --git a/apps/billets-app/src/lib/hooks/use-show-bottom-tab-bar/index.ts b/apps/billets-app/src/lib/hooks/use-show-bottom-tab-bar/index.ts new file mode 100644 index 00000000..ba10559f --- /dev/null +++ b/apps/billets-app/src/lib/hooks/use-show-bottom-tab-bar/index.ts @@ -0,0 +1 @@ +export * from './use-show-bottom-tab-bar' diff --git a/apps/billets-app/src/lib/hooks/use-show-bottom-tab-bar/use-show-bottom-tab-bar.tsx b/apps/billets-app/src/lib/hooks/use-show-bottom-tab-bar/use-show-bottom-tab-bar.tsx new file mode 100644 index 00000000..c388697f --- /dev/null +++ b/apps/billets-app/src/lib/hooks/use-show-bottom-tab-bar/use-show-bottom-tab-bar.tsx @@ -0,0 +1,15 @@ +import { useUIStore } from '@/lib/stores/ui-store' +import { useEffect } from 'react' +import { useShallow } from 'zustand/shallow' + +export function useShowBottomTabBar() { + const { showBottomTabBar } = useUIStore( + useShallow((state) => ({ + showBottomTabBar: state.showBottomTabBar, + })), + ) + + useEffect(() => { + showBottomTabBar() + }, [showBottomTabBar]) +} diff --git a/apps/billets-app/src/screens/home-screen/home-screen.tsx b/apps/billets-app/src/screens/home-screen/home-screen.tsx index 243171a5..55c0dc86 100644 --- a/apps/billets-app/src/screens/home-screen/home-screen.tsx +++ b/apps/billets-app/src/screens/home-screen/home-screen.tsx @@ -6,6 +6,7 @@ import { useUserCurrentLocationStore, } from '@/features' import { ConcertListItemT } from '@/features/concert/ui/concert-list/concert-list.types' +import { useShowBottomTabBar } from '@/lib' import { AnimatePresence, CommonScreenLayout } from '@/ui' import { useScrollToTop } from '@react-navigation/native' import { Suspense, useCallback, useRef, useState } from 'react' @@ -19,6 +20,7 @@ const SuspenseHomeScreen = () => { const navigation = useHomeScreenNavigation() const listRef = useRef(null) useScrollToTop(listRef) + useShowBottomTabBar() const [locationModalVisible, setLocationModalVisible] = useState(false) const { latitude, longitude } = useUserCurrentLocationStore( diff --git a/apps/billets-app/src/screens/my-screen/my-screen.tsx b/apps/billets-app/src/screens/my-screen/my-screen.tsx index a3137786..5fafaf09 100644 --- a/apps/billets-app/src/screens/my-screen/my-screen.tsx +++ b/apps/billets-app/src/screens/my-screen/my-screen.tsx @@ -1,4 +1,5 @@ import { SubscribedConcertList, SubscribedConcertListSkeleton } from '@/features' +import { useShowBottomTabBar } from '@/lib' import { $api } from '@/lib/api/openapi-client' import useGetMeQuery from '@/lib/react-query/queries/useGetMeQuery' import { CommonScreenLayout, MyScreenLandingLayout } from '@/ui' @@ -60,6 +61,9 @@ const SuspenseMyScreen = () => { const navigation = useMyScreenNavigation() const { logout } = useContext(AuthContext) const { data: user } = useGetMeQuery() + + useShowBottomTabBar() + const onPressLoginButton = useCallback(() => { navigation.navigate('LoginStackNavigation', { screen: 'LoginSelectionScreen', From ddfa195318be7385bb924541d7cfbe92313c64df Mon Sep 17 00:00:00 2001 From: yungblud Date: Mon, 23 Dec 2024 09:36:55 +0900 Subject: [PATCH 11/12] feat(billets-app): :art: enhanced map list with empty indicator --- apps/billets-app/src/features/search/store/search-store.ts | 2 +- .../src/features/search/store/search-store.types.ts | 4 ++-- .../search/ui/search-bottom-list/search-bottom-list.tsx | 4 ++-- .../search-location-concert-list.tsx | 2 ++ apps/billets-app/src/screens/search-screen/search-screen.tsx | 3 ++- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/billets-app/src/features/search/store/search-store.ts b/apps/billets-app/src/features/search/store/search-store.ts index ac038434..8241e246 100644 --- a/apps/billets-app/src/features/search/store/search-store.ts +++ b/apps/billets-app/src/features/search/store/search-store.ts @@ -8,7 +8,7 @@ export const useSearchStore = create((set) => ({ selectedLocationFilter: null, snapIndex: FULLY_EXPANDED_SNAP_INDEX, viewMode: 'list', - locationConcerts: [], + locationConcerts: null, setSnapIndex: (index) => set({ snapIndex: index }), setViewMode: (mode) => set({ viewMode: mode }), setKeyword: (keyword) => set({ keyword }), diff --git a/apps/billets-app/src/features/search/store/search-store.types.ts b/apps/billets-app/src/features/search/store/search-store.types.ts index 6da9d53b..43562491 100644 --- a/apps/billets-app/src/features/search/store/search-store.types.ts +++ b/apps/billets-app/src/features/search/store/search-store.types.ts @@ -15,7 +15,7 @@ export interface SearchStoreState { selectedLocationFilter: SearchStoreLocationFilterType | null snapIndex: SearchStoreSnapIndex viewMode: SearchStoreViewMode - locationConcerts: SearchStoreLocationConcert[] + locationConcerts: SearchStoreLocationConcert[] | null } export interface SearchStoreActions { @@ -23,6 +23,6 @@ export interface SearchStoreActions { setSelectedLocationFilter: (filter: SearchStoreLocationFilterType | null) => void setSnapIndex: (index: SearchStoreSnapIndex) => void setViewMode: (mode: SearchStoreViewMode) => void - setLocationConcerts: (locationConcerts: SearchStoreLocationConcert[]) => void + setLocationConcerts: (locationConcerts: SearchStoreLocationConcert[] | null) => void } export type SearchStore = SearchStoreState & SearchStoreActions diff --git a/apps/billets-app/src/features/search/ui/search-bottom-list/search-bottom-list.tsx b/apps/billets-app/src/features/search/ui/search-bottom-list/search-bottom-list.tsx index 779b5982..bb313029 100644 --- a/apps/billets-app/src/features/search/ui/search-bottom-list/search-bottom-list.tsx +++ b/apps/billets-app/src/features/search/ui/search-bottom-list/search-bottom-list.tsx @@ -14,12 +14,12 @@ export const SearchBottomList = memo( debouncedSearchKeyword: string latitude: number | null longitude: number | null - locationConcerts: SearchStoreLocationConcert[] + locationConcerts: SearchStoreLocationConcert[] | null }) => { if (debouncedSearchKeyword) { return } - if (locationConcerts.length > 0) { + if (Array.isArray(locationConcerts)) { return } if (latitude !== null && longitude !== null) { diff --git a/apps/billets-app/src/features/search/ui/search-location-concert-list/search-location-concert-list.tsx b/apps/billets-app/src/features/search/ui/search-location-concert-list/search-location-concert-list.tsx index 139f7558..8db84220 100644 --- a/apps/billets-app/src/features/search/ui/search-location-concert-list/search-location-concert-list.tsx +++ b/apps/billets-app/src/features/search/ui/search-location-concert-list/search-location-concert-list.tsx @@ -1,3 +1,4 @@ +import { CommonListEmpty } from '@/ui' import { colors } from '@coldsurfers/ocean-road' import { BottomSheetFlatList } from '@gorhom/bottom-sheet' import { useFocusEffect } from '@react-navigation/native' @@ -49,6 +50,7 @@ export const SearchLocationConcertList = memo(({ locationConcerts }: SearchLocat onEndReached={onEndReached} style={styles.list} contentContainerStyle={styles.contentContainer} + ListEmptyComponent={} ListFooterComponent={isLoading ? : null} /> ) diff --git a/apps/billets-app/src/screens/search-screen/search-screen.tsx b/apps/billets-app/src/screens/search-screen/search-screen.tsx index 0f200b87..4ba6d392 100644 --- a/apps/billets-app/src/screens/search-screen/search-screen.tsx +++ b/apps/billets-app/src/screens/search-screen/search-screen.tsx @@ -178,8 +178,9 @@ export const SearchScreen = () => { latitude: latitude ?? 37.78825, longitude: longitude ?? -122.4324, }) + setLocationConcerts(null) } - }, [initialize, latitude, longitude, selectedLocationFilter]) + }, [initialize, latitude, longitude, selectedLocationFilter, setLocationConcerts]) return ( From a2c8b8a1b5201188e699b2317c387fb35b323080 Mon Sep 17 00:00:00 2001 From: yungblud Date: Mon, 23 Dec 2024 09:48:37 +0900 Subject: [PATCH 12/12] feat(billets-app): :package: native version 1.6.2 --- apps/billets-app/android/app/build.gradle | 2 +- apps/billets-app/ios/FstvlLifeApp.xcodeproj/project.pbxproj | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/billets-app/android/app/build.gradle b/apps/billets-app/android/app/build.gradle index d65cd96c..13ba01fd 100644 --- a/apps/billets-app/android/app/build.gradle +++ b/apps/billets-app/android/app/build.gradle @@ -98,7 +98,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 14 - versionName "1.6.1" + versionName "1.6.2" resValue "string", "CodePushDeploymentKey", project.env.get("ANDROID_CODE_PUSH_DEPLOYMENT_KEY") ?: "" } diff --git a/apps/billets-app/ios/FstvlLifeApp.xcodeproj/project.pbxproj b/apps/billets-app/ios/FstvlLifeApp.xcodeproj/project.pbxproj index f2bfa07b..d13ae67f 100644 --- a/apps/billets-app/ios/FstvlLifeApp.xcodeproj/project.pbxproj +++ b/apps/billets-app/ios/FstvlLifeApp.xcodeproj/project.pbxproj @@ -600,7 +600,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.6.1; + MARKETING_VERSION = 1.6.2; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -632,7 +632,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.6.1; + MARKETING_VERSION = 1.6.2; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -923,7 +923,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.6.1; + MARKETING_VERSION = 1.6.2; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC",