custom.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. $(document).ready(function () {
  2. function cleanupStaleModalState() {
  3. if (!document.querySelector('.modal.show')) {
  4. document.querySelectorAll('.modal-backdrop').forEach(function (backdrop) {
  5. backdrop.remove();
  6. });
  7. document.body.classList.remove('modal-open');
  8. document.body.style.removeProperty('padding-right');
  9. document.body.style.removeProperty('overflow');
  10. }
  11. }
  12. function getNotificationBadge() {
  13. return document.getElementById('notification-badge');
  14. }
  15. function updateNotificationBadge(count) {
  16. const badge = getNotificationBadge();
  17. if (!badge) {
  18. return;
  19. }
  20. const safeCount = Math.max(0, parseInt(count || 0, 10));
  21. badge.dataset.count = String(safeCount);
  22. badge.classList.toggle('d-none', safeCount === 0);
  23. }
  24. function incrementNotificationBadge() {
  25. const badge = getNotificationBadge();
  26. if (!badge) {
  27. return;
  28. }
  29. const current = parseInt(badge.dataset.count || '0', 10);
  30. updateNotificationBadge(current + 1);
  31. }
  32. function markNotificationRead(notificationId) {
  33. if (!notificationId || !window.notificationsReadUrlTemplate) {
  34. return;
  35. }
  36. fetch(window.notificationsReadUrlTemplate.replace('__id__', String(notificationId)), {
  37. method: 'POST',
  38. headers: {
  39. 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
  40. 'Accept': 'application/json',
  41. },
  42. keepalive: true,
  43. })
  44. .then(function (response) {
  45. if (!response.ok) {
  46. return null;
  47. }
  48. return response.json();
  49. })
  50. .then(function (data) {
  51. if (data && typeof data.unread !== 'undefined') {
  52. updateNotificationBadge(data.unread);
  53. }
  54. })
  55. .catch(function () {
  56. // noop
  57. });
  58. }
  59. function appendWsPopup(notification) {
  60. const container = document.getElementById('ws-notification-container');
  61. if (!container || !notification) {
  62. return;
  63. }
  64. const popup = document.createElement('div');
  65. popup.className = 'ws-notification-popup';
  66. popup.dataset.id = String(notification.id || '');
  67. const safeTitle = notification.title || 'Уведомление';
  68. const bodyHtml = notification.message_html || notification.message || '';
  69. popup.innerHTML =
  70. '<div class="ws-notification-header">' +
  71. '<strong>' + safeTitle + '</strong>' +
  72. '<button type="button" class="btn-close btn-sm ws-notification-close" aria-label="Закрыть"></button>' +
  73. '</div>' +
  74. '<div class="ws-notification-body">' + bodyHtml + '</div>';
  75. const closeBtn = popup.querySelector('.ws-notification-close');
  76. if (closeBtn) {
  77. closeBtn.addEventListener('click', function (event) {
  78. event.stopPropagation();
  79. markNotificationRead(notification.id);
  80. popup.remove();
  81. });
  82. }
  83. container.appendChild(popup);
  84. }
  85. cleanupStaleModalState();
  86. window.addEventListener('pageshow', cleanupStaleModalState);
  87. if ($('.main-alert').length) {
  88. setTimeout(function () {
  89. $('.main-alert').fadeTo(2000, 500).slideUp(500, function () {
  90. $('.main-alert').slideUp(500);
  91. });
  92. }, 3000);
  93. }
  94. const user = localStorage.getItem('user');
  95. if (user > 0) {
  96. const socket = new WebSocket(localStorage.getItem('socketAddress'));
  97. socket.onopen = function () {
  98. console.log('[WS] Connected. Listen messages for user ' + user);
  99. };
  100. socket.onmessage = function (event) {
  101. const received = JSON.parse(event.data);
  102. if (parseInt(received.data.user_id, 10) !== parseInt(user, 10)) {
  103. return;
  104. }
  105. const action = received.data.action;
  106. if (action === 'persistent_notification') {
  107. incrementNotificationBadge();
  108. appendWsPopup(received.data.notification);
  109. return;
  110. }
  111. if (action !== 'message') {
  112. return;
  113. }
  114. if (received.data.payload.download) {
  115. document.location.href = '/storage/export/' + received.data.payload.download;
  116. }
  117. if (received.data.payload.link) {
  118. document.location.href = received.data.payload.link;
  119. setTimeout(function () {
  120. document.location.reload();
  121. }, 2000);
  122. }
  123. setTimeout(function () {
  124. if (received.data.payload.error) {
  125. $('.alerts').append('<div class="main-alert2 alert alert-danger" role="alert">' + received.data.message + '</div>');
  126. } else {
  127. $('.alerts').append('<div class="main-alert2 alert alert-success" role="alert">' + received.data.message + '</div>');
  128. }
  129. setTimeout(function () {
  130. $('.main-alert2').fadeTo(2000, 500).slideUp(500, function () {
  131. $('.main-alert2').slideUp(500);
  132. });
  133. }, 3000);
  134. }, 1000);
  135. };
  136. socket.onclose = function (event) {
  137. if (event.wasClean) {
  138. console.log('[WS] Closed clear, code=' + event.code + ' reason=' + event.reason);
  139. } else {
  140. console.log('[WS] Connection lost', event);
  141. }
  142. };
  143. socket.onerror = function (error) {
  144. console.log('[error]', error);
  145. };
  146. }
  147. });