Skip to content

Open the web's achievements dialog from a profile's badges - #4

Merged
simonhamp merged 1 commit into
mainfrom
feat/badge-panel
Oct 18, 2026
Merged

simonhamp merged 1 commit into
mainfrom
feat/badge-panel

Conversation

@simonhamp

Copy link
Copy Markdown
Member

Tapping the achievements shelf on a profile now opens the web's achievements dialog as a bottom sheet, like the commit sheet. Tapping a badge opens it with that badge outlined in the profile's lane colour.

The sheet follows achievements-dialog.tsx: the title with the rank chip, the handle with XP and how many badges are unlocked, the bar to the next rank, then the Badges and Unix Ranks tabs. Each badge shows its icon, name, description, a tick once unlocked or its progress, and a bar under a locked badge that takes more than one step. The ranks tab lists every rank with its XP and marks the current one. The XP formula line closes it, as on the web. The shelf also gets the web's chevron after the unlocked count.

No API change was needed. The signed profile already carries everything the dialog shows: rank names, levels, thresholds, descriptions and whether each is reached. The web dialog shows no unlock dates or categories. The app was dropping rank descriptions and achieved, so Achievements now reads ranks into a small Rank class.

Things to look at:

  • EDGE can't scroll a scroll view to a child. Core's ScrollView::autoScrollTo() exists, but the Blade collector doesn't map an attribute to it and the mobile-ui renderers ignore auto_scroll_to. So a badge in the second half of the list opens the sheet with scroll-anchor="bottom", which brings the end of the list, and that badge, into view. On an iPhone 17 Pro every badge ends up on screen this way. On a phone as short as an SE, badges 5 and 6 could sit just below the fold. The jump to the end also happens while the sheet is still sliding up, so you can see the list move for a frame or two. A real scroll-to would need changes in mobile-air and mobile-ui.
  • The web's locked cards are dashed. A dashed box costs about ninety nodes here, so locked cards are a faint wash with a solid hairline, like the shelf's chips.
  • The sheet draws nothing until it opens, and only the tab showing, so the profile's frames stay small (about 150 nodes with 12 badges, 66 on the ranks tab).
  • The shelf's badge chips had a broken accessibility label. A double-quoted PHP string inside a native attribute ends the attribute early, so VoiceOver read out part of a Blade expression. Badge::statusLabel() replaces it.
  • The XP bar moved into a progress-bar partial that the shelf and the sheet share.
  • OpensAchievements is a trait so Profile.php only gains a use line, since other open work touches that screen too.

How I checked it: php artisan test passes (637 tests) and Pint is clean. Then on an iPhone 17 Pro simulator against a local devnull running nunomaduro/dev-null.sh#130's API, signed in as local_dev. On my own profile I opened the sheet from the shelf, scrolled to the end, tapped "uptime" (in progress, first half) and saw it outlined, and switched to Unix Ranks. On someone else's profile I scrolled the badge row and tapped "pipe" (unlocked, second half), and the sheet opened at the end with it outlined in their lane. Dismissing and reopening started on Badges again. Both tabs checked in dark mode too. I compared each against the web dialog at phone width. Screenshots and a recording are kept locally.

🤖 Generated with Claude Code

Tapping the rank, the unlocked count or a badge on a profile's shelf opens
the web's achievements dialog as a sheet: the rank and XP, the bar to the
next rank, every badge with its progress, and the Unix ranks on a second
tab. A badge opens it with that badge outlined in the owner's lane.

EDGE can't scroll to a child, so a badge in the second half of the list
opens the sheet at the end of the list, where the badge is in view.

The shelf's badge labels read part of a Blade expression, because a double
quoted string inside a native attribute ends the attribute. They now come
from Badge::statusLabel().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@simonhamp
simonhamp merged commit 7e640bc into main Oct 18, 2026
1 check passed
@simonhamp
simonhamp deleted the feat/badge-panel branch October 19, 2026 09:26
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