Skip to content

Commit f424a7e

Browse files
authored
feat: add navigation and playback broadcast to compare two fleet debugger tabs side by side (#294)
1 parent 1a03fa8 commit f424a7e

File tree

1 file changed

+58
-10
lines changed

1 file changed

+58
-10
lines changed

src/App.js

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,26 @@ class App extends React.Component {
175175
this.initializeData().then(() => {
176176
this.updateMapAndAssociatedData();
177177
});
178+
179+
// Initialize the Broadcast Channel
180+
this.syncChannel = new BroadcastChannel("app_playback_sync");
181+
this.syncChannel.onmessage = this.handleSyncMessage;
182+
178183
document.addEventListener("keydown", this.handleKeyPress);
179184
}
180185

186+
// Handle incoming signals from the other tab
187+
handleSyncMessage = (event) => {
188+
const { action } = event.data;
189+
190+
// We pass 'true' to these methods to indicate the action came from a sync.
191+
// This tells the method NOT to broadcast it back, preventing infinite loops.
192+
if (action === "NEXT") this.handleNextEvent(true);
193+
if (action === "PREVIOUS") this.handlePreviousEvent(true);
194+
if (action === "TOGGLE_PLAY") this.handlePlayStop(true);
195+
if (action === "SET_SPEED") this.handleSpeedChange(event.data.speed, true);
196+
};
197+
181198
initializeData = async () => {
182199
const datasets = await this.checkUploadedDatasets();
183200
if (!datasets[0]) {
@@ -345,48 +362,79 @@ class App extends React.Component {
345362
}
346363
};
347364

348-
handleNextEvent = () => {
365+
handleNextEvent = (fromSync = false) => {
349366
this.handleRowChange("next");
367+
368+
// Only broadcast if this action originated from this tab
369+
if (!fromSync && this.syncChannel) {
370+
this.syncChannel.postMessage({ action: "NEXT" });
371+
}
350372
};
351373

352-
handlePreviousEvent = () => {
374+
handlePreviousEvent = (fromSync = false) => {
353375
this.handleRowChange("previous");
376+
377+
// Only broadcast if this action originated from this tab
378+
if (!fromSync && this.syncChannel) {
379+
this.syncChannel.postMessage({ action: "PREVIOUS" });
380+
}
354381
};
355382

356-
handlePlayStop = () => {
383+
handlePlayStop = (fromSync = false) => {
357384
this.setState((prevState) => {
358385
if (!prevState.isPlaying) {
359386
this.timerID = setInterval(() => {
360-
this.handleNextEvent();
387+
this.handleNextEvent(true);
361388
}, prevState.playSpeed);
362389
} else {
363390
clearInterval(this.timerID);
364391
}
365392
return { isPlaying: !prevState.isPlaying };
366393
});
394+
395+
// Only broadcast the play/stop toggle if it originated from this tab
396+
if (!fromSync && this.syncChannel) {
397+
this.syncChannel.postMessage({ action: "TOGGLE_PLAY" });
398+
}
367399
};
368400

369-
handleSpeedChange = (event) => {
370-
const newSpeed = parseInt(event.target.value);
401+
handleSpeedChange = (eventOrSpeed, fromSync = false) => {
402+
// Determine the new speed depending on if it came from an event or a direct value
403+
const newSpeed =
404+
typeof eventOrSpeed === "object" && eventOrSpeed.target
405+
? parseInt(eventOrSpeed.target.value)
406+
: parseInt(eventOrSpeed);
407+
371408
this.setState({ playSpeed: newSpeed }, () => {
372409
if (this.state.isPlaying) {
373410
clearInterval(this.timerID);
374411
this.timerID = setInterval(() => {
375-
this.handleNextEvent();
412+
this.handleNextEvent(true);
376413
}, newSpeed);
377414
}
378415
});
416+
417+
// Broadcast the speed change to other tabs
418+
if (!fromSync && this.syncChannel) {
419+
this.syncChannel.postMessage({ action: "SET_SPEED", speed: newSpeed });
420+
}
379421
};
380422

381423
handleKeyPress = (event) => {
424+
// Ignore keystrokes if the user is typing in an input or select box
425+
if (["INPUT", "TEXTAREA", "SELECT"].includes(event.target.tagName)) return;
426+
382427
if (["ArrowLeft", ",", "<"].includes(event.key)) {
383-
this.handlePreviousEvent();
428+
this.handlePreviousEvent(); // Defaults to fromSync=false, so it broadcasts!
384429
} else if (["ArrowRight", ".", ">"].includes(event.key)) {
385-
this.handleNextEvent();
430+
this.handleNextEvent(); // Defaults to fromSync=false, so it broadcasts!
386431
}
387432
};
388433

389434
componentWillUnmount() {
435+
if (this.syncChannel) {
436+
this.syncChannel.close();
437+
}
390438
clearInterval(this.timerID);
391439
document.removeEventListener("keydown", this.handleKeyPress);
392440
if (this._outsideClickHandler) {
@@ -1113,7 +1161,7 @@ class App extends React.Component {
11131161
<button onClick={this.handleNextEvent}>Next&nbsp;&gt;</button>
11141162
</div>
11151163
<div>
1116-
<button onClick={this.handlePlayStop}>{this.state.isPlaying ? "Stop" : "Play"}</button>
1164+
<button onClick={() => this.handlePlayStop()}>{this.state.isPlaying ? "Stop" : "Play"}</button>
11171165
<select value={this.state.playSpeed} onChange={this.handleSpeedChange}>
11181166
<option value="100">0.1s</option>
11191167
<option value="250">0.25s</option>

0 commit comments

Comments
 (0)