feat(lists): surface list contributors on the list detail header - #68
Open
Adron wants to merge 1 commit into
Open
feat(lists): surface list contributors on the list detail header#68Adron wants to merge 1 commit into
Adron wants to merge 1 commit into
Conversation
iOS surfaced watchers — who has been *granted* access — but never
contributors, who have actually *used* it. On a shared list the latter is
the more useful signal, and the web has shown it on the list header since
ContributorAvatarStack landed.
- add `listContributors(listId:)` in a new `APIClient+ListContributors`
feature extension (not `APIClient.swift`), returning the route's full
ranked set; `GET /api/lists/{id}/contributors` does not page
- add a `ListContributor` model carrying the server's added/edited counts
and `score`, with a `ListContributorsResult` wrapper for the total
- add `ContributorAvatarStack` (cascading avatars, "+N" overflow) and
`ListContributorsSheet`, whose rows mirror the WatchersListView style
- mount the stack as a tappable header section in `ListDetailView`,
fetched concurrently with the existing schema/items/connections loads
- hide the affordance entirely for GitHub-backed lists, whose contributor
set the route returns empty by design, and below two contributors, so a
single-owner list stays uncluttered (matches the web's threshold)
- the contributor fetch is non-fatal: a failure or a 403 leaves the stack
hidden rather than failing the whole list
Row writes are untouched — GitHub rows still go through the full-row
`updateItem`, since a partial PUT renames the issue to "Untitled".
Closes #58
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013Xq12igmUH4tsNhKy54dY6
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
iOS showed list watchers — people who have been granted access — but never contributors, the people who have actually written rows. On a shared list that is the more useful signal, and the web has surfaced it on the list header since
ContributorAvatarStacklanded. This adds the iOS equivalent: a tappable avatar stack in theListDetailViewheader that opens a ranked sheet.Closes #58
What's included
Services/APIClient+ListContributors.swift, new):listContributors(listId:)hittingGET /api/lists/{id}/contributors. Lives in a per-feature extension file rather thanAPIClient.swift, per CLAUDE.md. The route returns the full ranked set with no server paging, so the client neither pages nor re-sorts — re-sorting would put iOS out of step with the web's order.Models/ListContributor.swift, new):ListContributor(user +addedCount/editedCount/score) and aListContributorsResultwrapper carryingtotalContributors. Keys arrive camelCase off the backend'sserialize(), which passes through the sharedconvertFromSnakeCasedecoder unchanged.Views/ListContributorsView.swift, new):ContributorAvatarStack— cascading avatars with a "+N" overflow bubble — andListContributorsSheet, whose rows mirror theWatchersListViewrow style (36pt circular avatar, display name,@username · added N · edited M). Both have#Previews.Views/ListsView.swift): the stack mounts as a tappable header section inListDetailView, fetched concurrently with the existing schema/items/connections loads rather than adding a serial round-trip to every list open..swiftfiles registered inproject.pbxprojvia thexcodeprojgem (no synced groups in this project).Behavior gates
totalContributors < 2threshold, so a single-owner list stays uncluttered.Not changed
No row-write path was touched. GitHub row writes still go through the full-row
updateItem— a partialPUTwould rebuild the issue and rename it to"Untitled". The diff onListsView.swiftis purely additive;updateItem,setGitHubState,saveEdit, andupdateFieldare untouched.Testing
xcodebuild -scheme InterlinedList -destination 'platform=iOS Simulator,id=<pinned UDID>' -parallel-testing-enabled NO build— BUILD SUCCEEDEDAPIClientListContributorsTests— 12 tests, 0 failures, covering: route + method, list-id percent-encoding, decode of the aggregation counts, preservation of server ranking, null and emptydisplayNamefallback, the web-matchingadded N · edited Msubtitle, the empty set GitHub-backed lists return, 401/403 propagation, and the stack's accessibility-label pluralization.Accessibility
.accessibilityLabelon the stack reads "3 contributors" (singularizing at one), with a hint that it opens the full list; sheet rows announce name plus added/edited counts.🤖 Generated with Claude Code