Rewrite reddit links to use the Narwhal 2 URI scheme

4/5/2025, 11:24:00 AM

  1. // ==UserScript==
  2. // @name Rewrite reddit links to use the Narwhal 2 URI scheme
  3. // @namespace codes.heals.violentmonkey.narwhal2-opener
  4. // @match https://*/*
  5. // @require https://update.greasyfork.org/scripts/446257/1059316/waitForKeyElements%20utility%20function.js
  6. // @grant GM_getValue
  7. // @grant GM_setValue
  8. // @grant GM_registerMenuCommand
  9. // @grant GM_unregisterMenuCommand
  10. // @grant GM_addValueChangeListener
  11. // @grant window.close
  12. // @run-at document-start
  13. // @version 1.3
  14. // @author HealsCodes
  15. // @description 4/5/2025, 11:24:00 AM
  16. // @license GPL3
  17. // ==/UserScript==
  18.  
  19. const redditLinkDomains = ['https://reddit.com', 'https://m.reddit.com', 'https://www.reddit.com', 'https://reddit.app.link'];
  20.  
  21. function redirectHrefAndCloseIfPossible() {
  22. // handle a new tab that is about to load a reddit-url (opened from another app for example)
  23. for(const prefix of redditLinkDomains) {
  24. if (window.location.href.startsWith(prefix)) {
  25. let encodedUrl = encodeURIComponent(window.location.href);
  26. window.location.href = 'narwhal://open-url/' + encodedUrl;
  27. setTimeout(() => window.close(), 500);
  28. return true;
  29. }
  30. }
  31.  
  32. return false;
  33. }
  34.  
  35. function updatePageLinks(element) {
  36. 'use strict';
  37.  
  38. function updateTextContent(textContent) {
  39. if (GM_getValue('addWhaleMark', true)) {
  40. return textContent + ' ' + String.fromCodePoint(0x1f433);
  41. }
  42. return textContent;
  43. }
  44.  
  45. // Function to extract the original URL and redirect to Narwhal
  46. function redirectToNarwhal(link) {
  47. var originalUrl = link.href;
  48. var originalTextContent = link.textContent;
  49.  
  50. if (originalUrl.startsWith('narwhal://')) {
  51. // already processed
  52. return;
  53. }
  54.  
  55. for(const prefix of redditLinkDomains) {
  56. if (originalUrl.startsWith(prefix)) {
  57. let encodedUrl = encodeURIComponent(originalUrl);
  58. let textContent = updateTextContent(originalTextContent);
  59. Object.assign(link, {
  60. textContent: textContent,
  61. href: 'narwhal://open-url/' + encodedUrl
  62. });
  63. return;
  64. }
  65. }
  66. }
  67.  
  68. if (element !== undefined) {
  69. redirectToNarwhal(element);
  70. } else {
  71. document.querySelectorAll(`a[href*="reddit."]`).forEach(link => redirectToNarwhal(link));
  72. }
  73. }
  74.  
  75. function menuCommandTitle(state) {
  76. let checked = (state != null) ? state : GM_getValue('addWhaleMark', true);
  77. return String.fromCodePoint(checked ? 0x2611 : 0x2610) + ' Add ' + String.fromCodePoint(0x1f433) + ' to Narwhal links';
  78. }
  79.  
  80. let valueChangedListener = GM_addValueChangeListener('addWhaleMark', function(name, oldVal, newVal, remote) {
  81. GM_unregisterMenuCommand(menuCommandTitle(oldVal));
  82. GM_registerMenuCommand(menuCommandTitle(newVal), (_) => GM_setValue('addWhaleMark', !GM_getValue('addWhaleMark', true)));
  83. });
  84.  
  85.  
  86. (function () {
  87. // move straight to Narwhal is we're already on reddit
  88. if (redirectHrefAndCloseIfPossible()) {
  89. return;
  90. }
  91. // register the context menu handler
  92. GM_registerMenuCommand(menuCommandTitle(null), (_) => GM_setValue('addWhaleMark', !GM_getValue('addWhaleMark', true)));
  93. // run on ajax updates
  94. waitForKeyElements(`a[href*="reddit."]`, (element) => updatePageLinks(), false);
  95. // run on initial page load
  96. document.updatePageLinks(undefined);
  97. })();