app.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  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 subscribeButton = document.getElementById("subscribeButton");
  14. const subscribeForm = document.getElementById("subscribeForm");
  15. const errorField = document.getElementById("error");
  16. const subscribe = (topic) => {
  17. if (Notification.permission !== "granted") {
  18. Notification.requestPermission().then((permission) => {
  19. if (permission === "granted") {
  20. subscribeInternal(topic, 0);
  21. } else {
  22. showNotificationDeniedError();
  23. }
  24. });
  25. } else {
  26. subscribeInternal(topic, 0);
  27. }
  28. };
  29. const subscribeInternal = (topic, delaySec) => {
  30. setTimeout(() => {
  31. // Render list entry
  32. let topicEntry = document.getElementById(`topic-${topic}`);
  33. if (!topicEntry) {
  34. topicEntry = document.createElement('li');
  35. topicEntry.id = `topic-${topic}`;
  36. topicEntry.innerHTML = `${topic} <button onclick="test('${topic}')">Test</button> <button onclick="unsubscribe('${topic}')">Unsubscribe</button>`;
  37. topicsList.appendChild(topicEntry);
  38. }
  39. topicsHeader.style.display = '';
  40. // Open event source
  41. let eventSource = new EventSource(`${topic}/sse`);
  42. eventSource.onopen = () => {
  43. topicEntry.innerHTML = `${topic} <button onclick="test('${topic}')">Test</button> <button onclick="unsubscribe('${topic}')">Unsubscribe</button>`;
  44. delaySec = 0; // Reset on successful connection
  45. };
  46. eventSource.onerror = (e) => {
  47. const newDelaySec = (delaySec + 5 <= 15) ? delaySec + 5 : 15;
  48. topicEntry.innerHTML = `${topic} <i>(Reconnecting in ${newDelaySec}s ...)</i> <button disabled="disabled">Test</button> <button onclick="unsubscribe('${topic}')">Unsubscribe</button>`;
  49. eventSource.close()
  50. subscribeInternal(topic, newDelaySec);
  51. };
  52. eventSource.onmessage = (e) => {
  53. const event = JSON.parse(e.data);
  54. new Notification(event.message);
  55. };
  56. topics[topic] = eventSource;
  57. localStorage.setItem('topics', JSON.stringify(Object.keys(topics)));
  58. }, delaySec * 1000);
  59. };
  60. const unsubscribe = (topic) => {
  61. topics[topic].close();
  62. delete topics[topic];
  63. localStorage.setItem('topics', JSON.stringify(Object.keys(topics)));
  64. document.getElementById(`topic-${topic}`).remove();
  65. if (Object.keys(topics).length === 0) {
  66. topicsHeader.style.display = 'none';
  67. }
  68. };
  69. const test = (topic) => {
  70. fetch(`/${topic}`, {
  71. method: 'PUT',
  72. body: `This is a test notification for topic ${topic}!`
  73. })
  74. };
  75. const showError = (msg) => {
  76. errorField.innerHTML = msg;
  77. topicField.disabled = true;
  78. subscribeButton.disabled = true;
  79. };
  80. const showBrowserIncompatibleError = () => {
  81. showError("Your browser is not compatible to use the web-based desktop notifications.");
  82. };
  83. const showNotificationDeniedError = () => {
  84. showError("You have blocked desktop notifications for this website. Please unblock them and refresh to use the web-based desktop notifications.");
  85. };
  86. subscribeForm.onsubmit = function () {
  87. if (!topicField.value) {
  88. return false;
  89. }
  90. subscribe(topicField.value);
  91. topicField.value = "";
  92. return false;
  93. };
  94. // Disable Web UI if notifications of EventSource are not available
  95. if (!window["Notification"] || !window["EventSource"]) {
  96. showBrowserIncompatibleError();
  97. } else if (Notification.permission === "denied") {
  98. showNotificationDeniedError();
  99. }
  100. // Reset UI
  101. topicField.value = "";
  102. // Restore topics
  103. const storedTopics = localStorage.getItem('topics');
  104. if (storedTopics && Notification.permission === "granted") {
  105. const storedTopicsArray = JSON.parse(storedTopics)
  106. storedTopicsArray.forEach((topic) => { subscribeInternal(topic, 0); });
  107. if (storedTopicsArray.length === 0) {
  108. topicsHeader.style.display = 'none';
  109. }
  110. } else {
  111. topicsHeader.style.display = 'none';
  112. }