Skip to content

feat: exclude hidden content from feed and handle hidden states#46

Merged
jaydonkc merged 2 commits intodevfrom
feature/POLY-22-Exclude-hidden-content-from-feeds-and-handle-hidden-states
Mar 2, 2026
Merged

feat: exclude hidden content from feed and handle hidden states#46
jaydonkc merged 2 commits intodevfrom
feature/POLY-22-Exclude-hidden-content-from-feeds-and-handle-hidden-states

Conversation

@lheutchy
Copy link
Copy Markdown
Collaborator

@lheutchy lheutchy commented Feb 24, 2026

Linked Issues

Closes #22
Linear: POLY-22

Summary

Implemented hidden-listing handling in the frontend so moderated content is not shown in normal browse flows.

Changes:

Added ListingUnavailable component with:

  • message: “This listing is no longer available”
  • “Back to Browse” button

Added reusable HiddenBanner for owner-only hidden listing view

Updated listing detail screen to:

  • show unavailable state for non-owners on hidden listings
  • show banner + listing content for owners on hidden listings
  • hide edit action when listing is hidden

Added defensive client-side filtering in home feed to exclude hidden listings

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

Still requires testing

Summary by CodeRabbit

  • New Features
    • Hidden listings are now excluded from the main browse list.
    • Non-owners see an "unavailable" screen with a Back to Browse button for hidden listings.
    • Listing owners viewing their hidden listings see a notification banner.
    • Owners are prevented from editing listings while hidden.
    • A support/appeal contact link is provided from the hidden-listing banner.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 24, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da8878d and 6e1fc34.

📒 Files selected for processing (3)
  • frontend/app/listings/[id].tsx
  • frontend/app/listings/[id]/edit.tsx
  • frontend/components/HiddenBanner.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/app/listings/[id].tsx

📝 Walkthrough

Walkthrough

Client-side filtering and owner-aware UI were added to hide listings marked as hidden: browse lists filter out hidden items; detail and edit pages gate visibility and editing based on ownership and hidden status; two new UI components surface unavailable/hidden states.

Changes

Cohort / File(s) Summary
Browse list
frontend/app/index.tsx
Apply defensive client-side filter to exclude listings with isHidden === true from the public browse list.
Detail & Edit pages
frontend/app/listings/[id].tsx, frontend/app/listings/[id]/edit.tsx
Add imports for ListingUnavailable and HiddenBanner; wait for currentUserSubject; compute isOwner/isHidden/isHiddenOwnerView; render ListingUnavailable for non-owner viewers of hidden listings; show HiddenBanner for owners; prevent edits when listing is hidden.
New UI components
frontend/components/HiddenBanner.tsx, frontend/components/ListingUnavailable.tsx
Add HiddenBanner (support mailto appeal, styled banner) and ListingUnavailable (centered unavailable message with router navigation).
Manifest
package.json
Minor dependency/manifest tweak (lines changed +2/-1).

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Browse as Browse Page
    participant Detail as Listing Detail Page
    participant Client as Client Data Layer
    participant Router as Router

    User->>Browse: Open browse
    Browse->>Client: Fetch listings
    Client-->>Browse: Return listings (includes isHidden flags)
    Browse->>Browse: Filter out listings where isHidden === true
    Browse-->>User: Show filtered listings

    User->>Detail: Open listing [id]
    Detail->>Client: Fetch listing by id
    Client-->>Detail: Return listing
    alt listing.isHidden === true and currentUser != sellerId
        Detail->>Router: Render ListingUnavailable
        Router-->>User: "This listing is no longer available"
    else listing.isHidden === true and currentUser === sellerId
        Detail->>Detail: Render listing + HiddenBanner
        Detail-->>User: Show listing with hidden banner and no Edit button
    else listing.isHidden !== true
        Detail->>Detail: Render normal listing view (Edit allowed for owner)
        Detail-->>User: Show listing details
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Poem

🐰✨ I hop through code and tidy trails of hay,
Hidden hops are tucked so others stray away,
Owners see a banner, gentle as the dawn,
Visitors find “gone” and turn their paws along. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR addresses hidden listing handling but does not implement the search and filtering system or backend changes required by the linked issue #22. Either implement the search/filtering system and backend schema updates from issue #22, or clarify if this PR is addressing a different issue related to hidden content moderation.
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Out of Scope Changes check ❓ Inconclusive All changes focus on frontend hidden listing handling, which is a valid concern but appears unrelated to the search/filtering system requirements in linked issue #22. Verify whether this PR is correctly linked to issue #22 or if it should reference a separate issue for hidden content moderation.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main changes: excluding hidden content from feeds and handling hidden listing states throughout the frontend.
Description check ✅ Passed The description covers the main changes and uses the template format, but the 'How to Test' section lacks specific expected behavior descriptions and screenshots/demos remain incomplete.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/POLY-22-Exclude-hidden-content-from-feeds-and-handle-hidden-states

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.

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

🧹 Nitpick comments (3)
frontend/app/listings/[id]/edit.tsx (2)

145-151: Two consecutive early-return blocks render the same component — merge them.

Both listing === null and isHidden guards return <ListingUnavailable />. Combining them removes redundancy and makes the intent clearer.

♻️ Proposed refactor
-  if (listing === null) {
-    return <ListingUnavailable />;
-  }
-
-  if (isHidden) {
-    return <ListingUnavailable />;
-  }
+  if (listing === null || isHidden) {
+    return <ListingUnavailable />;
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/app/listings/`[id]/edit.tsx around lines 145 - 151, The two
consecutive early-return checks both render ListingUnavailable; combine them
into a single guard that returns <ListingUnavailable /> when either listing ===
null or isHidden is true (e.g., if (listing === null || isHidden) return
<ListingUnavailable />) to remove redundancy and clarify intent in the edit
component where listing, isHidden, and ListingUnavailable are used.

17-17: Import alias inconsistency with the rest of the file.

Line 16 uses a relative path ('../../../components/TagInput'); Line 17 switches to the @/ alias for ListingUnavailable. Pick one style and apply it consistently.

♻️ Proposed fix (normalize to relative path)
-import ListingUnavailable from '@/components/ListingUnavailable';
+import ListingUnavailable from '../../../components/ListingUnavailable';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/app/listings/`[id]/edit.tsx at line 17, The import for
ListingUnavailable uses the alias '@/components/ListingUnavailable' while other
imports in this file (e.g., the relative '../../../components/TagInput') use
relative paths; make them consistent by switching the ListingUnavailable import
to the same relative style (use the appropriate relative path to
components/ListingUnavailable) so all imports (including TagInput and
ListingUnavailable) follow the same pattern.
frontend/components/HiddenBanner.tsx (1)

3-4: Placeholder support email must be replaced before shipping.

The comment on Line 3 explicitly flags that 'support@polybuys.com' is not the real address. This will go to production as-is if not resolved.

Do you want me to open a tracking issue to ensure this is replaced with the verified support address before release?

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

In `@frontend/components/HiddenBanner.tsx` around lines 3 - 4, Replace the
placeholder SUPPORT_EMAIL constant in HiddenBanner.tsx with the verified support
address (or load it from configuration/env if you need different values per
environment); update the const SUPPORT_EMAIL = 'support@polybuys.com' to the
real email or to a process.env or config lookup (and ensure any components using
SUPPORT_EMAIL continue to receive the updated value).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/app/index.tsx`:
- Around line 115-116: Move the hidden-listing filter from the client into the
backend query so pagination counts (isDone and continueCursor) are computed
after excluding hidden items: update the backend getListings implementation (see
backend/convex/listings.ts and the logic around the existing search filter at
line ~795) to add isHidden !== true to the query and ensure
continueCursor/isDone are derived from the filtered result; keep the client-side
defensive guard in frontend/app/index.tsx (the allListings -> listings filter)
as a last-resort safety net but do not rely on it for pagination correctness.

In `@frontend/app/listings/`[id].tsx:
- Around line 36-42: The hidden-listing check has a race when currentUserSubject
is still undefined; update the loading guard to wait for both subscriptions
before rendering the hidden-owner logic: specifically treat listing ===
undefined OR currentUserSubject === undefined as "still loading" (but allow
currentUserSubject === null to proceed), so that isOwner (currentUserSubject ===
listing.sellerId), isHidden, and isHiddenOwnerView are only evaluated after both
values are defined and then only non-owners see <ListingUnavailable />; update
the component where currentUserSubject, listing, isOwner, isHidden,
isHiddenOwnerView and ListingUnavailable are referenced.

In `@frontend/components/HiddenBanner.tsx`:
- Line 7: The onAppealPress handler currently calls
Linking.openURL(`mailto:${SUPPORT_EMAIL}`) and discards the returned Promise;
update onAppealPress to handle rejections by appending a .catch(...) to
Linking.openURL and present a user-friendly Alert on failure, and ensure Alert
is added to the component imports so the Alert API is available; keep the mailto
call and SUPPORT_EMAIL usage the same, but surface errors via Alert.alert(...)
instead of letting the rejection be unhandled.

---

Nitpick comments:
In `@frontend/app/listings/`[id]/edit.tsx:
- Around line 145-151: The two consecutive early-return checks both render
ListingUnavailable; combine them into a single guard that returns
<ListingUnavailable /> when either listing === null or isHidden is true (e.g.,
if (listing === null || isHidden) return <ListingUnavailable />) to remove
redundancy and clarify intent in the edit component where listing, isHidden, and
ListingUnavailable are used.
- Line 17: The import for ListingUnavailable uses the alias
'@/components/ListingUnavailable' while other imports in this file (e.g., the
relative '../../../components/TagInput') use relative paths; make them
consistent by switching the ListingUnavailable import to the same relative style
(use the appropriate relative path to components/ListingUnavailable) so all
imports (including TagInput and ListingUnavailable) follow the same pattern.

In `@frontend/components/HiddenBanner.tsx`:
- Around line 3-4: Replace the placeholder SUPPORT_EMAIL constant in
HiddenBanner.tsx with the verified support address (or load it from
configuration/env if you need different values per environment); update the
const SUPPORT_EMAIL = 'support@polybuys.com' to the real email or to a
process.env or config lookup (and ensure any components using SUPPORT_EMAIL
continue to receive the updated value).

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2afb9ed and da8878d.

📒 Files selected for processing (5)
  • frontend/app/index.tsx
  • frontend/app/listings/[id].tsx
  • frontend/app/listings/[id]/edit.tsx
  • frontend/components/HiddenBanner.tsx
  • frontend/components/ListingUnavailable.tsx

Comment thread frontend/app/index.tsx
Comment on lines +115 to +116
// Defensive client-side guard in case stale cache includes hidden listings.
const listings = allListings.filter((listing) => listing.isHidden !== true);
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

Client-side hidden filter can produce visible pages shorter than PAGE_SIZE, potentially starving the infinite-scroll trigger.

isDone and continueCursor are determined by the backend before this filter runs. If the backend returns a full page of 20 but some are hidden, FlatList receives fewer than 20 items. With onEndReachedThreshold={0.5}, if the resulting list is too short to fill the viewport, onEndReached may never fire and the user is stuck with an incomplete page.

The comment calls this a defensive guard for stale cache, which is fair — but the ideal fix is to push the isHidden filter into the getListings backend query (consistent with how the search endpoint already does it per backend/convex/listings.ts:795). The client-side guard can then remain as a true last-resort safety net without affecting pagination arithmetic.

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

In `@frontend/app/index.tsx` around lines 115 - 116, Move the hidden-listing
filter from the client into the backend query so pagination counts (isDone and
continueCursor) are computed after excluding hidden items: update the backend
getListings implementation (see backend/convex/listings.ts and the logic around
the existing search filter at line ~795) to add isHidden !== true to the query
and ensure continueCursor/isDone are derived from the filtered result; keep the
client-side defensive guard in frontend/app/index.tsx (the allListings ->
listings filter) as a last-resort safety net but do not rely on it for
pagination correctness.

Comment thread frontend/app/listings/[id].tsx
Comment thread frontend/components/HiddenBanner.tsx Outdated
@jaydonkc
Copy link
Copy Markdown
Collaborator

jaydonkc commented Mar 2, 2026

Created clean companion branch jaydon/clean-pr-46.

Feedback:

  • This is merge-friendly and focused.
  • Hidden-listing UX protections are valuable and should remain intact through later deep-link changes.
  • Low-risk PR; good candidate for early merge.

@jaydonkc jaydonkc merged commit 080f174 into dev Mar 2, 2026
3 checks passed
@dfed25 dfed25 mentioned this pull request Mar 11, 2026
5 tasks
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