@ -1,4 +1,3 @@
import prettyMilliseconds from 'pretty-ms' ;
import { createTippy } from '../modules/tippy.js' ;
import { createTippy } from '../modules/tippy.js' ;
import { GET } from '../modules/fetch.js' ;
import { GET } from '../modules/fetch.js' ;
import { hideElem , showElem } from '../utils/dom.js' ;
import { hideElem , showElem } from '../utils/dom.js' ;
@ -10,28 +9,31 @@ export function initStopwatch() {
return ;
return ;
}
}
const stopwatchEl = document . querySelector ( '.active-stopwatch-trigger ' ) ;
const stopwatchEls = document . querySelectorAll ( '.active-stopwatch' ) ;
const stopwatchPopup = document . querySelector ( '.active-stopwatch-popup' ) ;
const stopwatchPopup = document . querySelector ( '.active-stopwatch-popup' ) ;
if ( ! stopwatchEl || ! stopwatchPopup ) {
if ( ! stopwatchEls . length || ! stopwatchPopup ) {
return ;
return ;
}
}
// global stop watch (in the head_navbar), it should always work in any case either the EventSource or the PeriodicPoller is used.
const seconds = stopwatchEls [ 0 ] ? . getAttribute ( 'data-seconds' ) ;
if ( seconds ) {
updateStopwatchTime ( parseInt ( seconds ) ) ;
}
for ( const stopwatchEl of stopwatchEls ) {
stopwatchEl . removeAttribute ( 'href' ) ; // intended for noscript mode only
stopwatchEl . removeAttribute ( 'href' ) ; // intended for noscript mode only
createTippy ( stopwatchEl , {
createTippy ( stopwatchEl , {
content : stopwatchPopup ,
content : stopwatchPopup . cloneNode ( true ) ,
placement : 'bottom-end' ,
placement : 'bottom-end' ,
trigger : 'click' ,
trigger : 'click' ,
maxWidth : 'none' ,
maxWidth : 'none' ,
interactive : true ,
interactive : true ,
hideOnClick : true ,
hideOnClick : true ,
theme : 'default' ,
} ) ;
} ) ;
// global stop watch (in the head_navbar), it should always work in any case either the EventSource or the PeriodicPoller is used.
const currSeconds = document . querySelector ( '.stopwatch-time' ) ? . getAttribute ( 'data-seconds' ) ;
if ( currSeconds ) {
updateStopwatchTime ( currSeconds ) ;
}
}
let usingPeriodicPoller = false ;
let usingPeriodicPoller = false ;
@ -124,10 +126,9 @@ async function updateStopwatch() {
function updateStopwatchData ( data ) {
function updateStopwatchData ( data ) {
const watch = data [ 0 ] ;
const watch = data [ 0 ] ;
const btnEl = document . querySelector ( '.active-stopwatch-trigger ' ) ;
const btnEls = document . querySelectorAll ( '.active-stopwatch' ) ;
if ( ! watch ) {
if ( ! watch ) {
clearStopwatchTimer ( ) ;
hideElem ( btnEls ) ;
hideElem ( btnEl ) ;
} else {
} else {
const { repo _owner _name , repo _name , issue _index , seconds } = watch ;
const { repo _owner _name , repo _name , issue _index , seconds } = watch ;
const issueUrl = ` ${ appSubUrl } / ${ repo _owner _name } / ${ repo _name } /issues/ ${ issue _index } ` ;
const issueUrl = ` ${ appSubUrl } / ${ repo _owner _name } / ${ repo _name } /issues/ ${ issue _index } ` ;
@ -137,31 +138,28 @@ function updateStopwatchData(data) {
const stopwatchIssue = document . querySelector ( '.stopwatch-issue' ) ;
const stopwatchIssue = document . querySelector ( '.stopwatch-issue' ) ;
if ( stopwatchIssue ) stopwatchIssue . textContent = ` ${ repo _owner _name } / ${ repo _name } # ${ issue _index } ` ;
if ( stopwatchIssue ) stopwatchIssue . textContent = ` ${ repo _owner _name } / ${ repo _name } # ${ issue _index } ` ;
updateStopwatchTime ( seconds ) ;
updateStopwatchTime ( seconds ) ;
showElem ( btnEl ) ;
showElem ( btnEls ) ;
}
}
return Boolean ( data . length ) ;
return Boolean ( data . length ) ;
}
}
let updateTimeIntervalId = null ; // holds setInterval id when active
// TODO: This flickers on page load, we could avoid this by making a custom
function clearStopwatchTimer ( ) {
// element to render time periods. Feeding a datetime in backend does not work
if ( updateTimeIntervalId !== null ) {
// when time zone between server and client differs.
clearInterval ( updateTimeIntervalId ) ;
updateTimeIntervalId = null ;
}
}
function updateStopwatchTime ( seconds ) {
function updateStopwatchTime ( seconds ) {
const secs = parseInt ( seconds ) ;
if ( ! Number . isFinite ( seconds ) ) return ;
if ( ! Number . isFinite ( secs ) ) return ;
const datetime = ( new Date ( Date . now ( ) - seconds * 1000 ) ) . toISOString ( ) ;
for ( const parent of document . querySelectorAll ( '.header-stopwatch-dot' ) ) {
clearStopwatchTimer ( ) ;
const existing = parent . querySelector ( ':scope > relative-time' ) ;
const stopwatch = document . querySelector ( '.stopwatch-time' ) ;
if ( existing ) {
// TODO: replace with <relative-time> similar to how system status up time is shown
existing . setAttribute ( 'datetime' , datetime ) ;
const start = Date . now ( ) ;
} else {
const updateUi = ( ) => {
const el = document . createElement ( 'relative-time' ) ;
const delta = Date . now ( ) - start ;
el . setAttribute ( 'format' , 'micro' ) ;
const dur = prettyMilliseconds ( secs * 1000 + delta , { compact : true } ) ;
el . setAttribute ( 'datetime' , datetime ) ;
if ( stopwatch ) stopwatch . textContent = dur ;
el . setAttribute ( 'lang' , 'en-US' ) ;
} ;
el . setAttribute ( 'title' , '' ) ; // make <relative-time> show no title and therefor no tooltip
updateUi ( ) ;
parent . append ( el ) ;
updateTimeIntervalId = setInterval ( updateUi , 1000 ) ;
}
}
}
}