Skip to content

mixer: fix preview image not showing reversed on page load#2582

Open
sensei-hacker wants to merge 1 commit intoiNavFlight:maintenance-9.xfrom
sensei-hacker:fix/mixer-preview-reversed-on-load
Open

mixer: fix preview image not showing reversed on page load#2582
sensei-hacker wants to merge 1 commit intoiNavFlight:maintenance-9.xfrom
sensei-hacker:fix/mixer-preview-reversed-on-load

Conversation

@sensei-hacker
Copy link
Member

@sensei-hacker sensei-hacker commented Mar 3, 2026

Summary

On the Mixer tab, when the FC has motor_direction_inverted set to ON, the props-out (reversed) preview image was not shown on initial page load — only the props-in image appeared. Toggling the radio button manually would correctly swap it, but the on-load state was incorrect.

Root cause

Settings.processHtml calls configureInputs() to populate data-setting inputs via async MSP requests, then immediately calls the tab callback without waiting for those requests to complete. Inside processHtml, $mixerPreset.trigger('change') fires the preset change handler synchronously, which calls updateMotorDirection(). At that point the motor_direction_inverted radio buttons have not been set yet (still at their HTML default), so the wrong image is loaded.

Changes

  • tabs/mixer.js: Accept the settingsPromise parameter that Settings.processHtml already passes to every tab callback, and call updateMotorDirection() again once it resolves so the correct image is shown after the MSP setting values arrive.

This matches the existing pattern used in receiver.js and configuration.js for the same class of async timing issue.

Testing

  • Verified root cause via code inspection: configureInputs() is async, callback fires before radio buttons are populated
  • Verified fix mechanism via browser console: promise .then() fires after radio buttons are set
  • Set motor_direction_inverted = ON on physical FC (RP2350_PICO, INAV 9.0.0) to reproduce; fix confirmed correct by code review
  • Manual toggle of the Motor Direction radio buttons continues to work correctly (change event handlers unaffected)

On initial tab load, $mixerPreset.trigger('change') calls
updateMotorDirection() synchronously before Settings.configureInputs()
has populated the motor_direction_inverted radio buttons via async MSP
requests. This meant the default (props-in) image was always shown even
when the FC had reversed motor direction configured.

Fix: accept the settingsPromise passed by Settings.processHtml and
re-run updateMotorDirection() once it resolves, so the correct image
is shown after the settings values arrive. The change event handlers
are unaffected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@qodo-code-review
Copy link
Contributor

Review Summary by Qodo

Fix mixer preview image not showing reversed on load

🐞 Bug fix

Grey Divider

Walkthroughs

Description
• Fix preview image not showing reversed on initial page load
• Accept settingsPromise parameter and re-run updateMotorDirection after async MSP requests complete
• Ensures motor_direction_inverted setting is applied before image is rendered
• Matches existing async timing pattern used in receiver.js and configuration.js
Diagram
flowchart LR
  A["Page Load"] --> B["processHtml called"]
  B --> C["trigger preset change"]
  C --> D["updateMotorDirection runs early"]
  D --> E["Wrong image shown"]
  B --> F["configureInputs async MSP requests"]
  F --> G["settingsPromise resolves"]
  G --> H["updateMotorDirection runs again"]
  H --> I["Correct image shown"]
Loading

Grey Divider

File Changes

1. tabs/mixer.js 🐞 Bug fix +7/-1

Add async settings promise handling to mixer tab

• Modified processHtml() function signature to accept settingsPromise parameter
• Added promise chain to call updateMotorDirection() after settings async requests complete
• Included error handling to log failures if settings load fails
• Ensures motor direction preview image displays correctly on initial page load

tabs/mixer.js


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Mar 3, 2026

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Async callback after tab cleanup 🐞 Bug ⛯ Reliability
Description
settingsPromise.then(() => updateMotorDirection()) can run after a user switches away from the
Mixer tab, when the Mixer DOM is gone. updateMotorDirection() assumes .mixerPreview img exists
and dereferences $img[0].complete, which will throw if the element is missing, potentially
breaking the UI with a runtime exception.
Code

tabs/mixer.js[R852-853]

+        settingsPromise.then(() => updateMotorDirection())
+            .catch((error) => console.error('Settings load failed, motor direction not updated:', error));
Evidence
The PR adds a post-settings-load callback that always re-runs updateMotorDirection(). Tab
switching cleanup kills intervals/MSP callbacks and calls the tab’s cleanup(), but does not/cannot
cancel the already-created settingsPromise, so the .then(...) can still execute after the tab is
cleaned up. In that situation, updateMotorDirection() can fail because it unconditionally accesses
$img[0].complete even when the jQuery selection is empty (no DOM element).

tabs/mixer.js[849-855]
tabs/mixer.js[663-678]
js/gui.js[97-108]
tabs/mixer.js[879-884]
js/settings.js[647-655]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`settingsPromise.then(() =&gt; updateMotorDirection())` can execute after the Mixer tab is switched away from and cleaned up. `updateMotorDirection()` assumes required DOM exists and does `$img[0].complete` without checking `$img.length`, which can throw.

## Issue Context
- Tab switching cleanup does not cancel the already-started `settingsPromise`.
- The added `.then(...)` runs later, so it may run after DOM teardown.

## Fix Focus Areas
- tabs/mixer.js[849-855]
- tabs/mixer.js[663-678]
- tabs/mixer.js[879-884]

## Implementation notes
- In the `settingsPromise.then(...)` handler: check `GUI.active_tab === &#x27;mixer&#x27;` and that the preview image exists before calling `updateMotorDirection()`.
- In `updateMotorDirection()` (and inside the `import(...).then(...)` callback): guard `const $img = $(&#x27;.mixerPreview img&#x27;); if ($img.length === 0) return;` and ensure `$img[0]` exists before reading `.complete`.
- Optionally set a tab-local `destroyed` flag in `TABS.mixer.cleanup` and check it in the promise handler and `updateMotorDirection()`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant