@@ -20,11 +20,16 @@ class PingControls extends StatelessWidget {
2020 final canStartAuto = autoValidation == PingValidation .valid;
2121 final isTxRxAutoRunning = appState.autoPingEnabled && appState.autoMode == AutoMode .txRx;
2222 final isRxAutoRunning = appState.autoPingEnabled && appState.autoMode == AutoMode .rxOnly;
23- final cooldownActive = appState.cooldownTimer.isRunning; // Shared cooldown for both TX Ping and TX/RX Auto
23+ final cooldownActive = appState.cooldownTimer.isRunning; // Shared cooldown after disabling Active Mode
2424 final cooldownRemaining = appState.cooldownTimer.remainingSec;
2525 final rxWindowActive = appState.rxWindowTimer.isRunning; // RX listening window after ping
2626 final rxWindowRemaining = appState.rxWindowTimer.remainingSec;
27- final isPingSending = appState.isPingSending; // True immediately when button clicked
27+ final isPingSending = appState.isPingSending; // True immediately when manual ping button clicked
28+ final isPingInProgress = appState.isPingInProgress; // True during entire ping + RX window (includes auto pings)
29+ final autoPingWaiting = appState.autoPingTimer.isRunning; // Waiting for next auto ping
30+ final autoPingRemaining = appState.autoPingTimer.remainingSec;
31+ final discoveryWindowActive = appState.discoveryWindowTimer.isRunning; // Discovery listening window countdown (Passive Mode)
32+ final discoveryWindowRemaining = appState.discoveryWindowTimer.remainingSec;
2833
2934 // TX is blocked when offline mode is active and connected
3035 final txBlockedByOffline = appState.offlineMode && appState.isConnected;
@@ -80,63 +85,100 @@ class PingControls extends StatelessWidget {
8085 // Action buttons row
8186 Row (
8287 children: [
83- // Send Ping button - disabled when offline mode is active, but works during Passive Mode
84- // Shows active state with pulse animation during sending and RX listening window
88+ // Send Ping button
89+ // State flow: "Send Ping" → "Sending..." → "Listening Xs" → "Send Ping"
90+ // Also shows cooldown countdown when Active Mode is disabled or Passive Mode is listening
91+ // When Active/Passive Mode is running, just shows "Send Ping" or "Cooldown" (disabled)
8592 Expanded (
8693 child: _ActionButton (
8794 icon: Icons .cell_tower,
8895 label: txBlockedByOffline
8996 ? 'TX Disabled'
90- : isPingSending
91- ? 'Sending...'
92- : rxWindowActive
93- ? 'Listening ${rxWindowRemaining }s'
94- : (cooldownActive && ! isTxRxAutoRunning ? '$cooldownRemaining s' : 'Send Ping' ),
97+ : isTxRxAutoRunning
98+ ? 'Send Ping' // Just disabled when Active Mode is running
99+ : isPingSending
100+ ? 'Sending...'
101+ : rxWindowActive
102+ ? 'Listening ${rxWindowRemaining }s' // Manual ping listening (works during Passive Mode too)
103+ : discoveryWindowActive
104+ ? 'Cooldown ${discoveryWindowRemaining }s' // Cooldown during Passive Mode listening
105+ : cooldownActive
106+ ? 'Cooldown ${cooldownRemaining }s' // After Active Mode disabled
107+ : 'Send Ping' ,
95108 color: const Color (0xFF0EA5E9 ), // sky-500
96- enabled: canPing && ! isTxRxAutoRunning && ! cooldownActive && ! txBlockedByOffline && ! rxWindowActive && ! isPingSending,
97- isActive: isPingSending || rxWindowActive,
109+ enabled: canPing && ! isTxRxAutoRunning && ! cooldownActive && ! txBlockedByOffline && ! rxWindowActive && ! isPingSending && ! discoveryWindowActive ,
110+ isActive: ( isPingSending || rxWindowActive) && ! isTxRxAutoRunning, // Only active during manual ping flow
98111 onPressed: () => _sendPing (context, appState),
99- showCooldown: cooldownActive && ! isTxRxAutoRunning && ! txBlockedByOffline,
100- subtitle: txBlockedByOffline ? 'Offline Mode' : ((isPingSending || rxWindowActive) ? null : moveSubtitle),
112+ showCooldown: false , // No longer needed - countdown shown in label
113+ subtitle: txBlockedByOffline ? 'Offline Mode' : ((isPingSending || rxWindowActive || cooldownActive || discoveryWindowActive ) ? null : moveSubtitle),
101114 subtitleColor: txBlockedByOffline ? Colors .orange : Colors .orange.shade600,
102115 ),
103116 ),
104117 const SizedBox (width: 10 ),
105118
106- // Active Mode button - disabled when offline mode is active
107- // Can start even when tooCloseToLastPing - ping will be skipped until user moves
119+ // Active Mode button (toggle)
120+ // When ON: shows "Sending..." → "Listening Xs" → "Next ping in Xs" cycle
121+ // When OFF after being ON: shows "Cooldown Xs" like other buttons
122+ // During manual ping: shows "Cooldown Xs" (disabled)
108123 Expanded (
109124 child: _ActionButton (
110125 icon: Icons .sensors,
111126 label: txBlockedByOffline
112127 ? 'TX Disabled'
113- : (cooldownActive && ! isTxRxAutoRunning && ! isRxAutoRunning
114- ? '$cooldownRemaining s'
115- : 'Active Mode' ),
128+ : isTxRxAutoRunning
129+ ? (isPingInProgress && ! rxWindowActive
130+ ? 'Sending...' // Brief moment while ping is being sent
131+ : rxWindowActive
132+ ? 'Listening ${rxWindowRemaining }s' // During RX window
133+ : autoPingWaiting
134+ ? 'Next ping ${autoPingRemaining }s' // Waiting for next auto ping
135+ : 'Active Mode' ) // Initial state before first ping
136+ : rxWindowActive
137+ ? 'Cooldown ${rxWindowRemaining }s' // During manual ping
138+ : cooldownActive
139+ ? 'Cooldown ${cooldownRemaining }s' // After Active Mode disabled
140+ : 'Active Mode' ,
116141 color: isTxRxAutoRunning
117142 ? const Color (0xFF22C55E ) // green-500
118143 : const Color (0xFF6366F1 ), // indigo-500
119- enabled: (isTxRxAutoRunning || (canStartAuto && ! isRxAutoRunning && ! cooldownActive)) && ! txBlockedByOffline,
120- isActive: isTxRxAutoRunning,
144+ enabled: (isTxRxAutoRunning || (canStartAuto && ! isRxAutoRunning && ! cooldownActive && ! isPingSending && ! rxWindowActive )) && ! txBlockedByOffline,
145+ isActive: isTxRxAutoRunning && (isPingInProgress || rxWindowActive || autoPingWaiting), // Active during sending/listening/waiting phases
121146 onPressed: () => _toggleTxRxAuto (context, appState),
147+ showCooldown: false , // No longer needed - countdown shown in label
122148 subtitle: txBlockedByOffline ? 'Offline Mode' : null ,
123149 subtitleColor: Colors .orange,
124150 ),
125151 ),
126152 const SizedBox (width: 10 ),
127153
128- // Passive Mode button
129- // Passive Mode is passive listening - needs connection + antenna + power config, no cooldown/GPS/distance checks
154+ // Passive Mode button (toggle)
155+ // When ON: shows "Listening..." → "Next Disc Xs" cycle
156+ // When OFF: returns to normal, Active Mode re-enables immediately
157+ // Disabled during manual ping countdown phases, shows "Cooldown Xs"
158+ // When Active Mode is running, just shows "Passive Mode" (disabled, no countdown)
130159 Expanded (
131160 child: _ActionButton (
132161 icon: Icons .hearing,
133- label: 'Passive Mode' ,
162+ label: isRxAutoRunning
163+ ? (discoveryWindowActive
164+ ? 'Listening ${discoveryWindowRemaining }s' // During discovery listening window
165+ : autoPingWaiting
166+ ? 'Next Disc ${autoPingRemaining }s' // Waiting for next discovery
167+ : 'Passive Mode' ) // Initial state before first discovery
168+ : isTxRxAutoRunning
169+ ? 'Passive Mode' // Just disabled when Active Mode is running
170+ : rxWindowActive
171+ ? 'Cooldown ${rxWindowRemaining }s' // During manual ping listening
172+ : cooldownActive
173+ ? 'Cooldown ${cooldownRemaining }s' // After Active Mode disabled
174+ : 'Passive Mode' ,
134175 color: isRxAutoRunning
135176 ? const Color (0xFF22C55E ) // green-500
136177 : const Color (0xFF6366F1 ), // indigo-500
137178 enabled: isRxAutoRunning || (appState.isConnected && ! isTxRxAutoRunning &&
179+ ! isPingSending && ! rxWindowActive && ! cooldownActive &&
138180 prefs.externalAntennaSet && isPowerSet),
139- isActive: isRxAutoRunning,
181+ isActive: isRxAutoRunning && (discoveryWindowActive || autoPingWaiting), // Active during listening/waiting phases
140182 onPressed: () => _toggleRxAuto (context, appState),
141183 ),
142184 ),
0 commit comments