Skip to content

feat: integrate and polish profile network tab - #22

Merged
quangshuynh merged 4 commits into
mainfrom
feat/follow-network-export
Sep 21, 2026
Merged

quangshuynh merged 4 commits into
mainfrom
feat/follow-network-export

Conversation

@quangshuynh

Copy link
Copy Markdown
Owner

Follow-up to #21, which was merged before this work existed and cannot receive these commits. This PR carries the post-#21 Network-tab integration plus progressive disclosure for large lists.

1. Network became a profile result tab

The standalone Followers / Following destination is gone: the hero and audit-header links, the separate page and its duplicated header, the second username input, "Back to portfolio audit", and the ?tool=network / &network= URL state. Network is now the fifth tab — Overview · Audit · Repositories · Network · Markdown export — using the existing tab architecture and the existing ?view= parameter.

It operates on the already audited profile, so there is no second username to enter.

Lazy loading. Follower pages are fetched the first time the tab is opened, not during the audit, because unauthenticated requests share a small budget and a large network costs many pages. A finished attempt is cached per username; switching tabs does not refetch. A failed attempt is cached too, with an explicit Try again — silently retrying on every tab switch is exactly wrong when the cause is a rate limit. Auditing a different profile invalidates everything.

2. Following who don't follow back

following − followers, comparing logins case-insensitively (GitHub treats them as case-insensitive identities) while keeping the spelling and order of the following response.

Derived only when both lists are complete. If either is incomplete the section is hidden and its count reads Unavailable, never 0 — a login missing from a partial followers list may simply sit on a page that never arrived, and calling that account a non-follower would be unsound. The same rule already gates the Markdown export.

3. Progressive disclosure

Each list renders its first 25 accounts with Show 25 more / Show all / Collapse, plus shared Expand all / Collapse all that appear only when a list is long enough to need them. For @quangshuynh the initial panel drops from 542 rendered chips to 75.

Worth reviewing closely:

  • Only the visible slice reaches the DOM — the arrays are sliced before rendering, not hidden with CSS. Complete lists stay in memory.
  • Markdown is unaffected. It is still built from the authoritative network, so Copy and Download export everything regardless of what is expanded. A test asserts the downloaded file is byte-identical across expand and collapse, and that entries past index 25 are present while the UI shows 25.
  • Lists of ≤25 render in full and read 12 users, never a misleading Showing 25 of 12. Empty lists keep their existing empty state with no controls.
  • Sections are independent; expanding Following leaves Followers at 25. Visible counts reset on profile change and on Try again, so a fresh list never inherits Showing 75 of ….
  • Controls are real buttons with explicit accessible names. Focus moves only when the control just used hides itself, and the active element is read before anything is hidden — the browser blurs a hidden button first, which silently dropped focus to body until fixed.

Nothing changes in GitHub pagination, completeness semantics, the derivation, request counts, repository auditing, scoring, or portfolio candidacy.

Verification

Command Result
npm run check pass
npm test 218 pass / 0 fail
npm run eval No corpus outcome changed
npm run test:browser 25 pass / 0 fail
git diff --check clean

Also reviewed against the live API at desktop and 375px mobile: 54 followers / 270 following / 218 not following back, 75 chips rendered, all 542 entries in the Markdown, no horizontal overflow, controls wrapping cleanly.

Add deriveNotFollowingBack, the following list minus the followers list, and
report its count and entries in the network Markdown alongside the existing
two sections.

GitHub logins are case-insensitive identities, so the comparison folds case
while the results keep the spelling and ordering of the following response.

The difference is derived only when both lists were retrieved completely. A
login missing from a partial followers list may sit on a page that never
arrived, so an incomplete retrieval yields null rather than an unsafe claim
that an account does not follow back. buildMarkdown already refuses to run on
incomplete data, so the export stays gated by the same rule.
Move the follower network from a standalone top-level tool into a fifth
profile result tab, between Repositories and Markdown export, using the
existing tab architecture rather than a second navigation mechanism.

Removed with it: the hero and audit-header Followers / Following links, the
standalone page and its duplicated header, the second username input and its
form, and the ?tool=network and &network= URL state. Deep linking now uses the
existing ?view= tab parameter.

The tab operates on the already audited profile, so there is no second
username to enter. Its lists are fetched the first time the tab is opened
rather than during the audit, because unauthenticated requests share a small
budget and a large network costs many pages. A finished attempt is cached
against the username it belongs to, so switching tabs does not refetch; a
failed attempt is cached too and offers an explicit Try again, since silently
retrying on every tab switch is exactly wrong when the cause is a rate limit.

networkState stays outside appState and is invalidated whenever a different
profile is audited or private mode is entered, so one profile's followers can
never appear under another. The request generation counter still discards a
late response from a previous profile.

An incomplete retrieval hides the derived section and shows its count as
Unavailable rather than implying zero.
Replace the standalone-tool wording with the tab it is now: it works on the
audited profile, loads lazily on first open, caches per username, and derives
following who don't follow back only when both lists are complete.
Each of the three relationship lists now renders its first 25 accounts and
grows on request, so a profile following several hundred accounts no longer
opens as a wall of chips. For @quangshuynh the initial panel drops from 542
rendered chips to 75.

Every section owns its visible count and shows Show 25 more, Show all and
Collapse, plus shared Expand all and Collapse all controls that appear only
when some list is actually long enough to need them. A list of 25 or fewer
renders in full and reads "12 users" rather than a misleading "Showing 25 of
12"; an empty list keeps its existing empty state with no controls attached.

Only the visible slice reaches the DOM. The complete lists stay in memory and
the Markdown is still built from the authoritative network, so Copy and
Download are unaffected by what happens to be expanded. Nothing about GitHub
pagination, completeness semantics, the derivation, request counts, auditing
or scoring changes: this is presentation only.

Visible counts reset whenever the panel is cleared, which covers auditing a
different profile and Try again, so a freshly loaded list never inherits
"Showing 75 of ...". Switching tabs does not re-render, so an expanded list
stays expanded.

Controls are real buttons with explicit accessible names. Focus only moves
when the control the reader just used hides itself, and the active element is
read before anything is hidden, since the browser blurs a hidden button first.
@vercel

vercel Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
gitprofilelens Ready Ready Preview Sep 21, 2026 4:43pm UTC

@quangshuynh
quangshuynh merged commit 3e89974 into main Sep 21, 2026
4 checks passed
@quangshuynh
quangshuynh deleted the feat/follow-network-export branch September 21, 2026 16:46

This branch was successfully deployed

1 active deployment
Preview — 2fbf0c2e Deployed Sep 21, 2026 by vercel[bot]
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