siyuan/app/src/util/backForward.ts

321 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {hasClosestBlock, isInEmbedBlock} from "../protyle/util/hasClosest";
import {getContenteditableElement} from "../protyle/wysiwyg/getBlock";
import {focusByOffset, focusByRange, getSelectionOffset} from "../protyle/util/selection";
import {hideElements} from "../protyle/ui/hideElements";
import {fetchPost, fetchSyncPost} from "./fetch";
import {Constants} from "../constants";
import {Wnd} from "../layout/Wnd";
import {getInstanceById, getWndByLayout} from "../layout/util";
import {Tab} from "../layout/Tab";
import {Editor} from "../editor";
import {scrollCenter} from "./highlightById";
import {zoomOut} from "../menus/protyle";
import {showMessage} from "../dialog/message";
import {saveScroll} from "../protyle/scroll/saveScroll";
import {getAllModels} from "../layout/getAll";
import {App} from "../index";
import {onGet} from "../protyle/util/onGet";
let forwardStack: IBackStack[] = [];
let previousIsBack = false;
const focusStack = async (app: App, stack: IBackStack) => {
hideElements(["gutter", "toolbar", "hint", "util", "dialog"], stack.protyle);
let blockElement: HTMLElement;
if (!document.contains(stack.protyle.element)) {
const response = await fetchSyncPost("/api/block/checkBlockExist", {id: stack.protyle.block.rootID});
if (!response.data) {
// 页签删除
return false;
}
let wnd: Wnd;
// 获取光标所在 tab
const element = document.querySelector(".layout__wnd--active");
if (element) {
wnd = getInstanceById(element.getAttribute("data-id")) as Wnd;
}
if (!wnd) {
// 中心 tab
wnd = getWndByLayout(window.siyuan.layout.centerLayout);
}
if (wnd) {
const info = await fetchSyncPost("/api/block/getBlockInfo", {id: stack.id});
if (info.code === 3) {
showMessage(info.msg);
return;
}
const tab = new Tab({
title: info.data.rootTitle,
docIcon: info.data.rootIcon,
callback(tab) {
const scrollAttr = saveScroll(stack.protyle, true) as IScrollAttr;
scrollAttr.rootId = stack.protyle.block.rootID;
scrollAttr.focusId = stack.id;
scrollAttr.focusStart = stack.position.start;
scrollAttr.focusEnd = stack.position.end;
window.siyuan.storage[Constants.LOCAL_FILEPOSITION][stack.protyle.block.rootID] = scrollAttr;
const editor = new Editor({
app: app,
tab,
blockId: stack.zoomId || stack.protyle.block.rootID,
rootId: stack.protyle.block.rootID,
action: stack.zoomId ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL] : [Constants.CB_GET_FOCUS]
});
tab.addModel(editor);
}
});
if (window.siyuan.config.fileTree.openFilesUseCurrentTab) {
let unUpdateTab: Tab;
// 不能 reverse, 找到也不能提前退出循环,否则 https://github.com/siyuan-note/siyuan/issues/3271
wnd.children.forEach((item) => {
if (item.headElement && item.headElement.classList.contains("item--unupdate") && !item.headElement.classList.contains("item--pin")) {
unUpdateTab = item;
}
});
wnd.addTab(tab);
if (unUpdateTab) {
wnd.removeTab(unUpdateTab.id);
}
} else {
wnd.addTab(tab);
}
wnd.showHeading();
// 替换被关闭的 protyle
const protyle = (tab.model as Editor).editor.protyle;
stack.protyle = protyle;
forwardStack.forEach(item => {
if (!document.contains(item.protyle.element) && item.protyle.block.rootID === info.data.rootID) {
item.protyle = protyle;
}
});
window.siyuan.backStack.forEach(item => {
if (!document.contains(item.protyle.element) && item.protyle.block.rootID === info.data.rootID) {
item.protyle = protyle;
}
});
if (info.data.rootID === stack.id) {
focusByOffset(protyle.title.editElement, stack.position.start, stack.position.end);
} else {
Array.from(protyle.wysiwyg.element.querySelectorAll(`[data-node-id="${stack.id}"]`)).find((item: HTMLElement) => {
if (!isInEmbedBlock(item)) {
blockElement = item;
return true;
}
});
focusByOffset(getContenteditableElement(blockElement), stack.position.start, stack.position.end);
scrollCenter(protyle, blockElement);
}
return true;
} else {
return false;
}
}
if (stack.protyle.block.rootID === stack.id) {
if (stack.protyle.title.editElement.getBoundingClientRect().height === 0) {
// 切换 tab
stack.protyle.model.parent.parent.switchTab(stack.protyle.model.parent.headElement);
// 需要更新 range否则 resize 中 `保持光标位置不变` 会导致光标跳动
stack.protyle.toolbar.range = undefined;
}
focusByOffset(stack.protyle.title.editElement, stack.position.start, stack.position.end);
return true;
}
Array.from(stack.protyle.wysiwyg.element.querySelectorAll(`[data-node-id="${stack.id}"]`)).find((item: HTMLElement) => {
if (!isInEmbedBlock(item)) {
blockElement = item;
return true;
}
});
if (blockElement &&
// 即使块存在,折叠的情况需要也需要 zoomOut否则折叠块内的光标无法定位
(!stack.zoomId || (stack.zoomId && stack.zoomId === stack.protyle.block.id))
) {
if (blockElement.getBoundingClientRect().height === 0) {
// 切换 tab
stack.protyle.model.parent.parent.switchTab(stack.protyle.model.parent.headElement);
}
focusByOffset(getContenteditableElement(blockElement), stack.position.start, stack.position.end);
scrollCenter(stack.protyle, blockElement);
getAllModels().outline.forEach(item => {
if (item.blockId === stack.protyle.block.rootID) {
item.setCurrent(blockElement);
}
});
return true;
}
if (stack.protyle.element.parentElement) {
const response = await fetchSyncPost("/api/block/checkBlockExist", {id: stack.id});
if (!response.data) {
// 块被删除
if (getSelection().rangeCount > 0) {
focusByRange(getSelection().getRangeAt(0));
}
return false;
}
// 动态加载导致内容移除 https://github.com/siyuan-note/siyuan/issues/10692
if (!blockElement && !stack.zoomId && !stack.protyle.scroll.element.classList.contains("fn__none")) {
fetchPost("/api/filetree/getDoc", {
id: stack.id,
mode: 3,
size: window.siyuan.config.editor.dynamicLoadBlocks,
}, getResponse => {
onGet({
data: getResponse,
protyle: stack.protyle,
afterCB() {
Array.from(stack.protyle.wysiwyg.element.querySelectorAll(`[data-node-id="${stack.id}"]`)).find((item: HTMLElement) => {
if (!isInEmbedBlock(item)) {
blockElement = item;
return true;
}
});
if (!blockElement) {
return;
}
getAllModels().outline.forEach(item => {
if (item.blockId === stack.protyle.block.rootID) {
item.setCurrent(blockElement);
}
});
focusByOffset(getContenteditableElement(blockElement), stack.position.start, stack.position.end);
scrollCenter(stack.protyle, blockElement, true);
}
});
});
return true;
}
// 缩放
zoomOut({
protyle: stack.protyle,
id: stack.zoomId || stack.protyle.block.rootID,
isPushBack: false,
callback: () => {
Array.from(stack.protyle.wysiwyg.element.querySelectorAll(`[data-node-id="${stack.id}"]`)).find((item: HTMLElement) => {
if (!isInEmbedBlock(item)) {
blockElement = item;
return true;
}
});
if (!blockElement) {
return;
}
getAllModels().outline.forEach(item => {
if (item.blockId === stack.protyle.block.rootID) {
item.setCurrent(blockElement);
}
});
focusByOffset(getContenteditableElement(blockElement), stack.position.start, stack.position.end);
scrollCenter(stack.protyle, blockElement, true);
}
});
return true;
}
};
export const goBack = async (app: App) => {
if (window.siyuan.backStack.length === 0) {
if (forwardStack.length > 0) {
await focusStack(app, forwardStack[forwardStack.length - 1]);
}
return;
}
document.querySelector("#barForward")?.classList.remove("toolbar__item--disabled");
if (!previousIsBack &&
// 页签被关闭时应优先打开该页签,页签存在时即可返回上一步,不用再重置光标到该页签上
document.contains(window.siyuan.backStack[window.siyuan.backStack.length - 1].protyle.element)) {
forwardStack.push(window.siyuan.backStack.pop());
}
let stack = window.siyuan.backStack.pop();
while (stack) {
const isFocus = await focusStack(app, stack);
if (isFocus) {
forwardStack.push(stack);
break;
} else {
stack = window.siyuan.backStack.pop();
}
}
previousIsBack = true;
if (window.siyuan.backStack.length === 0) {
document.querySelector("#barBack")?.classList.add("toolbar__item--disabled");
}
};
export const goForward = async (app: App) => {
if (forwardStack.length === 0) {
if (window.siyuan.backStack.length > 0) {
await focusStack(app, window.siyuan.backStack[window.siyuan.backStack.length - 1]);
}
return;
}
document.querySelector("#barBack")?.classList.remove("toolbar__item--disabled");
if (previousIsBack) {
window.siyuan.backStack.push(forwardStack.pop());
}
let stack = forwardStack.pop();
while (stack) {
const isFocus = await focusStack(app, stack);
if (isFocus) {
window.siyuan.backStack.push(stack);
break;
} else {
stack = forwardStack.pop();
}
}
previousIsBack = false;
if (forwardStack.length === 0) {
document.querySelector("#barForward")?.classList.add("toolbar__item--disabled");
}
};
export const pushBack = (protyle: IProtyle, range?: Range, blockElement?: Element) => {
if (!protyle.model) {
return;
}
if (!blockElement && range) {
blockElement = hasClosestBlock(range.startContainer) as Element;
}
if (!blockElement) {
return;
}
let editElement;
if (blockElement.classList.contains("protyle-title__input")) {
editElement = blockElement;
} else {
editElement = getContenteditableElement(blockElement);
}
if (editElement) {
const position = getSelectionOffset(editElement, undefined, range);
const id = blockElement.getAttribute("data-node-id") || protyle.block.rootID;
const lastStack = window.siyuan.backStack[window.siyuan.backStack.length - 1];
if (lastStack && lastStack.id === id && (
(protyle.block.showAll && lastStack.zoomId === protyle.block.id) || (!lastStack.zoomId && !protyle.block.showAll)
)) {
lastStack.position = position;
} else {
if (forwardStack.length > 0) {
if (previousIsBack) {
window.siyuan.backStack.push(forwardStack.pop());
}
forwardStack = [];
document.querySelector("#barForward")?.classList.add("toolbar__item--disabled");
}
window.siyuan.backStack.push({
position,
id,
protyle,
zoomId: protyle.block.showAll ? protyle.block.id : undefined,
});
if (window.siyuan.backStack.length > Constants.SIZE_UNDO) {
window.siyuan.backStack.shift();
}
previousIsBack = false;
}
if (window.siyuan.backStack.length > 1) {
document.querySelector("#barBack")?.classList.remove("toolbar__item--disabled");
}
}
};