fix: keep ordinary marker dialog close synchronous

User-Visible: no
Issue: #485
This commit is contained in:
Matysh
2026-09-09 03:53:33 +03:00
parent 3938caf00a
commit d433b64cf0
4 changed files with 38 additions and 15 deletions
+8 -14
View File
@@ -190,6 +190,7 @@ import {
radarAfterBindingChange, radarConfigFromDraft, radarDraft, recognizeRadar,
type RadarEditorDraft,
} from './radar-editor';
import { finishMarkerDialogClose } from './marker-dialog-close';
import { renderRadarSection } from './editors/radar-section';
import { radarDiscardRequest, RadarSetupController } from './radar-setup';
import {
@@ -7327,25 +7328,18 @@ public _openDeviceInbox(): void {
};
}
public _closeMarkerDialog(): void { void this._closeMarkerDialogGuarded(); }
public _closeMarkerDialog(): void {
if (this._radarSetup.isDirty()) { void this._closeMarkerDialogGuarded(); return; }
this._radarSetup.reset();
finishMarkerDialogClose(this.host);
}
private async _closeMarkerDialogGuarded(): Promise<void> {
if (!await this._radarSetup.discardIfAllowed()) {
if (!await this._radarSetup.discardIfAllowed()) {
await this.host.updateComplete; this.host.renderRoot
.querySelector<HpDialog>('#marker-dialog')?.rejectClose();
return;
}
this.host._markerDialog = null;
if (this.host._deviceInboxReturn) {
const restored = { ...this.host._deviceInboxReturn };
this.host._deviceInbox = restored;
this.host._deviceInboxReturn = null;
if (restored.anchor) {
void this.host.updateComplete.then(() => requestAnimationFrame(() => {
const selector = `.device-inbox-row[data-binding="${CSS.escape(restored.anchor!)}"]`;
this.host.renderRoot.querySelector<HTMLElement>(selector)?.scrollIntoView({ block: 'nearest' });
}));
}
}
this._closeMarkerDialog();
}
public _deviceInboxCandidates(showEntities: boolean) {
+22
View File
@@ -0,0 +1,22 @@
interface MarkerDialogCloseHost<T extends { anchor?: string | null }> {
_markerDialog: unknown | null;
_deviceInboxReturn: T | null;
_deviceInbox: T | null;
updateComplete: Promise<unknown>;
renderRoot: ParentNode;
}
export function finishMarkerDialogClose<T extends { anchor?: string | null }>(
host: MarkerDialogCloseHost<T>,
): void {
host._markerDialog = null;
if (!host._deviceInboxReturn) return;
const restored = { ...host._deviceInboxReturn } as T;
host._deviceInbox = restored;
host._deviceInboxReturn = null;
if (!restored.anchor) return;
void host.updateComplete.then(() => requestAnimationFrame(() => {
const selector = `.device-inbox-row[data-binding="${CSS.escape(restored.anchor!)}"]`;
host.renderRoot.querySelector<HTMLElement>(selector)?.scrollIntoView({ block: 'nearest' });
}));
}
+4
View File
@@ -140,6 +140,10 @@ export class RadarSetupController {
public isActive(): boolean { return this.active !== null; }
public isDirty(): boolean {
return !!this.active && (this.active.mount !== null || this.active.phase !== 'mount');
}
public syncMarker(markerId: string): void {
if (this.active && this.active.markerId !== markerId) this.reset();
}
+4 -1
View File
@@ -26,9 +26,11 @@ test('on-plan installation changes only the editor draft until ordinary Save', (
confirmDiscard: async () => true,
});
assert.equal(controller.begin('radar', draft(), {
id: 'living', name: 'Living', poly: [[0, 0], [1, 0], [1, 1], [0, 1]],
id: 'living', name: 'Living', poly: [[0, 0], [1, 0], [1, 1], [0, 1]],
}, 5, 7), true);
assert.equal(controller.isDirty(), false, 'an untouched setup may close synchronously');
controller.choosePoint(pointer(250, 400));
assert.equal(controller.isDirty(), true, 'placing the mount makes the setup discard-sensitive');
controller.choosePoint(pointer(250, 100));
assert.equal(applied, null, 'the setup surface must not persist or apply before confirmation');
controller.apply();
@@ -54,6 +56,7 @@ test('cancelling calibration releases its draft subscription exactly once', asyn
assert.equal(await controller.cancel(), true);
assert.equal(await controller.cancel(), true);
assert.equal(calls, 1);
assert.equal(controller.isDirty(), false);
});
test('dirty calibration stays open when discard confirmation is rejected', async () => {