← back to Dw Theme Hamburger

snippets/recently-viewed.liquid

194 lines

{% comment %}
  Recently Viewed Products — Tracks product views in localStorage,
  renders a product grid. Works on product pages, homepage, and collection pages.
{% endcomment %}

<div
  id="dw-recently-viewed"
  class="recently-viewed-section"
  data-section-id="{{ section.id }}"
  data-max-products="{{ section.settings.max_products }}"
  data-current-handle="{{ product.handle | default: '' }}"
  style="display:none;max-width:1200px;margin:0 auto;padding:32px 20px 48px;"
>
  <h2 style="font-family:-apple-system,system-ui,sans-serif;font-size:22px;font-weight:600;text-align:center;margin:0 0 8px;color:#1a1a1a;letter-spacing:0.01em;">Recently Viewed</h2>
  <p style="text-align:center;color:#888;font-size:13px;margin:0 0 28px;">Products you've browsed</p>
  <div id="dw-rv-grid" style="display:grid;grid-template-columns:repeat(3,1fr);gap:20px;"></div>
</div>

<style>
  .rv-card {
    text-decoration:none;
    color:inherit;
    display:block;
    border-radius:6px;
    overflow:hidden;
    background:#fff;
    border:1px solid #f0ece6;
    transition:box-shadow 0.2s,transform 0.15s;
  }
  .rv-card:hover {
    box-shadow:0 4px 16px rgba(0,0,0,0.08);
    transform:translateY(-2px);
  }
  .rv-card-img {
    width:100%;
    aspect-ratio:1/1;
    object-fit:cover;
    display:block;
    background:#f5f5f0;
  }
  .rv-card-info {
    padding:12px 14px;
    font-family:-apple-system,system-ui,sans-serif;
  }
  .rv-card-vendor {
    font-size:11px;
    font-weight:500;
    color:#999;
    text-transform:uppercase;
    letter-spacing:0.04em;
    margin:0 0 3px;
  }
  .rv-card-title {
    font-size:13px;
    font-weight:500;
    color:#333;
    margin:0 0 6px;
    line-height:1.3;
    display:-webkit-box;
    -webkit-line-clamp:2;
    -webkit-box-orient:vertical;
    overflow:hidden;
  }
  .rv-card-price {
    font-size:13px;
    font-weight:600;
    color:#111;
  }
  /* Smaller thumbnails on index/non-product pages */
  .recently-viewed-section.rv-index #dw-rv-grid {
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)) !important;
    gap: 12px !important;
  }
  .recently-viewed-section.rv-index .rv-card-img {
    aspect-ratio: 1/1;
  }
  .recently-viewed-section.rv-index .rv-card-info {
    padding: 8px 10px;
  }
  .recently-viewed-section.rv-index .rv-card-vendor {
    font-size: 9px;
  }
  .recently-viewed-section.rv-index .rv-card-title {
    font-size: 11px;
    -webkit-line-clamp: 1;
  }
  .recently-viewed-section.rv-index .rv-card-price {
    font-size: 11px;
  }
  @media(max-width:768px) {
    #dw-rv-grid {
      grid-template-columns:repeat(2,1fr) !important;
      gap:12px !important;
    }
    .rv-card-info { padding:8px 10px; }
    .rv-card-title { font-size:12px; }
  }
</style>

<script>
(function() {
  var STORAGE_KEY = 'dw_recently_viewed';
  var MAX_STORED = 20;
  var container = document.getElementById('dw-recently-viewed');
  var grid = document.getElementById('dw-rv-grid');
  if (!container || !grid) return;

  var maxShow = 6; // 2 rows of 3 (Steve 2026-07-13) — fixed at 6 regardless of the max_products setting
  var currentHandle = container.dataset.currentHandle || '';

  // === TRACK current product ===
  if (currentHandle && currentHandle.length > 0) {
    try {
      var stored = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
      // Remove if already exists (move to front)
      stored = stored.filter(function(h) { return h !== currentHandle; });
      // Add to front
      stored.unshift(currentHandle);
      // Trim to max
      if (stored.length > MAX_STORED) stored = stored.slice(0, MAX_STORED);
      localStorage.setItem(STORAGE_KEY, JSON.stringify(stored));
    } catch(e) {}
  }

  // === RENDER recently viewed ===
  try {
    var handles = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
    // Exclude current product
    handles = handles.filter(function(h) { return h !== currentHandle; });
    // Limit display
    handles = handles.slice(0, maxShow);

    if (handles.length < 1) return; // Don't show unless 2+ products

    container.style.display = 'block';
    // Add rv-index class on non-product pages for smaller thumbnails
    if (!currentHandle) container.classList.add('rv-index');

    var loaded = 0;
    var total = handles.length;
    var cards = {};

    handles.forEach(function(handle, index) {
      fetch('/products/' + handle + '.js')
        .then(function(r) { return r.ok ? r.json() : null; })
        .then(function(product) {
          loaded++;
          if (!product) { checkDone(); return; }
          // Skip products with no image (prevents blank cards)
          if (!product.featured_image) { checkDone(); return; }

          var img = product.featured_image || '';
          if (img && !img.startsWith('http')) img = 'https:' + img;
          // Resize to 400px for performance
          if (img) img = img.replace(/(\.[^.]+)$/, '_400x$1');

          // Price removed from recently viewed cards
          var vendor = product.vendor || '';
          var title = product.title || '';
          // Remove vendor from title if it's appended
          if (vendor && title.includes(' | ' + vendor)) {
            title = title.split(' | ' + vendor)[0];
          }

          var card = document.createElement('a');
          card.href = '/products/' + handle;
          card.className = 'rv-card';
          card.innerHTML =
            '<img class="rv-card-img" src="' + img + '" alt="' + title.replace(/"/g, '') + '" loading="lazy">' +
            '<div class="rv-card-info">' +
              '<p class="rv-card-vendor">' + vendor + '</p>' +
              '<p class="rv-card-title">' + title + '</p>' +
              
            '</div>';

          cards[index] = card;
          checkDone();
        })
        .catch(function() { loaded++; checkDone(); });
    });

    function checkDone() {
      if (loaded < total) return;
      // Insert in order
      for (var i = 0; i < total; i++) {
        if (cards[i]) grid.appendChild(cards[i]);
      }
    }

  } catch(e) {}
})();
</script>