feat: fixed a ton of stuff, standardize UI#86
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded
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 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughThis 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
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
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
Estimated code review effort🎯 5 (Critical) | ⏱️ ~95 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ❌ 3❌ Failed checks (2 warnings, 1 inconclusive)
✏️ 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 |
|
@cursor babysit this PR |
|
No. I fixed these CodeRabbit items:
I did not fix these remaining CodeRabbit comments:
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. |
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 (2)
backend/convex/listings.ts (1)
628-640:⚠️ Potential issue | 🟠 MajorRe-check
soldstatus insideinternalUpdateListingbefore patching.The action checks
listing.status === 'sold'at line 638, but the actual update happens later in theinternalUpdateListingmutation. 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 insideinternalUpdateListingbefore callingctx.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 | 🟡 MinorPotential button overlap when both
onToggleSaveandonManagePressare provided.Both
saveButton(lines 276-287) andmanageButton(lines 288-307) are absolutely positioned attop: spacing.xs, right: spacing.xs. If a consumer passes bothonToggleSaveandonManagePressprops, the buttons will render on top of each other.Consider either:
- Making the props mutually exclusive via TypeScript discriminated union
- Adjusting positioning so they don't conflict
- 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 addingaccessibilityStateto 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 oncolors.*tokens fromtheme/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
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (37)
backend/convex/__tests__/listings.test.tsbackend/convex/blocks.tsbackend/convex/listings.tsfrontend/app/(tabs)/_layout.tsxfrontend/app/(tabs)/inbox.tsxfrontend/app/(tabs)/index.tsxfrontend/app/(tabs)/my-listings.tsxfrontend/app/(tabs)/search.tsxfrontend/app/(tabs)/search/_layout.tsxfrontend/app/(tabs)/search/index.tsxfrontend/app/(tabs)/settings.tsxfrontend/app/_layout.tsxfrontend/app/account-settings.tsxfrontend/app/auth/login.tsxfrontend/app/conversations/[id].tsxfrontend/app/conversations/new.tsxfrontend/app/listings/[id].tsxfrontend/app/listings/[id]/edit.tsxfrontend/app/listings/new.tsxfrontend/app/profile/[userId].tsxfrontend/app/profile/edit.tsxfrontend/components/FilterBar.tsxfrontend/components/ImageLightbox.tsxfrontend/components/ListingCard.tsxfrontend/components/MyListingActionsSheet.tsxfrontend/components/PriceRangePicker.tsxfrontend/components/ReportModal.tsxfrontend/components/SortPicker.tsxfrontend/components/ui/Field.tsxfrontend/components/ui/FilterChips.tsxfrontend/components/ui/KeyboardAwareScreen.tsxfrontend/components/ui/ScreenHeader.tsxfrontend/components/ui/SectionCard.tsxfrontend/components/ui/index.tsfrontend/hooks/useRecentSearches.tsfrontend/package.jsonfrontend/types/filters.ts
💤 Files with no reviewable changes (2)
- frontend/hooks/useRecentSearches.ts
- frontend/app/(tabs)/search.tsx
| const blockedId = await resolvePeerUserId(ctx, args.blockedId); | ||
| if (!blockedId) { | ||
| return null; | ||
| } |
There was a problem hiding this comment.
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.
| "react": "19.2.0", | ||
| "react-dom": "19.2.0", | ||
| "react-native": "0.83.2", | ||
| "react-native-reanimated": "^3.19.2", |
There was a problem hiding this comment.
🧩 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>
There was a problem hiding this comment.
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 | 🟡 MinorCI is failing on formatting for this file.
Prettier --checkreported 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 usingKeyboardAwareScreenhere 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
📒 Files selected for processing (2)
backend/convex/__tests__/blocks.test.tsfrontend/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>
* 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>


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