How to make MemberScript upvoting work with Finsweet CMS Load pagination when combining save and upvote features? Answered

Post author
James Bishop

Hi :wave:. I’m trying to combine MemberScript #106 (saving/unsaving) with MemberScript #62 (upvoting) in the same collection list, so users can both save and upvote items in a directory.

I need to use Finsweet CMS Load to get saving/unsaving to work, but when it’s enabled, upvoting breaks — only the first item in the list can be upvoted, and the rest become unclickable. If I disable pagination (which Finsweet CMS Load requires), upvoting works fine. But as soon as pagination is re-enabled, upvoting stops working again.

Is there anyone who can help with a fix or update the script maybe? Or suggest a workaround? Thanks! 🙏

Comments

2 comments

  • Comment author
    A J

    Hey James Bishop, you could explore another tool named Jetboost for the upvoting feature and see if it works with the existing setup of saving and unsaving with CMS load.If you want it to be natively solved within the existing stack of Memberstack, Webflow and Make. let me try combining these use-cases and test what's possible.

    I have modified the upvote button script and have tested the combined use-case in a dummy site, it seems to work as required.You can clone your page and try replacing the upvote button script in the body section of the code with this new one:

    <!-- 💙 MEMBERSCRIPT #62 v0.2 💙 UPVOTE FORM -->
    <script> 
      document.addEventListener('DOMContentLoaded', function() {
        const memberstack = window.$memberstackDom;
        const upvoteButtons = document.querySelectorAll('[ms-code="upvote-button"]');
        const upvoteForms = document.querySelectorAll('[ms-code="upvote-form"]');
        const upvotedValues = document.querySelectorAll('[ms-code="upvoted-value"]');
        const upvoteCounts = document.querySelectorAll('[ms-code="upvote-count"]');
        let clickTimeout; // Variable to store the timer
        let lastClickedButton = null; // Variable to store the last clicked button
    
        // Function to handle upvote button click
        function handleUpvoteButtonClick(event) {
          event.preventDefault();
          const button = event.currentTarget;
    
          // Clear the timer if the same button is clicked
          if (button === lastClickedButton) {
            clearTimeout(clickTimeout);
          }
          
          lastClickedButton = button; // Store the reference to the currently clicked button
    
          // Set a new timer
          clickTimeout = setTimeout(function() {
            const form = button.closest('form');
            const cmsId = button.getAttribute('data-cms-id');
            const upvotedValue = form.querySelector('[ms-code="upvoted-value"]');
            const upvoteCount = form.querySelector('[ms-code="upvote-count"]');
    
            if (button.classList.contains('is-true')) {
              // Remove upvote
              button.classList.remove('is-true');
              upvotedValue.value = 'false';
              upvoteCount.textContent = parseInt(upvoteCount.textContent) - 1;
    
              memberstack.getMemberJSON()
                .then(function(memberData) {
                  if (memberData.data && memberData.data.upvotes) {
                    const upvotes = memberData.data.upvotes;
                    const index = upvotes.indexOf(cmsId);
                    if (index !== -1) {
                      upvotes.splice(index, 1);
                      memberstack.updateMemberJSON({ json: memberData.data });
                    }
                  }
                })
                .catch(function(error) {
                  console.error('Error retrieving/updating member data:', error);
                });
            } else {
              // Add upvote
              button.classList.add('is-true');
              upvotedValue.value = 'true';
              upvoteCount.textContent = parseInt(upvoteCount.textContent) + 1;
    
              memberstack.getMemberJSON()
                .then(function(memberData) {
                  memberData.data = memberData.data || {};
                  memberData.data.upvotes = memberData.data.upvotes || [];
                  memberData.data.upvotes.push(cmsId);
                  memberstack.updateMemberJSON({ json: memberData.data });
                })
                .catch(function(error) {
                  console.error('Error retrieving/updating member data:', error);
                });
            }
    
            // Make the API call
            fetch(form.action, {
              method: form.method,
              headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
              },
              body: new URLSearchParams(new FormData(form))
            })
              .then(function(response) {
                if (response.ok) {
                  // Handle successful API response
                  return response.json();
                } else {
                  // Handle API error
                  throw new Error('API Error');
                }
              })
              .then(function(data) {
                // Handle API response to update vote count
                upvoteCount.textContent = data.upvoteCount; // Replace with the actual property holding the updated vote count
              })
              .catch(function(error) {
                console.error('API Error:', error);
              });
          }, 200); // 0.2 seconds
        }
    
        // Attach event listeners to upvote buttons
        upvoteButtons.forEach(function(button) {
          button.addEventListener('click', handleUpvoteButtonClick);
        });
        
        function checkMemberUpvotes(upvoteButtons) {
          // Check if member has upvotes on page load
          memberstack.getMemberJSON()
            .then(function(memberData) {
              if (memberData.data && memberData.data.upvotes) {
                const upvotes = memberData.data.upvotes;
                upvoteButtons.forEach(function(button) {
                  const cmsId = button.getAttribute('data-cms-id');
                  if (upvotes.includes(cmsId)) {
                    button.classList.add('is-true');
                    const form = button.closest('form');
                    const upvotedValue = form.querySelector('[ms-code="upvoted-value"]');
                    upvotedValue.value = 'true';
                  }
                });
              }
            })
            .catch(function(error) {
              console.error('Error retrieving member data:', error);
            });
        }
        
        checkMemberUpvotes(upvoteButtons);
      
     // Set up a MutationObserver
        const observer = new MutationObserver((mutations) => {
          mutations.forEach((mutation) => {
            if (mutation.type === 'childList') {
    
              mutation.addedNodes.forEach((node) => {
                if (node.nodeType === Node.ELEMENT_NODE) {
                  const newUpvoteButtons = node.querySelectorAll('[ms-code="upvote-button"]');
                  const memberIdInputs = node.querySelectorAll('input[data-ms-member="id"]');              
                  let memberId;
                  if (localStorage.getItem("_ms-mem")) {
                    const memberData = JSON.parse(localStorage.getItem("_ms-mem"));
                    memberId = memberData.id;
                  }
                  checkMemberUpvotes(newUpvoteButtons);
                  newUpvoteButtons.forEach(function(button) {
                    button.addEventListener('click', handleUpvoteButtonClick);                
                  });  
                   memberIdInputs.forEach(function(input) {
                    if (memberId) {
                      input.value = memberId;                  
                    }
                  });
                }
              });
            }
          });
        });
    
        // Start observing the document
        observer.observe(document.body, { childList: true, subtree: true });
        
      });
    </script>
    

    Once done, save and publish it. This should ideally work as the results shown below. Hope this helps.

    0
  • Comment author
    James Bishop

    Boom, works a treat. 👊 Thank you so much!! Didn’t want to go down the Jetboost route coz it doesnt play nicely at all with Attributes so this is ideal

    0

Please sign in to leave a comment.