mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
fix double saving of undo states
This commit is contained in:
parent
84de8aec3e
commit
becac6ade8
2 changed files with 35 additions and 10 deletions
|
@ -40,7 +40,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
const key = Symbol("richText");
|
const key = Symbol("richText");
|
||||||
const [context, setContextProperty] = contextProperty<RichTextInputAPI>(key);
|
const [context, setContextProperty] = contextProperty<RichTextInputAPI>(key);
|
||||||
const [globalInputHandler, setupGlobalInputHandler] = useInputHandler();
|
const [globalInputHandler, setupGlobalInputHandler] = useInputHandler( { handleUndo: false });
|
||||||
const [lifecycle, instances, setupLifecycleHooks] =
|
const [lifecycle, instances, setupLifecycleHooks] =
|
||||||
lifecycleHooks<RichTextInputAPI>();
|
lifecycleHooks<RichTextInputAPI>();
|
||||||
const apiStore = writable<SurroundedAPI | null>(null);
|
const apiStore = writable<SurroundedAPI | null>(null);
|
||||||
|
@ -97,7 +97,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
const nodes = getNormalizingNodeStore();
|
const nodes = getNormalizingNodeStore();
|
||||||
const [richTextPromise, resolve] = useRichTextResolve();
|
const [richTextPromise, resolve] = useRichTextResolve();
|
||||||
const { mirror, preventResubscription } = useDOMMirror();
|
const { mirror, preventResubscription } = useDOMMirror();
|
||||||
const [inputHandler, setupInputHandler] = useInputHandler();
|
const [inputHandler, setupInputHandler] = useInputHandler({ handleUndo: true });
|
||||||
const [customStyles, stylesResolve] = promiseWithResolver<Record<string, any>>();
|
const [customStyles, stylesResolve] = promiseWithResolver<Record<string, any>>();
|
||||||
|
|
||||||
export function attachShadow(element: Element): void {
|
export function attachShadow(element: Element): void {
|
||||||
|
|
|
@ -96,23 +96,37 @@ function getCaretPosition(element: Element) {
|
||||||
* Prevents that too many event listeners are attached and allows for some
|
* Prevents that too many event listeners are attached and allows for some
|
||||||
* coordination between them.
|
* coordination between them.
|
||||||
*/
|
*/
|
||||||
function useInputHandler(): [InputHandlerAPI, SetupInputHandlerAction] {
|
interface InputHandlerOptions {
|
||||||
|
handleUndo: boolean;
|
||||||
|
}
|
||||||
|
function useInputHandler(
|
||||||
|
options: InputHandlerOptions,
|
||||||
|
): [InputHandlerAPI, SetupInputHandlerAction] {
|
||||||
const beforeInput = new HandlerList<InputEventParams>();
|
const beforeInput = new HandlerList<InputEventParams>();
|
||||||
const insertText = new HandlerList<InsertTextParams>();
|
const insertText = new HandlerList<InsertTextParams>();
|
||||||
const afterInput = new HandlerList<EventParams>();
|
const afterInput = new HandlerList<EventParams>();
|
||||||
|
|
||||||
const undoManager = new UndoManager();
|
let undoManager: UndoManager | null = null;
|
||||||
let hasSetupObserver = false;
|
let isUndoing = false;
|
||||||
const config = {
|
|
||||||
|
let observer: MutationObserver | null = null;
|
||||||
|
const observerConfig = {
|
||||||
attributes: true,
|
attributes: true,
|
||||||
childList: true,
|
childList: true,
|
||||||
subtree: true,
|
subtree: true,
|
||||||
};
|
};
|
||||||
const observer = new MutationObserver(onMutation);
|
let hasSetupObserver = false;
|
||||||
|
|
||||||
|
if (options.handleUndo){
|
||||||
|
undoManager = new UndoManager();
|
||||||
|
observer = new MutationObserver(onMutation);
|
||||||
|
}
|
||||||
|
|
||||||
function onMutation(mutationsList: MutationRecord[]) {
|
function onMutation(mutationsList: MutationRecord[]) {
|
||||||
|
if(isUndoing) return;
|
||||||
|
|
||||||
const element = <Element> mutationsList[0].target;
|
const element = <Element> mutationsList[0].target;
|
||||||
undoManager.register(element.innerHTML, getMaxOffset(element));
|
undoManager!.register(element.innerHTML, getMaxOffset(element));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onBeforeInput(this: Element, event: InputEvent): Promise<void> {
|
async function onBeforeInput(this: Element, event: InputEvent): Promise<void> {
|
||||||
|
@ -149,14 +163,17 @@ function useInputHandler(): [InputHandlerAPI, SetupInputHandlerAction] {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onInput(this: Element, event: Event): Promise<void> {
|
async function onInput(this: Element, event: Event): Promise<void> {
|
||||||
|
await afterInput.dispatch({ event });
|
||||||
|
|
||||||
|
if (!undoManager) { return; }
|
||||||
if (!hasSetupObserver) {
|
if (!hasSetupObserver) {
|
||||||
observer.observe(this, config);
|
observer!.observe(this, observerConfig);
|
||||||
hasSetupObserver = true;
|
hasSetupObserver = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const position = getCaretPosition(this);
|
const position = getCaretPosition(this);
|
||||||
undoManager.register(this.innerHTML, position - 1);
|
undoManager.register(this.innerHTML, position - 1);
|
||||||
undoManager.clearRedoStack();
|
undoManager.clearRedoStack();
|
||||||
await afterInput.dispatch({ event });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const pointerDown = new HandlerList<{ event: PointerEvent }>();
|
const pointerDown = new HandlerList<{ event: PointerEvent }>();
|
||||||
|
@ -186,11 +203,19 @@ function useInputHandler(): [InputHandlerAPI, SetupInputHandlerAction] {
|
||||||
} else if (event.code === "Tab") {
|
} else if (event.code === "Tab") {
|
||||||
specialKey.dispatch({ event, action: "tab" });
|
specialKey.dispatch({ event, action: "tab" });
|
||||||
} else if ((event.ctrlKey || event.metaKey) && event.key == "z") {
|
} else if ((event.ctrlKey || event.metaKey) && event.key == "z") {
|
||||||
|
if (!undoManager) { return; }
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
isUndoing = true;
|
||||||
undoManager.undo(this);
|
undoManager.undo(this);
|
||||||
|
setTimeout(() => { isUndoing = false }); //reset the flag when the current execution stack is cleared
|
||||||
} else if ((event.ctrlKey || event.metaKey) && event.key == "y") {
|
} else if ((event.ctrlKey || event.metaKey) && event.key == "y") {
|
||||||
|
if (!undoManager) { return; }
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
isUndoing = true;
|
||||||
undoManager.redo(this);
|
undoManager.redo(this);
|
||||||
|
setTimeout(() => { isUndoing = false }); //reset the flag when the current execution stack is cleared
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue