Share Google Slides controls

Share Google Slides controls, useful for meetings...

  1. // ==UserScript==
  2. // @name Share Google Slides controls
  3. // @namespace http://lostinbrittany.dev
  4. // @version 2024-07-15
  5. // @description Share Google Slides controls, useful for meetings...
  6. // @author Horacio Gonzalez <horacio.gonzalez@gmail.com>
  7. // @match https://docs.google.com/presentation/*
  8. // @icon https://lostinbrittany.org/favicon.ico
  9. // @grant none
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. /*jshint esversion: 11 */
  14.  
  15. (async function() {
  16. 'use strict';
  17. const {html, render} = await import('https://cdn.jsdelivr.net/npm/lit-html@3.1.4/+esm');
  18.  
  19. // Get the pathname of the current page
  20. const pathname = window.location.pathname;
  21.  
  22. const slidedeck = pathname.replace('/presentation/d/','').split('/')[0];
  23.  
  24. console.log(`Presentation Id: ${slidedeck}`);
  25.  
  26. let generatedQRCode = await getQRCode();
  27. showDialog();
  28.  
  29. async function getQRCode() {
  30. let resp = await fetch("https://shared-google-slides-control.cleverapps.io/qrcode", {
  31. method: 'POST',
  32. headers: {
  33. 'Content-Type': 'application/json',
  34. },
  35. body: JSON.stringify({url:`https://shared-google-slides-control.cleverapps.io/ui/#${slidedeck}`}),
  36. });
  37. let generatedQRCode = await resp.text();
  38. return generatedQRCode;
  39. }
  40.  
  41. function showDialog() {
  42. const dialog = html`<dialog id="shareDialog">
  43. <form method="dialog">
  44. <h1>Remote control URL</h1>
  45. <p>
  46. <a href="https://shared-google-slides-control.cleverapps.io/ui/#${slidedeck}">
  47. https://shared-google-slides-control.cleverapps.io/ui/#${slidedeck}
  48. </a>
  49. </p>
  50. <div style="display:flex;flex-flow:row;justify-content:center;"><img src="${generatedQRCode}"></div>
  51. <div style="display:flex;flex-flow:row;justify-content:center;gap:2rem;">
  52. <button autofocus id="copyDialog" style="padding:0.5rem;min-width:10rem">Copy</button>
  53. <button id="closeDialog" style="padding:0.5rem;min-width:10rem">Close</button>
  54. </div>
  55. </form>
  56. </dialog>`;
  57.  
  58. let dialogParent = document.createElement('div');
  59. document.body.appendChild(dialogParent);
  60. render(dialog, dialogParent);
  61. document.getElementById('shareDialog').showModal();
  62.  
  63. const closeButton = document.getElementById("closeDialog");
  64. closeButton.addEventListener("click", () => {
  65. document.getElementById('shareDialog').close();
  66. });
  67. const copyButton = document.getElementById("copyDialog");
  68. copyButton.addEventListener("click", () => {
  69. console.log(`Link: https://shared-google-slides-control.cleverapps.io/ui/#${slidedeck}`);
  70. navigator.clipboard.writeText(`https://shared-google-slides-control.cleverapps.io/ui/#${slidedeck}`);
  71. });
  72. }
  73.  
  74. // Function to send a keyboard event
  75. function sendKeyboardEvent(key, code, keyCode, element = document.body) {
  76. const event = new KeyboardEvent('keydown', {
  77. key: key,
  78. code: code,
  79. keyCode: keyCode,
  80. charCode: keyCode,
  81. which: keyCode,
  82. bubbles: true,
  83. cancelable: true
  84. });
  85.  
  86. element.dispatchEvent(event);
  87. console.log(`Sent keyboard event: ${key}`);
  88. }
  89.  
  90.  
  91. // URL of the WebSocket server
  92. const wsUrl = `wss://shared-google-slides-control.cleverapps.io/websocket/${slidedeck}`;
  93.  
  94.  
  95. let socket;
  96. let reconnectInterval = 1000; // Start with a 1 second delay
  97. const maxReconnectInterval = 30000; // Maximum delay of 30 seconds
  98.  
  99. function connectWebSocket() {
  100. socket = new WebSocket(wsUrl);
  101.  
  102. socket.addEventListener('open', function(event) {
  103. console.log('WebSocket connection opened:', event);
  104. reconnectInterval = 1000; // Reset reconnect interval on successful connection
  105.  
  106.  
  107. });
  108.  
  109. // Event listener for receiving messages from the server
  110. socket.addEventListener('message', function (event) {
  111. console.log('Message from server:', event.data);
  112.  
  113. switch (event.data) {
  114. case 'next':
  115. sendKeyboardEvent('ArrowRight', 'ArrowRight', 39);
  116. break;
  117. case 'previous':
  118. sendKeyboardEvent('ArrowLeft', 'ArrowLeft', 37);
  119. break;
  120.  
  121. }
  122. });
  123.  
  124. // Event listener for when the connection is closed
  125. socket.addEventListener('close', function (event) {
  126. console.log('WebSocket connection closed:', event);
  127. attemptReconnect();
  128. });
  129.  
  130. socket.addEventListener('error', function(event) {
  131. console.error('WebSocket error:', event);
  132. socket.close(); // Close the socket on error to trigger the reconnect logic
  133. });
  134. }
  135.  
  136. function attemptReconnect() {
  137. console.log(`Attempting to reconnect in ${reconnectInterval / 1000} seconds...`);
  138. setTimeout(function() {
  139. reconnectInterval = Math.min(reconnectInterval * 2, maxReconnectInterval); // Exponential backoff
  140. connectWebSocket();
  141. }, reconnectInterval);
  142. }
  143.  
  144. // Initial WebSocket connection
  145. connectWebSocket();
  146. })();