diff --git a/api/src/zero.ts b/api/src/zero.ts index 7790f48..e13ad8b 100644 --- a/api/src/zero.ts +++ b/api/src/zero.ts @@ -24,7 +24,7 @@ import { Configuration, CountryCode, PlaidApi, PlaidEnvironments, Products } fro import { randomUUID } from "crypto"; import { db } from "./db"; import { balance, plaidAccessTokens, plaidLink, transaction } from "@money/shared/db"; -import { asc, desc, eq, sql, type InferInsertModel } from "drizzle-orm"; +import { asc, desc, eq, inArray, sql, type InferInsertModel } from "drizzle-orm"; const configuration = new Configuration({ @@ -123,6 +123,7 @@ const createMutators = (authData: AuthData | null) => { id: randomUUID(), user_id: authData.user.id, plaid_id: tx.transaction_id, + account_id: tx.account_id, name: tx.name, amount: tx.amount as any, datetime: tx.datetime ? new Date(tx.datetime) : new Date(tx.date), @@ -132,7 +133,15 @@ const createMutators = (authData: AuthData | null) => { await db.insert(transaction).values(transactions).onConflictDoNothing({ target: transaction.plaid_id, - }) + }); + + const txReplacingPendingIds = data.transactions + .filter(t => t.pending_transaction_id) + .map(t => t.pending_transaction_id!); + + await db.delete(transaction) + .where(inArray(transaction.plaid_id, txReplacingPendingIds)); + } }, diff --git a/app/index.tsx b/app/index.tsx index a4c3a5d..dfc8ccd 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -1,29 +1,47 @@ import { authClient } from '@/lib/auth-client'; -import { Button, Linking, Pressable, ScrollView, Text, View } from 'react-native'; +import { Pressable, ScrollView, Text, View } from 'react-native'; import { useQuery, useZero } from "@rocicorp/zero/react"; import { queries, type Mutators, type Schema } from '@money/shared'; import { useEffect, useState } from 'react'; -import { transaction } from '@/shared/src/db'; export default function HomeScreen() { const { data: session } = authClient.useSession(); 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); + const [accountIdx, setAccountIdx] = useState(0); + + const account = balances.at(accountIdx)!; + + const filteredTransactions = transactions + .filter(t => t.account_id == account.plaid_id) useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { if (event.key === "j") { setIdx((prevIdx) => { - if (prevIdx + 1 == transactions.length) return prevIdx; + if (prevIdx + 1 == filteredTransactions.length) return prevIdx; return prevIdx + 1 }); } else if (event.key === "k") { setIdx((prevIdx) => prevIdx == 0 ? 0 : prevIdx - 1); + } else if (event.key == 'g') { + setIdx(0); + } else if (event.key == "G") { + setIdx(transactions.length - 1); + } else if (event.key == 'R') { + z.mutate.link.updateTransactions(); + z.mutate.link.updateBalences(); + } else if (event.key == 'h') { + setAccountIdx((prevIdx) => prevIdx == 0 ? 0 : prevIdx - 1); + } else if (event.key == 'l') { + setAccountIdx((prevIdx) => { + if (prevIdx + 1 == balances.length) return prevIdx; + return prevIdx + 1 + }); } }; @@ -32,33 +50,32 @@ export default function HomeScreen() { return () => { window.removeEventListener("keydown", handleKeyDown); }; - }, [transactions]); + }, [filteredTransactions, balances]); + + function lpad(n: number): string { + const LEN = 9; + const nstr = n.toFixed(2).toLocaleString(); + return Array.from({ length: LEN - nstr.length }).join(" ") + nstr; + } return ( - {plaidLink &&