Skip to content

Make GET-style skin the default and fix scan card flow#20

Merged
darthnithin merged 5 commits intomainfrom
stealth
Mar 19, 2026
Merged

Make GET-style skin the default and fix scan card flow#20
darthnithin merged 5 commits intomainfrom
stealth

Conversation

@darthnithin
Copy link
Copy Markdown
Owner

Summary

  • make the GET-inspired mobile skin the default across the app shell, share screen, request flow, and auth surfaces
  • replace the scan action with a dedicated GET-style scan card modal and static GET-like bottom navigation treatment
  • speed up and stabilize scan card generation by passing donor identity directly, adding claim-generation timing logs, and bootstrapping missing weekly pool and allowance records in the claims API

Testing

  • npm run mobile:typecheck
  • npm run dashboard:typecheck

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Mar 19, 2026

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

Project Deployment Actions Updated (UTC)
slugswap Ready Ready Preview, Comment Mar 19, 2026 5:52am

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 626cec8069

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +191 to +192
const [newPool] = await db
.insert(schema.weeklyPools)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Make weekly pool creation idempotent

This insert is now in the claim-generation hot path, but weekly_pools.week_start is unique and there is no onConflict... or retry here. On the first request of a new week (or after a reset), two callers can both observe weeklyPool.length === 0; one succeeds and the other will hit a unique-key error, surfacing a 500 instead of returning a claim.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 131bed5f1c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +190 to +194
let resolvedUserId = initialUserId;

if (!resolvedUserId) {
const {
data: { user },
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Ignore route-supplied user IDs when opening Scan Card

ScanCardScreen prefers the userId query param over supabase.auth.getUser(), and then posts that value unchanged to generateClaimCode. Because the client/API still treat userId as the requester identity, any signed-in user who opens a crafted slugswap://scan-card?userId=<victim> link can mint and refresh claim codes against the victim's allowance instead of their own.

Useful? React with 👍 / 👎.

Comment on lines 225 to +229
if (userAllowance.length === 0) {
return NextResponse.json(
{ error: "No allowance found for this week" },
{ status: 400 }
);
const { config } = await getAdminConfig();
const defaultWeeklyLimit = config.defaultWeeklyAllowance;
const [newAllowance] = await db
.insert(schema.userAllowances)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Prevent duplicate allowance rows on first claim generation

This new insert runs in the claim-generation path, but user_allowances has no unique (user_id, weekly_pool_id) constraint. If a first-time requester double-submits while their allowance is still missing, both requests can create separate rows; later redemption handling in this same route only loads limit(1), so one row becomes invisible and the user's remaining balance/usage can drift.

Useful? React with 👍 / 👎.

@darthnithin
Copy link
Copy Markdown
Owner Author

is everything resolved @codex?

@chatgpt-codex-connector
Copy link
Copy Markdown

Status

No — not everything is resolved yet. Based on the current HEAD on work, 2 of the 4 substantive review issues are fixed, and 2 still remain open.

What is resolved

1) Scan Card no longer trusts route-supplied userId

ScanCardScreen now gets the signed-in user directly from Supabase instead of reading userId from route params, and it calls generateClaimCode, refreshClaimCode, and checkRedemption without passing a caller-controlled user ID. That closes both the “crafted deep link uses a victim’s allowance” issue and the “signed-in donor’s ID gets posted as requester ID” issue. apps/mobile/app/scan-card.tsxL79-L90 apps/mobile/app/scan-card.tsxL178-L219 lib/api.tsL269-L338

2) Claims API now authenticates requester identity server-side

The claims endpoints now derive userId from the bearer token via authenticateAppUser() instead of trusting request-body/query-string userId values for generate/history/refresh/delete/check-redemption. That means the backend no longer accepts arbitrary requester identities from the client. apps/dashboard/app/api/claims/[action]/route.tsL146-L205 apps/dashboard/app/api/claims/[action]/route.tsL231-L248 apps/dashboard/app/api/claims/[action]/route.tsL515-L588 apps/dashboard/app/api/claims/[action]/route.tsL650-L684 apps/dashboard/app/api/claims/[action]/route.tsL791-L824

3) Weekly pool creation is now idempotent

The weekly_pools insert now uses onConflictDoNothing({ target: week_start }) and re-queries if another request won the race, so the unique week_start constraint should no longer cause a first-request-of-the-week 500. apps/dashboard/app/api/claims/[action]/route.tsL259-L293 db/schema.tsL26-L34

What is still not resolved

1) Requester-tab navigation is still effectively removed

The Expo tab bar is still hidden globally with display: 'none'. apps/mobile/app/(tabs)/_layout.tsxL31-L39

The custom GetMobileTabBar still only makes the home item pressable when onHomePress is supplied; every other nav item is rendered as a plain View, not a button. apps/mobile/components/GetMobileTabBar.tsxL25-L66

And that custom bar is still only mounted on the share screen. I do not see an equivalent bottom-nav control rendered on the requester screen, nor any in-app route from share to /(tabs)/(request); the share hero still pushes to /scan-card instead. apps/mobile/app/(tabs)/(share)/index.tsxL530-L537 apps/mobile/app/(tabs)/(share)/index.tsxL754-L755 apps/mobile/app/(tabs)/(request)/index.tsxL168-L215

So the original navigation concern is still valid in code terms, even though one comment thread argued it was “not real issue.”

2) Duplicate user_allowances rows are still possible

user_allowances still has no unique constraint on (user_id, weekly_pool_id) in either schema file. db/schema.tsL62-L72 apps/dashboard/lib/server/schema.tsL73-L88

And the hot-path insert in claim generation still does a plain insert with no conflict handling or retry path. Two simultaneous first-time claim requests can still both create allowance rows before either sees the other. apps/dashboard/app/api/claims/[action]/route.tsL296-L321

Bottom line

If by “everything” you mean “all meaningful review findings,” then no:

  • Fixed: scan-card identity spoofing / route-param trust
  • Fixed: weekly-pool creation race
  • Still open: requester-tab reachability/navigation
  • Still open: duplicate user_allowances race

Suggested next steps

To fully close the remaining feedback, I’d recommend:

  1. Restore a real way to reach /(tabs)/(request)

  2. Make requester allowance creation idempotent

Because I did not make any code changes in this pass, I did not create a commit or a follow-up PR.

Checks I ran

  • pwd && rg --files -g 'AGENTS.md' -g 'CLAUDE.md' -g 'SKILL.md' .
  • sed -n '1,240p' CLAUDE.md && printf '\n---\n' && git status --short && printf '\n---\n' && git branch --show-current && printf '\n---\n' && git log --oneline --decorate -n 5
  • sed -n '1,220p' 'apps/mobile/app/(tabs)/_layout.tsx' && printf '\n---FILE---\n' && sed -n '1,260p' 'apps/mobile/components/GetMobileTabBar.tsx' && printf '\n---FILE---\n' && sed -n '1,260p' 'apps/mobile/app/(tabs)/(share)/index.tsx' && printf '\n---FILE---\n' && sed -n '1,260p' 'apps/mobile/app/scan-card.tsx' && printf '\n---FILE---\n' && sed -n '220,380p' 'apps/dashboard/app/api/claims/[action]/route.ts'
  • git diff --stat 85c4669cc78330157032930b6fe5bb356b7e7326..HEAD && printf '\n---COMMITS---\n' && git log --oneline 85c4669cc78330157032930b6fe5bb356b7e7326..HEAD
  • rg -n "GetMobileTabBar|scan-card|generateClaimCode\\(|useLocalSearchParams|userId" apps/mobile apps/dashboard lib db -S
  • sed -n '1,220p' 'apps/mobile/app/(tabs)/(request)/index.tsx' && printf '\n---MID---\n' && sed -n '220,520p' 'apps/mobile/app/(tabs)/(request)/index.tsx'
  • git show --stat --oneline 16338e6 && printf '\n---PATCH---\n' && git show --unified=40 16338e6 -- 'apps/mobile/app/scan-card.tsx' 'apps/dashboard/app/api/claims/[action]/route.ts'
  • sed -n '1,160p' db/schema.ts && printf '\n---\n' && sed -n '1,160p' apps/dashboard/lib/server/schema.ts && printf '\n---\n' && rg -n "userAllowances|weeklyPools|onConflictDoNothing|onConflictDoUpdate|unique\\(" apps/dashboard/app/api/claims/[action]/route.ts db/schema.ts apps/dashboard/lib/server/schema.ts -S
  • nl -ba 'apps/mobile/app/(tabs)/_layout.tsx' | sed -n '1,120p' && printf '\n---\n' && nl -ba 'apps/mobile/app/(tabs)/(share)/index.tsx' | sed -n '520,780p' && printf '\n---\n' && nl -ba 'apps/mobile/app/(tabs)/(request)/index.tsx' | sed -n '1,240p' && printf '\n---\n' && nl -ba 'apps/mobile/app/scan-card.tsx' | sed -n '1,240p' && printf '\n---\n' && nl -ba 'apps/dashboard/app/api/claims/[action]/route.ts' | sed -n '240,340p'
  • nl -ba lib/api.ts | sed -n '250,340p' && printf '\n---\n' && nl -ba lib/api.ts | sed -n '340,440p'
  • rg -n "\\(tabs\\)/\\(request\\)|autoGenerate|router\\.push\\('/scan-card'|router\\.push\\('/\\(tabs\\)/\\(request\\)'|router\\.replace\\('/\\(tabs\\)/\\(request\\)'" apps/mobile -S
  • nl -ba apps/mobile/components/GetMobileTabBar.tsx | sed -n '1,140p' && printf '\n---\n' && nl -ba db/schema.ts | sed -n '55,90p' && printf '\n---\n' && nl -ba apps/dashboard/lib/server/schema.ts | sed -n '70,95p'

View task →

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 16338e635a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +35 to +36
tabBarStyle: {
display: 'none',
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Restore in-app access to the requester screen

Fresh evidence beyond the earlier thread: I searched apps/mobile for routes into /(tabs)/(request) and only found the tab registration plus the request screen's own self-router.replace; lib/auth-context.tsx:80-82 still sends every signed-in user to /(tabs)/(share), and apps/mobile/app/(tabs)/(share)/index.tsx:530-536 only opens /scan-card when isGetLinked is true (otherwise it starts GET linking). With the tab bar hidden here, non-donor/requester users now have no in-app path to the request flow at all.

Useful? React with 👍 / 👎.

Comment on lines +646 to +649
const auth = await authenticateAppUser(req);
if ("response" in auth) {
return auth.response;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve admin access to claim deletion

Checked apps/dashboard/app/admin-dashboard-client.tsx:733-744: the admin UI still deletes claims by POSTing the selected userId plus claimCodeId and relies on the admin session cookie. This new auth gate rejects any request without a Supabase bearer token before the body is read, so deleting claims from the admin dashboard now returns 401 and bounces admins to /admin/login instead of removing the claim.

Useful? React with 👍 / 👎.

# Conflicts:
#	apps/dashboard/lib/server/claims/donor-usage.ts
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@darthnithin darthnithin merged commit 6afd926 into main Mar 19, 2026
3 of 4 checks passed
@darthnithin darthnithin deleted the stealth branch March 19, 2026 05:57
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