Saved Items Page¶
The SavedPage serves as a personalized bookmarking hub for users on Socon-MKT. Because the platform combines social networking with e-commerce, users can save both standard social posts and marketplace products. This page cleanly separates the two content types into distinct, easily navigable tabs.
The interface utilizes a wide 1100px maximum container to accommodate the dense product grids alongside standard vertical social feeds.
Component Architecture¶
The page relies on a single backend query, utilizing client-side logic to route the data into specialized rendering components based on the content type.
1. SavedPage (Main Wrapper)¶
This component handles the primary data ingestion, infinite scrolling, and tab state management.
- Data Separation (
savedProducts&savedPosts): Rather than making two separate API calls, it uses auseMemohook to iterate over the flattenedallSavedItemsarray. It splits the array into two separate lists based on theis_productboolean attached to each post. - Tab Navigation: Manages an
activeTabstate ("products" or "posts"), conditionally rendering either theProductTaborPostTabcomponent while displaying the precise count of items in each tab header. - Infinite Scroll: Utilizes the standard
react-intersection-observersentinel to fetch older saved items dynamically as the user scrolls to the bottom of either tab.
2. ProductTab¶
Renders saved e-commerce listings in a responsive grid layout (2 columns on mobile, up to 4 on desktop).
- Visual Logic: Prioritizes media. If the product has a video, it renders a play button overlay. If it lacks media entirely, it dynamically renders a styled text block displaying the caption or title.
- Price Tag: Displays the rotated "Soko Price Tag" overlay if the item has an associated price.
- Action Routing: Clicking anywhere on the card routes the user to the dedicated
/marketplace/:idview, while clicking the "Unsave" button strictly fires a mutation and stops event propagation.
3. PostTab¶
Renders saved social media posts in a vertical, single-column feed.
- Layout: Emulates the Main Feed layout, displaying the author's avatar, business title, and the full text caption.
- Media Constraints: Restricts attached media (images or videos) to a maximum width of 400px to maintain readability and prevent massive images from dominating the screen.
- Action Routing: Clicking the post routes the user to the specific
/feeds/:idview.
State Management & Optimistic UI¶
The Saved Items page leverages @tanstack/react-query to ensure the UI feels instantaneous, even when removing items from the list.
- Optimistic Unsave (
useUnsavedPost): When a user clicks "Unsave" on either a product or a post, the application does not wait for a server response. The mutation instantly cancels outgoing fetches, manually filters out the targetpostIdfrom the cached infinite query pages, and updates the DOM immediately. If the server request fails, it rolls the cache back to its previous state. - Infinite Scrolling (
useFetchPostSaved): Continuously appends older saved items by extracting thec(cursor) parameter from the backend'snextpagination link.
API Endpoints & Data Structures¶
The page interacts with the backend via two main endpoints using the globally configured Axios authApi instance.
1. Fetch Saved Items (Infinite)¶
Retrieves a paginated list of all items (both posts and products) saved by the current user.
- Endpoint:
GET /posts/post_saved/ - Query Parameters:
c(Cursor string for pagination) - Authorization: Required (Bearer Token)
- Response Structure (
SavedPostListResponse):{ "next": "url_string | null", "previous": "url_string | null", "results": [ { "id": "string", "post": { "id": "string", "title": "string | null", "price": "string | null", "unit": "string | null", "is_product": true, "caption": "string", "author": { "profile": { "seller_profile": "string | null" } }, "media": [ { "file": "string_url", "media_type": "image | video", "thumbnail": "string_url | null" } ] } } ] }
2. Toggle Save Status (Unsave)¶
Toggles the saved status of a specific post. In the context of this page, it acts as a delete/unsave action.
- Endpoint:
POST /posts/toggle_post_save/ - Payload:
{ "postId": "post_id_string" } - Authorization: Required (Bearer Token)