app.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. /**
  2. * Hello, dear curious visitor. I am not a web-guy, so please don't judge my horrible JS code.
  3. * In fact, please do tell me about all the things I did wrong and that I could improve. I've been trying
  4. * to read up on modern JS, but it's just a little much.
  5. *
  6. * Feel free to open tickets at https://github.com/binwiederhier/ntfy/issues. Thank you!
  7. */
  8. /* All the things */
  9. let topics = {};
  10. const topicsHeader = document.getElementById("topicsHeader");
  11. const topicsList = document.getElementById("topicsList");
  12. const topicField = document.getElementById("topicField");
  13. const notifySound = document.getElementById("notifySound");
  14. const subscribeButton = document.getElementById("subscribeButton");
  15. const subscribeForm = document.getElementById("subscribeForm");
  16. const errorField = document.getElementById("error");
  17. const subscribe = (topic) => {
  18. if (Notification.permission !== "granted") {
  19. Notification.requestPermission().then((permission) => {
  20. if (permission === "granted") {
  21. subscribeInternal(topic, 0);
  22. } else {
  23. showNotificationDeniedError();
  24. }
  25. });
  26. } else {
  27. subscribeInternal(topic, 0);
  28. }
  29. };
  30. const subscribeInternal = (topic, delaySec) => {
  31. setTimeout(() => {
  32. // Render list entry
  33. let topicEntry = document.getElementById(`topic-${topic}`);
  34. if (!topicEntry) {
  35. topicEntry = document.createElement('li');
  36. topicEntry.id = `topic-${topic}`;
  37. topicEntry.innerHTML = `${topic} <button onclick="test('${topic}')">Test</button> <button onclick="unsubscribe('${topic}')">Unsubscribe</button>`;
  38. topicsList.appendChild(topicEntry);
  39. }
  40. topicsHeader.style.display = '';
  41. // Open event source
  42. let eventSource = new EventSource(`${topic}/sse`);
  43. eventSource.onopen = () => {
  44. topicEntry.innerHTML = `${topic} <button onclick="test('${topic}')">Test</button> <button onclick="unsubscribe('${topic}')">Unsubscribe</button>`;
  45. delaySec = 0; // Reset on successful connection
  46. };
  47. eventSource.onerror = (e) => {
  48. const newDelaySec = (delaySec + 5 <= 15) ? delaySec + 5 : 15;
  49. topicEntry.innerHTML = `${topic} <i>(Reconnecting in ${newDelaySec}s ...)</i> <button disabled="disabled">Test</button> <button onclick="unsubscribe('${topic}')">Unsubscribe</button>`;
  50. eventSource.close()
  51. subscribeInternal(topic, newDelaySec);
  52. };
  53. eventSource.onmessage = (e) => {
  54. const event = JSON.parse(e.data);
  55. new Notification(event.message);
  56. notifySound.play();
  57. };
  58. topics[topic] = eventSource;
  59. localStorage.setItem('topics', JSON.stringify(Object.keys(topics)));
  60. }, delaySec * 1000);
  61. };
  62. const unsubscribe = (topic) => {
  63. topics[topic].close();
  64. delete topics[topic];
  65. localStorage.setItem('topics', JSON.stringify(Object.keys(topics)));
  66. document.getElementById(`topic-${topic}`).remove();
  67. if (Object.keys(topics).length === 0) {
  68. topicsHeader.style.display = 'none';
  69. }
  70. };
  71. const test = (topic) => {
  72. fetch(`/${topic}`, {
  73. method: 'PUT',
  74. body: `This is a test notification for topic ${topic}!`
  75. })
  76. };
  77. const showError = (msg) => {
  78. errorField.innerHTML = msg;
  79. topicField.disabled = true;
  80. subscribeButton.disabled = true;
  81. };
  82. const showBrowserIncompatibleError = () => {
  83. showError("Your browser is not compatible to use the web-based desktop notifications.");
  84. };
  85. const showNotificationDeniedError = () => {
  86. showError("You have blocked desktop notifications for this website. Please unblock them and refresh to use the web-based desktop notifications.");
  87. };
  88. subscribeForm.onsubmit = function () {
  89. if (!topicField.value) {
  90. return false;
  91. }
  92. subscribe(topicField.value);
  93. topicField.value = "";
  94. return false;
  95. };
  96. // Disable Web UI if notifications of EventSource are not available
  97. if (!window["Notification"] || !window["EventSource"]) {
  98. showBrowserIncompatibleError();
  99. } else if (Notification.permission === "denied") {
  100. showNotificationDeniedError();
  101. }
  102. // Reset UI
  103. topicField.value = "";
  104. // Restore topics
  105. const storedTopics = localStorage.getItem('topics');
  106. if (storedTopics && Notification.permission === "granted") {
  107. const storedTopicsArray = JSON.parse(storedTopics)
  108. storedTopicsArray.forEach((topic) => { subscribeInternal(topic, 0); });
  109. if (storedTopicsArray.length === 0) {
  110. topicsHeader.style.display = 'none';
  111. }
  112. } else {
  113. topicsHeader.style.display = 'none';
  114. }