feat: group account operations by transaction with direction badges#27
Open
feat: group account operations by transaction with direction badges#27
Conversation
Operations on the account detail page are now grouped by parent transaction using collapsible <details>, with IN/OUT direction badges on payment-like ops showing the counterparty. Pagination counts transactions (up to 10 per page) instead of operations, and memo is surfaced inline via Horizon's join=transactions parameter (zero extra round-trips). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Critical: - Fix end-of-history pagination data loss: track reachedLimit to correctly report HasMore when breaking out of the loop on a partial trailing page (previously, remaining records in that page became unreachable via "Load More"). - Remove dead `|| currentGroup != nil` from hasMore computation; the reconcile block already clears currentGroup unconditionally. - Clarify the function doc: the empty-NextCursor invariant is a guarantee of the function, not a burden on callers. - Extract operationFetcher interface + fetchTransactionGroups helper to make the grouping state machine directly unit-testable. Add comprehensive table-driven tests: empty stream, single/multi-op groups, groupLimit boundary, blockchain exhausted, all-spam drop, mixed spam kept, force-flush on cap, all-spam deadlock guard, error propagation, cursor passthrough, LedgerCloseTime-as-CreatedAt. High: - Spam filter now operates at TRANSACTION level: a group is dropped only if every op is spam-classified. Mixed groups preserve all ops so the "+N more" count matches the transaction detail page. - Promote force-flush log to slog.Warn with tx_hash and partial_op_count so operators see the event in production. - Add slog.Warn for the all-spam-window deadlock guard. - Log slog.Warn when join=transactions returns no Transaction data (unexpected SDK/Horizon regression). - Handler now surfaces operations fetch errors via TxGroupsError field rendered as an inline notice, instead of silently showing an empty section. Error log now includes ops_cursor for triage. - TransactionGroup gains a Partial bool flag for force-flushed groups; template renders a PARTIAL badge and explanatory note. - Document the TransactionGroup.Operations non-empty invariant. - Add doc comment to dict template helper; add error-path tests for odd-arg-count and non-string-key cases. Medium: - Add DirectionSelf constant for self-payments (viewer on both sides); template renders "↻ SELF" badge with purple theme. - Render failed-transaction badge on both single-row and multi-op groups; add op-group-failed CSS class with reduced opacity. - Use base.Transaction.LedgerCloseTime (from the join) for the group's CreatedAt instead of the first op's timestamp. - Add account_merge branch to op-amount partial so directional merges render "(full balance)" instead of an empty gap. - Move op-body doc comment to the correct position above its define. - Render the transaction source account in the expanded group meta when it differs from the viewer, so collectGroupAccountIDs' name lookup is not wasted. - Add memo-absent path fallback: if payment has empty From/To and no direction, render "(missing party)" instead of silently eliding. - Add TestCollectGroupAccountIDs covering dedup across groups, DataValue-as-stellar-ID, and group-level SourceAccount. - Add template tests for: failed badge, partial badge, error notice, self-direction badge. Low: - Add doc comment to addOpIDs. - Document OperationDirection zero-value decision and IsValid() scope. - Document Horizon-order invariant on the group-boundary check. - Restore "Newest first" comment on OrderDesc. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
<details>card with the first op as the clickable summary and+N morebadge; expanding reveals the memo + remaining ops + "view tx →" link.Technical details
Join: "transactions"to the Horizon operations request — memo/source/success data comes inline at zero extra HTTP cost.GetAccountTransactionGroupsreplacesGetAccountOperations; the grouping state machine includes deadlock guards (force-flush partial group if safety caps fire before any group completes; never returnHasMore=truewith an empty cursor).model.OperationDirectionwith constants (DirectionIn,DirectionOut,DirectionNone) +IsValid(), per the project's constrained-type convention.dicttemplate helper enables anop-bodypartial that's shared across the single-op row, the multi-op summary row, and each nested sub-op — DRY across all rendering paths.TxGroups(no spurious section header).[open]state via user click).Test plan
go test ./...— all passinggo vet ./...— cleangofmt -l .— cleanTestSetOperationDirection(12 cases),TestOperationDirectionIsValid, template render tests for multi-op groups and empty-groups case<details>expand behavior🤖 Generated with Claude Code