app.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  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. notifySound.play();
  56. new Notification(topic, {
  57. body: event.message,
  58. icon: '/static/img/favicon.png'
  59. });
  60. };
  61. topics[topic] = eventSource;
  62. localStorage.setItem('topics', JSON.stringify(Object.keys(topics)));
  63. }, delaySec * 1000);
  64. };
  65. const unsubscribe = (topic) => {
  66. topics[topic].close();
  67. delete topics[topic];
  68. localStorage.setItem('topics', JSON.stringify(Object.keys(topics)));
  69. document.getElementById(`topic-${topic}`).remove();
  70. if (Object.keys(topics).length === 0) {
  71. topicsHeader.style.display = 'none';
  72. }
  73. };
  74. const test = (topic) => {
  75. fetch(`/${topic}`, {
  76. method: 'PUT',
  77. body: `This is a test notification`
  78. })
  79. };
  80. const showError = (msg) => {
  81. errorField.innerHTML = msg;
  82. topicField.disabled = true;
  83. subscribeButton.disabled = true;
  84. };
  85. const showBrowserIncompatibleError = () => {
  86. showError("Your browser is not compatible to use the web-based desktop notifications.");
  87. };
  88. const showNotificationDeniedError = () => {
  89. showError("You have blocked desktop notifications for this website. Please unblock them and refresh to use the web-based desktop notifications.");
  90. };
  91. subscribeForm.onsubmit = function () {
  92. if (!topicField.value) {
  93. return false;
  94. }
  95. subscribe(topicField.value);
  96. topicField.value = "";
  97. return false;
  98. };
  99. // Disable Web UI if notifications of EventSource are not available
  100. if (!window["Notification"] || !window["EventSource"]) {
  101. showBrowserIncompatibleError();
  102. } else if (Notification.permission === "denied") {
  103. showNotificationDeniedError();
  104. }
  105. // Reset UI
  106. topicField.value = "";
  107. // Restore topics
  108. const storedTopics = localStorage.getItem('topics');
  109. if (storedTopics && Notification.permission === "granted") {
  110. const storedTopicsArray = JSON.parse(storedTopics)
  111. storedTopicsArray.forEach((topic) => { subscribeInternal(topic, 0); });
  112. if (storedTopicsArray.length === 0) {
  113. topicsHeader.style.display = 'none';
  114. }
  115. } else {
  116. topicsHeader.style.display = 'none';
  117. }