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;
|
||||
this.previousFocus = !modalFocused && focused instanceof HTMLElement ? focused : undefined;
|
||||
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.
|
||||
if (!modalFocused) bypass.focus();
|
||||
}
|
||||
@@ -83,12 +87,24 @@ export class SyncGuardOverlay {
|
||||
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 {
|
||||
if (!this.root) return;
|
||||
const doc = this.workspace.ownerDocument;
|
||||
const focused = doc.activeElement;
|
||||
const ownsFocus = focused instanceof Node && this.root.contains(focused);
|
||||
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 = undefined;
|
||||
this.stage = undefined;
|
||||
|
||||
@@ -188,4 +188,28 @@ describe("SyncGuardOverlay", () => {
|
||||
expect(doc.activeElement).toBe(button);
|
||||
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