notifications.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. 'use strict';
  2. var notification_data = JSON.parse(document.getElementById('notification_data').textContent);
  3. /** Boolean meaning 'some tab have stream' */
  4. const STORAGE_KEY_STREAM = 'stream';
  5. /** Number of notifications. May be increased or reset */
  6. const STORAGE_KEY_NOTIF_COUNT = 'notification_count';
  7. var notifications, delivered;
  8. var notifications_mock = { close: function () { } };
  9. function get_subscriptions() {
  10. helpers.xhr('GET', '/api/v1/auth/subscriptions', {
  11. retries: 5,
  12. entity_name: 'subscriptions'
  13. }, {
  14. on200: create_notification_stream
  15. });
  16. }
  17. function create_notification_stream(subscriptions) {
  18. // sse.js can't be replaced to EventSource in place as it lack support of payload and headers
  19. // see https://developer.mozilla.org/en-US/docs/Web/API/EventSource/EventSource
  20. notifications = new SSE(
  21. '/api/v1/auth/notifications', {
  22. withCredentials: true,
  23. payload: 'topics=' + subscriptions.map(function (subscription) { return subscription.authorId; }).join(','),
  24. headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
  25. });
  26. delivered = [];
  27. var start_time = Math.round(new Date() / 1000);
  28. notifications.onmessage = function (event) {
  29. if (!event.id) return;
  30. var notification = JSON.parse(event.data);
  31. console.info('Got notification:', notification);
  32. // Ignore not actual and delivered notifications
  33. if (start_time > notification.published || delivered.includes(notification.videoId)) return;
  34. delivered.push(notification.videoId);
  35. let notification_count = helpers.storage.get(STORAGE_KEY_NOTIF_COUNT) || 0;
  36. notification_count++;
  37. helpers.storage.set(STORAGE_KEY_NOTIF_COUNT, notification_count);
  38. update_ticker_count();
  39. // permission for notifications handled on settings page. JS handler is in handlers.js
  40. if (window.Notification && Notification.permission === 'granted') {
  41. var notification_text = notification.liveNow ? notification_data.live_now_text : notification_data.upload_text;
  42. notification_text = notification_text.replace('`x`', notification.author);
  43. var system_notification = new Notification(notification_text, {
  44. body: notification.title,
  45. icon: '/ggpht' + new URL(notification.authorThumbnails[2].url).pathname,
  46. img: '/ggpht' + new URL(notification.authorThumbnails[4].url).pathname
  47. });
  48. system_notification.onclick = function (e) {
  49. open('/watch?v=' + notification.videoId, '_blank');
  50. };
  51. }
  52. };
  53. notifications.addEventListener('error', function (e) {
  54. console.warn('Something went wrong with notifications, trying to reconnect...');
  55. notifications = notifications_mock;
  56. setTimeout(get_subscriptions, 1000);
  57. });
  58. notifications.stream();
  59. }
  60. function update_ticker_count() {
  61. var notification_ticker = document.getElementById('notification_ticker');
  62. const notification_count = helpers.storage.get(STORAGE_KEY_STREAM);
  63. if (notification_count > 0) {
  64. notification_ticker.innerHTML =
  65. '<span id="notification_count">' + notification_count + '</span> <i class="icon ion-ios-notifications"></i>';
  66. } else {
  67. notification_ticker.innerHTML =
  68. '<i class="icon ion-ios-notifications-outline"></i>';
  69. }
  70. }
  71. function start_stream_if_needed() {
  72. // random wait for other tabs set 'stream' flag
  73. setTimeout(function () {
  74. if (!helpers.storage.get(STORAGE_KEY_STREAM)) {
  75. // if no one set 'stream', set it by yourself and start stream
  76. helpers.storage.set(STORAGE_KEY_STREAM, true);
  77. notifications = notifications_mock;
  78. get_subscriptions();
  79. }
  80. }, Math.random() * 1000 + 50); // [0.050 .. 1.050) second
  81. }
  82. addEventListener('storage', function (e) {
  83. if (e.key === STORAGE_KEY_NOTIF_COUNT)
  84. update_ticker_count();
  85. // if 'stream' key was removed
  86. if (e.key === STORAGE_KEY_STREAM && !helpers.storage.get(STORAGE_KEY_STREAM)) {
  87. if (notifications) {
  88. // restore it if we have active stream
  89. helpers.storage.set(STORAGE_KEY_STREAM, true);
  90. } else {
  91. start_stream_if_needed();
  92. }
  93. }
  94. });
  95. addEventListener('load', function () {
  96. var notification_count_el = document.getElementById('notification_count');
  97. var notification_count = notification_count_el ? parseInt(notification_count_el.textContent) : 0;
  98. helpers.storage.set(STORAGE_KEY_NOTIF_COUNT, notification_count);
  99. if (helpers.storage.get(STORAGE_KEY_STREAM))
  100. helpers.storage.remove(STORAGE_KEY_STREAM);
  101. start_stream_if_needed();
  102. });
  103. addEventListener('unload', function () {
  104. // let chance to other tabs to be a streamer via firing 'storage' event
  105. if (notifications) helpers.storage.remove(STORAGE_KEY_STREAM);
  106. });