From 1551d399784a147f5d3d64ec912c63d6d2a956bc Mon Sep 17 00:00:00 2001 From: Max Koon <22125083+k2on@users.noreply.github.com> Date: Wed, 15 Oct 2025 12:04:45 -0400 Subject: [PATCH] feat: update multiple balances --- api/src/zero.ts | 36 ++++++++++++-- app/index.tsx | 13 ++++- shared/src/db/schema/public.ts | 11 +++++ shared/src/mutators.ts | 1 + shared/src/queries.ts | 5 ++ shared/src/zero-schema.gen.ts | 87 ++++++++++++++++++++++++++++++++++ 6 files changed, 147 insertions(+), 6 deletions(-) diff --git a/api/src/zero.ts b/api/src/zero.ts index d1eca5f..77e7078 100644 --- a/api/src/zero.ts +++ b/api/src/zero.ts @@ -23,8 +23,8 @@ import { getHono } from "./hono"; import { Configuration, CountryCode, PlaidApi, PlaidEnvironments, Products } from "plaid"; import { randomUUID } from "crypto"; import { db } from "./db"; -import { plaidAccessTokens, plaidLink, transaction } from "@money/shared/db"; -import { eq } from "drizzle-orm"; +import { balance, plaidAccessTokens, plaidLink, transaction } from "@money/shared/db"; +import { asc, desc, eq, sql } from "drizzle-orm"; const configuration = new Configuration({ @@ -118,11 +118,39 @@ const createMutators = (authData: AuthData | null) => { id: randomUUID(), user_id: authData.user.id, name: t.name, - amount: t.amount, + amount: t.amount.toString(), }); } - } + }, + async updateBalences() { + isLoggedIn(authData); + const accounts = await db.query.plaidAccessTokens.findMany({ + where: eq(plaidAccessTokens.userId, authData.user.id), + }); + if (accounts.length == 0) { + console.error("No accounts"); + return; + } + + for (const account of accounts) { + const { data } = await plaidClient.accountsBalanceGet({ + access_token: account.token + }); + await db.insert(balance).values(data.accounts.map(bal => ({ + id: randomUUID(), + user_id: authData.user.id, + plaid_id: bal.account_id, + avaliable: bal.balances.available as any, + current: bal.balances.current as any, + name: bal.name, + }))).onConflictDoUpdate({ + target: balance.plaid_id, + set: { current: sql.raw(`excluded.${balance.current.name}`), avaliable: sql.raw(`excluded.${balance.avaliable.name}`) } + }) + } + + }, } } as const satisfies Mutators; } diff --git a/app/index.tsx b/app/index.tsx index 6c3aaab..819df8b 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -1,4 +1,3 @@ -import { SafeAreaView } from 'react-native-safe-area-context'; import { authClient } from '@/lib/auth-client'; import { Button, Linking, ScrollView, Text, View } from 'react-native'; import { useQuery, useZero } from "@rocicorp/zero/react"; @@ -11,6 +10,7 @@ export default function HomeScreen() { const z = useZero(); const [plaidLink] = useQuery(queries.getPlaidLink(session)); const [transactions] = useQuery(queries.allTransactions(session)); + const [balances] = useQuery(queries.getBalances(session)); const [idx, setIdx] = useState(0); @@ -28,7 +28,6 @@ export default function HomeScreen() { window.addEventListener("keydown", handleKeyDown); - // Cleanup listener on unmount return () => { window.removeEventListener("keydown", handleKeyDown); }; @@ -39,6 +38,16 @@ export default function HomeScreen() { {plaidLink &&