-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
-
67
-
68
-
69
-
70
-
71
-
72
-
73
-
74
-
75
-
76
-
77
-
78
-
79
-
80
-
81
-
82
-
83
-
84
-
85
-
86
-
87
-
88
-
89
-
90
-
91
-
92
-
93
-
94
-
95
-
96
-
97
-
98
-
99
-
100
-
101
-
102
-
103
-
104
-
105
-
106
-
107
-
108
-
109
-
110
-
111
-
112
-
113
-
114
-
115
-
116
-
117
-
118
-
119
-
120
-
121
-
122
-
123
-
124
-
125
-
126
-
127
-
128
-
129
-
130
-
131
-
132
-
133
-
134
-
135
-
136
-
137
-
138
-
139
-
140
-
141
-
142
-
143
-
144
-
145
-
146
-
147
-
148
-
149
-
150
-
151
-
152
-
153
-
154
-
155
-
156
-
157
-
158
-
159
-
160
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) {
return;
}
const stopwatchEl = $('.active-stopwatch-trigger');
if (!stopwatchEl.length) {
return;
}
stopwatchEl.removeAttr('href'); // intended for noscript mode only
stopwatchEl.popup({
position: 'bottom right',
hoverable: true,
});
// form handlers
$('form > button', stopwatchEl).on('click', function () {
$(this).parent().trigger('submit');
});
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);
});
worker.port.addEventListener('messageerror', () => {
console.error('Unable to deserialize message');
});
worker.port.postMessage({
type: 'start',
url: `${window.location.origin}${appSubUrl}/user/events`,
});
worker.port.addEventListener('message', (event) => {
if (!event.data || !event.data.type) {
console.error(event);
return;
}
if (event.data.type === 'stopwatches') {
updateStopwatchData(JSON.parse(event.data.data));
} else if (event.data.type === 'error') {
console.error(event.data);
} else if (event.data.type === 'logout') {
if (event.data.data !== 'here') {
return;
}
worker.port.postMessage({
type: 'close',
});
worker.port.close();
window.location.href = appSubUrl;
} else if (event.data.type === 'close') {
worker.port.postMessage({
type: 'close',
});
worker.port.close();
}
});
worker.port.addEventListener('error', (e) => {
console.error(e);
});
worker.port.start();
window.addEventListener('beforeunload', () => {
worker.port.postMessage({
type: 'close',
});
worker.port.close();
});
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);
}
}
async function updateStopwatchWithCallback(callback, timeout) {
const isSet = await updateStopwatch();
if (!isSet) {
timeout = notificationSettings.MinTimeout;
} else if (timeout < notificationSettings.MaxTimeout) {
timeout += notificationSettings.TimeoutStep;
}
callback(timeout);
}
async function updateStopwatch() {
const data = await $.ajax({
type: 'GET',
url: `${appSubUrl}/user/stopwatches`,
headers: {'X-Csrf-Token': csrfToken},
});
if (updateTimeInterval) {
clearInterval(updateTimeInterval);
updateTimeInterval = null;
}
return updateStopwatchData(data);
}
function updateStopwatchData(data) {
const watch = data[0];
const btnEl = $('.active-stopwatch-trigger');
if (!watch) {
if (updateTimeInterval) {
clearInterval(updateTimeInterval);
updateTimeInterval = null;
}
btnEl.addClass('hidden');
} else {
const {repo_owner_name, repo_name, issue_index, seconds} = watch;
const issueUrl = `${appSubUrl}/${repo_owner_name}/${repo_name}/issues/${issue_index}`;
$('.stopwatch-link').attr('href', issueUrl);
$('.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));
updateStopwatchTime(seconds);
btnEl.removeClass('hidden');
}
return !!data.length;
}
function updateStopwatchTime(seconds) {
const secs = parseInt(seconds);
if (!Number.isFinite(secs)) return;
const start = Date.now();
updateTimeInterval = setInterval(() => {
const delta = Date.now() - start;
const dur = prettyMilliseconds(secs * 1000 + delta, {compact: true});
$('.stopwatch-time').text(dur);
}, 1000);
}