Skip to content

[4/10] Join a project by asking to take one of its tasks - #356

Merged
hturnbull93 merged 4 commits into
mainfrom
join-by-claiming
Oct 23, 2026
Merged

hturnbull93 merged 4 commits into
mainfrom
join-by-claiming

Conversation

@hturnbull93

@hturnbull93 hturnbull93 commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

Screenshots: https://fedora.taila91c66.ts.net:8444/docs/20261023-114405-pr-356-join-and-claim-a-task.html

A volunteer now joins a project by asking to take one of its tasks. The bare "Join this project" button is gone. Offering to lead an ownerless project still works, from its own button.

UI (project page)

  • The header no longer has a "Join this project" button. A volunteer who isn't on the project sees Join and claim a task, which opens a modal task picker (full screen on mobile) listing open tasks with an inline Ask to take this action — no need to leave the Overview tab. Picking a task opens a second dialog with an optional message to the owner, then holds the task and sends the request. Once accepted, they can claim more tasks the same way, or from the Tasks tab, which shares the same ProjectTaskList component as the picker.
  • If no task is open, the header says No open tasks yet instead.
  • Offer to lead shows only while the project seeks an owner (isSeekingOwner). It opens a lead-only dialog ("Offer to lead this project") with an optional message.
  • Invites, accepting invites, and admins' Invite / Add now are unchanged. Owners still answer pending requests, including old plain join requests, from the People tab and the Inbox.

API

  • projects.expressInterest without a taskId now only accepts want_to_own, and only on a project that has no owner. A plain want_to_contribute gets a BAD_REQUEST: "Join a project by claiming one of its tasks". Joining a task as a helper with a taskId is unchanged.
  • Claiming used to be blocked for anyone who had withdrawn or turned down an invite. Now it is blocked only for someone the owner declined or removed (CLAIM_BLOCKING_INTEREST). Claiming is now the only way back onto a project, and the "You can ask again at any time" toast now holds true.
  • notifyOwnerOfInterest is only used by the claim path now. The owner's notification names the task and carries the volunteer's message: "Hal asked to take 'Design flyer': happy to do this", so it can be answered from the Inbox without opening the project. The email names the task in its subject and opening line, and shows "Their message" only when there is one.
  • Offering to lead an ownerless project now notifies the chapter's staff (project_lead_offer), cleared when the offer is answered. Previously nothing told anyone: admin triage only surfaces pending_review/needs_discussion projects, which never overlap with the statuses a project seeks an owner in, so this was the only way to request leadership on an approved project and it went unheard.
  • Holding a second task while an earlier request was still pending no longer skips notifying the owner. Each held task is new information to the owner, regardless of whether an earlier request from the same volunteer is still pending.
  • updateTask's self-claim path takes an optional message, shown on the interest and in the owner's email.

Tests

  • Router tests: refusing a plain join, an offer to lead on an owned project or a proposal, the invite check, claiming again after withdrawing or after turning down an invite versus after being declined, the chapter-staff notification on an offer to lead, and notifying the owner on a second held task. A new askToJoin helper in test/rpc.ts sets up applicants through the claim path.
  • Component tests: Offer to lead, No open tasks yet, the task-picker and claim-message dialogs (including closing each by Escape and by Cancel), and the request waiting beside Withdraw.
  • e2e (09): joining through the task-picker modal from the project page, Offer to lead on an ownerless project, and the no-open-tasks state. Old Join-button flows moved to these. API setup in 11, 20, 38, 42, 43 and 44 uses the new askToJoinViaApi.
  • Fixed a flaky ProjectEditor test (separate commit). The owner picked while a new draft was being created was saved as nobody whenever the session hadn't loaded yet.

Decisions

  • The API refuses plain joins; it doesn't only change the UI. In production, nothing else sends a plain want_to_contribute through expressInterest. Invites use invite, admins use assign, claims go through updateTask, and MCP and import don't touch interests. Leaving the endpoint open would keep a way round the new rule. Only test setup used it, and that now goes through the claim path.
  • Offer to lead needs a project with no owner, at the API as well. Otherwise a want_to_own request on an owned project would be a back door to a plain join. An old pending want_to_own on a project that has since found an owner is still accepted as a helper.
  • The label is "Join and claim a task". The issue calls it "Join and claim"; New-volunteer feedback: phases 3 to 6 and the status standard #314 had renamed the old tab-switch button to "Ask to take this" (lib/action-labels.ts), which the picker's own per-task action keeps.
  • No follow option. The app has no follow feature, so "No open tasks yet" stands alone.
  • The only plain join was on the project page. Project cards, lists, the dashboard and the task page only ever offered task claims, so they are unchanged.

Data impact: None. No migration. Existing pending plain-join requests can still be accepted or declined.

Closes #338

🤖 Generated with Claude Code

@hturnbull93 hturnbull93 changed the title Join a project by asking to take one of its tasks [5/12] Join a project by asking to take one of its tasks Oct 21, 2026
@hturnbull93
hturnbull93 changed the base branch from team-name-link to timeline-access October 22, 2026 17:11
@hturnbull93 hturnbull93 changed the title [5/12] Join a project by asking to take one of its tasks [4/10] Join a project by asking to take one of its tasks Oct 22, 2026
@hturnbull93
hturnbull93 force-pushed the timeline-access branch 2 times, most recently from d994f96 to d3683b5 Compare October 23, 2026 09:16
hturnbull93 and others added 2 commits October 23, 2026 10:16
The patch sent once the create lands read the user from the render that
started it, which can be before the session has loaded, so "I want to lead
this project" saved no owner. Read the signed-in user when the create lands.
This made ProjectEditor.test.tsx "saves edits made while the draft is still
being created" fail about half the time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Remove the bare Join this project button. A volunteer joins by asking to
take a task (Ask to take this), reached from a Pick a task to join button
in the project header; with no open task the header says No open tasks yet.
Offer to lead opens the lead-only request, and shows only while the
project seeks an owner.

expressInterest without a task now takes only want_to_own, on a project
that has no owner; a plain want_to_contribute join is refused. Withdrawing,
or turning down an invite, no longer blocks claiming, since claiming is
now the only way back in. Tests that set up an applicant ask for a task
instead (askToJoin, askToJoinViaApi).

Closes #338

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hturnbull93
hturnbull93 changed the base branch from timeline-access to main October 23, 2026 09:16
hturnbull93 and others added 2 commits October 23, 2026 13:24
Offering to lead an ownerless project created a pending interest but
never told anyone: admin triage only surfaces pending_review/
needs_discussion projects, which don't overlap with the statuses a
project seeks an owner in. Chapter staff now get a project_lead_offer
notification, cleared when the offer is answered.

Holding a second task while an earlier request was still pending
skipped notifying the owner entirely, because the self-claim branch
only ran its notify/interest-update block for a brand new interest
row. Each held task is new information to the owner regardless, so
the block now always runs.

Claiming a task can now carry an optional message to the owner, shown
on the interest and in its email.

Replaces the "Pick a task to join" tab-switch with a task-picker
modal (full screen on mobile) listing open tasks with an inline claim
action, so joining doesn't require leaving the Overview tab. The
picker and the Tasks tab now share one ProjectTaskList component.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…cation

The owner's Inbox notification only said "X asked to help out on the
project", so an owner answering from the Inbox never saw which task was
asked for or what the volunteer wrote. Its body now reads "X asked to
take 'Task': message" (cut to 200 characters, like contact requests).
The email names the task in its subject and opening line, and shows the
"Their message" box only when there is one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hturnbull93
hturnbull93 merged commit 5a6269f into main Oct 23, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Join projects by claiming a task: remove the bare "Join project" button

1 participant