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 a useMemo hook to iterate over the flattened allSavedItems array. It splits the array into two separate lists based on the is_product boolean attached to each post.
  • Tab Navigation: Manages an activeTab state ("products" or "posts"), conditionally rendering either the ProductTab or PostTab component while displaying the precise count of items in each tab header.
  • Infinite Scroll: Utilizes the standard react-intersection-observer sentinel 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/:id view, 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/:id view.

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 target postId from 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 the c (cursor) parameter from the backend's next pagination 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)