Remove Important YouTube Notifications

Removes the "Important" section in the YouTube notification menu, putting the notifications whithin it back to their appropriate position in the notification list.

  1. // ==UserScript==
  2. // @name Remove Important YouTube Notifications
  3. // @namespace greatest.deepsurf.us/en/users/1436613
  4. // @match https://www.youtube.com/*
  5. // @version 1.2.3
  6. // @license MIT
  7. // @author gosha305
  8. // @description Removes the "Important" section in the YouTube notification menu, putting the notifications whithin it back to their appropriate position in the notification list.
  9. // ==/UserScript==
  10.  
  11. let orderOfPrecedence = ["justnow", "secondsago", "minuteago", "minutesago", "hourago", "hoursago", "dayago", "daysago", "weekago", "weeksago", "monthago", "monthsago", "yearago", "yearsago"];
  12. let lookedUpOrderOfPrecedence = false;
  13. let regularNotificationPanel;
  14. let importantNotificationPanel;
  15.  
  16. const menuRemover = new CSSStyleSheet();
  17. document.adoptedStyleSheets.push(menuRemover);
  18.  
  19. let rule = menuRemover.cssRules[menuRemover.insertRule("#sections yt-multi-page-menu-section-renderer:has(ytd-notification-renderer),ytd-popup-container #sections:has(ytd-notification-renderer) #section-title {}")];
  20. rule.style.setProperty("display", "none");
  21.  
  22. (async function(){
  23. //to avoid infinitely waiting for the popup on pages with YouTube embeds
  24. if (!window.location.href.startsWith("https://www.youtube.com/")){
  25. return;
  26. }
  27.  
  28. let popupContainer;
  29. function findPopup(resolve){
  30. popupContainer = document.querySelector("ytd-popup-container")
  31. if (!popupContainer){
  32. setTimeout(() => findPopup(resolve), 200);
  33. } else {
  34. resolve();
  35. }
  36. }
  37. await new Promise(findPopup);
  38.  
  39. const popupObserver = new MutationObserver(function(){
  40. const notificationMenu = document.querySelector("ytd-popup-container")?.lastChild
  41. if (notificationMenu?.nodeName == "TP-YT-IRON-DROPDOWN"){
  42. const notificationObserver = new MutationObserver(function(mutationList){replaceNotifications(mutationList);});
  43. notificationObserver.observe(notificationMenu, {attributes: true});
  44. popupObserver.disconnect();
  45. }
  46. });
  47. popupObserver.observe(popupContainer, {childList: true, subtree:true})
  48.  
  49.  
  50. function replaceNotifications(mutationList){
  51. const notificationsMenus = document.querySelectorAll("yt-multi-page-menu-section-renderer:has(ytd-notification-renderer)");
  52. if (!notificationsMenus || !notificationsMenus.length){
  53. return;
  54. }
  55. const importantNotifications = notificationsMenus[0].querySelectorAll("#items > ytd-notification-renderer");
  56. if (!importantNotifications || !importantNotifications.length) {
  57. return;
  58. }
  59. regularNotificationPanel = notificationsMenus[1];
  60. if (!regularNotificationPanel){
  61. notificationsMenus[0].style.display = "unset";
  62. return;
  63. }
  64. regularNotificationPanelItems = regularNotificationPanel.querySelector("#items");
  65.  
  66. lookedUpOrderOfPrecedence = false;
  67. importantNotifications.forEach(notification => {regularNotificationPanelItems.insertBefore(notification,findAppropriatePosition(notification))});
  68. regularNotificationPanel.style.display = "unset";
  69. }
  70.  
  71. function findAppropriatePosition(element){
  72. const elementParts = element.querySelector(".metadata.style-scope.ytd-notification-renderer > yt-formatted-string:nth-of-type(2)").textContent.split(" ");
  73. let nonNumberPartsRank = orderOfPrecedence.indexOf(elementParts.filter(e => isNaN(Number(e))).join(''));
  74. if (!lookedUpOrderOfPrecedence && nonNumberPartsRank == -1){
  75. orderOfPrecedence = getOrderOfPrecendence()
  76. lookedUpOrderOfPrecedence = true;
  77. nonNumberPartsRank = orderOfPrecedence.indexOf(elementParts.filter(e => isNaN(Number(e))).join(''));
  78. }
  79. const NumberParts = elementParts.find(e => !isNaN(Number(e)))
  80. const rep = Array.from(regularNotificationPanel.querySelectorAll("ytd-notification-renderer")).find(e => !compareUploadDateText(e.querySelector(".metadata.style-scope.ytd-notification-renderer > yt-formatted-string:nth-of-type(2)").textContent, nonNumberPartsRank, NumberParts));
  81. return rep;
  82. }
  83.  
  84. //look up the order of notifications already in the panel to determine which words represent minutes/hours/days/months/years if the notifications aren't in English
  85.  
  86. function getOrderOfPrecendence(){
  87. const seen = new Set();
  88. const nonNumberDateParts = Array.from(regularNotificationPanel.querySelectorAll("ytd-notification-renderer .metadata.style-scope.ytd-notification-renderer > yt-formatted-string:nth-of-type(2)")).map(e => e.textContent.split(" ").filter(part => isNaN(Number(part))).join(''));
  89. return nonNumberDateParts.filter(e => {if (seen.has(e)) return false; seen.add(e); return true;})
  90. }
  91.  
  92. function compareUploadDateText(date1, nonNumberPartsRank2,date2NumberParts){
  93. const date1Parts = date1.split(" ");
  94. const nonNumberPartsRank1 = orderOfPrecedence.indexOf(date1Parts.filter(e => isNaN(Number(e))).join(''));
  95. if (nonNumberPartsRank1 < nonNumberPartsRank2){
  96. return true;
  97. } else if (nonNumberPartsRank1 > nonNumberPartsRank2){
  98. return false;
  99. } else {
  100. return Number(date1Parts.find(e => !isNaN(Number(e)))) < date2NumberParts
  101. }
  102. }
  103. })();