Repository navigation
[Mobile] Board and list views with filters - #23
Merged
Merged
Conversation
Closes #5. Columns come from the team's own statuses, so nothing is hardcoded -- a team that renames "Cancelled" to "Won't do" still gets it collapsed by default, because the rule is by category. Filters live in the route's query params rather than component state, the same choice the web makes and using the same short keys. That is what makes the board someone is looking at a link they can send, and it means a board URL copied out of the web app narrows this one the same way. Per-column point totals come from the server's own rollup rather than being summed from the loaded page: the issue list is one page, so a client-side total would quietly under-report a long column. The blocked marker is read straight off blocked_by_count for the same reason -- no per-card link fetch. Dragging is one Pan gesture with activateAfterLongPress rather than a long-press composed with a pan, so a column keeps scrolling vertically until a card actually lifts. Hit-testing converts the finger's screen position into the content coordinates the columns were measured in, which is what makes a drop land correctly while the strip is mid-scroll, and a card held against an edge keeps scrolling from a frame callback rather than waiting for the next move. A tap opens the same move-to-status sheet, so the board is usable without the gesture at all. Shared values are written through .set()/.get() rather than .value: the React Compiler treats a property assignment on a prop as a mutation, and the method API is what it accepts. Verified against the reference stack -- filters narrow server-side, a moved card stays moved, and the filtered view agrees -- plus the board tree renders under jest, which needed the worklets jest resolver. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <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.
Closes #5.
Stacked on #22 → #21 → #20. Review in that order; this PR's diff is only the board work.
What changed
Columns come from the team's own statuses, so nothing is hardcoded — a team that renames "Cancelled" to "Won't do" still gets it collapsed by default, because the rule is by category, not by name.
Filters live in the route's query params, not component state — the same choice the web makes, using the same short keys. That is what makes the board someone is looking at a link they can send, and it means a board URL copied out of the web app narrows this one identically.
Per-column point totals come from the server's own rollup rather than being summed from the loaded page: the issue list is one page (the API caps at 200), so a client-side total would quietly under-report a long column. The blocked marker reads straight off
blocked_by_countfor the same reason — no per-card link fetch.The drag
One
Pangesture withactivateAfterLongPressrather than a long-press composed with a pan, so a column keeps scrolling vertically until a card actually lifts instead of the card swallowing every touch that starts on it.Hit-testing converts the finger's screen position into the content coordinates the columns were measured in — that is what makes a drop land correctly while the strip is mid-scroll — and a card held against an edge keeps scrolling from a frame callback rather than waiting for the next movement.
Shared values are written through
.set()/.get()rather than.value: the React Compiler treats a property assignment on a prop as a mutation, and the method API is what it accepts.A tap on any card opens the same move-to-status sheet. That is the fallback the issue asks for, and it is also why the board is fully usable if the gesture misbehaves on real hardware — see below.
Verification
108 unit tests, plus 7 integration tests against a live instance: statuses come back ordered with colours and categories, every filter narrows server-side,
unassignedmaps onto the flag that overridesassignee_id, the estimate rollup is keyed by status id as a string, and a moved card stays moved with the filtered view agreeing — the acceptance criterion.The board tree also renders under jest (columns, per-column placement, the collapsed cancelled column, point totals, blocked marker), which needed the
react-native-workletsjest resolver.The drag gesture itself is unverified by touch — there is no device or emulator available here, so pointer tracking, the lift threshold and edge auto-scroll have only been checked as logic, not as a feel. The pure hit-testing and edge-scroll maths are unit tested at the boundaries. Worth a real device pass before this is relied on; the tap path covers the same action meanwhile.