feat(ranked): choose a 2v2 teammate before queueing - #4788
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughRanked 2v2 now persists teammate IDs, validates self-reference, includes teammates in queue joins, and displays waiting or ready party states. Ranked card rendering is consolidated, with keyboard interaction and localized teammate guidance. The Las Vegas Strip map translation is also added. ChangesRanked 2v2 pairing
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Player
participant RankedModal
participant RankedTeammate
participant MatchmakingModal
participant MatchmakingServer
Player->>RankedModal: enter teammate public ID
RankedModal->>RankedTeammate: persist teammate selection
MatchmakingModal->>RankedTeammate: read teammate selection
MatchmakingModal->>MatchmakingServer: join 2v2 with teammatePublicId
MatchmakingServer-->>MatchmakingModal: send party-status
MatchmakingModal-->>Player: show waiting or duo searching state
Possibly related PRs
Suggested labels: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Adds an optional Teammate field inside the 2v2 card on the ranked screen. Enter a friend's player id, and when they enter yours you queue as a duo on the same team. Left empty, 2v2 behaves exactly as it does today. - Chosen BEFORE the queue is joined, so there is no window where you can be matched solo while your teammate is still getting ready. - Stored in localStorage rather than passed through the open-matchmaking event, because the post-game Requeue path reloads the page: no in-memory value or event payload survives it. One source of truth means the ranked screen, the requeue URL and an in-place requeue all agree. - The field is pinned to the bottom of the card and out of the normal flow, so the 2v2 title and ELO stay aligned with the 1v1 card's. - The card becomes a div with button semantics because a <button> may not contain an <input>, and the input stops its own events from reaching the card. - Own id is rejected: it would hold the player out of matching forever. - partyStatus is only set from the server's party-status message, never assumed, so a client deployed ahead of the server queues normally instead of sitting on "waiting" forever. Needs openfrontio/infra#484 for the pairing itself.
8edabce to
a558482
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/client/Matchmaking.ts`:
- Around line 175-178: Reject a restored teammate ID that matches the
authenticated player’s public ID. In src/client/Matchmaking.ts lines 175-178,
update resetPartyState to filter the stored value before constructing a 2v2
join; in src/client/components/RankedModal.ts lines 84-88, revalidate the
restored value after loading the profile and clear the displayed self ID.
In `@src/client/RankedTeammate.ts`:
- Around line 9-29: Update getRankedTeammate and setRankedTeammate to maintain a
module-level fallback teammate value when localStorage is unavailable. Set or
clear this fallback alongside storage updates, and return it from
getRankedTeammate’s catch path so selections persist for the current session
while preserving existing storage behavior.
In `@tests/client/RankedTeammate.test.ts`:
- Around line 21-24: Update the test case around setRankedTeammate and
getRankedTeammate to store an actual empty string with
localStorage.setItem("ranked-2v2-teammate", "") instead of calling
setRankedTeammate(""). Keep the existing null assertion so the test covers
getRankedTeammate() handling a stored empty value.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: d3a36900-ef8f-49d7-8c6e-5da0717e3ac7
📒 Files selected for processing (5)
resources/lang/en.jsonsrc/client/Matchmaking.tssrc/client/RankedTeammate.tssrc/client/components/RankedModal.tstests/client/RankedTeammate.test.ts
…rage
- getRankedTeammate() takes the signed-in player's own id and rejects (and
clears) a stored value equal to it. Two accounts in one browser could leave
the other's id behind, which is this player's own; the server already refused
it, but the field still displayed it, so the player believed they were duo
queueing while actually solo.
- Mirror the value in memory so the field still works for the page when
localStorage is unavailable (private mode) rather than silently queueing solo.
- Test a persisted empty string directly, since setRankedTeammate("") removes
the key and never exercised that branch.
- Trim overlong comments.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/client/Matchmaking.ts`:
- Around line 178-184: Update the automatic reconnect retry path to call
resetPartyState() immediately before invoking connect(), ensuring stale waiting
or ready state is cleared before each retry. Keep the existing resetPartyState
behavior and normal connection flow unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: e3f7f75b-2d7b-430a-b04a-3e5f00ef9d8f
📒 Files selected for processing (4)
src/client/Matchmaking.tssrc/client/RankedTeammate.tssrc/client/components/RankedModal.tstests/client/RankedTeammate.test.ts
The watchdog and close-retry paths call connect() directly, so a stale waiting/ready survived the socket that reported it: after a reconnect the modal could claim you were searching as a duo for a few seconds when your teammate had dropped. partyStatus is per-connection state like connected/queueSize, so connect() now clears it and every entry path is covered.
|
Nice idea |
What
An optional Teammate field inside the 2v2 card on the ranked screen. Enter a friend's player id, and once they enter yours you queue as a duo on the same team. Left empty, 2v2 behaves exactly as it does today.
Why this shape
localStorage, not passed through the event. The post-game Requeue path navigates to/?requeue=2v2, which reloads the page, so no in-memory value or event payload can survive it. Reading storage at queue time makes the ranked screen, the requeue URL and an in-place requeue all agree, and it means a regular duo types the id once (public ids are permanent).Main.tstherefore needs no changes at all.partyStatusis only ever set from the server'sparty-statusmessage, never assumed locally. An optimistic "held" would leave a client deployed ahead of the server stuck on "waiting for teammate" forever while it was in fact queueing normally.Layout notes
divwithrole="button",tabindexand Enter/Space handling, because a<button>may not contain an<input>(invalid HTML, and every click in the field would fire the queue action). The input stops its own click/keydown from reaching the card.min-h-[9.5rem], so both live cards keep identical dimensions; the title/subtitle block and the ELO-subtitle expression are shared helpers rather than repeated per card.Tests
RankedTeammate.test.tscovers the round trip, the empty-value normalisation (so callers only test fornull) and survival across a reload. Client suites green at 746/746; prettier, eslint and tsc clean.Server
Needs openfrontio/infra#484 for the pairing itself. That should merge first; against an older server this degrades to ordinary solo 2v2 queueing.