mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-10-31 13:28:18 +00:00
fix(syncguard): prevent mobile input behind workspace overlay
This commit is contained in:
1 parent
d527fa9764
commit
ee0272a65f
2 files changed
+40
No files matched your search
@@ -57,6 +57,10 @@ export class SyncGuardOverlay {
|
|||||||
const modalFocused = focused instanceof Element && focused.closest(".modal-container") !== null;
|
const modalFocused = focused instanceof Element && focused.closest(".modal-container") !== null;
|
||||||
this.previousFocus = !modalFocused && focused instanceof HTMLElement ? focused : undefined;
|
this.previousFocus = !modalFocused && focused instanceof HTMLElement ? focused : undefined;
|
||||||
doc.addEventListener("keydown", this.blockKeyboard, true);
|
doc.addEventListener("keydown", this.blockKeyboard, true);
|
||||||
|
// Mobile input methods, dictation, and paste may mutate an editor without a keydown.
|
||||||
|
doc.addEventListener("beforeinput", this.blockEditorInput, true);
|
||||||
|
doc.addEventListener("paste", this.blockEditorInput, true);
|
||||||
|
doc.addEventListener("drop", this.blockEditorInput, true);
|
||||||
// Do not steal focus from an Obsidian conflict or setup dialogue which was already open.
|
// Do not steal focus from an Obsidian conflict or setup dialogue which was already open.
|
||||||
if (!modalFocused) bypass.focus();
|
if (!modalFocused) bypass.focus();
|
||||||
}
|
}
|
||||||
@@ -83,12 +87,24 @@ export class SyncGuardOverlay {
|
|||||||
if (event.key === "Tab") this.bypassButton?.focus();
|
if (event.key === "Tab") this.bypassButton?.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private readonly blockEditorInput = (event: Event): void => {
|
||||||
|
if (!this.root) return;
|
||||||
|
const target = event.target;
|
||||||
|
if (target instanceof Element && target.closest(".modal-container")) return;
|
||||||
|
if (target instanceof Node && this.root.contains(target)) return;
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
};
|
||||||
|
|
||||||
close(): void {
|
close(): void {
|
||||||
if (!this.root) return;
|
if (!this.root) return;
|
||||||
const doc = this.workspace.ownerDocument;
|
const doc = this.workspace.ownerDocument;
|
||||||
const focused = doc.activeElement;
|
const focused = doc.activeElement;
|
||||||
const ownsFocus = focused instanceof Node && this.root.contains(focused);
|
const ownsFocus = focused instanceof Node && this.root.contains(focused);
|
||||||
doc.removeEventListener("keydown", this.blockKeyboard, true);
|
doc.removeEventListener("keydown", this.blockKeyboard, true);
|
||||||
|
doc.removeEventListener("beforeinput", this.blockEditorInput, true);
|
||||||
|
doc.removeEventListener("paste", this.blockEditorInput, true);
|
||||||
|
doc.removeEventListener("drop", this.blockEditorInput, true);
|
||||||
this.root.remove();
|
this.root.remove();
|
||||||
this.root = undefined;
|
this.root = undefined;
|
||||||
this.stage = undefined;
|
this.stage = undefined;
|
||||||
|
|||||||
@@ -188,4 +188,28 @@ describe("SyncGuardOverlay", () => {
|
|||||||
expect(doc.activeElement).toBe(button);
|
expect(doc.activeElement).toBe(button);
|
||||||
overlay.dispose();
|
overlay.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("blocks mobile beforeinput and paste into hidden editors but permits input in Obsidian dialogues", () => {
|
||||||
|
const { overlay, doc, editor } = setup();
|
||||||
|
overlay.update({ phase: "transferring", receivedFiles: 30 });
|
||||||
|
const modal = new FakeElement(doc, "div");
|
||||||
|
modal.className = "modal-container";
|
||||||
|
const input = modal.createEl("input");
|
||||||
|
for (const eventType of ["beforeinput", "paste", "drop"]) {
|
||||||
|
const preventDefault = vi.fn();
|
||||||
|
const stopImmediatePropagation = vi.fn();
|
||||||
|
doc.listeners.get(eventType)?.({
|
||||||
|
target: editor, preventDefault, stopImmediatePropagation,
|
||||||
|
} as unknown as KeyboardEvent);
|
||||||
|
expect(preventDefault).toHaveBeenCalledOnce();
|
||||||
|
expect(stopImmediatePropagation).toHaveBeenCalledOnce();
|
||||||
|
|
||||||
|
doc.listeners.get(eventType)?.({
|
||||||
|
target: input, preventDefault, stopImmediatePropagation,
|
||||||
|
} as unknown as KeyboardEvent);
|
||||||
|
expect(preventDefault).toHaveBeenCalledOnce();
|
||||||
|
}
|
||||||
|
overlay.dispose();
|
||||||
|
expect(doc.listeners.size).toBe(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
Reference in new issue
Block a user