Skip to content

Fix hidden mobile submit dropdown and stalled MetaMask connections#904

Merged
JoaquinBN merged 6 commits into
devfrom
JoaquinBN/mobile-dropdown-and-metamask-fix
Jul 6, 2026
Merged

Fix hidden mobile submit dropdown and stalled MetaMask connections#904
JoaquinBN merged 6 commits into
devfrom
JoaquinBN/mobile-dropdown-and-metamask-fix

Conversation

@JoaquinBN

@JoaquinBN JoaquinBN commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Show the Submit Contribution type/mission dropdown in full on mobile: the form shell's overflow-x: hidden forced computed overflow-y: auto, turning the shell into a scroll container that clipped the menu below its bottom edge (only ~15px of ~320px was visible before a type was picked). overflow-x: clip keeps the horizontal clipping without capturing vertical overflow. PR Fix mobile navigation and project dropdown #900 only rebuilt the project picker; the type dropdown was never fixed.
  • Layer the type dropdown (z-10z-[60]) above the linked-project panel that PR Fix mobile navigation and project dropdown #900 raised to z-20, which otherwise paints over the reopened dropdown when a milestone type is selected.
  • Recover MetaMask connections from stale Connect SDK sessions: explicit MetaMask selection now tears down any persisted session (time-boxed at 3s) before connect(). With a stale session, the SDK sent the request over a dead relay channel and opened MetaMask via a payload-less deeplink — the app foregrounded with nothing to approve and the portal hung. The same wedge hit retries while a prior attempt was still connecting. A fresh pairing embeds the approval request in the deeplink itself, so the MetaMask app always shows the connect sheet. Desktop QR modal and extension routing unchanged (explicit selection already re-prompts via forceRequest).
  • Hide the mobile sidebar's Submit Contribution shortcut for logged-out visitors; it now renders inside the same auth gate as My Submissions.

Validation

  • Logged-out mobile sidebar verified in headless Chromium: "How it works" present, Submit Contribution and My Submissions absent.
  • Live repro of the CSS bug and fix at 375px (headless Chromium): with overflow-x: hidden computed overflow-y is auto and only 15px of the 322px menu is visible; with clip it stays visible and the menu renders in full above the z-20 panel.
  • Desktop flow (no extension): wallet selector → MetaMask → SDK "Connect with Metamask" modal renders with live QR + install-extension section.
  • Mobile flow (iPhone UA): wallet selector → MetaMask → no QR modal; display_uri fires with metamask://connect/mwp?p=<payload> (fresh pairing carrying the request).
  • Self-heal: closing the connecting view mid-handshake and selecting MetaMask again emits a second, distinct payload-carrying deeplink instead of the pre-fix "Existing connection is pending" throw with a payload-less deeplink.
  • npx vitest run: 177 passed / 23 failed, failures identical to the pre-existing dev baseline (Profile, api, routes, AuthButton, meta); includes 4 new tests for the disconnect-first behavior.
  • npm run build passes.

Summary by CodeRabbit

  • New Features

    • Mobile contribution dropdowns now display above other interface elements more reliably.
    • The mobile sidebar now shows the “Submit Contribution” option only for signed-in users.
  • Bug Fixes

    • Fixed an issue where the contribution dropdown could be clipped on mobile.
    • Improved MetaMask connection recovery so stale sessions no longer block the approval prompt and reconnect flow.

JoaquinBN added 5 commits July 6, 2026 19:43
The Submit Contribution type and mission dropdown opened below the
bottom edge of the form shell on mobile, where it was swallowed by the
shell's scroll clipping, so before picking a type users saw almost none
of the menu. The shell now clips only horizontal bleed without becoming
a vertical scroll container, letting the menu overflow and render in
full. The menu also layers above the linked-project panel, which the
project picker rework had raised above it, so reopening the dropdown
with a milestone type selected no longer hides it behind that panel.

## Claude Implementation Notes
- frontend/src/components/portal/submit-contribution/SubmitContribution.svelte: mobile `.submit-form-shell` rule changed from `overflow-x: hidden` to `overflow-x: clip` (hidden forces computed overflow-y to auto, creating a scroll container that clipped the absolutely-positioned type dropdown); `.type-dropdown-menu` raised from `z-10` to `z-[60]` so it paints above the `relative z-20` linked-project panel introduced by PR #900
- frontend/CLAUDE.md: documented the overflow-x hidden vs clip gotcha and the dropdown z-layering convention under Common Issues & Solutions
Choosing MetaMask now tears down any persisted Connect SDK session
before connecting. Previously, a session whose wallet side was gone
(app reinstalled, session expired, or left over from an earlier flow
iteration) made the SDK send the connection request over the dead relay
channel and open MetaMask through a payload-less deeplink: the app came
to the foreground with nothing to approve and the portal hung forever.
The same wedge occurred when a prior attempt was still pending. A fresh
pairing embeds the approval request in the deeplink itself, so the
MetaMask app always shows the connect sheet. Teardown is time-boxed so
an unreachable relay cannot stall the flow, and the desktop QR modal
and extension routing are unchanged.

## Claude Implementation Notes
- frontend/src/lib/metamaskConnect.js: getMetaMaskConnectProvider now awaits client.disconnect() (swallowing errors, capped at 3s via Promise.race) before client.connect(); disconnect resets the SDK core status from 'connecting', tears down the MWP transport, and clears the persisted transport type, guaranteeing the fresh-pairing deeplink branch that carries the request payload
- frontend/src/tests/metamaskConnect.test.js: new unit tests covering disconnect-before-connect ordering, tolerance of a hanging disconnect (fake timers past the 3s cap), tolerance of a rejecting disconnect, and normalization of SDK user-cancel errors to EIP-1193 code 4001
The Submit Contribution shortcut in the mobile sidebar's pinned bottom
area now only renders for signed-in users, alongside My Submissions,
instead of routing visitors into the wallet-connect flow.

## Claude Implementation Notes
- frontend/src/components/Sidebar.svelte: moved the Submit Contribution button inside the existing `{#if $authState.isAuthenticated}` block that gates My Submissions in the mobile pinned area; handleSubmitContribution and its analytics tracking are unchanged
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@JoaquinBN, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: bd345091-2519-48cc-b5e1-eed82c10ee39

📥 Commits

Reviewing files that changed from the base of the PR and between a6b3c10 and c0101e3.

📒 Files selected for processing (2)
  • frontend/src/lib/metamaskConnect.js
  • frontend/src/tests/metamaskConnect.test.js
📝 Walkthrough

Walkthrough

This PR fixes mobile dropdown clipping in the Submit Contribution form via z-index and overflow adjustments, gates the sidebar's mobile "Submit Contribution" button behind authentication, and adds stale-session cleanup before MetaMask connect with a timeout-bounded disconnect and corresponding tests.

Changes

Mobile dropdown clipping and sidebar visibility fix

Layer / File(s) Summary
Dropdown stacking and overflow fix
frontend/src/components/portal/submit-contribution/SubmitContribution.svelte, frontend/CLAUDE.md, CHANGELOG.md
Type dropdown z-index raised to z-[60] and mobile overflow-x changed from hidden to clip; troubleshooting entry and changelog note added.
Sidebar submit button auth gating
frontend/src/components/Sidebar.svelte
Mobile "Submit Contribution" button now renders only when $authState.isAuthenticated.

MetaMask stale session recovery

Layer / File(s) Summary
Pre-connect session cleanup and tests
frontend/src/lib/metamaskConnect.js, frontend/src/tests/metamaskConnect.test.js
Adds time-bounded disconnect() before connect() to clear stale sessions, with tests covering hanging/rejecting disconnect and error normalization.

Estimated code review effort: 2 (Simple) | ~12 minutes

Sequence Diagram(s)

sequenceDiagram
  participant App
  participant getMetaMaskConnectProvider
  participant SDKClient

  App->>getMetaMaskConnectProvider: request provider
  getMetaMaskConnectProvider->>SDKClient: disconnect()
  getMetaMaskConnectProvider->>getMetaMaskConnectProvider: race disconnect vs 3s timeout
  SDKClient-->>getMetaMaskConnectProvider: resolve/reject/timeout (ignored)
  getMetaMaskConnectProvider->>SDKClient: connect()
  SDKClient-->>getMetaMaskConnectProvider: provider or user-rejection error
  getMetaMaskConnectProvider-->>App: return provider
Loading

Possibly related PRs

  • genlayer-foundation/points#879: Both PRs modify getMetaMaskConnectProvider in frontend/src/lib/metamaskConnect.js, adding related connection logic and tests.
  • genlayer-foundation/points#880: Both PRs modify the connect() flow in getMetaMaskConnectProvider to ensure the approval prompt appears reliably.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly captures the main UI clipping fix and the MetaMask connection recovery change.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch JoaquinBN/mobile-dropdown-and-metamask-fix

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 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 `@frontend/src/lib/metamaskConnect.js`:
- Around line 94-107: The disconnect timeout value is hardcoded in Metamask
connect logic and duplicated in tests, so extract it into a shared exported
constant from metamaskConnect.js. Update the Promise.race timeout in the connect
flow to use that constant, and have the related test import the same symbol
instead of advancing timers by a literal so both stay in sync.
- Around line 104-107: The disconnect flow in metamaskConnect still leaves the
fallback setTimeout running after Promise.race settles, so update that logic to
keep a timer handle and clear it once client.disconnect() or the timeout branch
completes. Make the fix in the disconnect/cleanup path around the Promise.race
call so the timer is always cleared on both success and failure, preventing
stray pending timers during repeated connect attempts.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 9071b1df-03c7-40eb-92f5-39a4452fac36

📥 Commits

Reviewing files that changed from the base of the PR and between 265e503 and a6b3c10.

📒 Files selected for processing (6)
  • CHANGELOG.md
  • frontend/CLAUDE.md
  • frontend/src/components/Sidebar.svelte
  • frontend/src/components/portal/submit-contribution/SubmitContribution.svelte
  • frontend/src/lib/metamaskConnect.js
  • frontend/src/tests/metamaskConnect.test.js

Comment thread frontend/src/lib/metamaskConnect.js Outdated
Comment thread frontend/src/lib/metamaskConnect.js Outdated
@JoaquinBN JoaquinBN merged commit ae4f511 into dev Jul 6, 2026
3 checks passed
@JoaquinBN JoaquinBN deleted the JoaquinBN/mobile-dropdown-and-metamask-fix branch July 6, 2026 19:14
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.

1 participant