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.2
  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 < 2){
  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. regularNotificationPanelItems = regularNotificationPanel.querySelector("#items");
  61.  
  62. lookedUpOrderOfPrecedence = false;
  63. importantNotifications.forEach(notification => {regularNotificationPanelItems.insertBefore(notification,findAppropriatePosition(notification))});
  64. regularNotificationPanel.style.display = "unset";
  65. }
  66.  
  67. function findAppropriatePosition(element){
  68. const elementParts = element.querySelector(".metadata.style-scope.ytd-notification-renderer > yt-formatted-string:nth-of-type(2)").textContent.split(" ");
  69. let nonNumberPartsRank = orderOfPrecedence.indexOf(elementParts.filter(e => isNaN(Number(e))).join(''));
  70. if (!lookedUpOrderOfPrecedence && nonNumberPartsRank == -1){
  71. orderOfPrecedence = getOrderOfPrecendence()
  72. lookedUpOrderOfPrecedence = true;
  73. nonNumberPartsRank = orderOfPrecedence.indexOf(elementParts.filter(e => isNaN(Number(e))).join(''));
  74. }
  75. const NumberParts = elementParts.find(e => !isNaN(Number(e)))
  76. 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));
  77. return rep;
  78. }
  79.  
  80. //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
  81.  
  82. function getOrderOfPrecendence(){
  83. const seen = new Set();
  84. 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(''));
  85. return nonNumberDateParts.filter(e => {if (seen.has(e)) return false; seen.add(e); return true;})
  86. }
  87.  
  88. function compareUploadDateText(date1, nonNumberPartsRank2,date2NumberParts){
  89. const date1Parts = date1.split(" ");
  90. const nonNumberPartsRank1 = orderOfPrecedence.indexOf(date1Parts.filter(e => isNaN(Number(e))).join(''));
  91. if (nonNumberPartsRank1 < nonNumberPartsRank2){
  92. return true;
  93. } else if (nonNumberPartsRank1 > nonNumberPartsRank2){
  94. return false;
  95. } else {
  96. return Number(date1Parts.find(e => !isNaN(Number(e)))) < date2NumberParts
  97. }
  98. }
  99. })();