Skip to content

fix(webapp): Public links says "Loading…", not a premature "no" (BEA-78) - #127

Open
ssowonny wants to merge 1 commit into
mainfrom
bea-78-ph-scan-bug-no-public-links-renders-before-the-shares
Open

fix(webapp): Public links says "Loading…", not a premature "no" (BEA-78)#127
ssowonny wants to merge 1 commit into
mainfrom
bea-78-ph-scan-bug-no-public-links-renders-before-the-shares

Conversation

@ssowonny

@ssowonny ssowonny commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

TL;DR

  • The Public links panel answered "is anything of ours public right now?" with a confident no before it had looked — then swapped to a table listing an active, never-expiring link.
  • Root cause is in the shared SharesTable, not either caller: an empty array read as a settled answer. One loading prop, honored above the empty branch.
  • The org-wide cross-project audit table had the identical bug and is fixed by the same prop.
  • isLoading, not isPendingOrgAdmin's shares query is enabled: owner, and a disabled query pins isPending true forever.
  • Known gap: the section is still below the fold with no cue it exists. That's BEA-79, deliberately not in here.

Closes BEA-78.

                   ┌──────────────────────────────────┐
                   │  Public links: the three states  │
                   └─────────────────┬────────────────┘
           ┌─────────────────────────┼─────────────────────────┐
┌────────────────────┐    ┌────────────────────┐    ┌────────────────────┐
│      loading       │    │       empty        │    │       listed       │
├────────────────────┤    ├────────────────────┤    ├────────────────────┤
│ a muted row        │    │ "No public links." │    │ index.md           │
│ no verdict         │    │ once the answer    │    │ by mira@           │
│ WAS MISSING        │    │ has landed         │    │ no expiry          │
└────────────────────┘    └────────────────────┘    └────────────────────┘

What changed

in flight (shares request throttled)
before before
after after

Once the response lands, both are the same table — the fix only replaces the frame where the panel was lying:

loaded

Three files, one idea each:

  • SharesTable.tsxloading?: boolean, branch above shares.length === 0, same .admin-list / .admin-empty shell so the section is one row tall in either state and doesn't jump when the data arrives.
  • ProjectSettings.tsxPublicLinks destructures isLoading off useShares and passes it. The if (error) return null guard is untouched, so a failed shares request still hides the section entirely.
  • OrgAdmin.tsx — same, off its own useQuery for /api/orgs/<id>/shares.

ShareBanner is unchanged on purpose: it returns null on an empty list, and an absent banner makes no claim.

Why isLoading

The spec said isPending; the plan flagged it and this PR follows the plan. OrgAdmin.tsx sets enabled: owner on the shares query, and TanStack reports isPending: true for a disabled query — a non-owner would get a permanent skeleton. The section happens to sit inside {owner && …} today, so nothing would be seen, but that is incidental protection. isLoading (pending and fetching) costs nothing and makes it impossible.

What was run

  • go build ./..., go vet ./..., go test ./... — all packages pass.
  • npm run e2e — 154 passed, 1 skipped (the skip is the pre-existing sec14fe test.skip).
  • New spec browse.spec.ts:368 throttles **/api/p/*/shares by 2s, asserts the loading row is visible and "No public links." has count 0 while in flight, then asserts the shared row appears. Verified it fails on the unfixed bundle — reverted the prop, rebuilt, watched it go red, restored.
  • npm run build in internal/webapp/frontend; rebuilt internal/webapp/static is committed and check-dist.sh shows only the expected asset rotation.

No architecture diagram change: SharesTable appears in webapp-frontend.md as a name inside the components namespace, with no props or relationships drawn, and an optional prop changes neither.

The one loose end

BEA-79 moves this exact call site when it reorders the Settings sections. This lands first (it's the older issue), so that move applies on top — but whoever takes BEA-79 should expect the static/ bundle to conflict, and resolve it by re-running npm run build, never by picking a side.

Build session

cd $(git worktree list | grep bea-78 | awk '{print $1}') && claude --resume 781691ae-3db7-410a-90df-13d90aa9396d

(Only works on the machine that ran the build.)

The public-links panel is where a member answers "is anything of ours
public right now?". SharesTable read an empty array as a settled answer,
and both callers hand it `shares || []` while the request is still in
flight — so the panel printed "No public links." as a confident NO, then
swapped to a table listing an active, never-expiring link.

Fix at the choke point: an optional `loading` prop honored above the
empty branch, in the same .admin-list/.admin-empty shell so the section
is one row tall either way and doesn't jump when the data lands. Wired at
both call sites — project Settings and the org-wide cross-project audit,
which had the identical bug.

`isLoading`, not `isPending`: OrgAdmin's shares query is `enabled: owner`,
and TanStack reports isPending true forever for a disabled query.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ssowonny
ssowonny requested a review from thefron August 5, 2026 10:16
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