@@ -60,14 +60,52 @@ const state = {
6060 gpsAgeUpdateTimer : null , // Timer for updating GPS age display
6161 meshMapperTimer : null , // Timer for delayed MeshMapper API call
6262 cooldownEndTime : null , // Timestamp when cooldown period ends
63- cooldownUpdateTimer : null // Timer to re-enable controls after cooldown
63+ cooldownUpdateTimer : null , // Timer to re-enable controls after cooldown
64+ autoCountdownTimer : null , // Timer for auto-ping countdown display
65+ nextAutoPingTime : null // Timestamp when next auto-ping will occur
6466} ;
6567
6668// ---- UI helpers ----
6769function setStatus ( text , color = "text-slate-300" ) {
6870 statusEl . textContent = text ;
6971 statusEl . className = `font-semibold ${ color } ` ;
7072}
73+ function updateAutoCountdownStatus ( ) {
74+ if ( ! state . running || ! state . nextAutoPingTime ) {
75+ return ;
76+ }
77+
78+ const remainingMs = state . nextAutoPingTime - Date . now ( ) ;
79+ if ( remainingMs <= 0 ) {
80+ setStatus ( "Sending auto ping..." , "text-sky-300" ) ;
81+ return ;
82+ }
83+
84+ const remainingSec = Math . ceil ( remainingMs / 1000 ) ;
85+ setStatus ( `Waiting for next auto ping (${ remainingSec } s)` , "text-slate-300" ) ;
86+ }
87+ function startAutoCountdown ( intervalMs ) {
88+ // Stop any existing countdown
89+ stopAutoCountdown ( ) ;
90+
91+ // Set the next ping time
92+ state . nextAutoPingTime = Date . now ( ) + intervalMs ;
93+
94+ // Update immediately
95+ updateAutoCountdownStatus ( ) ;
96+
97+ // Update every second
98+ state . autoCountdownTimer = setInterval ( ( ) => {
99+ updateAutoCountdownStatus ( ) ;
100+ } , 1000 ) ;
101+ }
102+ function stopAutoCountdown ( ) {
103+ if ( state . autoCountdownTimer ) {
104+ clearInterval ( state . autoCountdownTimer ) ;
105+ state . autoCountdownTimer = null ;
106+ }
107+ state . nextAutoPingTime = null ;
108+ }
71109function isInCooldown ( ) {
72110 return state . cooldownEndTime && Date . now ( ) < state . cooldownEndTime ;
73111}
@@ -435,27 +473,42 @@ async function sendPing(manual = false) {
435473
436474 let lat , lon , accuracy ;
437475
438- // Use the selected interval to determine if GPS fix is fresh enough
439- const intervalMs = getSelectedIntervalMs ( ) ;
440- const maxAge = intervalMs + GPS_FRESHNESS_BUFFER_MS ; // Allow buffer beyond interval
441-
442- if ( state . lastFix && ( Date . now ( ) - state . lastFix . tsMs ) < maxAge ) {
476+ // In auto mode, always use the most recent GPS coordinates from the watch
477+ // In manual mode, get fresh GPS if needed
478+ if ( ! manual && state . running ) {
479+ // Auto mode: use GPS watch data
480+ if ( ! state . lastFix ) {
481+ // If no GPS fix yet in auto mode, skip this ping and wait for watch to acquire location
482+ console . warn ( "Auto ping skipped: waiting for GPS fix" ) ;
483+ setStatus ( "Waiting for GPS fix..." , "text-amber-300" ) ;
484+ return ;
485+ }
443486 lat = state . lastFix . lat ;
444487 lon = state . lastFix . lon ;
445488 accuracy = state . lastFix . accM ;
446489 } else {
447- // Get fresh GPS coordinates
448- const pos = await getCurrentPosition ( ) ;
449- lat = pos . coords . latitude ;
450- lon = pos . coords . longitude ;
451- accuracy = pos . coords . accuracy ;
452- state . lastFix = {
453- lat,
454- lon,
455- accM : accuracy ,
456- tsMs : Date . now ( ) ,
457- } ;
458- updateGpsUi ( ) ;
490+ // Manual mode: check if we have recent enough GPS data
491+ const intervalMs = getSelectedIntervalMs ( ) ;
492+ const maxAge = intervalMs + GPS_FRESHNESS_BUFFER_MS ; // Allow buffer beyond interval
493+
494+ if ( state . lastFix && ( Date . now ( ) - state . lastFix . tsMs ) < maxAge ) {
495+ lat = state . lastFix . lat ;
496+ lon = state . lastFix . lon ;
497+ accuracy = state . lastFix . accM ;
498+ } else {
499+ // Get fresh GPS coordinates for manual ping
500+ const pos = await getCurrentPosition ( ) ;
501+ lat = pos . coords . latitude ;
502+ lon = pos . coords . longitude ;
503+ accuracy = pos . coords . accuracy ;
504+ state . lastFix = {
505+ lat,
506+ lon,
507+ accM : accuracy ,
508+ tsMs : Date . now ( ) ,
509+ } ;
510+ updateGpsUi ( ) ;
511+ }
459512 }
460513
461514 const payload = buildPayload ( lat , lon ) ;
@@ -500,7 +553,14 @@ async function sendPing(manual = false) {
500553
501554 // Set status to idle after map update
502555 if ( state . connection ) {
503- setStatus ( "Idle" , "text-slate-300" ) ;
556+ // If in auto mode, start countdown. Otherwise, set to idle
557+ if ( state . running ) {
558+ // Restart the countdown for next auto ping
559+ const intervalMs = getSelectedIntervalMs ( ) ;
560+ startAutoCountdown ( intervalMs ) ;
561+ } else {
562+ setStatus ( "Idle" , "text-slate-300" ) ;
563+ }
504564 }
505565 } , MAP_REFRESH_DELAY_MS ) ;
506566
@@ -539,6 +599,7 @@ function stopAutoPing(ignoreCheck = false) {
539599 clearInterval ( state . autoTimerId ) ;
540600 state . autoTimerId = null ;
541601 }
602+ stopAutoCountdown ( ) ;
542603 stopGeoWatch ( ) ;
543604 state . running = false ;
544605 updateAutoButton ( ) ;
@@ -572,6 +633,8 @@ function startAutoPing() {
572633 state . autoTimerId = setInterval ( ( ) => {
573634 sendPing ( false ) . catch ( console . error ) ;
574635 } , intervalMs ) ;
636+
637+ // Countdown will be started after the first ping completes
575638}
576639
577640// ---- BLE connect / disconnect ----
@@ -625,6 +688,7 @@ async function connect() {
625688 clearTimeout ( state . cooldownUpdateTimer ) ;
626689 state . cooldownUpdateTimer = null ;
627690 }
691+ stopAutoCountdown ( ) ;
628692 state . cooldownEndTime = null ;
629693
630694 state . lastFix = null ;
0 commit comments