Skip to content

add invitation system - #475

Open
Moomin928 wants to merge 9 commits into
mainfrom
invitation-system
Open

add invitation system#475
Moomin928 wants to merge 9 commits into
mainfrom
invitation-system

Conversation

@Moomin928

@Moomin928 Moomin928 commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds a referral code system:

Backend:

New ReferralCodes and Referrals tables (replaces the old InviteRedemptions table and InviteCode column on AppUsers)
GET /api/referral/my-code — lazily ensures and returns the user's unique referral code (self-healing: generates one on first access if it's missing, regardless of whether the Auth0 login webhook already created it)
POST /api/referral/redeem — validates and records a redemption (only allowed before onboarding is complete; prevents self-use and double redemption)
Codes are derived from each row's own auto-increment Id via Sqids (guaranteed unique by construction, no random-collision retries needed), using an uppercase-only alphabet that excludes visually ambiguous characters
Error handling: 404 code/user not found, 400 self-referral, 403 already onboarded, 409 already redeemed

Frontend:

Invite code redemption added to the onboarding flow, plus an "Invite Friends" entry in Settings showing the user's own code
One-tap copy for the user's own code
Input field (up to 12 characters) to redeem another user's code
Localised error messages (EN/ZH) per error type

Design note: redemption is intentionally limited to the onboarding window — this is a deliberate anti-abuse decision, not an oversight. Consequences: existing (already-onboarded) users cannot participate, and tapping Skip on any onboarding step permanently forfeits the ability to redeem, since it marks the user as onboarded. There is no redeem entry point outside onboarding by design.

  • User-facing — announce it

@Moomin928

Copy link
Copy Markdown
Collaborator Author

@Moomin928

Copy link
Copy Markdown
Collaborator Author
Simulator Screenshot - iPhone 17 Pro Max - 2026-07-17 at 00 31 20 Simulator Screenshot - iPhone 17 Pro Max - 2026-07-17 at 00 31 25

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown

Test Results

98 tests   98 ✅  8s ⏱️
 1 suites   0 💤
 1 files     0 ❌

Results for commit b62f0b9.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Package Line Rate Branch Rate Complexity Health
BlotzTask 4% 31% 2653
BlotzTask 4% 31% 2653
Summary 4% (4184 / 110588) 31% (852 / 2714) 5306

@Moomin928

Copy link
Copy Markdown
Collaborator Author
Simulator Screenshot - iPhone 17 Pro Max - 2026-07-27 at 18 22 53 Simulator Screenshot - iPhone 17 Pro Max - 2026-07-27 at 18 26 08 Simulator Screenshot - iPhone 17 Pro Max - 2026-07-27 at 18 41 26

@sol-wizard sol-wizard left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

🤖 AI-generated PR review. Treat the inline comments as suggestions, not verdicts — use your own judgement and verify each before changing anything.

Core flow looks sound. The three Major inline comments are what I'd resolve before merge.

One PR-level issue (no line to anchor to): the description doesn't match the code.
It describes an InviteRedemptions table, an InviteCode column on AppUsers, /api/invite/* routes, and a redeem input in Settings. Actual: ReferralCodes + Referrals, api/referral/*, and no redeem input in Settings — the migration drops InviteRedemptions and InviteCode. Looks like the description describes the first iteration. Worth updating since pr-release-note-check.yml gates on it.

Nits — feel free to ignore
  • nit: naming split across the stack — mobile feature/invite/ + InviteCodeDTO vs backend Referrals/ReferralCode/api/referral.
  • nit: whitespace-only edits in AppUserConfiguration.cs, AppUser.cs, BlotzTaskDbContext.cs.
  • speculative — feel free to ignore: a concurrent first-time GET /my-code could double-insert and surface a 409 from the unique index; retry fixes it, and the double-redeem race is correctly absorbed by DbUpdateException → 409. I'd leave both.

Test worth adding: one handler test over RedeemReferralCode covering self-referral / already-redeemed / onboarding-gate. User-isolation logic, cheap with an in-memory context, and the comment on line 36 shows those branches are easy to conflate.

Assumptions: diff-only review, nothing built or run. I assumed InviteRedemptions / AppUsers.InviteCode were merged to main earlier, since this migration drops them without creating them — please confirm they were never populated in staging/prod, as DropTable is destructive with no data-migration step. I did not check badge seed config for the new enum values.

Comment thread blotztask-api/Modules/Referrals/Commands/RedeemReferralCode.cs Outdated
Comment thread blotztask-api/Modules/Referrals/Commands/RedeemReferralCode.cs
Comment thread blotztask-mobile/src/shared/util/queryClient.ts Outdated
Comment thread blotztask-mobile/src/shared/util/queryClient.ts Outdated
Comment thread blotztask-api/Modules/Users/Commands/SyncUser.cs Outdated
Comment thread blotztask-api/Modules/Referrals/Services/ReferralCodeGenerator.cs
Comment thread blotztask-api/Modules/Badges/Enum/TriggerAction.cs
@Moomin928
Moomin928 force-pushed the invitation-system branch from 2cc9e13 to 6f5afc9 Compare July 28, 2026 12:59
lyx2-2
lyx2-2 previously requested changes Jul 29, 2026
Comment thread blotztask-mobile/src/feature/invite/hooks/useRedeemInviteCode.ts Outdated

@LiChenyang-GZ LiChenyang-GZ left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

AI-generated review. These are suggestions from an automated senior-level pass, not verdicts. Please verify each point yourself before acting — some may be wrong or missing context I don't have.

Solid feature overall. Things that are clearly right: the unique index on Referrals.RefereeUserId plus the Restrict FKs means a concurrent double-redeem gets caught by the DB even if it slips past the AnyAsync check (and DbUpdateException maps to 409, which happens to be the correct status). DeleteUser clearing Referrals manually while ReferralCodes cascades is the right split. ToUpperInvariant() normalisation lines up with the uppercase-only generator alphabet, and dropping I/O/0/1 from it is a nice touch.

dotnet build passes locally (0 errors).

Inline comments cover the concrete code issues. Three things that don't fit inline:

1. The PR description doesn't match the diff

Description says Actual
InviteRedemptions table + AppUsers.InviteCode column ReferralCodes + Referrals tables
GET/POST /api/invite/* /api/referral/my-code, /api/referral/redeem
Settings has an input to redeem another user's code Settings only shows/copies your own code; redeem lives in onboarding
No mention of the 403 "onboarding only" restriction, which is the single biggest behavioural constraint in this PR
No mention that onboarding goes from 3 steps to 4

This affects the release note too: it says "find it in Settings", and users can indeed see their code there, but there is no entry point anywhere to use someone else's code after onboarding. As written the checklist is misleading.

2. Question: is the onboarding-only window intended?

handleFinish in onboarding-screen.tsx is shared by both the Skip button and Get Started, so either path sets IsOnboarded = true permanently. Two consequences: a user who taps Skip can never redeem, and every existing user is already onboarded, so none of them can participate at all.

If that's a deliberate anti-abuse decision it's fine — just worth stating in the description. If not, either add a redeem entry point in Settings, or relax the backend rule to something like "within N days of signup and not yet redeemed".

3. Minor / nits — feel free to ignore

  • EnsureReferralCode's two-phase write (insert with Code = null, then update) exists only because the code is derived from the identity Id. The cost is that Code stays nullable forever and every new user pays two SaveChanges. A random 8-char code from the same alphabet plus a uniqueness retry would let Code be IsRequired(). Both designs are defensible; the current one works.
  • EventValueKey.InviteCount / TriggerAction.InviteRedeemed are unused //To do placeholders — probably cleaner to add them in the badge PR that actually consumes them.
  • Naming is split three ways: backend Referral, route /api/referral, mobile feature/invite and "invite" copy. Settling on one word would save real grep time later.
  • Whitespace-only edits in AppUser.cs, AppUserConfiguration.cs and BlotzTaskDbContext.cs add diff noise.

One test worth writing

RedeemReferralCodeCommandHandler's three guards (self-referral, double redeem, onboarding gate) are user-isolation logic that breaks silently when the onboarding rule changes, and they're short to cover with an in-memory/SQLite context. I wouldn't add anything beyond that here.

Comment thread blotztask-mobile/src/feature/invite/hooks/useRedeemInviteCode.ts Outdated
Comment thread blotztask-api/Modules/Referrals/Commands/RedeemReferralCode.cs
Comment thread blotztask-api/Modules/Referrals/Queries/GetMyReferralCode.cs Outdated
Comment thread blotztask-api/Modules/Referrals/Services/ReferralCodeGenerator.cs
@Moomin928
Moomin928 dismissed lyx2-2’s stale review July 31, 2026 10:54

Both flagged issues are fixed: the migration no longer drops InviteRedemptions/AppUsers.InviteCode (confirmed no references remain), and the mobile hook now uses meta.errorMap (read by queryClient) instead of the unused errorNs.

{
CompleteOffsetMins
CompleteOffsetMins,
//To do

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

what is this todo ?

example of TODO: //TODO: Need to complete this section and use in reward once product manager confirm what to do with the invitation badge

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.

4 participants