diff --git a/app/src/layout/dock/Files.ts b/app/src/layout/dock/Files.ts index 07087e611..c827f2722 100644 --- a/app/src/layout/dock/Files.ts +++ b/app/src/layout/dock/Files.ts @@ -866,7 +866,7 @@ data-type="navigation-root" data-path="/"> } window.siyuan.storage[Constants.LOCAL_FILESPATHS].forEach((item: IFilesPath) => { item.openPaths.forEach((openPath) => { - this.selectItem(item.notebookId, openPath, undefined, false); + this.selectItem(item.notebookId, openPath, undefined, false, false); }); }); if (!init) { @@ -1081,7 +1081,11 @@ data-type="navigation-root" data-path="/"> }, 2); } - private onLsSelect(data: { files: IFile[], box: string, path: string }, filePath: string, setStorage: boolean) { + private async onLsSelect(data: { + files: IFile[], + box: string, + path: string + }, filePath: string, setStorage: boolean, isSetCurrent: boolean) { let fileHTML = ""; data.files.forEach((item: IFile) => { fileHTML += this.genFileHTML(item); @@ -1102,28 +1106,30 @@ data-type="navigation-root" data-path="/"> emojiElement.textContent = unicode2Emoji(window.siyuan.storage[Constants.LOCAL_IMAGES].folder); } liElement.insertAdjacentHTML("afterend", ``); - data.files.forEach((item: IFile) => { + let newLiElement; + for (let i = 0; i < data.files.length; i++) { + const item = data.files[i]; if (filePath === item.path) { - this.selectItem(data.box, filePath, undefined, setStorage); + newLiElement = await this.selectItem(data.box, filePath, undefined, setStorage, isSetCurrent); } else if (filePath.startsWith(item.path.replace(".sy", ""))) { - fetchPost("/api/filetree/listDocsByPath", { + const response = await fetchSyncPost("/api/filetree/listDocsByPath", { notebook: data.box, path: item.path - }, response => { - this.selectItem(response.data.box, filePath, response.data, setStorage); }); + newLiElement = await this.selectItem(response.data.box, filePath, response.data, setStorage, isSetCurrent); } - }); - if (setStorage) { - this.setCurrent(this.element.querySelector(`ul[data-url="${data.box}"] li[data-path="${filePath}"]`)); } + if (isSetCurrent) { + this.setCurrent(newLiElement); + } + return newLiElement; } - private setCurrent(target: HTMLElement, isScroll = true) { + public setCurrent(target: HTMLElement, isScroll = true) { if (!target) { return; } - this.element.querySelectorAll("li").forEach((liItem) => { + this.element.querySelectorAll("li.b3-list-item--focus").forEach((liItem) => { liItem.classList.remove("b3-list-item--focus"); }); target.classList.add("b3-list-item--focus"); @@ -1155,11 +1161,11 @@ data-type="navigation-root" data-path="/"> }); } - public selectItem(notebookId: string, filePath: string, data?: { + public async selectItem(notebookId: string, filePath: string, data?: { files: IFile[], box: string, path: string - }, setStorage = true) { + }, setStorage = true, isSetCurrent = true) { const treeElement = this.element.querySelector(`[data-url="${notebookId}"]`); if (!treeElement) { // 有文件树和编辑器的布局初始化时,文件树还未挂载 @@ -1181,24 +1187,24 @@ data-type="navigation-root" data-path="/"> if (liElement.getAttribute("data-path") === filePath) { if (setStorage) { - this.setCurrent(liElement); this.getOpenPaths(); - } else { - this.element.querySelector(".b3-list-item--focus")?.classList.remove("b3-list-item--focus"); } - return; + if (isSetCurrent) { + this.setCurrent(liElement); + } + return liElement; } if (data && data.path === currentPath) { - this.onLsSelect(data, filePath, setStorage); + liElement = await this.onLsSelect(data, filePath, setStorage, isSetCurrent); } else { - fetchPost("/api/filetree/listDocsByPath", { + const response = await fetchSyncPost("/api/filetree/listDocsByPath", { notebook: notebookId, path: currentPath - }, response => { - this.onLsSelect(response.data, filePath, setStorage); }); + liElement = await this.onLsSelect(response.data, filePath, setStorage, isSetCurrent); } + return liElement; } private getOpenPaths() { diff --git a/app/src/mobile/dock/MobileFiles.ts b/app/src/mobile/dock/MobileFiles.ts index 0dda14d00..ebc05ec5f 100644 --- a/app/src/mobile/dock/MobileFiles.ts +++ b/app/src/mobile/dock/MobileFiles.ts @@ -5,7 +5,7 @@ import {Constants} from "../../constants"; import {getDisplayName, pathPosix, setNoteBook} from "../../util/pathName"; import {initFileMenu, initNavigationMenu, sortMenu} from "../../menus/navigation"; import {showMessage} from "../../dialog/message"; -import {fetchPost} from "../../util/fetch"; +import {fetchPost, fetchSyncPost} from "../../util/fetch"; import {genUUID} from "../../util/genID"; import {openMobileFileById} from "../editor"; import {unicode2Emoji} from "../../emoji"; @@ -361,7 +361,7 @@ export class MobileFiles extends Model { } window.siyuan.storage[Constants.LOCAL_FILESPATHS].forEach((item: IFilesPath) => { item.openPaths.forEach((openPath) => { - this.selectItem(item.notebookId, openPath, undefined, false); + this.selectItem(item.notebookId, openPath, undefined, false, false); }); }); if (!init) { @@ -577,20 +577,14 @@ export class MobileFiles extends Model { }, 2); } - private onLsSelect(data: { files: IFile[], box: string, path: string }, filePath: string, setStorage: boolean) { + private async onLsSelect(data: { + files: IFile[], + box: string, + path: string + }, filePath: string, setStorage: boolean, isSetCurrent: boolean) { let fileHTML = ""; data.files.forEach((item: IFile) => { fileHTML += this.genFileHTML(item); - if (filePath === item.path) { - this.selectItem(data.box, filePath, undefined, setStorage); - } else if (filePath.startsWith(item.path.replace(".sy", ""))) { - fetchPost("/api/filetree/listDocsByPath", { - notebook: data.box, - path: item.path - }, response => { - this.selectItem(response.data.box, filePath, response.data, setStorage); - }); - } }); if (fileHTML === "") { return; @@ -600,26 +594,45 @@ export class MobileFiles extends Model { // 文件展开时,刷新 liElement.nextElementSibling.remove(); } - liElement.querySelector(".b3-list-item__arrow").classList.add("b3-list-item__arrow--open"); - liElement.insertAdjacentHTML("afterend", ``); - if (setStorage) { - this.setCurrent(this.element.querySelector(`ul[data-url="${data.box}"] li[data-path="${filePath}"]`)); + const arrowElement = liElement.querySelector(".b3-list-item__arrow"); + arrowElement.classList.add("b3-list-item__arrow--open"); + arrowElement.parentElement.classList.remove("fn__hidden"); + const emojiElement = liElement.querySelector(".b3-list-item__icon"); + if (emojiElement.textContent === unicode2Emoji(window.siyuan.storage[Constants.LOCAL_IMAGES].file)) { + emojiElement.textContent = unicode2Emoji(window.siyuan.storage[Constants.LOCAL_IMAGES].folder); } + liElement.insertAdjacentHTML("afterend", ``); + let newLiElement; + for (let i = 0; i < data.files.length; i++) { + const item = data.files[i]; + if (filePath === item.path) { + newLiElement = await this.selectItem(data.box, filePath, undefined, setStorage, isSetCurrent); + } else if (filePath.startsWith(item.path.replace(".sy", ""))) { + const response = await fetchSyncPost("/api/filetree/listDocsByPath", { + notebook: data.box, + path: item.path + }); + newLiElement = await this.selectItem(response.data.box, filePath, response.data, setStorage, isSetCurrent); + } + } + if (isSetCurrent) { + this.setCurrent(newLiElement); + } + return newLiElement; } - private setCurrent(target: HTMLElement) { + public setCurrent(target: HTMLElement, isScroll = true) { if (!target) { return; } - this.element.querySelectorAll("li").forEach((liItem) => { + this.element.querySelectorAll("li.b3-list-item--focus").forEach((liItem) => { liItem.classList.remove("b3-list-item--focus"); }); target.classList.add("b3-list-item--focus"); - const titleHeight = this.actionsElement.clientHeight; - if (target.offsetTop - titleHeight < this.element.scrollTop) { - this.element.scrollTop = target.offsetTop - titleHeight; - } else if (target.offsetTop - this.element.clientHeight - titleHeight + target.clientHeight > this.element.scrollTop) { - this.element.scrollTop = target.offsetTop - this.element.clientHeight - titleHeight + target.clientHeight; + + if (isScroll) { + const elementRect = this.element.getBoundingClientRect(); + this.element.scrollTop = this.element.scrollTop + (target.getBoundingClientRect().top - (elementRect.top + elementRect.height / 2)); } } @@ -644,11 +657,11 @@ export class MobileFiles extends Model { }); } - public selectItem(notebookId: string, filePath: string, data?: { + public async selectItem(notebookId: string, filePath: string, data?: { files: IFile[], box: string, path: string - }, setStorage = true) { + }, setStorage = true, isSetCurrent = true) { const treeElement = this.element.querySelector(`[data-url="${notebookId}"]`); if (!treeElement) { // 有文件树和编辑器的布局初始化时,文件树还未挂载 @@ -670,24 +683,24 @@ export class MobileFiles extends Model { if (liElement.getAttribute("data-path") === filePath) { if (setStorage) { - this.setCurrent(liElement); this.getOpenPaths(); - } else { - this.element.querySelector(".b3-list-item--focus")?.classList.remove("b3-list-item--focus"); } - return; + if (isSetCurrent) { + this.setCurrent(liElement); + } + return liElement; } if (data && data.path === currentPath) { - this.onLsSelect(data, filePath, setStorage); + liElement = await this.onLsSelect(data, filePath, setStorage, isSetCurrent); } else { - fetchPost("/api/filetree/listDocsByPath", { + const response = await fetchSyncPost("/api/filetree/listDocsByPath", { notebook: notebookId, path: currentPath - }, response => { - this.onLsSelect(response.data, filePath, setStorage); }); + liElement = await this.onLsSelect(response.data, filePath, setStorage, isSetCurrent); } + return liElement; } private getOpenPaths() { diff --git a/app/src/plugin/API.ts b/app/src/plugin/API.ts index afbd99870..c720c50f9 100644 --- a/app/src/plugin/API.ts +++ b/app/src/plugin/API.ts @@ -30,6 +30,7 @@ import {globalCommand} from "../boot/globalEvent/command/global"; import {exportLayout} from "../layout/util"; import {saveScroll} from "../protyle/scroll/saveScroll"; import {hasClosestByClassName} from "../protyle/util/hasClosest"; +import {Files} from "../layout/dock/Files"; let openTab; let openWindow; @@ -248,6 +249,43 @@ const getActiveEditor = (wndActive = true) => { return editor; }; +export const expandDocTree = async (options: { + id: string, + isSetCurrent?: boolean +}) => { + let isNotebook = false; + window.siyuan.notebooks.find(item => { + if (options.id === item.id) { + isNotebook = true; + return true; + } + }); + let liElement: HTMLElement; + let notebookId = options.id; + const file = getModelByDockType("file") as Files; + if (typeof options.isSetCurrent === "undefined") { + options.isSetCurrent = true; + } + if (isNotebook) { + liElement = file.element.querySelector(`.b3-list[data-url="${options.id}"]`).firstElementChild as HTMLElement; + } else { + const response = await fetchSyncPost("api/block/getBlockInfo", {id: options.id}); + notebookId = response.data.box; + liElement = await file.selectItem(response.data.box, response.data.path, undefined, undefined, options.isSetCurrent); + } + if (!liElement) { + return; + } + if (options.isSetCurrent || typeof options.isSetCurrent === "undefined") { + file.setCurrent(liElement); + } + const toggleElement = liElement.querySelector(".b3-list-item__arrow"); + if (toggleElement.classList.contains("b3-list-item__arrow--open")) { + return; + } + file.getLeaf(liElement, notebookId); +}; + export const API = { adaptHotkey: updateHotkeyTip, confirm: confirmDialog, @@ -281,4 +319,5 @@ export const API = { openAttributePanel, saveLayout, globalCommand, + expandDocTree };