Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions web/Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ tasks:
dev:
desc: Run the ui development server
cmds:
- npm install
- npm start

build:
Expand Down
71 changes: 69 additions & 2 deletions web/src/Accounts.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { createElement, createContext, useContext, useState } from 'react'
import { Transaction, Account, Balances, TransactionsResponse } from './PlaidApi'
import { createElement, createContext, useContext, useCallback, useState } from 'react'
import { Transaction, Account, Balances, Envelope, TransactionsResponse } from './PlaidApi'

interface AccountContext {
account: Account|null
transactions: Array<Transaction>
envelopes: Array<Envelope>
loadAccount: () => Promise<void>
loadBalance: () => Promise<void>
loadEnvelopes: () => Promise<void>
appendEnvelope: (e: Envelope) => void
removeEnvelope: (e: Envelope) => void
updateEnvelope: (e: Envelope) => void
spendFromEnvelope: (e: Envelope, trx: Transaction) => void
error: Error|null
}

Expand All @@ -29,11 +35,22 @@ export async function getBalances(): Promise<any> {
throw new Error(response.statusText)
}

export async function getEnvelopes(): Promise<Array<Envelope>> {
const response = await fetch('/api/get_envelopes', { method: 'POST' })
if (response.ok) {
const data = await response.json()
return data as Array<Envelope>
}
throw new Error(response.statusText)
}

function useAccountState(): AccountContext {
const [account, setAccount] = useState<Account|null>(null)
const [balance, setBalance] = useState<Balances|null>(null)
const [transactions, setTransactions] = useState<Array<Transaction>>([])
const [envelopes, setEnvelopes] = useState<Array<Envelope>>([])
const [error, setError] = useState<Error|null>(null)

async function loadAccount () {
const {transactions, accounts} = await getTransactions()
// TODO: Protected envelopes are actually savings accounts.
Expand All @@ -52,11 +69,55 @@ function useAccountState(): AccountContext {
async function loadBalance () {
getBalances().then()
}
async function loadEnvelopes () {
const envelopes = await getEnvelopes()
setEnvelopes(envelopes)
}
const appendEnvelope = useCallback((e: Envelope) => {
setEnvelopes(current => [...current, e])
}, [])
const removeEnvelope = useCallback((e: Envelope) => {
setEnvelopes(current => {
return current.filter(c => c.id != e.id)
})
}, [])
const updateEnvelope = useCallback((e: Envelope) => {
setEnvelopes(current => {
return current.map((c) => {
if (c.id == e.id) return {...c, ...e}
return c
})
})
}, [])
const spendFromEnvelope = useCallback((e: Envelope, trx: Transaction) => {
setTransactions(current => {
return current.map(c => {
if (c.transaction_id == trx.transaction_id) {
return {...c, envelope_id: e.id }
}
return c
})
})
setEnvelopes(current => {
return current.map((c) => {
if (c.id == e.id) {
return {...c, balance: (c.balance - Math.min(c.balance, trx.amount)) }
}
return c
})
})
}, [envelopes, transactions])
return {
account,
transactions,
envelopes,
loadAccount,
loadBalance,
loadEnvelopes,
appendEnvelope,
removeEnvelope,
updateEnvelope,
spendFromEnvelope,
error
}
}
Expand All @@ -68,8 +129,14 @@ const noop = async () => {
const accountContext = createContext<AccountContext>({
account: null,
transactions: [],
envelopes: [],
loadAccount: noop,
loadBalance: noop,
loadEnvelopes: noop,
appendEnvelope: noop,
removeEnvelope: noop,
updateEnvelope: noop,
spendFromEnvelope: noop,
error: new Error('Invalid Context! This is used out of context')
})

Expand Down
Loading