From 26897d407eafc9d277644db6637174d853c6d0c8 Mon Sep 17 00:00:00 2001 From: Dustin Do Date: Tue, 6 Aug 2024 23:51:19 +0700 Subject: [PATCH] feat(mobile): add store data refetching in interval --- apps/mobile/app/(app)/(tabs)/index.tsx | 46 ++++++------ apps/mobile/app/_layout.tsx | 2 +- apps/mobile/stores/category/hooks.tsx | 18 +++-- .../stores/core/store-interval-update.tsx | 30 ++++++++ .../stores/{ => core}/store-provider.tsx | 8 ++- apps/mobile/stores/core/stores.const.ts | 1 + apps/mobile/stores/core/stores.d.ts | 4 ++ .../{ => core}/use-reset-all-stores.tsx | 6 +- apps/mobile/stores/transaction/hooks.tsx | 71 +++++++++++++++---- 9 files changed, 144 insertions(+), 42 deletions(-) create mode 100644 apps/mobile/stores/core/store-interval-update.tsx rename apps/mobile/stores/{ => core}/store-provider.tsx (92%) create mode 100644 apps/mobile/stores/core/stores.const.ts create mode 100644 apps/mobile/stores/core/stores.d.ts rename apps/mobile/stores/{ => core}/use-reset-all-stores.tsx (76%) diff --git a/apps/mobile/app/(app)/(tabs)/index.tsx b/apps/mobile/app/(app)/(tabs)/index.tsx index 1e703e9a..cd2333f1 100644 --- a/apps/mobile/app/(app)/(tabs)/index.tsx +++ b/apps/mobile/app/(app)/(tabs)/index.tsx @@ -1,5 +1,4 @@ import { AmountFormat } from '@/components/common/amount-format' -import { ListSkeleton } from '@/components/common/list-skeleton' import { Toolbar } from '@/components/common/toolbar' import { HomeHeader } from '@/components/home/header' import { WalletStatistics } from '@/components/home/wallet-statistics' @@ -9,12 +8,9 @@ import { Text } from '@/components/ui/text' import { useColorScheme } from '@/hooks/useColorScheme' import { formatDateShort } from '@/lib/date' import { theme } from '@/lib/theme' -import { walletQueries } from '@/queries/wallet' import { useTransactionList } from '@/stores/transaction/hooks' -import { dayjsExtended } from '@6pm/utilities' import { t } from '@lingui/macro' import { useLingui } from '@lingui/react' -import { useQueryClient } from '@tanstack/react-query' import { format } from 'date-fns/format' import { LinearGradient } from 'expo-linear-gradient' import { groupBy, mapValues, orderBy, sumBy } from 'lodash-es' @@ -27,21 +23,31 @@ export default function HomeScreen() { const { top, bottom } = useSafeAreaInsets() const { colorScheme } = useColorScheme() const [walletAccountId, setWalletAccountId] = useState() - const queryClient = useQueryClient() + // const queryClient = useQueryClient() - const { transactions, isLoading, isRefetching, refetch } = useTransactionList( - { - walletAccountId, - // FIXME: This should be dynamic @bkdev98 - from: dayjsExtended().subtract(10, 'year').startOf('year').toDate(), - to: dayjsExtended().add(10, 'year').endOf('year').toDate(), - }, - ) + const { + transactions: allTransactions, + isLoading, + // isRefetching, + // refetch, + } = useTransactionList() // TODO: Update params here + + // const handleRefresh = () => { + // refetch() + // queryClient.invalidateQueries({ queryKey: walletQueries.list._def }) + // } - const handleRefresh = () => { - refetch() - queryClient.invalidateQueries({ queryKey: walletQueries.list._def }) - } + const transactions = useMemo( + () => + allTransactions.filter((t) => { + if (!walletAccountId) { + return true + } + + return t.walletAccountId === walletAccountId + }), + [allTransactions, walletAccountId], + ) const transactionsGroupByDate = useMemo(() => { const groupedByDay = groupBy(transactions, (transaction) => @@ -72,8 +78,8 @@ export default function HomeScreen() { } className="flex-1 bg-card" contentContainerStyle={{ paddingBottom: bottom + 32 }} - refreshing={isRefetching} - onRefresh={handleRefresh} + // refreshing={isRefetching} + // onRefresh={handleRefresh} sections={transactionsGroupByDate} keyExtractor={(item) => item.id} renderItem={({ item: transaction }) => ( @@ -96,7 +102,7 @@ export default function HomeScreen() { // } // }} onEndReachedThreshold={0.5} - ListFooterComponent={isLoading ? : null} + // ListFooterComponent={isLoading ? : null} /> {!transactions.length && !isLoading && ( diff --git a/apps/mobile/app/_layout.tsx b/apps/mobile/app/_layout.tsx index 3f9af1d6..c7a040ef 100644 --- a/apps/mobile/app/_layout.tsx +++ b/apps/mobile/app/_layout.tsx @@ -33,7 +33,7 @@ import { useWarmUpBrowser } from '@/hooks/use-warm-up-browser' import { useColorScheme } from '@/hooks/useColorScheme' import { queryClient } from '@/lib/client' import { LocaleProvider } from '@/locales/provider' -import { StoreProvider } from '@/stores/store-provider' +import { StoreProvider } from '@/stores/core/store-provider' import { BottomSheetModalProvider } from '@gorhom/bottom-sheet' import AsyncStorage from '@react-native-async-storage/async-storage' import NetInfo from '@react-native-community/netinfo' diff --git a/apps/mobile/stores/category/hooks.tsx b/apps/mobile/stores/category/hooks.tsx index dabe279d..6fccf557 100644 --- a/apps/mobile/stores/category/hooks.tsx +++ b/apps/mobile/stores/category/hooks.tsx @@ -10,17 +10,27 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { keyBy, omit } from 'lodash-es' import { useMemo } from 'react' import { z } from 'zod' +import type { StoreHookQueryOptions } from '../core/stores' import { categoryQueries } from './queries' import { useCategoryStore } from './store' -export const useCategoryList = () => { +export const useCategoryListQueryOptions = ( + queryOptions?: StoreHookQueryOptions, +) => { const categories = useCategoryStore().categories const setCategoriesState = useCategoryStore((state) => state.setCategories) - - const query = useQuery({ + return { ...categoryQueries.all({ setCategoriesState }), initialData: categories?.length > 0 ? categories : undefined, - }) + ...queryOptions, + } +} + +export const useCategoryList = (queryOptions?: StoreHookQueryOptions) => { + const categories = useCategoryStore().categories + const queryOpts = useCategoryListQueryOptions(queryOptions) + + const query = useQuery(queryOpts) const { categoriesDict, incomeCategories, expenseCategories } = useMemo(() => { diff --git a/apps/mobile/stores/core/store-interval-update.tsx b/apps/mobile/stores/core/store-interval-update.tsx new file mode 100644 index 00000000..361a7d76 --- /dev/null +++ b/apps/mobile/stores/core/store-interval-update.tsx @@ -0,0 +1,30 @@ +import { useSuspenseQueries } from '@tanstack/react-query' +import type { FC } from 'react' +import { useCategoryListQueryOptions } from '../category/hooks' +import { useTransactionListQueryOptions } from '../transaction/hooks' +import type { StoreHookQueryOptions } from './stores' +import { STORE_SYNC_INTERVAL } from './stores.const' + +export type StoreIntervalUpdateProps = { + interval?: number +} + +export const StoreIntervalUpdate: FC = ({ + interval = STORE_SYNC_INTERVAL, +}) => { + const queryOptions: StoreHookQueryOptions = { + refetchInterval: interval, + refetchIntervalInBackground: true, + } + const categoryListQueryOptions = useCategoryListQueryOptions(queryOptions) + const transactionListQueryOptions = useTransactionListQueryOptions( + undefined, + queryOptions, + ) + + useSuspenseQueries({ + queries: [{ ...categoryListQueryOptions }, transactionListQueryOptions], + }) + + return null +} diff --git a/apps/mobile/stores/store-provider.tsx b/apps/mobile/stores/core/store-provider.tsx similarity index 92% rename from apps/mobile/stores/store-provider.tsx rename to apps/mobile/stores/core/store-provider.tsx index c21764ed..0a8e7737 100644 --- a/apps/mobile/stores/store-provider.tsx +++ b/apps/mobile/stores/core/store-provider.tsx @@ -9,6 +9,7 @@ import { useEffect, useState, } from 'react' +import { StoreIntervalUpdate } from './store-interval-update' import { useResetAllStores } from './use-reset-all-stores' export type StoreProviderProps = { @@ -60,5 +61,10 @@ export const StoreProvider: FC = ({ children }) => { return null } - return children + return ( + <> + + {children} + + ) } diff --git a/apps/mobile/stores/core/stores.const.ts b/apps/mobile/stores/core/stores.const.ts new file mode 100644 index 00000000..93d18096 --- /dev/null +++ b/apps/mobile/stores/core/stores.const.ts @@ -0,0 +1 @@ +export const STORE_SYNC_INTERVAL = 1000 * 5 // 5 seconds diff --git a/apps/mobile/stores/core/stores.d.ts b/apps/mobile/stores/core/stores.d.ts new file mode 100644 index 00000000..cb499abd --- /dev/null +++ b/apps/mobile/stores/core/stores.d.ts @@ -0,0 +1,4 @@ +export type StoreHookQueryOptions = { + refetchInterval?: number + refetchIntervalInBackground?: boolean +} diff --git a/apps/mobile/stores/use-reset-all-stores.tsx b/apps/mobile/stores/core/use-reset-all-stores.tsx similarity index 76% rename from apps/mobile/stores/use-reset-all-stores.tsx rename to apps/mobile/stores/core/use-reset-all-stores.tsx index 00f10763..55d095bc 100644 --- a/apps/mobile/stores/use-reset-all-stores.tsx +++ b/apps/mobile/stores/core/use-reset-all-stores.tsx @@ -1,7 +1,7 @@ import { useCallback } from 'react' -import { useBudgetStore } from './budget/store' -import { useCategoryStore } from './category/store' -import { useTransactionStore } from './transaction/store' +import { useBudgetStore } from '../budget/store' +import { useCategoryStore } from '../category/store' +import { useTransactionStore } from '../transaction/store' export const useResetAllStores = () => { const resetBudgetStore = useBudgetStore((state) => state._reset) diff --git a/apps/mobile/stores/transaction/hooks.tsx b/apps/mobile/stores/transaction/hooks.tsx index a3fe21af..c44c3105 100644 --- a/apps/mobile/stores/transaction/hooks.tsx +++ b/apps/mobile/stores/transaction/hooks.tsx @@ -1,5 +1,6 @@ import { getHonoClient } from '@/lib/client' import { useMeQuery } from '@/queries/auth' +import { dayjsExtended } from '@6pm/utilities' import { type TransactionFormValues, type TransactionPopulated, @@ -11,20 +12,32 @@ import { keyBy } from 'lodash-es' import { useMemo } from 'react' import { z } from 'zod' import { useCategoryList } from '../category/hooks' +import type { StoreHookQueryOptions } from '../core/stores' import { transactionQueries } from './queries' import { useTransactionStore } from './store' -export function useTransactionList({ - from, - to, - walletAccountId, - budgetId, -}: { - from: Date - to: Date - walletAccountId?: string - budgetId?: string -}) { +// TODO: Update params here +const DEFAULT_FROM = dayjsExtended() + .subtract(10, 'year') + .startOf('year') + .toDate() +const DEFAULT_TO = dayjsExtended().add(10, 'year').endOf('year').toDate() + +export const useTransactionListQueryOptions = ( + { + // FIXME: This should be dynamic @bkdev98 + from = DEFAULT_FROM, + to = DEFAULT_TO, + // walletAccountId, + // budgetId, + }: { + from?: Date + to?: Date + walletAccountId?: string + budgetId?: string + } = {}, + queryOptions?: StoreHookQueryOptions, +) => { const transactionsInRangeFromStore = useTransactionStore().transactions.filter( (t) => @@ -33,7 +46,7 @@ export function useTransactionList({ const updateTransactionsByRange = useTransactionStore( (state) => state.updateTransactionsByRange, ) - const query = useQuery({ + return { ...transactionQueries.list({ filters: { from, to }, updateTransactionsByRangeInStore: updateTransactionsByRange, @@ -42,7 +55,39 @@ export function useTransactionList({ transactionsInRangeFromStore.length > 0 ? transactionsInRangeFromStore : undefined, - }) + ...queryOptions, + } +} + +export function useTransactionList( + { + // FIXME: This should be dynamic @bkdev98 + from = DEFAULT_FROM, + to = DEFAULT_TO, + walletAccountId, + budgetId, + }: { + from?: Date + to?: Date + walletAccountId?: string + budgetId?: string + } = {}, + queryOptions?: StoreHookQueryOptions, +) { + const transactionsInRangeFromStore = + useTransactionStore().transactions.filter( + (t) => new Date(t.date) >= from && new Date(t.date) <= to, + ) + const queryOpts = useTransactionListQueryOptions( + { + from, + to, + walletAccountId, + budgetId, + }, + queryOptions, + ) + const query = useQuery(queryOpts) const { transactions, transactionDict, totalExpense, totalIncome } = useMemo(() => {