Gab: Quick Share

Adds quick share button to each gab post. Clicking results in copying text with a link to that post into clipboard.

  1. // ==UserScript==
  2. // @name Gab: Quick Share
  3. // @version 3
  4. // @grant none
  5. // @require https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js
  6. // @match https://*.gab.com/*
  7. // @match https://*.gab.ai/*
  8. // @author monnef
  9. // @description Adds quick share button to each gab post. Clicking results in copying text with a link to that post into clipboard.
  10. // @namespace monnef.eu
  11. // ==/UserScript==
  12.  
  13. const debug = false;
  14.  
  15. const tagCl = 'gqs';
  16. const qsCl = tagCl + '-qs';
  17. const lTag = '[GQS]';
  18.  
  19. const dLog = (...x) => { if (debug) console.debug(lTag, ...x); };
  20. const log = (...x) => { console.log(lTag, ...x); }
  21. const logErr = (...x) => { console.error(lTag, ...x); }
  22.  
  23. const addStyle = (style) => {
  24. style = style instanceof Array ? style.join('\n') : style;
  25. $('head').append($('<style type="text/css">' + style + '</style>'));
  26. }
  27.  
  28. addStyle(`
  29. /* Gab: Quick Share by monnef */
  30.  
  31. .${qsCl} {
  32. float: right;
  33. margin-right: 1em;
  34. cursor: pointer;
  35. transform: scale(1.2);
  36. color: #666 !important;
  37. }
  38. .${qsCl}:hover {
  39. text-decoration: none !important;
  40. color: #fff !important;
  41. }
  42. .detailed-status__wrapper .${qsCl} {
  43. margin: 0;
  44. flex-grow: 1;
  45. text-align: center;
  46. }
  47. `);
  48.  
  49. const qsText = '🗲';
  50.  
  51. const qsFlashText = (el, text) => {
  52. el.text(text);
  53. setTimeout(() => el.text(qsText), 1000);
  54. };
  55.  
  56. const extractTextForQS = (el) => {
  57. const innerEls = el.find('.status__content p');
  58. const text = innerEls.map((i, x) => {
  59. const el = $(x);
  60. dLog('1, i=', i, 'x=', x, 'el=', el);
  61. return el.contents().map((j, y) => {
  62. dLog('2, j=', j, 'y=', y);
  63. if (y.nodeType === Node.TEXT_NODE) return y.nodeValue;
  64. const el2 = $(y);
  65. dLog('2. el2=', el2);
  66. if (y.tagName === 'A') {
  67. if (el2.hasClass('hashtag')
  68. || el2.hasClass('inner-post-mention')
  69. ) {
  70. return el2.text();
  71. };
  72. return el2.attr('href');
  73. } else if(y.tagName === 'IMG' && el2.hasClass('emojione')) {
  74. return el2.attr('alt');
  75. } else if (y.tagName === 'BR') {
  76. return '\n';
  77. }
  78. return el2.text();
  79. }).toArray().concat('\n');
  80. }).toArray().join('');
  81. return text;
  82. };
  83.  
  84. const handleQuickShare = (el) => {
  85. const qsEl = el.find(`.${qsCl}`);
  86. const postLinkEls = el.find('a.status__relative-time, a.detailed-status__datetime').filter((i, x) => x.href.includes('/posts/'));
  87. if (postLinkEls.length != 1) {
  88. logErr('failed to locate a post link', postLinkEls);
  89. qsFlashText(qsEl, '❎');
  90. } else {
  91. const text = extractTextForQS(el);
  92. const postLinkHref = postLinkEls.attr('href');
  93. const link = postLinkHref.startsWith('http') ? postLinkHref : `https://gab.com${postLinkHref}`;
  94. const toCopy = `${text}\n${link}`;
  95. dLog({link, text, toCopy});
  96. log('copying to clipboard:\n' + toCopy);
  97. navigator.clipboard.writeText(toCopy)
  98. .then(() => {
  99. log('copy ok');
  100. qsFlashText(qsEl, '📋✅');
  101. })
  102. .catch(e => {
  103. logErr('copy failed', e);
  104. // alert(`Copying to clipboard failed: ${e}`);
  105. qsFlashText(qsEl, '📋❎');
  106. });
  107. }
  108. };
  109.  
  110. const processOneGab = (el) => {
  111. const qsEl = $('<a/>').text(qsText).addClass(qsCl).attr('title', 'Quick Share\n(user-script by monnef)').click(() => handleQuickShare(el));
  112. el.find('.status__action-bar .status__action-bar__counter:last, .detailed-status__button:last').after(qsEl);
  113. };
  114.  
  115. const work = () => {
  116. $('div.status__wrapper, div.detailed-status__wrapper').each((idx, rEl) => {
  117. const el = $(rEl);
  118. if(el.hasClass(tagCl)) return;
  119. el.addClass(tagCl);
  120. processOneGab(el);
  121. });
  122. setTimeout(work, 500);
  123. }
  124.  
  125. $(() => {
  126. const suf = ' background-color: black; font-size: 150%;';
  127. console.info('%c' + lTag + ' 🐸👌 %cGab: Quick Share %cby 🔸%cmonnef🔸%c [🍍]',
  128. 'color: gray;' + suf,
  129. 'color: lime;'+ suf,
  130. 'color: white;' + suf,
  131. 'color: magenta;' + suf,
  132. 'color: gray;' + suf,
  133. );
  134. work();
  135. });