Skip to content

feat: fixed a ton of stuff, standardize UI#86

Merged
evan-taylor merged 4 commits intodevfrom
evan/overhaul
Apr 16, 2026
Merged

feat: fixed a ton of stuff, standardize UI#86
evan-taylor merged 4 commits intodevfrom
evan/overhaul

Conversation

@evan-taylor
Copy link
Copy Markdown
Collaborator

@evan-taylor evan-taylor commented Apr 16, 2026

Linked Issues

Closes #
Linear: (e.g., POLY-123)

Summary

Briefly explain the change and why.

How to Test

Steps to verify locally:

  • npm run lint
  • npm run typecheck
  • npm test
  • Manual flow:
    1. npm run dev:backend (in terminal A)
    2. npm run dev (in terminal B)
    3. Verify the change: <describe expected behavior/screens>

Checklist

  • Tests added/updated (if applicable)
  • Lint/tests pass locally (npm run lint)
  • Docs updated (README/ADR/changelog if needed)
  • Follows conventional commit format
  • No merge conflicts with dev

Screenshots / Demos

(if UI or visible behavior - attach images, videos, or GIFs)

Summary by CodeRabbit

  • New Features
    • Listings sorting controls and a new search experience with category filters and integrated sort picker
    • Reusable UI components: header, filter chips, keyboard-aware screen, section card, and image lightbox improvements
  • Bug Fixes
    • Prevent edits to sold listings; blocking/unblocking treats deleted/unresolved users as no-ops
  • Style
    • Unified surface/background colors, updated borders, selection/cursor accent color, and refined listing/profile layouts
  • Tests
    • Added tests verifying sold-listing update protection and block behavior with deleted users

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 16, 2026

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

Project Deployment Actions Updated (UTC)
poly-buys Ready Ready Preview, Comment Apr 16, 2026 6:19pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 16, 2026

Warning

Rate limit exceeded

@cursor[bot] has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 46 minutes and 14 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 46 minutes and 14 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 617c71ac-df2d-4d36-aa00-a59cb069bf34

📥 Commits

Reviewing files that changed from the base of the PR and between 15290fd and 182a4a4.

📒 Files selected for processing (4)
  • backend/convex/__tests__/listings.test.ts
  • backend/convex/listings.ts
  • frontend/app/conversations/[id].tsx
  • frontend/components/FilterBar.tsx
📝 Walkthrough

Walkthrough

This PR adds search and sort UI, rewrites the search stack, standardizes keyboard/safe-area behavior and text-input styling, introduces reusable UI primitives (ScreenHeader, FilterChips, KeyboardAwareScreen, SectionCard), prevents updates to sold listings (backend + tests), refactors blocks to tolerate deleted targets, and applies wide theming/styling adjustments across the app.

Changes

Cohort / File(s) Summary
Search stack & hook
frontend/app/(tabs)/search.tsx, frontend/app/(tabs)/search/_layout.tsx, frontend/app/(tabs)/search/index.tsx, frontend/hooks/useRecentSearches.ts
Removed old search screen and hook; added new SearchStack layout and fully rewritten SearchScreen with debounced input, category filters, cursor-based pagination, auth-aware saved-state, and adaptive web/mobile UI.
Sorting integration
frontend/components/SortPicker.tsx, frontend/components/FilterBar.tsx, frontend/types/filters.ts, frontend/app/(tabs)/index.tsx
Added SortPicker component and sort types/constants; integrated sort state into FilterBar and HomeScreen, switching listing queries to searchAndFilterListings with sort filter.
Reusable UI primitives
frontend/components/ui/ScreenHeader.tsx, frontend/components/ui/FilterChips.tsx, frontend/components/ui/KeyboardAwareScreen.tsx, frontend/components/ui/SectionCard.tsx, frontend/components/ui/index.ts
New shared UI components and barrel exports for consistent headers, chip filters, keyboard-aware screens, and section cards.
Listing protections & owner UX
backend/convex/listings.ts, backend/convex/__tests__/listings.test.ts, frontend/app/listings/[id].tsx, frontend/app/listings/[id]/edit.tsx, frontend/components/MyListingActionsSheet.tsx
Backend guard prevents updates when listing.status === 'sold' (test added); edit screen shows non-editable UI for sold/deleted; owner action visibility and actions sheet updated to hide edit for sold listings.
Blocks behavior & tests
backend/convex/blocks.ts, backend/convex/__tests__/blocks.test.ts
Replaced throwing resolver with resolvePeerUserId returning null for missing users; block/unblock/isBlocking/isBlockedBy now no-op/return gracefully when target unresolved; added test asserting blockUser returns null for deleted targets.
Listing card & management UI
frontend/components/ListingCard.tsx, frontend/components/MyListingActionsSheet.tsx
Added ListingCard statusBadge and onManagePress props; renders status pill and manage button (BlurView + 3-dot icon); actions sheet omits edit for sold status.
Home/MyListings/Inbox refactors
frontend/app/(tabs)/index.tsx, frontend/app/(tabs)/my-listings.tsx, frontend/app/(tabs)/inbox.tsx
Integrated sort into Home; replaced inline header/filters with ScreenHeader and FilterChips in MyListings; Inbox refactored to include search, safe-area handling, and a new header/search layout.
Keyboard & safe-area updates
frontend/app/listings/new.tsx, frontend/app/listings/[id]/edit.tsx, frontend/app/conversations/[id].tsx, frontend/app/conversations/new.tsx, frontend/app/profile/edit.tsx, frontend/components/ImageLightbox.tsx
Replaced ad-hoc KeyboardAvoidingView/ScrollView with KeyboardAwareScreen or dynamic header-height offsets; added useSafeAreaInsets for positioning and padding adjustments.
TextInput styling standardization
multiple files (auth/login.tsx, components/ui/Field.tsx, PriceRangePicker.tsx, ReportModal.tsx, conversation screens)
Applied consistent selectionColor and cursorColor = colors.primary across many TextInput usages.
Theming & minor styling
many frontend layout files (frontend/app/_layout.tsx, tab layouts, listings screens, settings, profile, etc.)
Unified background to colors.surface, replaced fixed border widths with StyleSheet.hairlineWidth, adjusted border radii and various style tokens; removed dark-mode-dependent tab tint logic.
New components & utilities
frontend/components/SortPicker.tsx, frontend/components/ui/*
Added SortPicker modal and several UI primitives (ScreenHeader, FilterChips, KeyboardAwareScreen, SectionCard).
Deps
frontend/package.json
Added expo-blur and react-native-reanimated (runtime deps for BlurView/animations).

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant SearchScreen
    participant ConvexAPI
    participant Database

    User->>SearchScreen: Type search term (debounced)
    SearchScreen->>SearchScreen: Debounce + reset pagination
    SearchScreen->>ConvexAPI: searchAndFilterListings(term, cursor, filters)
    ConvexAPI->>Database: Query listings + sort
    Database-->>ConvexAPI: Paginated results
    ConvexAPI-->>SearchScreen: Return results
    SearchScreen->>SearchScreen: Render/append results

    User->>SearchScreen: Scroll → request next page
    SearchScreen->>ConvexAPI: searchAndFilterListings(term, nextCursor, filters)
    ConvexAPI->>Database: Fetch next page
    Database-->>ConvexAPI: Page results
    ConvexAPI-->>SearchScreen: Return page
    SearchScreen->>SearchScreen: Append to list
Loading
sequenceDiagram
    actor User
    participant HomeScreen
    participant SortPicker
    participant ConvexAPI

    User->>HomeScreen: Open listings (sort: newest)
    HomeScreen->>ConvexAPI: searchAndFilterListings(filters={sortBy:'newest'})
    ConvexAPI-->>HomeScreen: Results
    User->>SortPicker: Open sort modal
    User->>SortPicker: Select 'price_asc'
    SortPicker->>HomeScreen: onSelect('price_asc')
    HomeScreen->>ConvexAPI: searchAndFilterListings(filters={sortBy:'price_asc'})
    ConvexAPI-->>HomeScreen: Sorted results
    HomeScreen->>HomeScreen: Re-render listings
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~95 minutes

Possibly related PRs

Suggested reviewers

  • jaydonkc
  • SamanSP1386

Poem

🐰 I hopped through code and tidied each trail,
New headers to lead, soft chips to impale,
Sold posts locked safe from the editor's tail,
Keyboards now dance, safe areas prevail,
A blink, a blur—our app prances without fail. ✨

🚥 Pre-merge checks | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is entirely a template with unfilled placeholders. No actual summary, linked issues, testing steps, checklist completion, or screenshots are provided, making it impossible to understand the changes or verify testing. Fill in all required sections: linked issues, a concrete summary of changes, actual testing steps/results, completed checklist items, and screenshots/demos for UI changes. Link to related Linear/GitHub issues and explain the 'standardization' effort.
Docstring Coverage ⚠️ Warning Docstring coverage is 2.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is overly vague and generic. 'fixed a ton of stuff, standardize UI' uses non-descriptive terms ('ton of stuff') that don't convey meaningful information about the primary changes in the changeset. Replace with a specific, concise title describing the main change, e.g., 'feat: standardize UI components and improve dark mode handling' or 'feat: refactor screens with new FilterChips and KeyboardAwareScreen components'.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch evan/overhaul

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@evan-taylor
Copy link
Copy Markdown
Collaborator Author

@cursor babysit this PR

@cursor
Copy link
Copy Markdown

cursor Bot commented Apr 16, 2026

No.

I fixed these CodeRabbit items:

  • backend/convex/blocks.ts / frontend/app/conversations/[id].tsx: the false-success block UX issue
  • frontend/app/conversations/[id].tsx: Prettier formatting failure

I did not fix these remaining CodeRabbit comments:

  • backend/convex/listings.ts: race-condition re-check for sold before patching/updating status
  • frontend/package.json / frontend/babel.config.js: add react-native-reanimated/plugin
  • frontend/components/ListingCard.tsx: save/manage button overlap risk
  • frontend/components/FilterBar.tsx: missing accessibilityState on sort chip
  • frontend/components/SortPicker.tsx: replace hardcoded colors with theme tokens
  • frontend/app/conversations/[id].tsx: refactor to KeyboardAwareScreen for consistency
  • PR-level CodeRabbit warnings: vague title, template PR body, docstring coverage

So the honest status is: I fixed 2 concrete review issues, but not all of them. If you want, I can take the remaining actionable CodeRabbit items in another pass and push them.

Open in Web Open in Cursor 

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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 (2)
backend/convex/listings.ts (1)

628-640: ⚠️ Potential issue | 🟠 Major

Re-check sold status inside internalUpdateListing before patching.

The action checks listing.status === 'sold' at line 638, but the actual update happens later in the internalUpdateListing mutation. Between the action's read and the mutation's patch at line 607, another request could mark the listing as sold, allowing the update to slip through. Add a status re-check inside internalUpdateListing before calling ctx.db.patch(args.id, patch) to enforce the invariant.

Same issue in changeListingStatus (lines 732-735).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/convex/listings.ts` around lines 628 - 640, The read-before-write
race allows a listing to become 'sold' between the action and the mutation; to
fix, re-fetch and re-check the listing status inside internalUpdateListing
immediately before calling ctx.db.patch(args.id, patch) and throw a
ConvexError('Cannot update a sold listing') if status === 'sold'; similarly, in
changeListingStatus re-query the listing right before performing the status
change (before ctx.db.patch or equivalent) and throw the same error if it is
already 'sold' to prevent stale updates.
frontend/components/ListingCard.tsx (1)

276-307: ⚠️ Potential issue | 🟡 Minor

Potential button overlap when both onToggleSave and onManagePress are provided.

Both saveButton (lines 276-287) and manageButton (lines 288-307) are absolutely positioned at top: spacing.xs, right: spacing.xs. If a consumer passes both onToggleSave and onManagePress props, the buttons will render on top of each other.

Consider either:

  1. Making the props mutually exclusive via TypeScript discriminated union
  2. Adjusting positioning so they don't conflict
  3. Adding a runtime guard or documentation clarifying they shouldn't be used together
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/components/ListingCard.tsx` around lines 276 - 307, The save and
manage buttons in ListingCard can overlap because both Pressable elements
rendered when onToggleSave and onManagePress are provided use absolute
positioning (styles.saveButton and styles.manageButton) at the same top/right
coordinates; fix by making the props mutually exclusive or by adjusting runtime
behavior: update the ListingCard component to either enforce a TypeScript
discriminated union for props (so onToggleSave and onManagePress cannot coexist)
or add a runtime guard in the render that, when both onToggleSave and
onManagePress are passed, shifts one button’s position (e.g., offset
manageButton right or down relative to saveButton) or only renders one with a
clear priority, and update the component’s prop-types/comments to document the
chosen behavior; refer to symbols onToggleSave, onManagePress,
styles.saveButton, styles.manageButton, and the ListingCard component when
applying the change.
🧹 Nitpick comments (2)
frontend/components/FilterBar.tsx (1)

115-128: Consider adding accessibilityState to the sort chip.

The category and price chips include accessibilityState={{ selected: ... }} for screen reader users. The sort chip is missing this attribute for consistency.

🔧 Add accessibilityState
         <Pressable
           style={({ pressed }) => [
             styles.chip,
             hasNonDefaultSort && styles.chipActive,
             pressed && styles.chipPressed,
           ]}
           onPress={onSortPress}
           accessibilityLabel={`Sort: ${LISTING_SORT_SHORT[sortBy]}`}
           accessibilityRole="button"
+          accessibilityState={{ selected: hasNonDefaultSort }}
         >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/components/FilterBar.tsx` around lines 115 - 128, The Sort chip
Pressable is missing accessibilityState which other chips use; update the
Pressable rendered by the Sort button (the one using onSortPress,
LISTING_SORT_SHORT and sortBy) to include accessibilityState={{ selected:
hasNonDefaultSort }} so screen readers get consistent selected state, keeping
existing accessibilityLabel and accessibilityRole unchanged and reusing the
hasNonDefaultSort boolean for the selected value.
frontend/components/SortPicker.tsx (1)

58-123: Consider using theme tokens instead of hardcoded colors.

This component uses inline hex values (#fff, #dbe6e1, #153428, etc.) while the rest of the PR standardizes on colors.* tokens from theme/tokens.ts. For consistency and maintainability, consider replacing these with the corresponding tokens (e.g., colors.white, colors.border, colors.primary).

♻️ Example token replacements
 sheetTapArea: {
-  backgroundColor: '#fff',
+  backgroundColor: colors.white,
   borderTopLeftRadius: 20,
   borderTopRightRadius: 20,
   borderWidth: 1,
-  borderColor: '#dbe6e1',
+  borderColor: colors.border,
   // ...
 },
 title: {
   fontSize: 19,
   fontWeight: '700',
   marginBottom: 14,
   textAlign: 'center',
-  color: '#153428',
+  color: colors.primary,
 },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/components/SortPicker.tsx` around lines 58 - 123, The styles object
in StyleSheet.create uses hardcoded hex colors (e.g., '#fff', '#dbe6e1',
'#153428', etc.); import the shared color tokens (e.g., colors from
theme/tokens) and replace those hex literals in the styles keys (overlay, sheet,
sheetTapArea, handle, title, option, optionSelected, optionText,
optionTextSelected, checkmark) with the appropriate colors.* tokens (e.g.,
colors.white, colors.border, colors.primary, colors.background, colors.text,
colors.muted) to match the repo's theming conventions and ensure consistent
naming.
🤖 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/blocks.ts`:
- Around line 53-56: The current resolver returns null for no-op paths which the
frontend treats as success; change the response shape to an explicit object with
a changed boolean so callers can distinguish no-op vs actual change: in the
block/unblock resolvers that call resolvePeerUserId (refer to resolvePeerUserId
and args.blockedId) return {changed: false} when resolvePeerUserId yields no id,
and likewise return {changed: true} only when you actually create or delete a
block row (and {changed: false} when no DB change occurred); update both the
blockUser and unblockUser code paths (the same logic around lines handling the
delete/create) to return this {changed: boolean} object instead of null or
undefined.
- Around line 49-56: The blockUser mutation can return null when the target user
is missing, but the frontend blockAction treats any resolved value as success;
update the blockAction callback to check the resolved value from blockUser({
blockedId: otherUserId }) and only show Alert.alert('User blocked', ...) when
the result is non-null/truthy, otherwise handle as a no-op (e.g., show an
appropriate message or do nothing); keep the existing .catch for errors. Ensure
this change targets the blockAction caller that invokes blockUser and its
.then() callback.

In `@frontend/package.json`:
- Line 40: Update the Babel configuration to register the Reanimated plugin: in
your babel.config.js module.exports function (the one calling api.cache(true)
and returning presets), add a plugins array if missing and include
'react-native-reanimated/plugin' as the last entry (i.e., return { presets:
['babel-preset-expo'], plugins: ['react-native-reanimated/plugin'] } or append
it to any existing plugins array) so Reanimated can initialize correctly.

---

Outside diff comments:
In `@backend/convex/listings.ts`:
- Around line 628-640: The read-before-write race allows a listing to become
'sold' between the action and the mutation; to fix, re-fetch and re-check the
listing status inside internalUpdateListing immediately before calling
ctx.db.patch(args.id, patch) and throw a ConvexError('Cannot update a sold
listing') if status === 'sold'; similarly, in changeListingStatus re-query the
listing right before performing the status change (before ctx.db.patch or
equivalent) and throw the same error if it is already 'sold' to prevent stale
updates.

In `@frontend/components/ListingCard.tsx`:
- Around line 276-307: The save and manage buttons in ListingCard can overlap
because both Pressable elements rendered when onToggleSave and onManagePress are
provided use absolute positioning (styles.saveButton and styles.manageButton) at
the same top/right coordinates; fix by making the props mutually exclusive or by
adjusting runtime behavior: update the ListingCard component to either enforce a
TypeScript discriminated union for props (so onToggleSave and onManagePress
cannot coexist) or add a runtime guard in the render that, when both
onToggleSave and onManagePress are passed, shifts one button’s position (e.g.,
offset manageButton right or down relative to saveButton) or only renders one
with a clear priority, and update the component’s prop-types/comments to
document the chosen behavior; refer to symbols onToggleSave, onManagePress,
styles.saveButton, styles.manageButton, and the ListingCard component when
applying the change.

---

Nitpick comments:
In `@frontend/components/FilterBar.tsx`:
- Around line 115-128: The Sort chip Pressable is missing accessibilityState
which other chips use; update the Pressable rendered by the Sort button (the one
using onSortPress, LISTING_SORT_SHORT and sortBy) to include
accessibilityState={{ selected: hasNonDefaultSort }} so screen readers get
consistent selected state, keeping existing accessibilityLabel and
accessibilityRole unchanged and reusing the hasNonDefaultSort boolean for the
selected value.

In `@frontend/components/SortPicker.tsx`:
- Around line 58-123: The styles object in StyleSheet.create uses hardcoded hex
colors (e.g., '#fff', '#dbe6e1', '#153428', etc.); import the shared color
tokens (e.g., colors from theme/tokens) and replace those hex literals in the
styles keys (overlay, sheet, sheetTapArea, handle, title, option,
optionSelected, optionText, optionTextSelected, checkmark) with the appropriate
colors.* tokens (e.g., colors.white, colors.border, colors.primary,
colors.background, colors.text, colors.muted) to match the repo's theming
conventions and ensure consistent naming.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 56f44caf-3cca-4a6c-947d-359847491271

📥 Commits

Reviewing files that changed from the base of the PR and between 734faab and cd98019.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (37)
  • backend/convex/__tests__/listings.test.ts
  • backend/convex/blocks.ts
  • backend/convex/listings.ts
  • frontend/app/(tabs)/_layout.tsx
  • frontend/app/(tabs)/inbox.tsx
  • frontend/app/(tabs)/index.tsx
  • frontend/app/(tabs)/my-listings.tsx
  • frontend/app/(tabs)/search.tsx
  • frontend/app/(tabs)/search/_layout.tsx
  • frontend/app/(tabs)/search/index.tsx
  • frontend/app/(tabs)/settings.tsx
  • frontend/app/_layout.tsx
  • frontend/app/account-settings.tsx
  • frontend/app/auth/login.tsx
  • frontend/app/conversations/[id].tsx
  • frontend/app/conversations/new.tsx
  • frontend/app/listings/[id].tsx
  • frontend/app/listings/[id]/edit.tsx
  • frontend/app/listings/new.tsx
  • frontend/app/profile/[userId].tsx
  • frontend/app/profile/edit.tsx
  • frontend/components/FilterBar.tsx
  • frontend/components/ImageLightbox.tsx
  • frontend/components/ListingCard.tsx
  • frontend/components/MyListingActionsSheet.tsx
  • frontend/components/PriceRangePicker.tsx
  • frontend/components/ReportModal.tsx
  • frontend/components/SortPicker.tsx
  • frontend/components/ui/Field.tsx
  • frontend/components/ui/FilterChips.tsx
  • frontend/components/ui/KeyboardAwareScreen.tsx
  • frontend/components/ui/ScreenHeader.tsx
  • frontend/components/ui/SectionCard.tsx
  • frontend/components/ui/index.ts
  • frontend/hooks/useRecentSearches.ts
  • frontend/package.json
  • frontend/types/filters.ts
💤 Files with no reviewable changes (2)
  • frontend/hooks/useRecentSearches.ts
  • frontend/app/(tabs)/search.tsx

Comment thread backend/convex/blocks.ts
Comment thread backend/convex/blocks.ts
Comment on lines +53 to +56
const blockedId = await resolvePeerUserId(ctx, args.blockedId);
if (!blockedId) {
return null;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Return explicit changed state for no-op paths to avoid false-success UX.

Line 53-Line 56 and Line 88-Line 91 resolve successfully even when no block row can be created/removed. In frontend/app/conversations/[id].tsx Line 197-Line 211, success UI is shown on any resolved blockUser promise, so users can see “User blocked” even when nothing changed.

Proposed response-shape fix
 export const blockUser = mutation({
   args: { blockedId: v.string() },
   handler: async (ctx, args) => {
@@
-    if (!blockedId) {
-      return null;
-    }
+    if (!blockedId) {
+      return { ok: true, changed: false as const };
+    }
@@
-    if (existing) {
-      return existing._id;
-    }
+    if (existing) {
+      return { ok: true, changed: false as const };
+    }
@@
-    return blockId;
+    return { ok: true, changed: true as const, blockId };
   },
 });
 export const unblockUser = mutation({
@@
-    if (!blockedId) {
-      return { ok: true };
-    }
+    if (!blockedId) {
+      return { ok: true, changed: false as const };
+    }
@@
-    if (existing) {
-      await ctx.db.delete(existing._id);
-    }
-
-    return { ok: true };
+    if (existing) {
+      await ctx.db.delete(existing._id);
+      return { ok: true, changed: true as const };
+    }
+    return { ok: true, changed: false as const };
   },
 });

Also applies to: 88-91

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/convex/blocks.ts` around lines 53 - 56, The current resolver returns
null for no-op paths which the frontend treats as success; change the response
shape to an explicit object with a changed boolean so callers can distinguish
no-op vs actual change: in the block/unblock resolvers that call
resolvePeerUserId (refer to resolvePeerUserId and args.blockedId) return
{changed: false} when resolvePeerUserId yields no id, and likewise return
{changed: true} only when you actually create or delete a block row (and
{changed: false} when no DB change occurred); update both the blockUser and
unblockUser code paths (the same logic around lines handling the delete/create)
to return this {changed: boolean} object instead of null or undefined.

Comment thread frontend/package.json
"react": "19.2.0",
"react-dom": "19.2.0",
"react-native": "0.83.2",
"react-native-reanimated": "^3.19.2",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check that react-native-reanimated babel plugin is configured
# Expected: babel.config.js should contain 'react-native-reanimated/plugin'

cat frontend/babel.config.js 2>/dev/null || echo "babel.config.js not found in frontend/"

Repository: codebox-calpoly/PolyBuys

Length of output: 175


Add react-native-reanimated/plugin to babel.config.js.

react-native-reanimated requires a Babel plugin to work correctly. Update babel.config.js to include 'react-native-reanimated/plugin' as the last plugin in the plugins array:

module.exports = function (api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
    plugins: ['react-native-reanimated/plugin'],
  };
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/package.json` at line 40, Update the Babel configuration to register
the Reanimated plugin: in your babel.config.js module.exports function (the one
calling api.cache(true) and returning presets), add a plugins array if missing
and include 'react-native-reanimated/plugin' as the last entry (i.e., return {
presets: ['babel-preset-expo'], plugins: ['react-native-reanimated/plugin'] } or
append it to any existing plugins array) so Reanimated can initialize correctly.

Co-authored-by: Evan Taylor <evan-taylor@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/app/conversations/[id].tsx (1)

1-652: ⚠️ Potential issue | 🟡 Minor

CI is failing on formatting for this file.

Prettier --check reported style issues; this needs a formatting pass before merge.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/app/conversations/`[id].tsx around lines 1 - 652, The file fails
Prettier formatting; run the project's formatter (e.g., Prettier) on this file
and commit the changes so it passes CI. Locate the ConversationDetailScreen
component and the styles object (styles) in this diff and apply the project's
formatting rules (via your editor or running npm/yarn prettier --write) to fix
quote usage, spacing, trailing commas, and JSX/TSX formatting inconsistencies,
then re-run Prettier --check before pushing.
🧹 Nitpick comments (1)
frontend/app/conversations/[id].tsx (1)

301-305: Consider using KeyboardAwareScreen here for consistency.

This screen now duplicates the same keyboard/safe-area pattern already centralized in frontend/components/ui/KeyboardAwareScreen.tsx (dynamic header offset + safe-area bottom handling). Moving to the shared wrapper would reduce drift and future maintenance.

♻️ Possible refactor sketch
-import { KeyboardAvoidingView, Platform, ... } from 'react-native';
+import { Platform, ... } from 'react-native';
+import KeyboardAwareScreen from '../../components/ui/KeyboardAwareScreen';

-  const insets = useSafeAreaInsets();
-  const headerHeight = useHeaderHeight();
+  const insets = useSafeAreaInsets();

-  return (
-    <KeyboardAvoidingView
-      style={styles.container}
-      behavior={Platform.OS === 'ios' ? 'padding' : undefined}
-      keyboardVerticalOffset={Platform.OS === 'ios' ? headerHeight : 0}
-    >
+  return (
+    <KeyboardAwareScreen style={styles.container}>
       ...
-      <View style={[styles.composerWrap, { paddingBottom: Math.max(insets.bottom, spacing.sm) }]}>
+      <View style={[styles.composerWrap, { paddingBottom: Math.max(insets.bottom, spacing.sm) }]}>
         ...
       </View>
-    </KeyboardAvoidingView>
+    </KeyboardAwareScreen>
   );

Also applies to: 403-446

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/app/conversations/`[id].tsx around lines 301 - 305, This screen
duplicates keyboard/safe-area handling using KeyboardAvoidingView and manual
headerHeight calculation; replace that block (the KeyboardAvoidingView wrapper
using styles.container, Platform.OS checks, and headerHeight) with the shared
KeyboardAwareScreen component from
frontend/components/ui/KeyboardAwareScreen.tsx to centralize behavior and
safe-area bottom handling—remove the manual keyboardVerticalOffset/behavior
logic and pass any required props (e.g., headerHeight or children/content) into
KeyboardAwareScreen so the existing dynamic header offset and safe-area handling
are used consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@frontend/app/conversations/`[id].tsx:
- Around line 1-652: The file fails Prettier formatting; run the project's
formatter (e.g., Prettier) on this file and commit the changes so it passes CI.
Locate the ConversationDetailScreen component and the styles object (styles) in
this diff and apply the project's formatting rules (via your editor or running
npm/yarn prettier --write) to fix quote usage, spacing, trailing commas, and
JSX/TSX formatting inconsistencies, then re-run Prettier --check before pushing.

---

Nitpick comments:
In `@frontend/app/conversations/`[id].tsx:
- Around line 301-305: This screen duplicates keyboard/safe-area handling using
KeyboardAvoidingView and manual headerHeight calculation; replace that block
(the KeyboardAvoidingView wrapper using styles.container, Platform.OS checks,
and headerHeight) with the shared KeyboardAwareScreen component from
frontend/components/ui/KeyboardAwareScreen.tsx to centralize behavior and
safe-area bottom handling—remove the manual keyboardVerticalOffset/behavior
logic and pass any required props (e.g., headerHeight or children/content) into
KeyboardAwareScreen so the existing dynamic header offset and safe-area handling
are used consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 99f52a93-66d5-4b4c-9d66-00c4cbf26994

📥 Commits

Reviewing files that changed from the base of the PR and between cd98019 and 15290fd.

📒 Files selected for processing (2)
  • backend/convex/__tests__/blocks.test.ts
  • frontend/app/conversations/[id].tsx

Co-authored-by: Evan Taylor <evan-taylor@users.noreply.github.com>
Co-authored-by: Evan Taylor <evan-taylor@users.noreply.github.com>
@evan-taylor evan-taylor merged commit 1d309f7 into dev Apr 16, 2026
5 checks passed
@evan-taylor evan-taylor deleted the evan/overhaul branch April 16, 2026 18:32
evan-taylor added a commit that referenced this pull request Apr 21, 2026
* feat: misc frontend UI fixes and improvements

- Fix duplicate sign-in messages on profile tab
- Redirect unauthenticated users to profile on Create Listing (web)
- Fix browser tab title persisting after leaving a listing
- Style placeholder text with lighter grey across profile and create listing forms
- Standardize Create Listing button to brand green (#154734)
- Capitalize category and condition values on listing detail page
- Add profile setup check before creating listings with visible warning banner
- Replace Alert.alert with cross-platform showAlert for web compatibility
- Add Delete button to My Listings with confirmation dialog

* fix: address PR review feedback

- Extract showAlert into shared utils/showAlert.ts module
- Prevent concurrent deletes in my-listings (guard + disable all buttons)
- Fix year reset to empty string in settings signed-out useEffect
- Remove unused Alert/Platform imports from new.tsx

* fix(vercel): declare @sentry/react-native in frontend workspace

Expo resolves @sentry/react-native/expo relative to the frontend package; it was only on the root workspace so Vercel installs did not expose the plugin and expo export failed.

Made-with: Cursor

* fix: address team feedback — auth timeout, seller avatar, download link, image lightbox

- Add 8s timeout + retry button to auth 'checking' step to prevent infinite spinner
- Replace empty grey seller avatar with initials (first letter of name) on listing detail
- Make seller block tappable, navigates to public profile
- Change 'Open in App' to 'Download App' with placeholder App Store URL
- Add download hint link to OpenInAppPrompt component
- Create ImageLightbox component for full-screen uncropped image viewing
- Wrap all listing hero images in Pressable to open lightbox on click
- Support keyboard navigation (Escape, ←, →) in lightbox on web

* fix: address PR feedback for UI components and auth flow

- Fix login retry timer leak

- Fix ImageLightbox filtered array index mismatch and add auto-focus on web

- Improve OpenInAppPrompt link error handling and accessibility labels

- Move APP_STORE_URL and APP_SCHEME to shared constants

* chore: satisfy eslint any warning for lightbox web props

* fixed search

* feat(frontend): dedicated account settings from profile tab

- Add /account-settings stack screen with message notifications, sign out, and delete account
- Profile tab shows a Settings row (and account settings on incomplete profile) linking there
- Web: OpenInAppPrompt for account settings deep link, consistent with profile tab
- Fix typed-route pushes with as never where the generated routes lag

Made-with: Cursor

* red box

* red box

* blocked users

* code rabbit fix

* more fixes

* feat: add Other as a report reason for listing moderation (POLY-70)

* fix: keyboard overlaps input on Create/Edit Listing screens (POLY-75)

* feat: ui changes to save/share buttons

* fix: login redirect bug

* fix: route profileless users to onboarding

Co-authored-by: Evan Taylor <evan-taylor@users.noreply.github.com>

* test: cover login onboarding redirect

* Match UI of MyListing and Home

* chore: remove ios build files from PR

* feat(frontend): dismissible flash banner and safe placement

- Add FlashProvider with bottom-inset positioning above tab/home area
- pointerEvents box-none/auto so banner receives taps; tap or Dismiss clears
- Shorter auto-dismiss when reduce motion is enabled
- Wire create/edit listing, mark sold, and report success to setFlash
- ReportModal optional onReportSuccess for non-blocking confirmation

Made-with: Cursor

* transparent

* more transparent

* chore(frontend): shared report copy; longer flash for reduce motion

- Add feedbackMessages constants for report success (flash + Alert)
- Use REPORT_SUBMITTED_MESSAGE in listing and profile screens
- Increase FLASH_DURATION_REDUCED_MS to 4000 for readability

Made-with: Cursor

* feat: remove tags from UI and backend (POLY-77)

* Remove unrelated changes from PR

* fix: coderabbit errors

* fix(vercel): declare @sentry/react-native in frontend workspace

Expo resolves @sentry/react-native/expo relative to the frontend package; it was only on the root workspace so Vercel installs did not expose the plugin and expo export failed.

Made-with: Cursor

* feat(frontend): polish listing cards (POLY-76)

- Vertical stack: multi-line title (2 lines home, 3 default), condition caption, price
- Clearer hierarchy: dark title, muted condition, bold accent price
- More padding and md card radius; softer shadow; slightly looser grid spacing on home

Made-with: Cursor

* feat(frontend): short description preview on listing cards

- Plain descriptions: up to 2 lines, footnote style, ellipsized
- Multi-line or -/• lists: up to 2 bullet rows with trimmed markers
- Include snippet in accessibility label (capped length)

Made-with: Cursor

* feat(frontend): smarter listing description bullets (heuristics)

- Extract buildDescriptionPreview: newlines/markers, semicolons, then sentences
- Use Intl.Segmenter when available with regex fallback for sentences
- Up to 3 bullets; support numbered line prefixes
- Tests for preview helper; exclude frontend __tests__ from tsc

Made-with: Cursor

* fix(frontend): omit prose after last bulleted line in card preview

- When 2+ lines start with list markers, only those lines become bullets
- descBlock uses overflow hidden as a layout safeguard

Made-with: Cursor

* fix(frontend): numbered lists without space after dot and inline 1. 2.

- Scan for digit+dot boundaries (not decimals like 2.0) across lines and spaces
- Relax line markers so 1.Item matches; run span scan before line-based bullets
- Tests for tight 1.x/2.x lines and single-line numbered lists

Made-with: Cursor

* feat(frontend): one bullet per description line when user uses Enter

- Drop short-line / looksLikeList gate: any 2+ non-empty lines become bullets
- Keep marked-only slice when 2+ lines start with list markers (trailing prose omitted)
- Cap 12 lines, 300 chars per line; strip optional -/1. prefixes per row

Made-with: Cursor

* fix(frontend): trim prose after last bullet (numbered + single dash)

- Numbered items: drop flush lines after first line in each span; keep indented wraps
- One dash bullet + following unmarked lines: show only the marked line
- Tests for numbered tail and single-dash + prose

Made-with: Cursor

* chore(frontend): address CodeRabbit ListingCard + preview tests

- Require listing.condition on ListingCard; exhaustive formatConditionLabel
- Memoize accessibilityLabel from parts; spacing.smPlus for cardDetailsHome
- Tests: 78-char cap and DESC_PREVIEW_BULLET_MAX

Made-with: Cursor

* feat: fixed a ton of stuff, standardize UI (#86)

* feat: fixed a ton of stuff, standardize UI

* fix block user success feedback

Co-authored-by: Evan Taylor <evan-taylor@users.noreply.github.com>

* style format conversation block flow

Co-authored-by: Evan Taylor <evan-taylor@users.noreply.github.com>

* fix listing sold state race checks

Co-authored-by: Evan Taylor <evan-taylor@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Evan Taylor <evan-taylor@users.noreply.github.com>

* feat: update UI themes and improve tab navigation (#87)

- Changed userInterfaceStyle to 'light' in app.json for consistent appearance.
- Refactored theme handling in _layout.tsx to use a custom navigation theme.
- Updated tab navigation styles in (tabs)/_layout.tsx to utilize nativeChrome for better visual consistency.
- Enhanced search and inbox screens to align with new theme settings.
- Added nativeChrome configuration for improved UI elements across the app.

This commit aims to standardize the UI and enhance user experience across different screens.

* feat: implement profile pictures

* fix: coderabbit changes, fix top border on profile page

* feat: add ci/cd for builds (#89)

* feat: add ci/cd for builds

* fix: don't build on backend or docs changes

* fix: inbox search keyboard close (#90)

* feat: fix xcode provisioning on ci/cd (#91)

* fix: add app id to expo (#92)

* feat: run convex deploy and add agent skill (#93)

* feat: run convex deploy and add agent skill

* fix: run convex deploy on manual ci too

* feat: add report and delete for entire conversations

* feat: fix prevent swipe actions from showing through on row press

* feat: add tooltip and add tap to close swipe

* feat: add report and delete for individual messaging

* fix(frontend): keyboard avoidance for price range picker modal

- KeyboardAvoidingView (iOS padding, Android height) above bottom sheet
- ScrollView so presets, inputs, and Apply stay reachable when keyboard is open
- Backdrop as sibling Pressable so sheet scrolling is not blocked
- Safe-area padding on scroll content; cap sheet height for smaller screens

Made-with: Cursor

* chore(frontend): dedupe Sentry dep; dim price picker backdrop

- Remove duplicate @sentry/react-native from package.json
- Restore modal scrim on PriceRangePicker backdrop (rgba 0.35)

Made-with: Cursor

* fix: remove listings when conversation reported, clean up functionality

* fix: no deleting individual messages

* fix: remove tooltip

* feat: redirect new users to home

* feat: redirect to home after logging in

* fix: add code rabbit changes

* fix: onboarding copy so first-time users do not see Welcome back (POLY-86)

* feat: add required field indicators and inline validation to listing creation (POLY-91)

* feat: add date posted metadata to listing cards and detail page (POLY-92)

* fix choppyness (#101)

* feat: add zoom support to listing photos

* fix: address lightbox lint warnings

* chore: rerun ci for listing photo zoom

* fix: address lightbox review feedback

* feat: prevent functionality on web

* fix: ci config setup (#109)

* fix: match Dispatch<SetStateAction> signature for onImagesChange

* Feat/marketing landing (#95)

* fix(vercel): declare @sentry/react-native in frontend workspace

Expo resolves @sentry/react-native/expo relative to the frontend package; it was only on the root workspace so Vercel installs did not expose the plugin and expo export failed.

Made-with: Cursor

* feat(frontend): add marketing landing page and /home feed route

- Add web-only landing at / with value props, CTAs, and App Store compliance
- Desktop: QR code and mailto draft for the download link
- Narrow web: official App Store badge linking to APP_STORE_URL
- Rename (tabs)/index to home so / is the landing without route conflicts
- Point in-app "home" navigation and post-auth default to /home
- Register root index in the stack; dedupe @sentry/react-native in package.json
- Refresh lockfile so workspace resolves expo-blur for typecheck/bundling

Made-with: Cursor

* feat(web): block auth routes and remove login CTAs from landing

- Add auth/login.web.tsx so /auth/login always redirects to / on web
- Remove Sign in header link and Sign up button from LandingScreen
- Clarify copy: sign-in is app-only; web is browse-focused

Made-with: Cursor

* chore(frontend): address CodeRabbit marketing + web auth UX

- index: redirect authenticated web users to /home; show boot spinner while auth loads
- LandingScreen: brand Link to /home; static QR asset (no third-party API); App Store
  badge uses Link target=_blank on web
- types/assets.d.ts: declare *.png for Metro image imports
- constants: document APP_STORE_URL + QR regeneration
- home: web save/create no longer hits blocked /auth/login; use app-only alerts

Made-with: Cursor

* feat: revamp landing page, add legal docs

* fix: board and coderabbit feedback

* fix: feedback

---------

Co-authored-by: Evan Taylor <eltaylor1104@gmail.com>

* Feature/poly 90 button visibility (#108)

* feat: improve search tab and button visibility

* feat: polish search, buttons, and messaging flows

* fix: address ui review feedback

* fix: padding and styling changes

---------

Co-authored-by: Evan Taylor <eltaylor1104@gmail.com>

* fixes

* fix: address listing form validation review feedback

* fix: align home route references with expo router

* feat: add support page and links across the application (#111)

- Introduced a new support page for user assistance, accessible via /support.
- Updated LandingScreen to include a link to the support page and added legal links for Privacy and Terms.
- Enhanced the footer to include a link to the support page.
- Implemented support document retrieval with contact options for user inquiries.
- Adjusted styles for legal links and contact information display.

* fix: restore web browse route targets

* fix: improve keyboard avoidance in chat and price picker

* fix: address keyboard and safety banner review feedback

* featL liquid glass app icon (#112)

* feat: add support page and links across the application

- Introduced a new support page for user assistance, accessible via /support.
- Updated LandingScreen to include a link to the support page and added legal links for Privacy and Terms.
- Enhanced the footer to include a link to the support page.
- Implemented support document retrieval with contact options for user inquiries.
- Adjusted styles for legal links and contact information display.

* feat: add liquid glass app icon

* fix: revert message keyboard avoidance changes

* fix: avoid native driver conflict in price picker

* fix: make profile email read-only

* feat: polish login and profile flows

* fix: tighten profile tab and major selection

* fix: polish frontend login and profile flows

* fix: standardize keyboard chrome across frontend flows

* feat: polish frontend launch readiness

* chore: apply final review hardening updates

* chore: apply final review hardening updates

* fix: address accessibility and profile review comments

* fix: address frontend review findings

* fix: harden native image uploads and backend validation

---------

Co-authored-by: Cole <hackman@calpoly.edu>
Co-authored-by: dfed25 <domfederico21@gmail.com>
Co-authored-by: Jaydon Chen <79879038+jaydonkc@users.noreply.github.com>
Co-authored-by: MatthewPhan <Matthewminhphan@gmail.com>
Co-authored-by: SamanSP1386 <saman.sepehr86@gmail.com>
Co-authored-by: Haixin <haixinhuang502@gmail.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Evan Taylor <evan-taylor@users.noreply.github.com>
Co-authored-by: lheutchy <lheutchy@gmail.com>
Co-authored-by: Taye-Staats <tayestaats@outlook.com>
Co-authored-by: dfed25 <150391626+dfed25@users.noreply.github.com>
Co-authored-by: BoB121isawesome <79879038+BoB121isawesome@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
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.

2 participants