mydealz Deal Update Indicator

Färbt den Dealbilderhintergrund anhand der erfolgten Updates ein

  1. // ==UserScript==
  2. // @name mydealz Deal Update Indicator
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0
  5. // @description Färbt den Dealbilderhintergrund anhand der erfolgten Updates ein
  6. // @author MD928835
  7. // @license MIT
  8. // @match https://www.mydealz.de/profile/*/deals*
  9. // @grant GM_xmlhttpRequest
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. // GraphQL query to fetch thread updates
  16. const UPDATE_QUERY = `query getThread($filter: IDFilter!) {
  17. thread(threadId: $filter) {
  18. threadUpdates {
  19. createdAt
  20. }
  21. }
  22. }`;
  23.  
  24. // Function to fetch updates for a thread
  25. async function fetchThreadUpdates(threadId) {
  26. return new Promise((resolve) => {
  27. GM_xmlhttpRequest({
  28. method: 'POST',
  29. url: 'https://www.mydealz.de/graphql',
  30. headers: {
  31. 'Content-Type': 'application/json'
  32. },
  33. data: JSON.stringify({
  34. query: UPDATE_QUERY,
  35. variables: { filter: { eq: threadId } }
  36. }),
  37. onload: function(response) {
  38. try {
  39. const result = JSON.parse(response.responseText);
  40. resolve(result.data?.thread?.threadUpdates || []);
  41. } catch (e) {
  42. console.error('Error parsing response', e);
  43. resolve([]);
  44. }
  45. },
  46. onerror: function(error) {
  47. console.error('Error fetching updates', error);
  48. resolve([]);
  49. }
  50. });
  51. });
  52. }
  53.  
  54. // Format date for display
  55. function formatUpdateDate(timestamp) {
  56. const date = new Date(timestamp * 1000);
  57. return date.toLocaleDateString('de-DE') + ' ' +
  58. date.toLocaleTimeString('de-DE', {hour: '2-digit', minute: '2-digit'});
  59. }
  60.  
  61. // Process each thread card
  62. async function processCard(card) {
  63. const article = card.closest('article');
  64. if (!article) return;
  65.  
  66. const threadIdMatch = article.id.match(/thread_(\d+)/);
  67. if (!threadIdMatch) return;
  68.  
  69. const threadId = threadIdMatch[1];
  70. const imageContainer = card.querySelector('.threadListCard-image');
  71. if (!imageContainer) return;
  72.  
  73. // Fetch updates for this thread
  74. const updates = await fetchThreadUpdates(threadId);
  75. const updateCount = updates.length;
  76.  
  77. // Sort updates by date (newest first)
  78. const sortedUpdates = [...updates].sort((a, b) => b.createdAt - a.createdAt);
  79.  
  80. // Remove existing classes
  81. imageContainer.classList.remove(
  82. 'update-0', 'update-1', 'update-2', 'update-3'
  83. );
  84.  
  85. // Add appropriate class based on update count
  86. if (updateCount > 0 && updateCount <= 3) {
  87. imageContainer.classList.add(`update-${updateCount}`);
  88.  
  89. // Add tooltip with update dates
  90. if (updateCount > 0) {
  91. const tooltipText = sortedUpdates.map(update =>
  92. formatUpdateDate(update.createdAt)
  93. ).join('\n');
  94.  
  95. imageContainer.setAttribute('title', `Updates:\n${tooltipText}`);
  96. }
  97. }
  98. }
  99.  
  100. // Add CSS styles
  101. const style = document.createElement('style');
  102. style.textContent = `
  103. .threadListCard-image.update-1 {
  104. background-color: #e8f5e9 !important; /* Light green */
  105. }
  106.  
  107. .threadListCard-image.update-2 {
  108. background-color: #fff9c4 !important; /* Light yellow */
  109. }
  110.  
  111. .threadListCard-image.update-3 {
  112. background-color: #ffcdd2 !important; /* Light red */
  113. }
  114.  
  115. .threadListCard-image[title] {
  116. cursor: help;
  117. }
  118. `;
  119. document.head.appendChild(style);
  120.  
  121. // Process all cards on page load
  122. async function processAllCards() {
  123. const cards = document.querySelectorAll('.threadListCard');
  124. for (const card of cards) {
  125. await processCard(card);
  126. }
  127. }
  128.  
  129. // Run initially
  130. processAllCards();
  131.  
  132. // Observe DOM changes for dynamic content
  133. const observer = new MutationObserver((mutations) => {
  134. mutations.forEach((mutation) => {
  135. mutation.addedNodes.forEach((node) => {
  136. if (node.nodeType === 1) { // Element node
  137. const cards = node.querySelectorAll ?
  138. node.querySelectorAll('.threadListCard') : [];
  139. cards.forEach(processCard);
  140. }
  141. });
  142. });
  143. });
  144.  
  145. observer.observe(document.body, {
  146. childList: true,
  147. subtree: true
  148. });
  149. })();