Refuse to join the pair, rather than undoing it a second later
Connecting both pods is the configuration in which the `−` pod stops reporting its own paddle (§2.3.1). `Cmd::Seen` has always known that and declined; an explicit `Cmd::Connect` walked straight past it, and housekeeping then closed the redundant link a second or so later — which looks like the app handling the case and is not the same thing. On the tablet, 2026-08-27 15:55:39, the pair was joined for 1.4 s and the `−` pod did not report another press for the rest of the session, across two fresh links and an app reinstall. So the rule now lives on both paths. `auto` is still armed by the refused request, so the fallback stands: the moment the `−` pod goes away, the `+` pod is taken on its next advertisement. And the UI stops offering the trap. The `+` pod's "Connect anyway" sat next to a working controller, which put the one action that breaks shifting a single tap from a rider hunting for a way to fix shifting. It now reads "Held in reserve", which is what that pod is. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -646,10 +646,36 @@ async fn run(
|
||||
};
|
||||
match cmd {
|
||||
Cmd::Connect { pod, address } => {
|
||||
// The rule `Cmd::Seen` has always enforced, applied
|
||||
// here too: with the `−` pod up there is nothing for a
|
||||
// `+` link to add and one specific thing for it to
|
||||
// break — connected as a pair, the `−` pod stops
|
||||
// reporting its own paddle (§2.3.1).
|
||||
//
|
||||
// Only the scan-driven path was guarded, so an explicit
|
||||
// request walked straight past it. Housekeeping then
|
||||
// closed the redundant link a second or so later, which
|
||||
// reads as the app handling it and is not the same
|
||||
// thing at all: on the tablet, 2026-08-27 15:55:39, the
|
||||
// pair was joined for 1.4 s and the `−` pod did not
|
||||
// report a press again for the rest of the session.
|
||||
//
|
||||
// `auto` is still armed below, so the fallback stands:
|
||||
// the moment the `−` pod goes away, the `+` pod is
|
||||
// taken on its next advertisement.
|
||||
let minus_up = !minus.idle();
|
||||
let slot = slot_mut(&mut minus, &mut plus, pod);
|
||||
// Asking for it by hand re-arms auto-connect, whatever
|
||||
// came before.
|
||||
slot.auto = true;
|
||||
if pod == PodId::Plus && minus_up {
|
||||
tracing::info!(
|
||||
"controller: refusing a + pod link while the − pod is up — \
|
||||
it relays the pair, and joining both stops it reporting \
|
||||
its own paddle"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
// A second click while a search is running means
|
||||
// "connect", which is what is already happening.
|
||||
if slot.connecting() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "BikeControl",
|
||||
"version": "0.2.4",
|
||||
"version": "0.2.5",
|
||||
"identifier": "paris.tourolle.bikecontrol",
|
||||
"build": {
|
||||
"frontendDist": "../ui/dist",
|
||||
@@ -43,7 +43,7 @@
|
||||
],
|
||||
"category": "Utility",
|
||||
"android": {
|
||||
"versionCode": 1204
|
||||
"versionCode": 1205
|
||||
},
|
||||
"shortDescription": "Indoor cycling trainer control",
|
||||
"longDescription": "Control a smart trainer over BLE, ride gradient profiles and synthetic waveforms, and record the result."
|
||||
|
||||
@@ -158,10 +158,15 @@
|
||||
<button class="btn ghost" onclick={() => disconnect(pod.pod)}>Disconnect</button>
|
||||
{:else if pod.state === 'searching'}
|
||||
<button class="btn ghost" onclick={() => disconnect(pod.pod)}>Stop</button>
|
||||
{:else if dormant}
|
||||
<!-- No "connect anyway". Joining the pair is the one action that
|
||||
stops the − pod reporting its own paddle (§2.3.1), and offering
|
||||
it beside a working controller put the failure one tap from a
|
||||
rider hunting for a fix. Rust refuses it too. The fallback is
|
||||
not lost: the moment the − pod goes, this pod is taken. -->
|
||||
<span class="held">Held in reserve</span>
|
||||
{:else}
|
||||
<button class="btn ghost" onclick={() => connect(pod.pod)}>
|
||||
{dormant ? 'Connect anyway' : 'Find it'}
|
||||
</button>
|
||||
<button class="btn ghost" onclick={() => connect(pod.pod)}>Find it</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -353,6 +358,12 @@
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.held {
|
||||
align-self: center;
|
||||
font-size: 0.8rem;
|
||||
color: var(--ink-faint);
|
||||
}
|
||||
|
||||
.note {
|
||||
flex: 1 1 100%;
|
||||
margin: 0;
|
||||
|
||||
Reference in New Issue
Block a user