mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-31 19:38:23 +00:00 
			
		
		
		
	Fix the JS error "EventSource is not defined" caused by some non-standard browsers (#20584)
* fall back to periodic poller Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
		| @@ -70,6 +70,13 @@ class Source { | ||||
| self.addEventListener('connect', (e) => { | ||||
|   for (const port of e.ports) { | ||||
|     port.addEventListener('message', (event) => { | ||||
|       if (!self.EventSource) { | ||||
|         // some browsers (like PaleMoon, Firefox<53) don't support EventSource in SharedWorkerGlobalScope. | ||||
|         // this event handler needs EventSource when doing "new Source(url)", so just post a message back to the caller, | ||||
|         // in case the caller would like to use a fallback method to do its work. | ||||
|         port.postMessage({type: 'no-event-source'}); | ||||
|         return; | ||||
|       } | ||||
|       if (event.data.type === 'start') { | ||||
|         const url = event.data.url; | ||||
|         if (sourcesByUrl[url]) { | ||||
|   | ||||
| @@ -28,14 +28,10 @@ async function receiveUpdateCount(event) { | ||||
|   try { | ||||
|     const data = JSON.parse(event.data); | ||||
|  | ||||
|     const notificationCount = document.querySelector('.notification_count'); | ||||
|     if (data.Count > 0) { | ||||
|       notificationCount.classList.remove('hidden'); | ||||
|     } else { | ||||
|       notificationCount.classList.add('hidden'); | ||||
|     for (const count of document.querySelectorAll('.notification_count')) { | ||||
|       count.classList.toggle('hidden', data.Count === 0); | ||||
|       count.textContent = `${data.Count}`; | ||||
|     } | ||||
|  | ||||
|     notificationCount.textContent = `${data.Count}`; | ||||
|     await updateNotificationTable(); | ||||
|   } catch (error) { | ||||
|     console.error(error, event); | ||||
| @@ -49,14 +45,24 @@ export function initNotificationCount() { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if (notificationSettings.EventSourceUpdateTime > 0 && !!window.EventSource && window.SharedWorker) { | ||||
|   let usingPeriodicPoller = false; | ||||
|   const startPeriodicPoller = (timeout, lastCount) => { | ||||
|     if (timeout <= 0 || !Number.isFinite(timeout)) return; | ||||
|     usingPeriodicPoller = true; | ||||
|     lastCount = lastCount ?? notificationCount.text(); | ||||
|     setTimeout(async () => { | ||||
|       await updateNotificationCountWithCallback(startPeriodicPoller, timeout, lastCount); | ||||
|     }, timeout); | ||||
|   }; | ||||
|  | ||||
|   if (notificationSettings.EventSourceUpdateTime > 0 && window.EventSource && window.SharedWorker) { | ||||
|     // Try to connect to the event source via the shared worker first | ||||
|     const worker = new SharedWorker(`${__webpack_public_path__}js/eventsource.sharedworker.js`, 'notification-worker'); | ||||
|     worker.addEventListener('error', (event) => { | ||||
|       console.error(event); | ||||
|       console.error('worker error', event); | ||||
|     }); | ||||
|     worker.port.addEventListener('messageerror', () => { | ||||
|       console.error('Unable to deserialize message'); | ||||
|       console.error('unable to deserialize message'); | ||||
|     }); | ||||
|     worker.port.postMessage({ | ||||
|       type: 'start', | ||||
| @@ -64,13 +70,16 @@ export function initNotificationCount() { | ||||
|     }); | ||||
|     worker.port.addEventListener('message', (event) => { | ||||
|       if (!event.data || !event.data.type) { | ||||
|         console.error(event); | ||||
|         console.error('unknown worker message event', event); | ||||
|         return; | ||||
|       } | ||||
|       if (event.data.type === 'notification-count') { | ||||
|         const _promise = receiveUpdateCount(event.data); | ||||
|       } else if (event.data.type === 'no-event-source') { | ||||
|         // browser doesn't support EventSource, falling back to periodic poller | ||||
|         if (!usingPeriodicPoller) startPeriodicPoller(notificationSettings.MinTimeout); | ||||
|       } else if (event.data.type === 'error') { | ||||
|         console.error(event.data); | ||||
|         console.error('worker port event error', event.data); | ||||
|       } else if (event.data.type === 'logout') { | ||||
|         if (event.data.data !== 'here') { | ||||
|           return; | ||||
| @@ -88,7 +97,7 @@ export function initNotificationCount() { | ||||
|       } | ||||
|     }); | ||||
|     worker.port.addEventListener('error', (e) => { | ||||
|       console.error(e); | ||||
|       console.error('worker port error', e); | ||||
|     }); | ||||
|     worker.port.start(); | ||||
|     window.addEventListener('beforeunload', () => { | ||||
| @@ -101,17 +110,7 @@ export function initNotificationCount() { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if (notificationSettings.MinTimeout <= 0) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   const fn = (timeout, lastCount) => { | ||||
|     setTimeout(() => { | ||||
|       const _promise = updateNotificationCountWithCallback(fn, timeout, lastCount); | ||||
|     }, timeout); | ||||
|   }; | ||||
|  | ||||
|   fn(notificationSettings.MinTimeout, notificationCount.text()); | ||||
|   startPeriodicPoller(notificationSettings.MinTimeout); | ||||
| } | ||||
|  | ||||
| async function updateNotificationCountWithCallback(callback, timeout, lastCount) { | ||||
|   | ||||
| @@ -2,7 +2,6 @@ import $ from 'jquery'; | ||||
| import prettyMilliseconds from 'pretty-ms'; | ||||
|  | ||||
| const {appSubUrl, csrfToken, notificationSettings, enableTimeTracking} = window.config; | ||||
| let updateTimeInterval = null; // holds setInterval id when active | ||||
|  | ||||
| export function initStopwatch() { | ||||
|   if (!enableTimeTracking) { | ||||
| @@ -26,14 +25,28 @@ export function initStopwatch() { | ||||
|     $(this).parent().trigger('submit'); | ||||
|   }); | ||||
|  | ||||
|   if (notificationSettings.EventSourceUpdateTime > 0 && !!window.EventSource && window.SharedWorker) { | ||||
|   // global stop watch (in the head_navbar), it should always work in any case either the EventSource or the PeriodicPoller is used. | ||||
|   const currSeconds = $('.stopwatch-time').attr('data-seconds'); | ||||
|   if (currSeconds) { | ||||
|     updateStopwatchTime(currSeconds); | ||||
|   } | ||||
|  | ||||
|   let usingPeriodicPoller = false; | ||||
|   const startPeriodicPoller = (timeout) => { | ||||
|     if (timeout <= 0 || !Number.isFinite(timeout)) return; | ||||
|     usingPeriodicPoller = true; | ||||
|     setTimeout(() => updateStopwatchWithCallback(startPeriodicPoller, timeout), timeout); | ||||
|   }; | ||||
|  | ||||
|   // if the browser supports EventSource and SharedWorker, use it instead of the periodic poller | ||||
|   if (notificationSettings.EventSourceUpdateTime > 0 && window.EventSource && window.SharedWorker) { | ||||
|     // Try to connect to the event source via the shared worker first | ||||
|     const worker = new SharedWorker(`${__webpack_public_path__}js/eventsource.sharedworker.js`, 'notification-worker'); | ||||
|     worker.addEventListener('error', (event) => { | ||||
|       console.error(event); | ||||
|       console.error('worker error', event); | ||||
|     }); | ||||
|     worker.port.addEventListener('messageerror', () => { | ||||
|       console.error('Unable to deserialize message'); | ||||
|       console.error('unable to deserialize message'); | ||||
|     }); | ||||
|     worker.port.postMessage({ | ||||
|       type: 'start', | ||||
| @@ -41,13 +54,16 @@ export function initStopwatch() { | ||||
|     }); | ||||
|     worker.port.addEventListener('message', (event) => { | ||||
|       if (!event.data || !event.data.type) { | ||||
|         console.error(event); | ||||
|         console.error('unknown worker message event', event); | ||||
|         return; | ||||
|       } | ||||
|       if (event.data.type === 'stopwatches') { | ||||
|         updateStopwatchData(JSON.parse(event.data.data)); | ||||
|       } else if (event.data.type === 'no-event-source') { | ||||
|         // browser doesn't support EventSource, falling back to periodic poller | ||||
|         if (!usingPeriodicPoller) startPeriodicPoller(notificationSettings.MinTimeout); | ||||
|       } else if (event.data.type === 'error') { | ||||
|         console.error(event.data); | ||||
|         console.error('worker port event error', event.data); | ||||
|       } else if (event.data.type === 'logout') { | ||||
|         if (event.data.data !== 'here') { | ||||
|           return; | ||||
| @@ -65,7 +81,7 @@ export function initStopwatch() { | ||||
|       } | ||||
|     }); | ||||
|     worker.port.addEventListener('error', (e) => { | ||||
|       console.error(e); | ||||
|       console.error('worker port error', e); | ||||
|     }); | ||||
|     worker.port.start(); | ||||
|     window.addEventListener('beforeunload', () => { | ||||
| @@ -78,22 +94,7 @@ export function initStopwatch() { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if (notificationSettings.MinTimeout <= 0) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   const fn = (timeout) => { | ||||
|     setTimeout(() => { | ||||
|       const _promise = updateStopwatchWithCallback(fn, timeout); | ||||
|     }, timeout); | ||||
|   }; | ||||
|  | ||||
|   fn(notificationSettings.MinTimeout); | ||||
|  | ||||
|   const currSeconds = $('.stopwatch-time').data('seconds'); | ||||
|   if (currSeconds) { | ||||
|     updateTimeInterval = updateStopwatchTime(currSeconds); | ||||
|   } | ||||
|   startPeriodicPoller(notificationSettings.MinTimeout); | ||||
| } | ||||
|  | ||||
| async function updateStopwatchWithCallback(callback, timeout) { | ||||
| @@ -114,12 +115,6 @@ async function updateStopwatch() { | ||||
|     url: `${appSubUrl}/user/stopwatches`, | ||||
|     headers: {'X-Csrf-Token': csrfToken}, | ||||
|   }); | ||||
|  | ||||
|   if (updateTimeInterval) { | ||||
|     clearInterval(updateTimeInterval); | ||||
|     updateTimeInterval = null; | ||||
|   } | ||||
|  | ||||
|   return updateStopwatchData(data); | ||||
| } | ||||
|  | ||||
| @@ -127,10 +122,7 @@ function updateStopwatchData(data) { | ||||
|   const watch = data[0]; | ||||
|   const btnEl = $('.active-stopwatch-trigger'); | ||||
|   if (!watch) { | ||||
|     if (updateTimeInterval) { | ||||
|       clearInterval(updateTimeInterval); | ||||
|       updateTimeInterval = null; | ||||
|     } | ||||
|     clearStopwatchTimer(); | ||||
|     btnEl.addClass('hidden'); | ||||
|   } else { | ||||
|     const {repo_owner_name, repo_name, issue_index, seconds} = watch; | ||||
| @@ -139,22 +131,31 @@ function updateStopwatchData(data) { | ||||
|     $('.stopwatch-commit').attr('action', `${issueUrl}/times/stopwatch/toggle`); | ||||
|     $('.stopwatch-cancel').attr('action', `${issueUrl}/times/stopwatch/cancel`); | ||||
|     $('.stopwatch-issue').text(`${repo_owner_name}/${repo_name}#${issue_index}`); | ||||
|     $('.stopwatch-time').text(prettyMilliseconds(seconds * 1000)); | ||||
|     updateTimeInterval = updateStopwatchTime(seconds); | ||||
|     updateStopwatchTime(seconds); | ||||
|     btnEl.removeClass('hidden'); | ||||
|   } | ||||
|  | ||||
|   return !!data.length; | ||||
| } | ||||
|  | ||||
| let updateTimeIntervalId = null; // holds setInterval id when active | ||||
| function clearStopwatchTimer() { | ||||
|   if (updateTimeIntervalId !== null) { | ||||
|     clearInterval(updateTimeIntervalId); | ||||
|     updateTimeIntervalId = null; | ||||
|   } | ||||
| } | ||||
| function updateStopwatchTime(seconds) { | ||||
|   const secs = parseInt(seconds); | ||||
|   if (!Number.isFinite(secs)) return null; | ||||
|   if (!Number.isFinite(secs)) return; | ||||
|  | ||||
|   clearStopwatchTimer(); | ||||
|   const $stopwatch = $('.stopwatch-time'); | ||||
|   const start = Date.now(); | ||||
|   return setInterval(() => { | ||||
|   const updateUi = () => { | ||||
|     const delta = Date.now() - start; | ||||
|     const dur = prettyMilliseconds(secs * 1000 + delta, {compact: true}); | ||||
|     $('.stopwatch-time').text(dur); | ||||
|   }, 1000); | ||||
|     $stopwatch.text(dur); | ||||
|   }; | ||||
|   updateUi(); | ||||
|   updateTimeIntervalId = setInterval(updateUi, 1000); | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user