You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
uid, displayName, net (cents; positive = owed, negative = owes)
Settlement
interface
from, fromName, to, toName, amount
Route
union type
login | groups | group{id} | profile | not-found
src/store/app.store.ts
Global Variables
Name
Type
Description
store
store object
Singleton observable app-state store created by createStore()
Functions
createStore()
Inputs: none
Outputs:{ getState, setState, subscribe }
Description: Creates and returns the observable store.
Workflow: Initialises state with default values (user:null, groups:[], etc.) and a Set<Listener>. Returns three methods that close over these values.
store.getState()
Inputs: none
Outputs:AppState
Description: Returns the current state snapshot.
store.setState(partial)
Inputs:Partial<AppState>
Outputs:void
Description: Merges partial update into state via spread, then calls notify() to invoke all subscribers.
store.subscribe(fn)
Inputs:Listener (() => void)
Outputs:() => void (unsubscribe)
Description: Adds listener to the set; returned function removes it.
src/router.ts
Functions
parseHash(hash)(private)
Inputs:string (raw window.location.hash)
Outputs:Route
Description: Maps hash strings (#/groups, #/groups/:id, etc.) to typed Route discriminated union values.
Workflow: Strips leading #, matches against fixed strings and a regex for group IDs, returns not-found for unrecognized paths.
navigate(route)
Inputs:Route
Outputs:void
Description: Pushes the appropriate hash string onto window.location.hash, triggering a hashchange event.
initRouter(handler)
Inputs:RouteHandler ((route: Route) => void)
Outputs:() => void (teardown)
Description: Attaches a hashchange listener that calls handler(parseHash(hash)); fires immediately for the current hash so the initial page renders.
Workflow: Creates onHashChange closure → adds to window → calls it once → returns removal function.
src/main.ts
Global Variables
Name
Type
Description
appEl
HTMLElement
#app root mount point
cleanupRoute
(() => void) | null
Teardown function for the currently mounted route component
INACTIVITY_MS
number
600_000 ms (10 min) — auto-logout threshold
inactivityTimer
ReturnType<setTimeout> | null
Handle for the current inactivity timeout
Functions
resetInactivityTimer()
Inputs: none
Outputs:void
Description: Clears any existing inactivity timeout and starts a fresh 10-min countdown that calls signOut() when it expires. No-ops if no user is logged in.
startInactivityTracking()
Inputs: none
Outputs:void
Description: Attaches passive click, keydown, mousemove, scroll, touchstart listeners on window, each calling resetInactivityTimer(). Starts the initial timer.
stopInactivityTracking()
Inputs: none
Outputs:void
Description: Clears the active timeout and nulls the reference; does not remove window listeners (they become no-ops while user is null).
handleRoute(route)
Inputs:Route
Outputs:void
Description: Auth guard + route dispatcher. Redirects unauthenticated users to #login and authenticated users away from #login to #groups. Tears down the previous component and mounts the new one.
Workflow:
Read user from store; apply redirect if auth state mismatches route.
Call cleanupRoute?.(), clear appEl.innerHTML.
Switch on route.name: mount renderLoginPage, renderGroupList, async-fetch group then renderExpenseList, or renderProfilePage.
Store returned cleanup function in cleanupRoute.
Entry sequence:initAuth(onReady) → inside onReady, register a store subscriber that re-evaluates redirects on auth changes and starts/stops inactivity tracking → call initRouter(handleRoute).
src/logic/settlement.ts
Functions
computeBalances(expenses, members)
Inputs:Expense[], GroupMember[]
Outputs:Balance[]
Description: Calculates each member's net balance in cents.
Workflow: Initialises net[uid]=0. For each expense, adds full amount to paidBy; subtracts equal share (round(amount/splitCount)) from each uid in splitBetween. Returns mapped Balance[].
simplifyDebts(balances)
Inputs:Balance[]
Outputs:Settlement[]
Description: Greedy two-pointer algorithm that minimises the number of payment transactions.
Workflow: Splits balances into creditors (net > 0) sorted descending and debtors (net < 0) sorted ascending. Pointer sweep: each iteration emits one Settlement for min(credit, |debt|), reduces both balances, and advances whichever pointer reaches zero.
formatCents(cents)
Inputs:number
Outputs:string
Description: Converts integer cents to display string $X.XX.
src/services/auth.service.ts
Global Variables
Name
Type
Description
pendingDisplayName
string | null
Module-level race-condition guard: set before createUserWithEmailAndPassword so the immediately-firing onAuthStateChanged can use it without a Firestore round-trip
Functions
initAuth(onReady)
Inputs:() => void
Outputs:() => void (unsubscribe from auth listener)
Description: Subscribes to Firebase Auth state changes. On sign-in: resolves display name (from pendingDisplayName or Firestore) and pushes user into store. On sign-out: sets user: null. Calls onReady() exactly once after the first event settles.
signUp(email, password, displayName)
Inputs:string, string, string
Outputs:Promise<void>
Description: Creates Firebase Auth user, sets display name on Auth profile, writes Firestore user record, updates store.
Description: Updates description, amount, paidBy, and splitBetween fields on an existing expense.
deleteExpense(groupId, expenseId)
Inputs:string, string
Outputs:Promise<void>
Description: Deletes the expense document.
src/services/user.service.ts
Functions
createUserRecord(user)
Inputs:User
Outputs:Promise<void>
Description: Writes uid, displayName, email, createdAt to users/{uid}.
getUserRecord(uid)
Inputs:string
Outputs:Promise<UserRecord | null>
Description: Fetches a user document by UID; returns null if not found.
updateDisplayName(uid, displayName)
Inputs:string, string
Outputs:Promise<void>
Description: Updates displayName in both Firestore and Firebase Auth profile simultaneously.
updateUserColor(uid, color)
Inputs:string, string
Outputs:Promise<void>
Description: Updates the color field in the Firestore user document.
getUserColors(uids)
Inputs:string[]
Outputs:Promise<Record<string, string>>
Description: Batch-fetches color values for multiple UIDs via Promise.all; omits UIDs with no color set.
getUserDisplayNames(uids)
Inputs:string[]
Outputs:Promise<Record<string, string>>
Description: Batch-fetches display names for multiple UIDs via Promise.all.
getUserByEmail(email)
Inputs:string
Outputs:Promise<UserRecord | null>
Description: Queries users collection by normalized email; returns first match or null.
src/services/log.service.ts
Functions
writeLog(groupId, entry)
Inputs:string, Omit<LogEntry, 'id' | 'createdAt'>
Outputs:Promise<void>
Description: Appends a log entry (with server timestamp) to groups/{groupId}/logs.
subscribeToLogs(groupId, onChange)
Inputs:string, (logs: LogEntry[]) => void
Outputs:Unsubscribe
Description: Real-time listener on logs ordered by createdAt descending.
src/components/auth/LoginPage.ts
Functions
renderLoginPage(container)
Inputs:HTMLElement
Outputs:void
Description: Mounts the authentication form; manages a mode state (login | signup | reset) and re-renders in place on mode changes.
Workflow: Defines inner render() which injects mode-appropriate HTML and rebinds events. Toggle buttons switch mode and call render(). On form submit: calls signIn, signUp, or resetPassword; shows inline error or success feedback.
Description: Mounts the group list screen; subscribes to groups; re-renders on every store update.
Workflow: Calls subscribeToGroups → callback pushes {groups, loading:false} into store and calls render(). Inner render() reads store and injects HTML. Event wiring: Open → navigate({name:'group',id}); Delete → confirm + deleteGroup; New → opens renderGroupCreate modal.
memberSummary(members)(private)
Inputs:{displayName: string}[]
Outputs:string
Description: Returns "A, B, C and N more" summary (max 3 names shown).
escapeHtml(s)(private)
Inputs:string
Outputs:string
Description: Escapes &, <, > for safe HTML insertion.
src/components/groups/GroupCreate.ts
Functions
renderGroupCreate(container, user, onClose)
Inputs:HTMLElement, User, () => void
Outputs:void
Description: Renders the group-creation modal. On submit: calls createGroup(name, {uid, displayName}) then onClose().
Workflow: Injects modal HTML → binds close on overlay click / cancel / ✕ → form submit validates name, disables button, calls createGroup, calls onClose.
src/components/expenses/ExpenseList.ts
Global Variables
Name
Type
Description
PALETTE
string[]
8-entry fallback color palette for avatar generation
Functions
avatarColor(uid, colorMap)(private)
Inputs:string, Record<string, string>
Outputs:string
Description: Returns the custom color from colorMap if present; otherwise hashes uid to a palette entry.
renderExpenseList(container, initialGroup)
Inputs:HTMLElement, Group
Outputs:() => void (cleanup)
Description: Main group detail screen. Manages three Firestore real-time subscriptions and member profile data; re-renders on any change. Returns a cleanup that cancels all subscriptions.
Workflow:
refreshMemberNames() — batch-calls getUserRecord for all members → populates memberNameMap / memberColorMap → sets profilesReady = true → calls render().
render() — builds full layout (header, member chips, tab bar, expense filters). Calls inner renderExpenseItems() and renderHistoryItems(). Mounts renderSettlementView in #settlement-root when expenses exist.
onSettlementConfirmed(from, to, amount) — calls addExpense with description 'Settlement payment'.
Subscriptions: subscribeToGroup → updates group, re-calls refreshMemberNames. subscribeToExpenses / subscribeToLogs → update local arrays, call render() when profilesReady.
Description: Invite modal with two flows: quick-add from contact history (members of other groups) and lookup by email or UID. Both paths call addMemberToGroup on success.
escapeHtml(s) / formatRelativeTime(date)(private)
Utility helpers for HTML escaping and relative timestamps ("just now", "5m ago", etc.).
Description: Add/Edit expense modal. When existing is provided, pre-fills fields and calls updateExpense; otherwise calls addExpense. Writes a log entry in both cases.
Workflow: Renders payer <select> and split <checkbox> list from group.members. On submit: parses dollars → cents, calls addExpense or updateExpense, calls writeLog, calls onClose.
escapeHtml(s)(private)
HTML escape utility.
src/components/settlement/SettlementView.ts
Global Variables
Name
Type
Description
PALETTE
string[]
8-entry fallback color palette
Functions
avatarColor(uid, colorMap)(private)
Inputs:string, Record<string, string>
Outputs:string
Description: Returns user color from map or hashed palette fallback.
Description: Renders balance summary, List/Graph toggle, and suggested payment rows. Appends createGraphSvg result into #settlement-graph-panel after setting innerHTML.
Workflow: Calls computeBalances and simplifyDebts to derive data. Injects HTML. Appends SVG element. Wires List/Graph toggle buttons to show/hide panels and call onViewChange. Confirm buttons call onConfirm(from, to, amount).
escapeHtml(s)(private)
HTML escape utility.
src/components/profile/ProfilePage.ts
Global Variables
Name
Type
Description
PALETTE
string[]
8-entry color palette for avatar color swatches
Functions
renderProfilePage(container)
Inputs:HTMLElement
Outputs:void
Description: Renders the profile screen with avatar color picker and username editor.