Making a swiper for fetch Reddit posts

42 Views Asked by At

const clientId = "CLIENT-ID";
const clientSecret = "CLIENT-SECRET";

async function fetchPosts() {
  const response = await fetch(
    "https://www.reddit.com/r/memes/hot.json?limit=10"
  );
  const data = await response.json();
  
  // Exclude stickied posts
  const nonStickiedPosts = data.data.children.filter(post => !post.data.stickied);

  return nonStickiedPosts;
}

function filterValidPosts(posts) {
  return posts.filter(
    (post) =>
      post.data.title &&
      post.data.url &&
      post.data.url.startsWith("http") &&
      post.data.url.includes(".gif") &&
      post.data.url.includes("v.redd.it") &&
      post.data.self && // Exclude self-posts
      !post.data.removed // Exclude deleted posts
  );
}


function convertToDirectLink(redditMediaLink) {
    const url = new URL(redditMediaLink);

    // Check if it's a Reddit preview link
    if (url.hostname === "preview.redd.it") {
        const [mediaId, format] = url.pathname.split("/").pop().split(".");

        if (mediaId && format) {
            // Build the direct link
            return `https://i.redd.it/${mediaId}.${format}`;
        }
    }

    // If it's not a Reddit preview link, return the original link
    return redditMediaLink;
}

// ...

// Function to render posts as cards on the webpage
async function renderPosts(posts) {
  const container = document.getElementById("postsContainer");
  container.innerHTML = "";

  for (let i = 0; i < posts.length; i += 3) {
    const row = document.createElement("div");
    row.classList.add("row");

    for (let j = i; j < i + 3 && j < posts.length; j++) {
      const post = posts[j];

      const card = document.createElement("div");
      card.classList.add("card");
      const cardContent = document.createElement("div");
      cardContent.classList.add("card-content");

      if (post.data.is_gallery) {
        // Handle Reddit galleries
        if (post.data.media_metadata) {
          // Iterate over media_metadata to get direct links
          for (const mediaId in post.data.media_metadata) {
            const galleryImage = post.data.media_metadata[mediaId];
            const directLink = convertToDirectLink(galleryImage.s.u); // Use the s.u field for direct link
            const image = document.createElement("img");
            image.src = directLink;
            image.alt = post.data.title;
            cardContent.appendChild(image);
          }
        }
      } else if (post.data.url) {
        // Handle posts with a single image or external links
        const image = document.createElement("img");
        image.src = await convertToDirectLink(post.data.url);
        image.alt = post.data.title;
        cardContent.appendChild(image);
      }

      cardContent.innerHTML += `<h3>${post.data.title}</h3>`;
      card.appendChild(cardContent);
      row.appendChild(card);
    }

    container.appendChild(row);
  }
}




async function fetchAndRenderNewPosts() {
  try {
    const posts = await fetchPosts();
    const validPosts = filterValidPosts(posts);

    if (validPosts.length > 0) {
      renderPosts(validPosts);
    } else {
      console.warn("No valid posts fetched. Trying again with new posts.");
      const newPosts = await fetchPosts();
      renderPosts(newPosts);
    }
  } catch (error) {
    console.error("Error fetching or rendering posts:", error);
  }
}

window.onload = function () {
  fetchAndRenderNewPosts();

  setInterval(fetchAndRenderNewPosts, 300000); // Fetch new posts every 5 minutes (adjust as needed)
};
body {
    margin: 0;
    padding: 0;
    font-family: Arial, sans-serif;
}

.container {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-around;
    align-items: flex-start;
    max-height: 100vh; /* Set a maximum height for the container */
    overflow-y: auto; /* Add vertical scroll if needed */
}

.row {
    display: flex;
    justify-content: space-around;
    margin-bottom: 20px; /* Adjust as needed for spacing between rows */
}

.card {
    margin: 10px;
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 5px;
    transition: box-shadow 0.3s ease-in-out;
    overflow: hidden;
    width: calc(33% - 20px); /* Adjust as needed for spacing between cards */
    max-width: 300px; /* Limit the maximum width of each card */

}

.card:hover {
    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
}

.card-content {
    position: relative;
}

.card img {
    width: 100%;
    height: 100%;
    object-fit: cover; /* Ensure the image covers the entire container */
}
<div class="container" id="postsContainer"></div> 

I made a Reddit post fetching web site and I'm able to get the galleries (posts with multiple images) in a card where all the images are put and also the single images posts as cards along with their title. Now for the gallery type posts, I want to make a swiper so that I can just swipe instead of seeing multiple images in a row in one card box, but I don't know how to do that.

function convertToDirectLink(redditMediaLink) {
    const url = new URL(redditMediaLink);

    // Check if it's a Reddit preview link
    if (url.hostname === "preview.redd.it") {
        const [mediaId, format] = url.pathname.split("/").pop().split(".");

        if (mediaId && format) {
            // Build the direct link
            return `https://i.redd.it/${mediaId}.${format}`;
        }
    }

    // If it's not a Reddit preview link, return the original link
    return redditMediaLink;
}

// ...

// Function to render posts as cards on the webpage
async function renderPosts(posts) {
  const container = document.getElementById("postsContainer");
  container.innerHTML = "";

  for (let i = 0; i < posts.length; i += 3) {
    const row = document.createElement("div");
    row.classList.add("row");

    for (let j = i; j < i + 3 && j < posts.length; j++) {
      const post = posts[j];

      const card = document.createElement("div");
      card.classList.add("card");
      const cardContent = document.createElement("div");
      cardContent.classList.add("card-content");

      if (post.data.is_gallery) {
        // Handle Reddit galleries
        if (post.data.media_metadata) {
          // Iterate over media_metadata to get direct links
          for (const mediaId in post.data.media_metadata) {
            const galleryImage = post.data.media_metadata[mediaId];
            const directLink = convertToDirectLink(galleryImage.s.u); // Use the s.u field for direct link
            const image = document.createElement("img");
            image.src = directLink;
            image.alt = post.data.title;
            cardContent.appendChild(image);
          }
        }
      } else if (post.data.url) {
        // Handle posts with a single image or external links
        const image = document.createElement("img");
        image.src = await convertToDirectLink(post.data.url);
        image.alt = post.data.title;
        cardContent.appendChild(image);
      }

      cardContent.innerHTML += `<h3>${post.data.title}</h3>`;
      card.appendChild(cardContent);
      row.appendChild(card);
    }

    container.appendChild(row);
  }
}

// ...



async function fetchAndRenderNewPosts() {
  try {
    const posts = await fetchPosts();
    const validPosts = filterValidPosts(posts);

    if (validPosts.length > 0) {
      renderPosts(validPosts);
    } else {
      console.warn("No valid posts fetched. Trying again with new posts.");
      const newPosts = await fetchPosts();
      renderPosts(newPosts);
    }
  } catch (error) {
    console.error("Error fetching or rendering posts:", error);
  }
}

window.onload = function () {
  fetchAndRenderNewPosts();

  setInterval(fetchAndRenderNewPosts, 300000); // Fetch new posts every 5 minutes (adjust as needed)
};

Code summary: this is everything I've done so far. The link converter converts the preview.redd.it link to i.redd.it for easier and high resolution displaying. The render part just renders the image in a card with 3 cards in each row.

1

There are 1 best solutions below

1
FiddlingAway On

This could be one way of doing it.

async function fetchPosts() {
  let data = {"kind":"Listing","data":{"children":[{"kind":"t3","data":{"subreddit":"memes","is_gallery":false,"title":"r/Memes is looking for new moderators! Interested? Fill out our application!","id":"18zq3y5","url":"https://docs.google.com/forms/d/e/1FAIpQLSfBlrL6LVOktwIdGubvbJ7REeh9vANiBTIpUecW63PHINQECg/viewform","stickied":false}},{"kind":"t3","data":{"subreddit":"memes","is_gallery":false,"title":"It was the 9/one- one of British TV","id":"19c1wez","url":"https://i.redd.it/hoqa37i19sdc1.jpeg","stickied":false}},{"kind":"t3","data":{"subreddit":"McMansionHell","is_gallery":true,"title":"Look at the upper left corner of the bathroom","media_metadata":{"5j8l66lt27b61":{"s":{"y":576,"x":768,"u":"https://preview.redd.it/5j8l66lt27b61.jpg?width=768&amp;format=pjpg&amp;auto=webp&amp;s=ba0c95d8ce7cf1d325dfac2564c2fdd55b036508"},"id":"5j8l66lt27b61"},"797pwyft27b61":{"s":{"y":413,"x":550,"u":"https://preview.redd.it/797pwyft27b61.jpg?width=550&amp;format=pjpg&amp;auto=webp&amp;s=a6dc4d0e1fac886c901e662b08662c100a5ee4e7"},"id":"797pwyft27b61"},"pj9sc1st27b61":{"s":{"y":576,"x":768,"u":"https://preview.redd.it/pj9sc1st27b61.jpg?width=768&amp;format=pjpg&amp;auto=webp&amp;s=637aed513c30179864c5a33302f44bd035771e0b"},"id":"pj9sc1st27b61"}},"gallery_data":{"items":[{"media_id":"5j8l66lt27b61","id":22269281},{"media_id":"797pwyft27b61","id":22269279},{"media_id":"pj9sc1st27b61","id":22269284}]},"id":"kwudba","url":"https://www.reddit.com/gallery/kwudba","stickied":false}},{"kind":"t3","data":{"subreddit":"McMansionHell","is_gallery":true,"title":"This is a dummy gallery","media_metadata":{"f9gczent27b61":{"s":{"y":576,"x":768,"u":"https://preview.redd.it/f9gczent27b61.jpg?width=768&amp;format=pjpg&amp;auto=webp&amp;s=1a6cc9447c9b8bac8b5a91de7c8c32247d558eaa"},"id":"f9gczent27b61"},"v2fyorvt27b61":{"s":{"y":576,"x":768,"u":"https://preview.redd.it/v2fyorvt27b61.jpg?width=768&amp;format=pjpg&amp;auto=webp&amp;s=76fb9f6db49dd5a379eebd9a0ae7d3fa86c88745"},"id":"v2fyorvt27b61"}},"gallery_data":{"items":[{"media_id":"5j8l66lt27b61","id":22269281},{"media_id":"v2fyorvt27b61","id":22269285}]},"id":"dummygal","url":"https://www.reddit.com/gallery/dummygal","stickied":false}}],"before":null}};
    
    // Exclude stickied posts
    const nonStickiedPosts = data.data.children.filter(post => !post.data.stickied);
  
    return nonStickiedPosts;
}
  
function filterValidPosts(posts) {
    return posts.filter(
      (post) =>
        post.data.title &&
        post.data.url &&
        post.data.url.startsWith("http") &&
        post.data.url.includes(".gif") &&
        post.data.url.includes("v.redd.it") &&
        post.data.self && // Exclude self-posts
        !post.data.removed // Exclude deleted posts
    );
  }
  
  
function convertToDirectLink(redditMediaLink) {
  const url = new URL(redditMediaLink);

  // Check if it's a Reddit preview link
  if (url.hostname === "preview.redd.it") {
    const [mediaId, format] = url.pathname.split("/").pop().split(".");

    if (mediaId && format) {
      // Build the direct link
      return `https://i.redd.it/${mediaId}.${format}`;
    }
  }

  // If it's not a Reddit preview link, return the original link
  return redditMediaLink;
}

// ...

// Function to render posts as cards on the webpage
async function renderPosts(posts) {
  const container = document.getElementById("postsContainer");
  container.innerHTML = "";

  for (let i = 0; i < posts.length; i += 3) {
    const row = document.createElement("div");
    row.classList.add("row");

    for (let j = i; j < i + 3 && j < posts.length; j++) {
      const post = posts[j];

      const card = document.createElement("div");
      card.classList.add("card");
      const cardContent = document.createElement("div");
      cardContent.classList.add("card-content");

            let customClass = "";
      let swiperStart = `
        <!-- Slider main container -->
        <div class="swiper">
            <!-- Additional required wrapper -->
          <div class="swiper-wrapper">
          <!-- Slides -->
      `;
      let swiperContent = "";
      let swiperEnd = `
            </div>
            <!-- If we need pagination -->
                <div class="swiper-pagination"></div>
          <!-- If we need navigation buttons -->
          <div class="swiper-button-prev"></div>
          <div class="swiper-button-next"></div>
          <!-- If we need scrollbar -->
          <div class="swiper-scrollbar"></div>
        </div>
      `;
      if (post.data.is_gallery) {
        // Handle Reddit galleries
        if (post.data.media_metadata) {
            customClass = " gallery-post";
          // Iterate over media_metadata to get direct links
          for (const mediaId in post.data.media_metadata) {
            const galleryImage = post.data.media_metadata[mediaId];
            const directLink = convertToDirectLink(galleryImage.s.u); // Use the s.u field for direct link
            swiperContent += `
                <div class="swiper-slide">
                <img alt="`+post.data.title+`" src="`+directLink+`">
              </div>
            `;
          }
          
          const divElem = document.createElement("DIV");
          divElem.innerHTML = swiperStart + swiperContent + swiperEnd;
          cardContent.appendChild(divElem);
          cardContent.className += customClass;
        }
      } else if (post.data.url) {
        // Handle posts with a single image or external links
        const image = document.createElement("img");
        image.src = await convertToDirectLink(post.data.url);
        image.alt = post.data.title;
        cardContent.appendChild(image);
      }

      cardContent.innerHTML += `<h3>${post.data.title}</h3>`;
      card.appendChild(cardContent);
      row.appendChild(card);
    }

    container.appendChild(row);
  }
  
  initSwiper();
}
  
async function fetchAndRenderNewPosts() {
  try {
    const posts = await fetchPosts();
    const validPosts = filterValidPosts(posts);

    if (validPosts.length > 0) {
      renderPosts(validPosts);
    } else {
      console.warn("No valid posts fetched. Trying again with new posts.");
      const newPosts = await fetchPosts();
      renderPosts(newPosts);
    }
  } catch (error) {
    console.error("Error fetching or rendering posts:", JSON.stringify(error));
  }
}
  
window.onload = function () {
  fetchAndRenderNewPosts();
};

function initSwiper() {
  if(document.querySelectorAll(".gallery-post").length > 0) {
    console.log("I'm in....");
    const swiper = new Swiper('.swiper', {
      // Optional parameters
      direction: 'horizontal',
      loop: true,

      // If we need pagination
      pagination: {
        el: '.swiper-pagination',
      },

      // Navigation arrows
      navigation: {
        nextEl: '.swiper-button-next',
        prevEl: '.swiper-button-prev',
      },
    });
  } else {
    console.log("I'm not in :(");
  }
}
body {
    margin: 0;
    padding: 0;
    font-family: Arial, sans-serif;
}

.container {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-around;
    align-items: flex-start;
    max-height: 100vh; /* Set a maximum height for the container */
    overflow-y: auto; /* Add vertical scroll if needed */
}

.row {
    display: flex;
    justify-content: space-around;
    margin-bottom: 20px; /* Adjust as needed for spacing between rows */
    min-width: 98vw;
}

.card {
    margin: 10px;
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 5px;
    transition: box-shadow 0.3s ease-in-out;
    overflow: hidden;
    width: calc(33% - 20px); /* Adjust as needed for spacing between cards */
    max-width: 300px; /* Limit the maximum width of each card */

}

.card:hover {
    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
}

.card-content {
    position: relative;
}

.card img {
    width: 100%;
    height: 100%;
    object-fit: cover; /* Ensure the image covers the entire container */
}
<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css"
/>
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
<div class="container" id="postsContainer"></div>


Changes made to your original code, for simplicity's sake

You'll notice that I've removed your initial fetching logic. This was done for testing purposes - a greatly reduced JSON was used to mimic the response from your original URL.

Also, since they were not used in the available code, I've taken the liberty of removing the clientId and the clientSecret from the code. You will, of course, use them, if there's a need for it.

The setInterval has also been removed, as the code I'm providing is just for testing purposes - you will, of course, include it in your original code.

There were minor changes in the CSS, as well, to accommodate for the styling of the swiper (min-width was set for your .row class - remove it, and you'll see why it was added).


Changes made to your original code, to include swiper.js

The function renderPosts has been adapted to contain the logic for creating the swiper's container, which will hold the images retrieved from reddit. In doing this, I've relied on the official documentation. Please note that this is just one way of doing it - feel free to experiment, if you find that the solution I've offered is not suitable for what you need.

Adding a custom class to the cards containing galleries is done so that we'll know whether there's a need to initialize the swiper, or not.

Finally, at the very end of your renderPosts function, I've included a new function - initSwiper. The function is straightforward, and adapted from what is in the official documentation (no scrollbar, horizontal swiping, the swiper is initialized if there are gallery posts, etc).