|
|
@ -2,7 +2,6 @@ import $ from 'jquery'; |
|
|
|
import prettyMilliseconds from 'pretty-ms'; |
|
|
|
import prettyMilliseconds from 'pretty-ms'; |
|
|
|
|
|
|
|
|
|
|
|
const {appSubUrl, csrfToken, notificationSettings, enableTimeTracking} = window.config; |
|
|
|
const {appSubUrl, csrfToken, notificationSettings, enableTimeTracking} = window.config; |
|
|
|
let updateTimeInterval = null; // holds setInterval id when active
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function initStopwatch() { |
|
|
|
export function initStopwatch() { |
|
|
|
if (!enableTimeTracking) { |
|
|
|
if (!enableTimeTracking) { |
|
|
@ -26,14 +25,28 @@ export function initStopwatch() { |
|
|
|
$(this).parent().trigger('submit'); |
|
|
|
$(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
|
|
|
|
// 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'); |
|
|
|
const worker = new SharedWorker(`${__webpack_public_path__}js/eventsource.sharedworker.js`, 'notification-worker'); |
|
|
|
worker.addEventListener('error', (event) => { |
|
|
|
worker.addEventListener('error', (event) => { |
|
|
|
console.error(event); |
|
|
|
console.error('worker error', event); |
|
|
|
}); |
|
|
|
}); |
|
|
|
worker.port.addEventListener('messageerror', () => { |
|
|
|
worker.port.addEventListener('messageerror', () => { |
|
|
|
console.error('Unable to deserialize message'); |
|
|
|
console.error('unable to deserialize message'); |
|
|
|
}); |
|
|
|
}); |
|
|
|
worker.port.postMessage({ |
|
|
|
worker.port.postMessage({ |
|
|
|
type: 'start', |
|
|
|
type: 'start', |
|
|
@ -41,13 +54,16 @@ export function initStopwatch() { |
|
|
|
}); |
|
|
|
}); |
|
|
|
worker.port.addEventListener('message', (event) => { |
|
|
|
worker.port.addEventListener('message', (event) => { |
|
|
|
if (!event.data || !event.data.type) { |
|
|
|
if (!event.data || !event.data.type) { |
|
|
|
console.error(event); |
|
|
|
console.error('unknown worker message event', event); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
if (event.data.type === 'stopwatches') { |
|
|
|
if (event.data.type === 'stopwatches') { |
|
|
|
updateStopwatchData(JSON.parse(event.data.data)); |
|
|
|
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') { |
|
|
|
} else if (event.data.type === 'error') { |
|
|
|
console.error(event.data); |
|
|
|
console.error('worker port event error', event.data); |
|
|
|
} else if (event.data.type === 'logout') { |
|
|
|
} else if (event.data.type === 'logout') { |
|
|
|
if (event.data.data !== 'here') { |
|
|
|
if (event.data.data !== 'here') { |
|
|
|
return; |
|
|
|
return; |
|
|
@ -65,7 +81,7 @@ export function initStopwatch() { |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
worker.port.addEventListener('error', (e) => { |
|
|
|
worker.port.addEventListener('error', (e) => { |
|
|
|
console.error(e); |
|
|
|
console.error('worker port error', e); |
|
|
|
}); |
|
|
|
}); |
|
|
|
worker.port.start(); |
|
|
|
worker.port.start(); |
|
|
|
window.addEventListener('beforeunload', () => { |
|
|
|
window.addEventListener('beforeunload', () => { |
|
|
@ -78,22 +94,7 @@ export function initStopwatch() { |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (notificationSettings.MinTimeout <= 0) { |
|
|
|
startPeriodicPoller(notificationSettings.MinTimeout); |
|
|
|
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); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
async function updateStopwatchWithCallback(callback, timeout) { |
|
|
|
async function updateStopwatchWithCallback(callback, timeout) { |
|
|
@ -114,12 +115,6 @@ async function updateStopwatch() { |
|
|
|
url: `${appSubUrl}/user/stopwatches`, |
|
|
|
url: `${appSubUrl}/user/stopwatches`, |
|
|
|
headers: {'X-Csrf-Token': csrfToken}, |
|
|
|
headers: {'X-Csrf-Token': csrfToken}, |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
if (updateTimeInterval) { |
|
|
|
|
|
|
|
clearInterval(updateTimeInterval); |
|
|
|
|
|
|
|
updateTimeInterval = null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return updateStopwatchData(data); |
|
|
|
return updateStopwatchData(data); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -127,10 +122,7 @@ function updateStopwatchData(data) { |
|
|
|
const watch = data[0]; |
|
|
|
const watch = data[0]; |
|
|
|
const btnEl = $('.active-stopwatch-trigger'); |
|
|
|
const btnEl = $('.active-stopwatch-trigger'); |
|
|
|
if (!watch) { |
|
|
|
if (!watch) { |
|
|
|
if (updateTimeInterval) { |
|
|
|
clearStopwatchTimer(); |
|
|
|
clearInterval(updateTimeInterval); |
|
|
|
|
|
|
|
updateTimeInterval = null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
btnEl.addClass('hidden'); |
|
|
|
btnEl.addClass('hidden'); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
const {repo_owner_name, repo_name, issue_index, seconds} = watch; |
|
|
|
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-commit').attr('action', `${issueUrl}/times/stopwatch/toggle`); |
|
|
|
$('.stopwatch-cancel').attr('action', `${issueUrl}/times/stopwatch/cancel`); |
|
|
|
$('.stopwatch-cancel').attr('action', `${issueUrl}/times/stopwatch/cancel`); |
|
|
|
$('.stopwatch-issue').text(`${repo_owner_name}/${repo_name}#${issue_index}`); |
|
|
|
$('.stopwatch-issue').text(`${repo_owner_name}/${repo_name}#${issue_index}`); |
|
|
|
$('.stopwatch-time').text(prettyMilliseconds(seconds * 1000)); |
|
|
|
|
|
|
|
updateStopwatchTime(seconds); |
|
|
|
updateStopwatchTime(seconds); |
|
|
|
btnEl.removeClass('hidden'); |
|
|
|
btnEl.removeClass('hidden'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return !!data.length; |
|
|
|
return !!data.length; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let updateTimeIntervalId = null; // holds setInterval id when active
|
|
|
|
|
|
|
|
function clearStopwatchTimer() { |
|
|
|
|
|
|
|
if (updateTimeIntervalId !== null) { |
|
|
|
|
|
|
|
clearInterval(updateTimeIntervalId); |
|
|
|
|
|
|
|
updateTimeIntervalId = null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
function updateStopwatchTime(seconds) { |
|
|
|
function updateStopwatchTime(seconds) { |
|
|
|
const secs = parseInt(seconds); |
|
|
|
const secs = parseInt(seconds); |
|
|
|
if (!Number.isFinite(secs)) return; |
|
|
|
if (!Number.isFinite(secs)) return; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
clearStopwatchTimer(); |
|
|
|
|
|
|
|
const $stopwatch = $('.stopwatch-time'); |
|
|
|
const start = Date.now(); |
|
|
|
const start = Date.now(); |
|
|
|
updateTimeInterval = setInterval(() => { |
|
|
|
const updateUi = () => { |
|
|
|
const delta = Date.now() - start; |
|
|
|
const delta = Date.now() - start; |
|
|
|
const dur = prettyMilliseconds(secs * 1000 + delta, {compact: true}); |
|
|
|
const dur = prettyMilliseconds(secs * 1000 + delta, {compact: true}); |
|
|
|
$('.stopwatch-time').text(dur); |
|
|
|
$stopwatch.text(dur); |
|
|
|
}, 1000); |
|
|
|
}; |
|
|
|
|
|
|
|
updateUi(); |
|
|
|
|
|
|
|
updateTimeIntervalId = setInterval(updateUi, 1000); |
|
|
|
} |
|
|
|
} |
|
|
|