refactor: unify authentication handling across backend modules#60
refactor: unify authentication handling across backend modules#60evan-taylor merged 1 commit intodevfrom
Conversation
- Replaced direct calls to `getUserIdentity` with `requireAuthUserId` in various modules to ensure consistent user authentication checks. - Updated ownership and user ID retrieval logic in listings, messages, profiles, and saved listings to utilize stable user IDs. - Enhanced error handling for unauthorized actions across multiple mutations and queries. - Removed legacy participant ID handling in messages and conversations to streamline user identification. - Improved overall code maintainability and clarity by consolidating authentication logic.
📝 WalkthroughWalkthroughThis PR refactors the authentication and user identity handling across the backend, replacing a complex alias-based participant identification system with stable user IDs through new auth helper utilities. Corresponding frontend changes remove alias/participant-key logic and introduce UI component theming and layout density options. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
docs/SCHEMA_MIGRATION.md (2)
134-138:⚠️ Potential issue | 🟡 MinorDocumentation inconsistency with implementation.
Lines 136-137 state that
v.string()fields hold "Auth identities (strings fromidentity.subject)", but the PR's implementation usesgetAuthUserId/requireAuthUserIdwhich returnsId<'users'>(Convex document IDs), notidentity.subject.Consider updating the summary to reflect the actual implementation: these fields now store stable Convex user document IDs, not raw auth identity subjects.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/SCHEMA_MIGRATION.md` around lines 134 - 138, The docs incorrectly say v.string() fields hold "Auth identities (strings from identity.subject)"; update the summary to say these fields store stable Convex user document IDs (Id<'users'>) as returned by getAuthUserId and requireAuthUserId in the implementation, and change any wording that implies raw auth subject strings to instead reference Convex user IDs / document references so the documentation matches the actual code behavior.
45-51:⚠️ Potential issue | 🟡 MinorCode example contradicts the warning above.
Line 19 warns: "Do not use
identity.subjectfor ownership keys." However, the "After" example on line 50 showssellerId: identity.subjectas the correct approach.The example should demonstrate using
requireAuthUserId:📝 Suggested fix
### Validation ```typescript // Before (with v.id('users')): sellerId: identity.subject as Id<'users'>; // ❌ Unsafe cast // After (with v.string()): -sellerId: identity.subject; // ✅ Type-safe, no cast needed +const userId = await requireAuthUserId(ctx); +sellerId: userId; // ✅ Type-safe stable ID</details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In
@docs/SCHEMA_MIGRATION.mdaround lines 45 - 51, The "After" example
contradicts the warning by using identity.subject directly; replace that line so
the example calls requireAuthUserId(ctx) to obtain a stable, type-safe userId
and then assigns that value to sellerId instead of using identity.subject;
update the snippet to reference requireAuthUserId and sellerId (and remove the
unsafe cast example or keep it as the "Before" example) so the docs demonstrate
using requireAuthUserId for ownership keys.</details> </blockquote></details> <details> <summary>backend/convex/listings.ts (2)</summary><blockquote> `149-170`: _⚠️ Potential issue_ | _🟠 Major_ **Same ownership comparison issue affects visibility.** The `isOwner` check on line 156 will fail for existing listings where `sellerId` is stored as `identity.subject`. Owners viewing their own hidden listings may see them as "not found". <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In
@backend/convex/listings.tsaround lines 149 - 170, getListing's owner check
fails when listing.sellerId is stored as an identity object (e.g. { subject })
so isOwner (computed in the getListing handler using getStableUserId) can be
false for actual owners; fix by normalizing the seller id before comparison (in
the getListing handler) — derive a canonicalSellerId from listing.sellerId
(extract .subject if it's an object, otherwise use the string) and then set
isOwner = !!userId && userId === canonicalSellerId so hidden and non-active
visibility checks behave correctly.</details> --- `30-43`: _⚠️ Potential issue_ | _🔴 Critical_ **Breaking change: ownership verification will fail for existing listings.** The `verifyOwnership` function now compares `listing.sellerId` (stored as `identity.subject` for existing records) against `userId` (now `Id<'users'>`). These formats are incompatible, causing ownership checks to fail for listings created before this change. **Impact:** Users will receive "You are not the owner of this listing" errors when trying to update or delete their existing listings. **Recommendation:** Either: 1. Migrate existing `listings.sellerId` values to the new `Id<'users'>` format, OR 2. Add a fallback lookup that maps `identity.subject` → `users._id` during the transition period <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In
@backend/convex/listings.tsaround lines 30 - 43, verifyOwnership currently
compares listing.sellerId to the Id<'users'> returned by requireAuthUserId,
which breaks for legacy listings where sellerId stores identity.subject; update
verifyOwnership to handle both formats by adding a fallback: after fetching
listing and userId (via requireAuthUserId), first compare listing.sellerId ===
userId, and if that fails and listing.sellerId appears to be a legacy
identity.subject value, resolve the corresponding user record (query users for a
document whose identity.subject matches listing.sellerId) and compare its _id to
userId; if a match return the listing, otherwise throw the existing ConvexError.
Ensure you reference verifyOwnership, listing.sellerId, identity.subject,
requireAuthUserId and users._id when implementing the lookup.</details> </blockquote></details> <details> <summary>backend/convex/profiles.ts (1)</summary><blockquote> `88-100`: _⚠️ Potential issue_ | _🔴 Critical_ **Breaking change: existing profiles will not be found.** The `getCurrentProfile` query now uses `Id<'users'>` to query the `by_userId` index. Existing profiles stored with `userId = identity.subject` will not be matched, causing authenticated users to appear as if they have no profile. **Impact:** Users will be prompted to re-onboard despite having existing profiles. <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In
@backend/convex/profiles.tsaround lines 88 - 100, getCurrentProfile is now
querying the profiles.by_userId index using an Id<'users'> typed userId which
won't match existing rows where profiles.userId was stored as the legacy
identity.subject string; change getCurrentProfile (and/or the lookup logic) to
first attempt the lookup with the typed stable userId returned by
getStableUserId and, if that returns no result, perform a fallback lookup using
the legacy string form (String(userId) or the original identity.subject) so both
existing profiles and new typed IDs are matched when querying the 'by_userId'
index.</details> </blockquote></details> <details> <summary>backend/convex/messages.ts (1)</summary><blockquote> `510-556`: _⚠️ Potential issue_ | _🟠 Major_ **Existing conversation lookup will fail, creating duplicates.** In `getOrCreateConversation`, the query on line 533-538 searches `by_listing_buyer_seller` with the new `buyerId` format. For users who previously started conversations (stored with `identity.subject`), the existing conversation won't be found, and a duplicate will be created. </blockquote></details> </blockquote></details>🧹 Nitpick comments (4)
frontend/components/ListingCard.tsx (1)
296-307: Empty style objects can be removed.
listingTitleHomeandlistingPriceHomeare empty objects with only comments. If no actual style overrides are needed for home density, consider removing these to reduce noise:♻️ Suggested cleanup
listingTitle: { ...typography.body, color: colors.primary, flex: 1, minWidth: 0, }, - listingTitleHome: { - // Tighter for home density - }, listingPrice: { ...typography.title2, fontSize: 17, color: colors.accent, flexShrink: 0, }, - listingPriceHome: { - // Same as base - },Then update the JSX to conditionally apply only when styles exist:
- <Text style={[styles.listingTitle, isHomeDensity && styles.listingTitleHome]} ...> + <Text style={styles.listingTitle} ...>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/components/ListingCard.tsx` around lines 296 - 307, Remove the no-op style objects listingTitleHome and listingPriceHome from the styles object in ListingCard.tsx and clean up any references in the JSX so you only apply existing styles (e.g., listingTitle, listingPrice) — where conditional application was used, change it to only include the style if it exists (or remove the conditional spread) so JSX doesn't reference the removed keys; update any className/style arrays to not include listingTitleHome/listingPriceHome.frontend/app/(tabs)/index.tsx (1)
342-369: Redundant condition in ListEmptyComponent.The
listings.length === 0check on line 343 is redundant sinceListEmptyComponentis only rendered by FlatList when the data array is empty.♻️ Suggested simplification
ListEmptyComponent={ - listings.length === 0 ? ( <View style={styles.stateContainer}> <ScreenState variant={loadError ? 'error' : 'empty'} title={ loadError ? "Couldn't load listings" : hasActiveFilters ? 'No listings match your filters' : 'No listings yet' } message={ loadError ? 'Check your connection and try again.' : hasActiveFilters ? 'Try a wider price range or fewer tags.' : 'Be the first to post something for campus.' } actionLabel={ loadError ? undefined : hasActiveFilters ? 'Clear Filters' : undefined } onRetry={loadError ? refreshListings : undefined} onAction={hasActiveFilters ? handleClearAll : undefined} /> </View> - ) : null }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/app/`(tabs)/index.tsx around lines 342 - 369, The ListEmptyComponent currently wraps the ScreenState JSX with an unnecessary conditional check on listings.length === 0; remove that redundant condition and return the ScreenState wrapper directly from ListEmptyComponent so FlatList can control when it's shown. Specifically, update the ListEmptyComponent prop to render the <View style={styles.stateContainer}><ScreenState ... /></View> unconditionally (keeping all props: variant, title, message, actionLabel, onRetry -> refreshListings, onAction -> handleClearAll) and remove the outer ternary that checks listings.length; no other logic changes to ScreenState, handleClearAll, or refreshListings are needed.backend/convex/reports.ts (1)
25-100: Inconsistent step numbering in comments.The validation step comments are numbered 2, 5, 6, 6, 7, 8 (lines 25, 30, 55, 68, 80, 90). Steps 1, 3, 4 are missing, and step 6 appears twice. Consider renumbering for clarity or removing the numbers entirely.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/convex/reports.ts` around lines 25 - 100, The inline comments in reports.ts around the report creation flow (e.g., the comment blocks referencing validation, target existence, duplicate check, rate limiting, create report, and auto-hide logic that surround symbols like MAX_NOTES_LENGTH, ctx.db.get, existingReport, recentReports, reportId, and allReports) use inconsistent numbering (2,5,6,6,7,8); remove the numeric prefixes or renumber them sequentially (1..N) so the steps read clearly (e.g., "1. Validate notes length", "2. Validate target exists", "3. Check duplicate report", etc.), and update or remove any duplicate "6." comment to keep the sequence consistent.backend/convex/messages.ts (1)
30-36: Consider removing wrapper function.
requireStableUserIdis a thin wrapper aroundrequireAuthUserIdwith no additional logic. Consider usingrequireAuthUserIddirectly throughout the file to reduce indirection, or document why the wrapper exists (e.g., future expansion plans).♻️ Proposed simplification
-/** Returns the stable auth user ID. Throws if not authenticated. */ -async function requireStableUserId( - ctx: MutationCtx | QueryCtx, - message = 'Unauthorized' -): Promise<Id<'users'>> { - return await requireAuthUserId(ctx, message); -}Then replace all
requireStableUserId(ctx)calls withrequireAuthUserId(ctx)orrequireAuthUserId(ctx, 'Unauthorized').🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/convex/messages.ts` around lines 30 - 36, The wrapper function requireStableUserId simply forwards to requireAuthUserId without adding behavior; remove requireStableUserId and replace its usages with direct calls to requireAuthUserId (e.g., replace requireStableUserId(ctx) with requireAuthUserId(ctx) or requireAuthUserId(ctx, 'Unauthorized') as needed), or if you prefer keeping it, add a comment documenting its intended future purpose—update any imports/exports and tests referencing requireStableUserId accordingly.🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed. Inline comments: In `@backend/convex/messages.ts`: - Around line 107-114: The permission check fails because convo.buyerId / convo.sellerId may be stored as an identity object (e.g., { subject }) while userId is a string; update the checks to normalize both participant IDs before comparing: derive normalizedBuyerId and normalizedSellerId (e.g., const normalizedBuyerId = typeof convo.buyerId === 'string' ? convo.buyerId : convo.buyerId?.subject) and use those in the isBuyer/isSeller checks and when computing recipientId and senderId (replace references to convo.buyerId/convo.sellerId with the normalized variables) so existing conversations with identity objects pass the permission check. - Around line 212-225: The listUserConversations query now uses requireStableUserId which returns Id<'users'>, but existing conversations were stored with buyerId/sellerId = identity.subject (legacy string), so queries against the by_buyer and by_seller indexes miss existing rows; update listUserConversations to query both id formats (the new stableId from requireStableUserId and the legacy subject from ctx.auth.identity.subject) for buyerId and sellerId (e.g., run queries for both values or use an OR/IN-equivalent and merge/deduplicate results), referencing the functions/values requireStableUserId, listUserConversations, by_buyer, by_seller, buyerId, sellerId, and identity.subject so existing conversations are returned. In `@backend/convex/profiles.ts`: - Around line 139-145: The duplicate-profile check only queries by userId and misses profiles stored under identity.subject, so update the check to look for either identifier: query profiles by userId (existingProfile) AND also query profiles by subject (e.g., identity.subject) and throw if either query returns a record; if there isn't already an index for subject, add or use an index such as 'by_subject' and call ctx.db.query('profiles').withIndex('by_subject', q => q.eq('subject', identity.subject)) (or run both queries in the createProfile function) to ensure users cannot create a second profile. --- Outside diff comments: In `@backend/convex/listings.ts`: - Around line 149-170: getListing's owner check fails when listing.sellerId is stored as an identity object (e.g. { subject }) so isOwner (computed in the getListing handler using getStableUserId) can be false for actual owners; fix by normalizing the seller id before comparison (in the getListing handler) — derive a canonicalSellerId from listing.sellerId (extract .subject if it's an object, otherwise use the string) and then set isOwner = !!userId && userId === canonicalSellerId so hidden and non-active visibility checks behave correctly. - Around line 30-43: verifyOwnership currently compares listing.sellerId to the Id<'users'> returned by requireAuthUserId, which breaks for legacy listings where sellerId stores identity.subject; update verifyOwnership to handle both formats by adding a fallback: after fetching listing and userId (via requireAuthUserId), first compare listing.sellerId === userId, and if that fails and listing.sellerId appears to be a legacy identity.subject value, resolve the corresponding user record (query users for a document whose identity.subject matches listing.sellerId) and compare its _id to userId; if a match return the listing, otherwise throw the existing ConvexError. Ensure you reference verifyOwnership, listing.sellerId, identity.subject, requireAuthUserId and users._id when implementing the lookup. In `@backend/convex/profiles.ts`: - Around line 88-100: getCurrentProfile is now querying the profiles.by_userId index using an Id<'users'> typed userId which won't match existing rows where profiles.userId was stored as the legacy identity.subject string; change getCurrentProfile (and/or the lookup logic) to first attempt the lookup with the typed stable userId returned by getStableUserId and, if that returns no result, perform a fallback lookup using the legacy string form (String(userId) or the original identity.subject) so both existing profiles and new typed IDs are matched when querying the 'by_userId' index. In `@docs/SCHEMA_MIGRATION.md`: - Around line 134-138: The docs incorrectly say v.string() fields hold "Auth identities (strings from identity.subject)"; update the summary to say these fields store stable Convex user document IDs (Id<'users'>) as returned by getAuthUserId and requireAuthUserId in the implementation, and change any wording that implies raw auth subject strings to instead reference Convex user IDs / document references so the documentation matches the actual code behavior. - Around line 45-51: The "After" example contradicts the warning by using identity.subject directly; replace that line so the example calls requireAuthUserId(ctx) to obtain a stable, type-safe userId and then assigns that value to sellerId instead of using identity.subject; update the snippet to reference requireAuthUserId and sellerId (and remove the unsafe cast example or keep it as the "Before" example) so the docs demonstrate using requireAuthUserId for ownership keys. --- Nitpick comments: In `@backend/convex/messages.ts`: - Around line 30-36: The wrapper function requireStableUserId simply forwards to requireAuthUserId without adding behavior; remove requireStableUserId and replace its usages with direct calls to requireAuthUserId (e.g., replace requireStableUserId(ctx) with requireAuthUserId(ctx) or requireAuthUserId(ctx, 'Unauthorized') as needed), or if you prefer keeping it, add a comment documenting its intended future purpose—update any imports/exports and tests referencing requireStableUserId accordingly. In `@backend/convex/reports.ts`: - Around line 25-100: The inline comments in reports.ts around the report creation flow (e.g., the comment blocks referencing validation, target existence, duplicate check, rate limiting, create report, and auto-hide logic that surround symbols like MAX_NOTES_LENGTH, ctx.db.get, existingReport, recentReports, reportId, and allReports) use inconsistent numbering (2,5,6,6,7,8); remove the numeric prefixes or renumber them sequentially (1..N) so the steps read clearly (e.g., "1. Validate notes length", "2. Validate target exists", "3. Check duplicate report", etc.), and update or remove any duplicate "6." comment to keep the sequence consistent. In `@frontend/app/`(tabs)/index.tsx: - Around line 342-369: The ListEmptyComponent currently wraps the ScreenState JSX with an unnecessary conditional check on listings.length === 0; remove that redundant condition and return the ScreenState wrapper directly from ListEmptyComponent so FlatList can control when it's shown. Specifically, update the ListEmptyComponent prop to render the <View style={styles.stateContainer}><ScreenState ... /></View> unconditionally (keeping all props: variant, title, message, actionLabel, onRetry -> refreshListings, onAction -> handleClearAll) and remove the outer ternary that checks listings.length; no other logic changes to ScreenState, handleClearAll, or refreshListings are needed. In `@frontend/components/ListingCard.tsx`: - Around line 296-307: Remove the no-op style objects listingTitleHome and listingPriceHome from the styles object in ListingCard.tsx and clean up any references in the JSX so you only apply existing styles (e.g., listingTitle, listingPrice) — where conditional application was used, change it to only include the style if it exists (or remove the conditional spread) so JSX doesn't reference the removed keys; update any className/style arrays to not include listingTitleHome/listingPriceHome.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID:
8dc88b8e-bed0-43c9-ada0-85e6afd0305e⛔ Files ignored due to path filters (1)
backend/convex/_generated/api.d.tsis excluded by!**/_generated/**📒 Files selected for processing (16)
backend/convex/__tests__/messages.test.tsbackend/convex/__tests__/profiles.test.tsbackend/convex/lib/authIdentity.tsbackend/convex/listings.tsbackend/convex/messages.tsbackend/convex/profiles.tsbackend/convex/pushNotifications.tsbackend/convex/reports.tsbackend/convex/savedListings.tsdocs/SCHEMA_MIGRATION.mdfrontend/app/(tabs)/index.tsxfrontend/app/auth/login.tsxfrontend/app/conversations/[id].tsxfrontend/components/ListingCard.tsxfrontend/components/TagInput.tsxfrontend/components/TagPicker.tsx
| const isBuyer = convo.buyerId === userId; | ||
| const isSeller = convo.sellerId === userId; | ||
| if (!isBuyer && !isSeller) { | ||
| throw new ConvexError('Forbidden'); | ||
| } | ||
|
|
||
| const senderId = isBuyerByAlias ? convo.buyerId : convo.sellerId; | ||
| const recipientId = isBuyerByAlias ? convo.sellerId : convo.buyerId; | ||
| const senderId = userId; | ||
| const recipientId = isBuyer ? convo.sellerId : convo.buyerId; |
There was a problem hiding this comment.
Permission checks will fail for existing conversations.
The buyer/seller comparison convo.buyerId === userId will fail for existing conversations where buyerId is stored as identity.subject. Users will receive "Forbidden" errors when trying to send messages in their existing conversations.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@backend/convex/messages.ts` around lines 107 - 114, The permission check
fails because convo.buyerId / convo.sellerId may be stored as an identity object
(e.g., { subject }) while userId is a string; update the checks to normalize
both participant IDs before comparing: derive normalizedBuyerId and
normalizedSellerId (e.g., const normalizedBuyerId = typeof convo.buyerId ===
'string' ? convo.buyerId : convo.buyerId?.subject) and use those in the
isBuyer/isSeller checks and when computing recipientId and senderId (replace
references to convo.buyerId/convo.sellerId with the normalized variables) so
existing conversations with identity objects pass the permission check.
| const userId = await requireStableUserId(ctx); | ||
|
|
||
| const [buyerConversations, sellerConversations] = await Promise.all([ | ||
| ctx.db | ||
| .query('conversations') | ||
| .withIndex('by_buyer', (q) => q.eq('buyerId', userId)) | ||
| .order('desc') | ||
| .collect(), | ||
| ctx.db | ||
| .query('conversations') | ||
| .withIndex('by_seller', (q) => q.eq('sellerId', userId)) | ||
| .order('desc') | ||
| .collect(), | ||
| ]); |
There was a problem hiding this comment.
Breaking change: existing conversations will not be found.
The listUserConversations query uses by_buyer and by_seller indexes with userId (now Id<'users'>). Existing conversations stored with buyerId/sellerId = identity.subject will not be matched.
Impact: Users will see an empty inbox despite having existing conversations.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@backend/convex/messages.ts` around lines 212 - 225, The listUserConversations
query now uses requireStableUserId which returns Id<'users'>, but existing
conversations were stored with buyerId/sellerId = identity.subject (legacy
string), so queries against the by_buyer and by_seller indexes miss existing
rows; update listUserConversations to query both id formats (the new stableId
from requireStableUserId and the legacy subject from ctx.auth.identity.subject)
for buyerId and sellerId (e.g., run queries for both values or use an
OR/IN-equivalent and merge/deduplicate results), referencing the
functions/values requireStableUserId, listUserConversations, by_buyer,
by_seller, buyerId, sellerId, and identity.subject so existing conversations are
returned.
| const existingProfile = await ctx.db | ||
| .query('profiles') | ||
| .withIndex('by_userId', (q) => q.eq('userId', identity.subject)) | ||
| .withIndex('by_userId', (q) => q.eq('userId', userId)) | ||
| .unique(); | ||
| if (existingProfile) { | ||
| throw new ConvexError('Profile already exists for this user'); | ||
| } |
There was a problem hiding this comment.
Duplicate profile guard bypassed for existing users.
The duplicate check queries by userId (now Id<'users'>). If a user's existing profile was stored with identity.subject, this check will not find it, allowing the user to create a second profile.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@backend/convex/profiles.ts` around lines 139 - 145, The duplicate-profile
check only queries by userId and misses profiles stored under identity.subject,
so update the check to look for either identifier: query profiles by userId
(existingProfile) AND also query profiles by subject (e.g., identity.subject)
and throw if either query returns a record; if there isn't already an index for
subject, add or use an index such as 'by_subject' and call
ctx.db.query('profiles').withIndex('by_subject', q => q.eq('subject',
identity.subject)) (or run both queries in the createProfile function) to ensure
users cannot create a second profile.
* implemented CRUD operations in backend * feat: implement Cal Poly email authentication with verification - Add user authentication using @convex-dev/auth with Password provider - Enforce @calpoly.edu email domain validation - Implement email verification flow with SendGrid integration - Create signup, login, and email verification screens - Add user schema and auth-related queries/mutations - Update shared types and utilities for email validation - Add AuthProvider to app layout with auth routes * docs: update environment variables documentation for auth * fix: secure email verification with token validation * feat: implemented profile viewing and editing * feat: add search and filtering for listings - Add condition field to listings (new, like_new, good, fair, poor) - Add searchAndFilterListings query with full-text search - Support category, price range, condition filters - Add sort options and cursor-based pagination (20 items/page) - Add compound indexes for performance * refactor: switch from Password+SendGrid to OTP+Resend - Replace Password provider with Resend OTP (8-digit code, 15 min expiry) - Add Cal Poly email domain validation - Simplify to single login screen (email → code) - Remove separate signup and verify-email screens - Switch from SendGrid to Resend for emails - Use @convex-dev/auth OTP pattern per reviewer feedback * Adding .env.examples back * fix: address CodeRabbit review suggestions for auth implementation * fix: configure ESM module resolution for backend typecheck * Normalize email to lowercase and trim whitespace * fix: added constraints to profile creation * POLY-10 add messaging and conversation schema and sendMessage mutation * POLY-10 test function for sendMessage mutation * POLY-10 feat: add retrieve conversation history query * POLY-10 feat: add listUserConversations query * POLY-10 fix: updated commenting and edited listingId in schema * feat: associate conversations with listings, read functionality, real-time message delivery * Sync Convex schema, generated files, and backend dependencies * POLY-10: Fix functions for updated schema * Add pagination to getProfiles query Updated getProfiles query to support pagination options. * Update package.json * Update convex dependency version to 1.31.5 * Update package.json * Update package.json * Downgrade convex dependency version to 1.17.2 * this should fix it * there we go * feat: implement custom OTP authentication backend (POLY-29) - Add otpCodes table with hashing and rate limiting - Implement requestOTP action with Cal Poly email validation - Implement verifyOTP mutation with attempt limiting - Replace @convex-dev/auth with custom OTP system - Add bcryptjs for secure OTP hashing - Integrate Resend for email delivery - Add emailVerified field to users table BREAKING CHANGE: Replaces @convex-dev/auth authentication system * chore: update package-lock.json for bcryptjs dependencies * fix: implement CodeRabbit security improvements - Make createAndStoreOTPInternal an internalMutation to prevent client access - Implement atomic rate-limit check and OTP storage in single transaction - Replace Math.random() with crypto.getRandomValues() for secure OTP generation - Add emailVerified field to User type to match backend schema Security improvements prevent race conditions and ensure cryptographically secure random number generation for OTP codes. * fix: make emailVerified optional for backward compatibility Make emailVerified field optional in schema and User type to prevent breaking existing user records that don't have this field. New users created via OTP will have emailVerified set to true, while existing users can be migrated gradually without validation errors. * feat: add report schema and submission endpoint (POLY-19) - Add reports table to Convex schema with indexes for target and reporter lookups - Implement createReport mutation with authentication, validation, and duplicate prevention - Add rate limiting (10 reports per 24 hours per user) - Create comprehensive test suite with 9 tests covering all acceptance criteria - Ensure reporterId is never exposed in responses * Fix(POLY-19): Address test failures and code review feedback & Feat(POLY-20): Implement auto-hide logic * fix: enforce auth in messaging queries and make debug mutation internal * feat: added tags field with validation * fix: fixed updateListing test cases * fix: add AsyncStorage for React Native auth storage * Fix(POLY-20): Prevent auto-hide from overwriting existing hidden context * feat(POLY-15): Add home feed sorting and filtering - Update getListings query with category, minPrice, maxPrice filters - Use by_status_createdAt index for deterministic newest-first ordering - Add price validation (min >= 0, max >= min) - Add FilterBar component with filter chips - Add CategoryPicker bottom sheet - Add PriceRangePicker bottom sheet with presets - Integrate filters into home screen with state management - Add 'No results' state with clear filters option - Add 7 tests for filter combinations * fix(POLY-15): address code review feedback - Refactor FilterBar to avoid nested TouchableOpacity - Add negative max price validation to PriceRangePicker * fix(POLY-15): add NaN/non-finite validation for price inputs - Trim whitespace from inputs before parsing - Treat empty/whitespace-only strings as undefined - Validate parsed values are finite with Number.isFinite - Show clear error message for invalid number input * done * done now * other issues done too * other issues done too * fixed errors * feat: add tag filters to getListings * fix: pass empty args to getListings useQuery * fix: modify listings.ts to pass local tests and ran npm install * fixed final errors * fixed final errors * fixed new errors * fixed new errors * fixed * Poly-16-Tags-Backend * removed redundancy * fix * fix * fix * Fix2 * fix: integrate coderabbit changes and edit testcases * fix: filter listings by tags in memory * feat: exclude hidden listings from getListings * Add validation for limit and price parameters in getListings query * Fix duplicate reports table in schema and update dependencies * Merge tag filtering with category/price filters - Add tag filtering to getListings query (OR logic) - Add category and price range filters - Create unified FilterBar with all three filter types - Add CategoryPicker and PriceRangePicker components - Support URL params for tag deep linking - Add comprehensive validation for price inputs * refactor: address code review feedback - Remove redundant arrayContaining assertion in schema test - Extract shared filter types to frontend/types/filters.ts - Remove duplicate CATEGORIES from CategoryPicker - Fix PriceRangePicker useEffect to only reset on modal open - Remove dead padding properties from chip style - Fix URL param sync in handleTagsChange * Fix duplicate useState import and unused styles in index.tsx * fix: exclude hidden listings from getListings query * fix: resolve merge conflicts and fix test configuration - Merge tag filtering tests with basic filtering tests from dev - Add @babel/preset-typescript to babel.config.js for proper TS support - Configure jest to use ts-jest for .ts files and babel-jest for .js files - Add price validation to getListings (minPrice/maxPrice checks) - Add getCurrentUserSubject query from dev branch - Fix test ordering by using _creationTime instead of createdAt - All 55 tests now passing * fix: remove duplicate functions from merge conflict - Remove duplicate getListings export (kept complete version with tag filtering) - Remove duplicate getCurrentUserSubject export - Remove unused normalizeTags function - All tests passing, linting clean * fix: remove duplicate listings variable declaration - Remove unfiltered listings query on line 13 - Keep filtered listings query that respects filter state - Fixes TypeScript error: Cannot redeclare block-scoped variable * fixed redundancy of validate/normalize * chore: upgrade Expo SDK to 54 and fix dependencies - Upgrade Expo from ~52.0.0 to ^54.0.33 - Upgrade React to 19.1.0 and React Native to 0.81.5 - Update all Expo packages to SDK 54 compatible versions - Add @auth/core dependency to backend to fix convex dev - Add .npmrc with legacy-peer-deps flag to handle peer dependency conflicts - Fix npm audit vulnerabilities * chore: add self-hosted Convex actions URL to environment configuration * testing * fixes * add docs for s3 convex config (#38) * fixes * delete * fixes * fixes * fixes * fixes * test added * fixes * update * tests * feat: integrate OpenAI Moderation API for listings and messages - Add moderationResults table to schema - Create moderation.ts with moderateContent internalAction - Refactor createListing/updateListing from mutations to actions - Refactor sendMessage from mutation to action - Add ConvexError responses for flagged content - Update all test files for action-based API with fetch mocks - Disable ts-jest diagnostics for convex-test ID serialization - Document OPENAI_API_KEY in .env.example Resolves: POLY-31, POLY-36, POLY-37 * fix: switch createListing/updateListing from useMutation to useAction in frontend Frontend was still using useMutation for createListing and updateListing, which are now actions after moderation integration. This caused TypeScript errors: FunctionReference<"action"> not assignable to FunctionReference<"mutation">. * fix: extract ConvexError.data for moderation rejection messages ConvexError stores the custom error string in .data, not .message. Without this fix, moderation rejection messages would not surface to the user in the Alert dialog. * feat: exclude hidden content from feed and handle hidden states * fix: code rabbit changes * WIP: messaging changes * chore: make POLY-38 schema rollout backward-compatible with backfill * feat: clean shareable links route and listing share action * feat: add production-ready report modal flow for listing detail * style: format ReportModal with Prettier * feat: rebuild clean image uploader with upload URL flow and timeout safeguards * feat: deep-linking listings rebased from nightly clean on latest dev * feat: expo upgrade, fix config, enhance ui, add animations - Deleted Babel configuration file as it is no longer needed. - Updated Jest configuration to specify Babel config file path. - Added environment variables for email handling in backend. - Enhanced email validation and error handling in ResendOTP function. - Improved frontend components with better loading states and animations. - Updated dependencies in package-lock.json for compatibility with new Expo version. * refactor: streamline auth configuration and improve search functionality - Updated auth configuration to use a unified provider domain from environment variables. - Simplified search component logic to enhance performance and maintain reactivity for first-page results. - Removed unnecessary cursor handling for improved clarity in state management. * chore: update expo-image-manipulator and expo-image-picker versions in package files - Upgraded expo-image-manipulator to version 55.0.9 and expo-image-picker to version 55.0.10 in both package-lock.json and package.json for improved functionality and compatibility. * fix: coderabbit feedback - Added a function to dynamically determine the app origin for shareable links in the ListingDetailScreen. - Updated the cancel button in both NewListingScreen and EditListingScreen to be disabled during submissions or pending uploads, with visual feedback for disabled state. * feat: enhance login functionality with return redirection - Integrated useLocalSearchParams to retrieve returnTo parameter for post-auth redirection. - Updated router.replace to redirect users to the specified return path after successful login, improving user experience. * feat: enhance login screen with verification step handling - Introduced a new state variable to manage the verification step in the login process. - Updated the rendering logic to display success messages conditionally based on the verification step, improving user feedback during authentication. * chore: update dependencies and enhance TypeScript configuration - Added expo-router version 55.0.3 to package.json and package-lock.json for improved routing capabilities. - Updated TypeScript configuration in tsconfig.json to include additional file types and improve module resolution. * feat: enhance listings and profiles management - Updated the updateListing action to return specific moderation error responses instead of throwing exceptions. - Added a new query to retrieve the current user's listings, including hidden and inactive items. - Introduced a new query to fetch the current authenticated user's full profile, including non-public fields. - Enhanced profile creation and update mutations to include additional fields and validation for email, year, and other profile attributes. - Improved error handling in listing creation and editing to provide user-friendly feedback for moderation issues. - Updated frontend components to reflect new profile and listing functionalities, including loading states and input validations. * refactor: improve error messaging in profile creation tests - Updated the test case for createProfile to clarify rejection conditions when no email is provided or available on identity. - Enhanced error message to be more user-friendly, specifying that an email is required to create a profile. * chore: update Node.js version in configuration files * fix: ci script * fix: ci checks * fix: ci linux lightningcss binary * fixed issue * fixed issue * potential redundent querires * null request bug * feat: implement push notifications and update dependencies - Added support for push notifications using @convex-dev/expo-push-notifications. - Updated the convex configuration to include push notification handling in message actions. - Enhanced tests to verify push notification functionality upon message creation. - Updated dependencies, including upgrading convex to version 1.32.0 and related Expo packages. - Modified configuration files to reflect changes in app identifiers and notification settings. * fix: improve push notification handling and enhance tests - Updated the sendMessage action to use a more structured approach for push notifications based on the environment. - Enhanced test setup for push notifications by refactoring the mock implementation for better clarity and maintainability. - Added error handling for push notification failures during message delivery. - Updated the useAuth hook to ensure push tokens are removed on sign-out, improving token management. * fix: update app slug for consistency * feat: add message seller button on listings * feat: create inbox screen * feat: create chat screen * feat: implement read receipts for inbox in nav bar * fix: remove errors when clicking inbox * fix: prevent duplicate conversations * feat: add read receipts to messages * fix: show buyer name for listing creator * fix: implemented code rabbit changes * fix: add more code rabbit changes * Development environment setup (#55) * docs: add AGENTS.md with Cursor Cloud development instructions * docs: update AGENTS.md with Convex cloud setup and login caveat * docs: update AGENTS.md to reflect Convex cloud only (no self-hosted) --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> * feat: UI overhaul (#57) * feat: add saved listings functionality and enhance listings queries - Implemented `savedListings` table with associated queries and mutations for saving, unsaving, and checking saved listings. - Added `getListingsBySeller` query to fetch public active listings by seller. - Updated `updateListingStatus` mutation to prevent changing status of sold listings. - Enhanced tests for new saved listings features and updated existing tests for listing status changes. - Improved UI components to support new functionalities and ensure better user experience. * fix: agent bugs * feat: integrate Sentry for error tracking and monitoring - Added `@sentry/react-native` dependency to the project. - Initialized Sentry in the application with the provided DSN for error tracking. - Updated `app.json` to include Sentry configuration for Expo. - Enhanced `_layout.tsx` to wrap the `RootLayout` component with Sentry for better error handling. * refactor: update Metro configuration for Sentry integration * feat: enhance authentication and profile management - Introduced AppReviewOTP for Apple App Review email verification. - Updated authentication flow to support specific email validation for the review process. - Refactored profile bounds to a shared constants module for better maintainability. - Improved error handling and user feedback in login and profile edit screens. - Enhanced saved listings functionality to mark deleted and hidden listings as unavailable. - Updated tests to cover new email validation logic and recent changes in saved listings. * feat: add optional App Review OTP configuration - Introduced optional App Review OTP functionality for Apple App Review login. - Updated backend and frontend to support dynamic email and code configuration via environment variables. - Enhanced email validation logic in the login process to accommodate the new OTP feature. - Updated documentation to reflect the new configuration options for App Review. * fix: icon and package mismatch (#59) * refactor: unify authentication handling across backend modules (#60) - Replaced direct calls to `getUserIdentity` with `requireAuthUserId` in various modules to ensure consistent user authentication checks. - Updated ownership and user ID retrieval logic in listings, messages, profiles, and saved listings to utilize stable user IDs. - Enhanced error handling for unauthorized actions across multiple mutations and queries. - Removed legacy participant ID handling in messages and conversations to streamline user identification. - Improved overall code maintainability and clarity by consolidating authentication logic. * feat: enhance My Listings screen with status filtering and action han… (#61) * feat: enhance My Listings screen with status filtering and action handling - Added status filter options ('all', 'active', 'inactive', 'sold') to the My Listings screen for better listing management. - Implemented action handling for editing, marking as sold, setting status, and deleting listings through a new action sheet. - Improved error handling with user-friendly alerts for various actions. - Updated UI components to reflect changes in listing management and enhance user experience. * feat: unify authentication redirection across screens - Implemented consistent use of `router.replace` for authentication redirection in various screens including Inbox, Home, My Listings, Search, Settings, and Listing Details. - Enhanced user experience by replacing sign-in prompts with loading indicators while redirecting to the login page. - Improved accessibility labels in ListingCard for better screen reader support. * feat: implement messaging block checks and notification preferences (#62) * feat: implement messaging block checks and notification preferences - Added functionality to prevent users from messaging each other if they have blocked one another, enhancing user experience and safety. - Introduced a new mutation to manage message notification preferences, allowing users to enable or disable notifications for incoming messages. - Updated the user schema to include a field for message notification settings. - Enhanced the login flow to prompt users for notification permissions, improving engagement with the messaging feature. - Added tests to ensure proper functionality of blocking and notification features. * chore: update package dependencies and app configuration - Updated various Expo package versions in package-lock.json and package.json for improved stability and features. - Added iOS icon path in app.json to enhance app branding on iOS devices. * feat: enhance user blocking functionality and notification handling - Implemented a new query to check if the current user is blocked by another user, improving user experience in conversations. - Updated the blockUser mutation to ensure proper user ID normalization and error handling for blocking actions. - Enhanced the Settings screen to provide better feedback during account deletion and sign-out processes. - Refactored notification handling to ensure users are informed about permission status and potential issues when enabling notifications. - Improved conversation detail screen to handle blocking states more effectively, preventing users from sending messages if blocked. * feat: improve user blocking and notification preference handling - Introduced a new function to validate peer user IDs in blocking mutations, enhancing error handling for user blocking actions. - Updated blockUser and unblockUser mutations to utilize the new validation function, ensuring proper user ID normalization. - Enhanced Settings screen to provide detailed alerts for notification preference updates, improving user feedback during push token management. - Improved conversation detail screen to handle blocking states more effectively, preventing message sending if users are blocked. * chore: update app configuration and package scripts - Removed iOS icon path from app.json to streamline configuration. - Updated package.json scripts for running Android and iOS to use 'expo run' commands, enhancing build process consistency. * feat: transition to Convex Cloud and update environment configurations (#53) * feat: transition to Convex Cloud and update environment configurations - Updated backend configuration to use Convex Cloud instead of self-hosted setup. - Revised environment variable instructions in documentation for backend and frontend. - Enhanced the My Listings screen with delete functionality and improved user feedback. - Updated various documentation files to reflect the migration to Convex Cloud and removed references to the legacy self-hosted setup. * docs: update README and contributing guide for local development setup - Added instructions for local setup without a Convex account in README.md. - Revised contributing guide to clarify environment variable configuration options for both Convex Cloud and local development. - Removed outdated Convex Cloud migration documentation and legacy self-hosted references. * fix: preserve settings route typing after rebase * fix: address Convex migration and settings review notes --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> * Improve messaging safety and polish the web experience (#63) * chore: update package dependencies and app configuration - Updated various Expo package versions in package-lock.json and package.json for improved stability and features. - Added iOS icon path in app.json to enhance app branding on iOS devices. * chore: update app configuration and package scripts - Removed iOS icon path from app.json to streamline configuration. - Updated package.json scripts for running Android and iOS to use 'expo run' commands, enhancing build process consistency. * feat: polish the Vercel web experience Make the Expo web app feel intentional on desktop while steering app-only flows back to mobile. Add Vercel-friendly config so preview and production builds resolve the right web origin. * fix: remove duplicate layout import after rebase * fix: clean up search list layout styles * fix: preserve web search state Keep the resolved app origin available at runtime and preserve active listing filters when web search updates or refreshes. * chore: manual GitHub Action for dev → main release PR (#64) * chore: update package dependencies and app configuration - Updated various Expo package versions in package-lock.json and package.json for improved stability and features. - Added iOS icon path in app.json to enhance app branding on iOS devices. * chore: update app configuration and package scripts - Removed iOS icon path from app.json to streamline configuration. - Updated package.json scripts for running Android and iOS to use 'expo run' commands, enhancing build process consistency. * feat: polish the Vercel web experience Make the Expo web app feel intentional on desktop while steering app-only flows back to mobile. Add Vercel-friendly config so preview and production builds resolve the right web origin. * fix: remove duplicate layout import after rebase * fix: clean up search list layout styles * fix: preserve web search state Keep the resolved app origin available at runtime and preserve active listing filters when web search updates or refreshes. * chore: add manual GitHub Action to open dev→main release PR - workflow_dispatch only; idempotent open PR or report existing - document usage in contributing releases section --------- Co-authored-by: dfed25 <domfederico21@gmail.com> Co-authored-by: Jaydon Chen <79879038+jaydonkc@users.noreply.github.com> Co-authored-by: Cole <hackman@calpoly.edu> Co-authored-by: Haixin <haixinhuang502@gmail.com> Co-authored-by: BoB121isawesome <79879038+BoB121isawesome@users.noreply.github.com> Co-authored-by: lheutchy <lheutchy@gmail.com> Co-authored-by: dfed25 <150391626+dfed25@users.noreply.github.com> Co-authored-by: MatthewPhan <Matthewminhphan@gmail.com> Co-authored-by: SamanSP1386 <saman.sepehr86@gmail.com> Co-authored-by: Jaydon Bot <jaydon-bot@local> Co-authored-by: Taye-Staats <tayestaats@outlook.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com>
* implemented CRUD operations in backend * feat: implement Cal Poly email authentication with verification - Add user authentication using @convex-dev/auth with Password provider - Enforce @calpoly.edu email domain validation - Implement email verification flow with SendGrid integration - Create signup, login, and email verification screens - Add user schema and auth-related queries/mutations - Update shared types and utilities for email validation - Add AuthProvider to app layout with auth routes * docs: update environment variables documentation for auth * fix: secure email verification with token validation * feat: implemented profile viewing and editing * feat: add search and filtering for listings - Add condition field to listings (new, like_new, good, fair, poor) - Add searchAndFilterListings query with full-text search - Support category, price range, condition filters - Add sort options and cursor-based pagination (20 items/page) - Add compound indexes for performance * refactor: switch from Password+SendGrid to OTP+Resend - Replace Password provider with Resend OTP (8-digit code, 15 min expiry) - Add Cal Poly email domain validation - Simplify to single login screen (email → code) - Remove separate signup and verify-email screens - Switch from SendGrid to Resend for emails - Use @convex-dev/auth OTP pattern per reviewer feedback * Adding .env.examples back * fix: address CodeRabbit review suggestions for auth implementation * fix: configure ESM module resolution for backend typecheck * Normalize email to lowercase and trim whitespace * fix: added constraints to profile creation * POLY-10 add messaging and conversation schema and sendMessage mutation * POLY-10 test function for sendMessage mutation * POLY-10 feat: add retrieve conversation history query * POLY-10 feat: add listUserConversations query * POLY-10 fix: updated commenting and edited listingId in schema * feat: associate conversations with listings, read functionality, real-time message delivery * Sync Convex schema, generated files, and backend dependencies * POLY-10: Fix functions for updated schema * Add pagination to getProfiles query Updated getProfiles query to support pagination options. * Update package.json * Update convex dependency version to 1.31.5 * Update package.json * Update package.json * Downgrade convex dependency version to 1.17.2 * this should fix it * there we go * feat: implement custom OTP authentication backend (POLY-29) - Add otpCodes table with hashing and rate limiting - Implement requestOTP action with Cal Poly email validation - Implement verifyOTP mutation with attempt limiting - Replace @convex-dev/auth with custom OTP system - Add bcryptjs for secure OTP hashing - Integrate Resend for email delivery - Add emailVerified field to users table BREAKING CHANGE: Replaces @convex-dev/auth authentication system * chore: update package-lock.json for bcryptjs dependencies * fix: implement CodeRabbit security improvements - Make createAndStoreOTPInternal an internalMutation to prevent client access - Implement atomic rate-limit check and OTP storage in single transaction - Replace Math.random() with crypto.getRandomValues() for secure OTP generation - Add emailVerified field to User type to match backend schema Security improvements prevent race conditions and ensure cryptographically secure random number generation for OTP codes. * fix: make emailVerified optional for backward compatibility Make emailVerified field optional in schema and User type to prevent breaking existing user records that don't have this field. New users created via OTP will have emailVerified set to true, while existing users can be migrated gradually without validation errors. * feat: add report schema and submission endpoint (POLY-19) - Add reports table to Convex schema with indexes for target and reporter lookups - Implement createReport mutation with authentication, validation, and duplicate prevention - Add rate limiting (10 reports per 24 hours per user) - Create comprehensive test suite with 9 tests covering all acceptance criteria - Ensure reporterId is never exposed in responses * Fix(POLY-19): Address test failures and code review feedback & Feat(POLY-20): Implement auto-hide logic * fix: enforce auth in messaging queries and make debug mutation internal * feat: added tags field with validation * fix: fixed updateListing test cases * fix: add AsyncStorage for React Native auth storage * Fix(POLY-20): Prevent auto-hide from overwriting existing hidden context * feat(POLY-15): Add home feed sorting and filtering - Update getListings query with category, minPrice, maxPrice filters - Use by_status_createdAt index for deterministic newest-first ordering - Add price validation (min >= 0, max >= min) - Add FilterBar component with filter chips - Add CategoryPicker bottom sheet - Add PriceRangePicker bottom sheet with presets - Integrate filters into home screen with state management - Add 'No results' state with clear filters option - Add 7 tests for filter combinations * fix(POLY-15): address code review feedback - Refactor FilterBar to avoid nested TouchableOpacity - Add negative max price validation to PriceRangePicker * fix(POLY-15): add NaN/non-finite validation for price inputs - Trim whitespace from inputs before parsing - Treat empty/whitespace-only strings as undefined - Validate parsed values are finite with Number.isFinite - Show clear error message for invalid number input * done * done now * other issues done too * other issues done too * fixed errors * feat: add tag filters to getListings * fix: pass empty args to getListings useQuery * fix: modify listings.ts to pass local tests and ran npm install * fixed final errors * fixed final errors * fixed new errors * fixed new errors * fixed * Poly-16-Tags-Backend * removed redundancy * fix * fix * fix * Fix2 * fix: integrate coderabbit changes and edit testcases * fix: filter listings by tags in memory * feat: exclude hidden listings from getListings * Add validation for limit and price parameters in getListings query * Fix duplicate reports table in schema and update dependencies * Merge tag filtering with category/price filters - Add tag filtering to getListings query (OR logic) - Add category and price range filters - Create unified FilterBar with all three filter types - Add CategoryPicker and PriceRangePicker components - Support URL params for tag deep linking - Add comprehensive validation for price inputs * refactor: address code review feedback - Remove redundant arrayContaining assertion in schema test - Extract shared filter types to frontend/types/filters.ts - Remove duplicate CATEGORIES from CategoryPicker - Fix PriceRangePicker useEffect to only reset on modal open - Remove dead padding properties from chip style - Fix URL param sync in handleTagsChange * Fix duplicate useState import and unused styles in index.tsx * fix: exclude hidden listings from getListings query * fix: resolve merge conflicts and fix test configuration - Merge tag filtering tests with basic filtering tests from dev - Add @babel/preset-typescript to babel.config.js for proper TS support - Configure jest to use ts-jest for .ts files and babel-jest for .js files - Add price validation to getListings (minPrice/maxPrice checks) - Add getCurrentUserSubject query from dev branch - Fix test ordering by using _creationTime instead of createdAt - All 55 tests now passing * fix: remove duplicate functions from merge conflict - Remove duplicate getListings export (kept complete version with tag filtering) - Remove duplicate getCurrentUserSubject export - Remove unused normalizeTags function - All tests passing, linting clean * fix: remove duplicate listings variable declaration - Remove unfiltered listings query on line 13 - Keep filtered listings query that respects filter state - Fixes TypeScript error: Cannot redeclare block-scoped variable * fixed redundancy of validate/normalize * chore: upgrade Expo SDK to 54 and fix dependencies - Upgrade Expo from ~52.0.0 to ^54.0.33 - Upgrade React to 19.1.0 and React Native to 0.81.5 - Update all Expo packages to SDK 54 compatible versions - Add @auth/core dependency to backend to fix convex dev - Add .npmrc with legacy-peer-deps flag to handle peer dependency conflicts - Fix npm audit vulnerabilities * chore: add self-hosted Convex actions URL to environment configuration * testing * fixes * add docs for s3 convex config (#38) * fixes * delete * fixes * fixes * fixes * fixes * test added * fixes * update * tests * feat: integrate OpenAI Moderation API for listings and messages - Add moderationResults table to schema - Create moderation.ts with moderateContent internalAction - Refactor createListing/updateListing from mutations to actions - Refactor sendMessage from mutation to action - Add ConvexError responses for flagged content - Update all test files for action-based API with fetch mocks - Disable ts-jest diagnostics for convex-test ID serialization - Document OPENAI_API_KEY in .env.example Resolves: POLY-31, POLY-36, POLY-37 * fix: switch createListing/updateListing from useMutation to useAction in frontend Frontend was still using useMutation for createListing and updateListing, which are now actions after moderation integration. This caused TypeScript errors: FunctionReference<"action"> not assignable to FunctionReference<"mutation">. * fix: extract ConvexError.data for moderation rejection messages ConvexError stores the custom error string in .data, not .message. Without this fix, moderation rejection messages would not surface to the user in the Alert dialog. * feat: exclude hidden content from feed and handle hidden states * fix: code rabbit changes * WIP: messaging changes * chore: make POLY-38 schema rollout backward-compatible with backfill * feat: clean shareable links route and listing share action * feat: add production-ready report modal flow for listing detail * style: format ReportModal with Prettier * feat: rebuild clean image uploader with upload URL flow and timeout safeguards * feat: deep-linking listings rebased from nightly clean on latest dev * feat: expo upgrade, fix config, enhance ui, add animations - Deleted Babel configuration file as it is no longer needed. - Updated Jest configuration to specify Babel config file path. - Added environment variables for email handling in backend. - Enhanced email validation and error handling in ResendOTP function. - Improved frontend components with better loading states and animations. - Updated dependencies in package-lock.json for compatibility with new Expo version. * refactor: streamline auth configuration and improve search functionality - Updated auth configuration to use a unified provider domain from environment variables. - Simplified search component logic to enhance performance and maintain reactivity for first-page results. - Removed unnecessary cursor handling for improved clarity in state management. * chore: update expo-image-manipulator and expo-image-picker versions in package files - Upgraded expo-image-manipulator to version 55.0.9 and expo-image-picker to version 55.0.10 in both package-lock.json and package.json for improved functionality and compatibility. * fix: coderabbit feedback - Added a function to dynamically determine the app origin for shareable links in the ListingDetailScreen. - Updated the cancel button in both NewListingScreen and EditListingScreen to be disabled during submissions or pending uploads, with visual feedback for disabled state. * feat: enhance login functionality with return redirection - Integrated useLocalSearchParams to retrieve returnTo parameter for post-auth redirection. - Updated router.replace to redirect users to the specified return path after successful login, improving user experience. * feat: enhance login screen with verification step handling - Introduced a new state variable to manage the verification step in the login process. - Updated the rendering logic to display success messages conditionally based on the verification step, improving user feedback during authentication. * chore: update dependencies and enhance TypeScript configuration - Added expo-router version 55.0.3 to package.json and package-lock.json for improved routing capabilities. - Updated TypeScript configuration in tsconfig.json to include additional file types and improve module resolution. * feat: enhance listings and profiles management - Updated the updateListing action to return specific moderation error responses instead of throwing exceptions. - Added a new query to retrieve the current user's listings, including hidden and inactive items. - Introduced a new query to fetch the current authenticated user's full profile, including non-public fields. - Enhanced profile creation and update mutations to include additional fields and validation for email, year, and other profile attributes. - Improved error handling in listing creation and editing to provide user-friendly feedback for moderation issues. - Updated frontend components to reflect new profile and listing functionalities, including loading states and input validations. * refactor: improve error messaging in profile creation tests - Updated the test case for createProfile to clarify rejection conditions when no email is provided or available on identity. - Enhanced error message to be more user-friendly, specifying that an email is required to create a profile. * chore: update Node.js version in configuration files * fix: ci script * fix: ci checks * fix: ci linux lightningcss binary * fixed issue * fixed issue * potential redundent querires * null request bug * feat: implement push notifications and update dependencies - Added support for push notifications using @convex-dev/expo-push-notifications. - Updated the convex configuration to include push notification handling in message actions. - Enhanced tests to verify push notification functionality upon message creation. - Updated dependencies, including upgrading convex to version 1.32.0 and related Expo packages. - Modified configuration files to reflect changes in app identifiers and notification settings. * fix: improve push notification handling and enhance tests - Updated the sendMessage action to use a more structured approach for push notifications based on the environment. - Enhanced test setup for push notifications by refactoring the mock implementation for better clarity and maintainability. - Added error handling for push notification failures during message delivery. - Updated the useAuth hook to ensure push tokens are removed on sign-out, improving token management. * fix: update app slug for consistency * feat: add message seller button on listings * feat: create inbox screen * feat: create chat screen * feat: implement read receipts for inbox in nav bar * fix: remove errors when clicking inbox * fix: prevent duplicate conversations * feat: add read receipts to messages * fix: show buyer name for listing creator * fix: implemented code rabbit changes * fix: add more code rabbit changes * Development environment setup (#55) * docs: add AGENTS.md with Cursor Cloud development instructions * docs: update AGENTS.md with Convex cloud setup and login caveat * docs: update AGENTS.md to reflect Convex cloud only (no self-hosted) --------- * feat: UI overhaul (#57) * feat: add saved listings functionality and enhance listings queries - Implemented `savedListings` table with associated queries and mutations for saving, unsaving, and checking saved listings. - Added `getListingsBySeller` query to fetch public active listings by seller. - Updated `updateListingStatus` mutation to prevent changing status of sold listings. - Enhanced tests for new saved listings features and updated existing tests for listing status changes. - Improved UI components to support new functionalities and ensure better user experience. * fix: agent bugs * feat: integrate Sentry for error tracking and monitoring - Added `@sentry/react-native` dependency to the project. - Initialized Sentry in the application with the provided DSN for error tracking. - Updated `app.json` to include Sentry configuration for Expo. - Enhanced `_layout.tsx` to wrap the `RootLayout` component with Sentry for better error handling. * refactor: update Metro configuration for Sentry integration * feat: enhance authentication and profile management - Introduced AppReviewOTP for Apple App Review email verification. - Updated authentication flow to support specific email validation for the review process. - Refactored profile bounds to a shared constants module for better maintainability. - Improved error handling and user feedback in login and profile edit screens. - Enhanced saved listings functionality to mark deleted and hidden listings as unavailable. - Updated tests to cover new email validation logic and recent changes in saved listings. * feat: add optional App Review OTP configuration - Introduced optional App Review OTP functionality for Apple App Review login. - Updated backend and frontend to support dynamic email and code configuration via environment variables. - Enhanced email validation logic in the login process to accommodate the new OTP feature. - Updated documentation to reflect the new configuration options for App Review. * fix: icon and package mismatch (#59) * refactor: unify authentication handling across backend modules (#60) - Replaced direct calls to `getUserIdentity` with `requireAuthUserId` in various modules to ensure consistent user authentication checks. - Updated ownership and user ID retrieval logic in listings, messages, profiles, and saved listings to utilize stable user IDs. - Enhanced error handling for unauthorized actions across multiple mutations and queries. - Removed legacy participant ID handling in messages and conversations to streamline user identification. - Improved overall code maintainability and clarity by consolidating authentication logic. * feat: enhance My Listings screen with status filtering and action han… (#61) * feat: enhance My Listings screen with status filtering and action handling - Added status filter options ('all', 'active', 'inactive', 'sold') to the My Listings screen for better listing management. - Implemented action handling for editing, marking as sold, setting status, and deleting listings through a new action sheet. - Improved error handling with user-friendly alerts for various actions. - Updated UI components to reflect changes in listing management and enhance user experience. * feat: unify authentication redirection across screens - Implemented consistent use of `router.replace` for authentication redirection in various screens including Inbox, Home, My Listings, Search, Settings, and Listing Details. - Enhanced user experience by replacing sign-in prompts with loading indicators while redirecting to the login page. - Improved accessibility labels in ListingCard for better screen reader support. * feat: implement messaging block checks and notification preferences (#62) * feat: implement messaging block checks and notification preferences - Added functionality to prevent users from messaging each other if they have blocked one another, enhancing user experience and safety. - Introduced a new mutation to manage message notification preferences, allowing users to enable or disable notifications for incoming messages. - Updated the user schema to include a field for message notification settings. - Enhanced the login flow to prompt users for notification permissions, improving engagement with the messaging feature. - Added tests to ensure proper functionality of blocking and notification features. * chore: update package dependencies and app configuration - Updated various Expo package versions in package-lock.json and package.json for improved stability and features. - Added iOS icon path in app.json to enhance app branding on iOS devices. * feat: enhance user blocking functionality and notification handling - Implemented a new query to check if the current user is blocked by another user, improving user experience in conversations. - Updated the blockUser mutation to ensure proper user ID normalization and error handling for blocking actions. - Enhanced the Settings screen to provide better feedback during account deletion and sign-out processes. - Refactored notification handling to ensure users are informed about permission status and potential issues when enabling notifications. - Improved conversation detail screen to handle blocking states more effectively, preventing users from sending messages if blocked. * feat: improve user blocking and notification preference handling - Introduced a new function to validate peer user IDs in blocking mutations, enhancing error handling for user blocking actions. - Updated blockUser and unblockUser mutations to utilize the new validation function, ensuring proper user ID normalization. - Enhanced Settings screen to provide detailed alerts for notification preference updates, improving user feedback during push token management. - Improved conversation detail screen to handle blocking states more effectively, preventing message sending if users are blocked. * chore: update app configuration and package scripts - Removed iOS icon path from app.json to streamline configuration. - Updated package.json scripts for running Android and iOS to use 'expo run' commands, enhancing build process consistency. * feat: transition to Convex Cloud and update environment configurations (#53) * feat: transition to Convex Cloud and update environment configurations - Updated backend configuration to use Convex Cloud instead of self-hosted setup. - Revised environment variable instructions in documentation for backend and frontend. - Enhanced the My Listings screen with delete functionality and improved user feedback. - Updated various documentation files to reflect the migration to Convex Cloud and removed references to the legacy self-hosted setup. * docs: update README and contributing guide for local development setup - Added instructions for local setup without a Convex account in README.md. - Revised contributing guide to clarify environment variable configuration options for both Convex Cloud and local development. - Removed outdated Convex Cloud migration documentation and legacy self-hosted references. * fix: preserve settings route typing after rebase * fix: address Convex migration and settings review notes --------- * Improve messaging safety and polish the web experience (#63) * chore: update package dependencies and app configuration - Updated various Expo package versions in package-lock.json and package.json for improved stability and features. - Added iOS icon path in app.json to enhance app branding on iOS devices. * chore: update app configuration and package scripts - Removed iOS icon path from app.json to streamline configuration. - Updated package.json scripts for running Android and iOS to use 'expo run' commands, enhancing build process consistency. * feat: polish the Vercel web experience Make the Expo web app feel intentional on desktop while steering app-only flows back to mobile. Add Vercel-friendly config so preview and production builds resolve the right web origin. * fix: remove duplicate layout import after rebase * fix: clean up search list layout styles * fix: preserve web search state Keep the resolved app origin available at runtime and preserve active listing filters when web search updates or refreshes. * chore: manual GitHub Action for dev → main release PR (#64) * chore: update package dependencies and app configuration - Updated various Expo package versions in package-lock.json and package.json for improved stability and features. - Added iOS icon path in app.json to enhance app branding on iOS devices. * chore: update app configuration and package scripts - Removed iOS icon path from app.json to streamline configuration. - Updated package.json scripts for running Android and iOS to use 'expo run' commands, enhancing build process consistency. * feat: polish the Vercel web experience Make the Expo web app feel intentional on desktop while steering app-only flows back to mobile. Add Vercel-friendly config so preview and production builds resolve the right web origin. * fix: remove duplicate layout import after rebase * fix: clean up search list layout styles * fix: preserve web search state Keep the resolved app origin available at runtime and preserve active listing filters when web search updates or refreshes. * chore: add manual GitHub Action to open dev→main release PR - workflow_dispatch only; idempotent open PR or report existing - document usage in contributing releases section --------- Co-authored-by: dfed25 <domfederico21@gmail.com> Co-authored-by: Jaydon Chen <79879038+jaydonkc@users.noreply.github.com> Co-authored-by: Cole <hackman@calpoly.edu> Co-authored-by: Haixin <haixinhuang502@gmail.com> Co-authored-by: BoB121isawesome <79879038+BoB121isawesome@users.noreply.github.com> Co-authored-by: lheutchy <lheutchy@gmail.com> Co-authored-by: dfed25 <150391626+dfed25@users.noreply.github.com> Co-authored-by: MatthewPhan <Matthewminhphan@gmail.com> Co-authored-by: SamanSP1386 <saman.sepehr86@gmail.com> Co-authored-by: Jaydon Bot <jaydon-bot@local> Co-authored-by: Taye-Staats <tayestaats@outlook.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com>
getUserIdentitywithrequireAuthUserIdin various modules to ensure consistent user authentication checks.Linked Issues
Closes #
Linear: (e.g., POLY-123)
Summary
Briefly explain the change and why.
How to Test
Steps to verify locally:
npm run lintnpm run typechecknpm testnpm run dev:backend(in terminal A)npm run dev(in terminal B)Checklist
npm run lint)devScreenshots / Demos
(if UI or visible behavior - attach images, videos, or GIFs)
Summary by CodeRabbit
New Features
Bug Fixes
Style
Documentation