Преглед на файлове

:art: https://github.com/siyuan-note/siyuan/issues/10652

Vanessa преди 9 месеца
родител
ревизия
b490acfcfb
променени са 4 файла, в които са добавени 139 реда и са изтрити 20 реда
  1. 1 0
      app/src/constants.ts
  2. 67 9
      app/src/layout/dock/Files.ts
  3. 69 10
      app/src/mobile/dock/MobileFiles.ts
  4. 2 1
      app/src/protyle/util/compatibility.ts

+ 1 - 0
app/src/constants.ts

@@ -117,6 +117,7 @@ export abstract class Constants {
     public static readonly LOCAL_PLUGINTOPUNPIN = "local-plugintopunpin";
     public static readonly LOCAL_FLASHCARD = "local-flashcard";
     public static readonly LOCAL_FILEPOSITION = "local-fileposition";
+    public static readonly LOCAL_FILESPATHS = "local-filespaths";
     public static readonly LOCAL_DIALOGPOSITION = "local-dialogposition";
     public static readonly LOCAL_SESSION_FIRSTLOAD = "local-session-firstload";
     public static readonly LOCAL_OUTLINE = "local-outline";

+ 67 - 9
app/src/layout/dock/Files.ts

@@ -14,13 +14,18 @@ import {fetchPost, fetchSyncPost} from "../../util/fetch";
 import {openEmojiPanel, unicode2Emoji} from "../../emoji";
 import {mountHelp, newNotebook} from "../../util/mount";
 import {confirmDialog} from "../../dialog/confirmDialog";
-import {isNotCtrl, isOnlyMeta, updateHotkeyTip} from "../../protyle/util/compatibility";
+import {isNotCtrl, isOnlyMeta, setStorageVal, updateHotkeyTip} from "../../protyle/util/compatibility";
 import {openFileById} from "../../editor/util";
 import {hasClosestByAttribute, hasClosestByTag, hasTopClosestByTag} from "../../protyle/util/hasClosest";
 import {isTouchDevice} from "../../util/functions";
 import {App} from "../../index";
 import {refreshFileTree} from "../../dialog/processSystem";
 
+type filesPath = {
+    notebookId: string,
+    openPaths: string[]
+}
+
 export class Files extends Model {
     public element: HTMLElement;
     public parent: Tab;
@@ -732,6 +737,11 @@ export class Files extends Model {
         } else {
             counterElement.classList.add("fn__none");
         }
+        window.siyuan.storage[Constants.LOCAL_FILESPATHS].forEach((item: filesPath) => {
+            item.openPaths.forEach((openPath) => {
+                this.selectItem(item.notebookId, openPath, undefined, false);
+            })
+        })
         if (!init) {
             return;
         }
@@ -932,18 +942,18 @@ export class Files extends Model {
         }, 2);
     }
 
-    private onLsSelect(data: { files: IFile[], box: string, path: string }, filePath: string) {
+    private onLsSelect(data: { files: IFile[], box: string, path: string }, filePath: string, setStorage: boolean) {
         let fileHTML = "";
         data.files.forEach((item: IFile) => {
             fileHTML += this.genFileHTML(item);
             if (filePath === item.path) {
-                this.selectItem(data.box, filePath);
+                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);
+                    this.selectItem(response.data.box, filePath, response.data, setStorage);
                 });
             }
         });
@@ -963,7 +973,9 @@ export class Files extends Model {
             emojiElement.textContent = unicode2Emoji(Constants.SIYUAN_IMAGE_FOLDER);
         }
         liElement.insertAdjacentHTML("afterend", `<ul>${fileHTML}</ul>`);
-        this.setCurrent(this.element.querySelector(`ul[data-url="${data.box}"] li[data-path="${filePath}"]`));
+        if (setStorage) {
+            this.setCurrent(this.element.querySelector(`ul[data-url="${data.box}"] li[data-path="${filePath}"]`));
+        }
     }
 
     private setCurrent(target: HTMLElement, isScroll = true) {
@@ -989,6 +1001,7 @@ export class Files extends Model {
         if (toggleElement.classList.contains("b3-list-item__arrow--open")) {
             toggleElement.classList.remove("b3-list-item__arrow--open");
             liElement.nextElementSibling?.remove();
+            this.getOpenPaths();
             return;
         }
         fetchPost("/api/filetree/listDocsByPath", {
@@ -1000,10 +1013,15 @@ export class Files extends Model {
                 return;
             }
             this.onLsHTML(response.data);
+            this.getOpenPaths();
         });
     }
 
-    public selectItem(notebookId: string, filePath: string, data?: { files: IFile[], box: string, path: string }) {
+    public selectItem(notebookId: string, filePath: string, data?: {
+        files: IFile[],
+        box: string,
+        path: string
+    }, setStorage = true) {
         const treeElement = this.element.querySelector(`[data-url="${notebookId}"]`);
         if (!treeElement) {
             // 有文件树和编辑器的布局初始化时,文件树还未挂载
@@ -1024,22 +1042,62 @@ export class Files extends Model {
         }
 
         if (liElement.getAttribute("data-path") === filePath) {
-            this.setCurrent(liElement);
+            if (setStorage) {
+                this.setCurrent(liElement);
+                this.getOpenPaths();
+            } else {
+                this.element.querySelector(".b3-list-item--focus")?.classList.remove("b3-list-item--focus");
+            }
             return;
         }
 
         if (data && data.path === currentPath) {
-            this.onLsSelect(data, filePath);
+            this.onLsSelect(data, filePath, setStorage);
         } else {
             fetchPost("/api/filetree/listDocsByPath", {
                 notebook: notebookId,
                 path: currentPath
             }, response => {
-                this.onLsSelect(response.data, filePath);
+                this.onLsSelect(response.data, filePath, setStorage);
             });
         }
     }
 
+    private getOpenPaths() {
+        const filesPaths: filesPath[] = [];
+        this.element.querySelectorAll('.b3-list[data-url]').forEach((item: HTMLElement) => {
+            const notebookPaths: filesPath = {
+                notebookId: item.getAttribute("data-url"),
+                openPaths: []
+            }
+            item.querySelectorAll(".b3-list-item__arrow--open").forEach((openItem) => {
+                const liElement = hasClosestByTag(openItem, "LI");
+                if (liElement) {
+                    notebookPaths.openPaths.push(liElement.getAttribute("data-path"));
+                }
+            })
+            if (notebookPaths.openPaths.length > 0) {
+                for (let i = 0; i < notebookPaths.openPaths.length; i++) {
+                    for (let j = i + 1; j < notebookPaths.openPaths.length; j++) {
+                        if (notebookPaths.openPaths[j].startsWith(notebookPaths.openPaths[i].replace(".sy", ""))) {
+                            notebookPaths.openPaths.splice(i, 1);
+                            j--;
+                        }
+                    }
+                }
+                notebookPaths.openPaths.forEach((openPath, index) => {
+                    const nextPath = this.element.querySelector(`[data-url="${notebookPaths.notebookId}"] li[data-path="${openPath}"]`)?.nextElementSibling?.firstElementChild?.getAttribute("data-path");
+                    if (nextPath) {
+                        notebookPaths.openPaths[index] = nextPath;
+                    }
+                });
+                filesPaths.push(notebookPaths);
+            }
+        });
+        window.siyuan.storage[Constants.LOCAL_FILESPATHS] = filesPaths;
+        setStorageVal(Constants.LOCAL_FILESPATHS, filesPaths);
+    }
+
     private genFileHTML = (item: IFile) => {
         let countHTML = "";
         if (item.count && item.count > 0) {

+ 69 - 10
app/src/mobile/dock/MobileFiles.ts

@@ -1,4 +1,4 @@
-import {hasTopClosestByTag} from "../../protyle/util/hasClosest";
+import {hasClosestByTag, hasTopClosestByTag} from "../../protyle/util/hasClosest";
 import {escapeHtml} from "../../util/escape";
 import {Model} from "../../layout/Model";
 import {Constants} from "../../constants";
@@ -15,6 +15,12 @@ import {newFile} from "../../util/newFile";
 import {MenuItem} from "../../menus/Menu";
 import {App} from "../../index";
 import {refreshFileTree} from "../../dialog/processSystem";
+import {setStorageVal} from "../../protyle/util/compatibility";
+
+type filesPath = {
+    notebookId: string,
+    openPaths: string[]
+}
 
 export class MobileFiles extends Model {
     public element: HTMLElement;
@@ -312,6 +318,11 @@ export class MobileFiles extends Model {
         } else {
             counterElement.classList.add("fn__none");
         }
+        window.siyuan.storage[Constants.LOCAL_FILESPATHS].forEach((item: filesPath) => {
+            item.openPaths.forEach((openPath) => {
+                this.selectItem(item.notebookId, openPath, undefined, false);
+            })
+        })
         if (!init) {
             return;
         }
@@ -430,7 +441,7 @@ export class MobileFiles extends Model {
                     removeElement.remove();
                     const counterElement = this.closeElement.querySelector(".counter");
                     counterElement.textContent = (parseInt(counterElement.textContent) - 1).toString();
-                    if (counterElement.textContent === "0")  {
+                    if (counterElement.textContent === "0") {
                         counterElement.classList.add("fn__none");
                     }
                 }
@@ -545,18 +556,18 @@ export class MobileFiles extends Model {
         }, 2);
     }
 
-    private onLsSelect(data: { files: IFile[], box: string, path: string }, filePath: string) {
+    private onLsSelect(data: { files: IFile[], box: string, path: string }, filePath: string, setStorage: boolean) {
         let fileHTML = "";
         data.files.forEach((item: IFile) => {
             fileHTML += this.genFileHTML(item);
             if (filePath === item.path) {
-                this.selectItem(data.box, filePath);
+                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);
+                    this.selectItem(response.data.box, filePath, response.data, setStorage);
                 });
             }
         });
@@ -570,7 +581,9 @@ export class MobileFiles extends Model {
         }
         liElement.querySelector(".b3-list-item__arrow").classList.add("b3-list-item__arrow--open");
         liElement.insertAdjacentHTML("afterend", `<ul>${fileHTML}</ul>`);
-        this.setCurrent(this.element.querySelector(`ul[data-url="${data.box}"] li[data-path="${filePath}"]`));
+        if (setStorage) {
+            this.setCurrent(this.element.querySelector(`ul[data-url="${data.box}"] li[data-path="${filePath}"]`));
+        }
     }
 
     private setCurrent(target: HTMLElement) {
@@ -594,6 +607,7 @@ export class MobileFiles extends Model {
         if (toggleElement.classList.contains("b3-list-item__arrow--open")) {
             toggleElement.classList.remove("b3-list-item__arrow--open");
             liElement.nextElementSibling?.remove();
+            this.getOpenPaths();
             return;
         }
         fetchPost("/api/filetree/listDocsByPath", {
@@ -605,10 +619,15 @@ export class MobileFiles extends Model {
                 return;
             }
             this.onLsHTML(response.data);
+            this.getOpenPaths();
         });
     }
 
-    public selectItem(notebookId: string, filePath: string, data?: { files: IFile[], box: string, path: string }) {
+    public selectItem(notebookId: string, filePath: string, data?: {
+        files: IFile[],
+        box: string,
+        path: string
+    }, setStorage = true) {
         const treeElement = this.element.querySelector(`[data-url="${notebookId}"]`);
         if (!treeElement) {
             // 有文件树和编辑器的布局初始化时,文件树还未挂载
@@ -629,22 +648,62 @@ export class MobileFiles extends Model {
         }
 
         if (liElement.getAttribute("data-path") === filePath) {
-            this.setCurrent(liElement);
+            if (setStorage) {
+                this.setCurrent(liElement);
+                this.getOpenPaths();
+            } else {
+                this.element.querySelector(".b3-list-item--focus")?.classList.remove("b3-list-item--focus");
+            }
             return;
         }
 
         if (data && data.path === currentPath) {
-            this.onLsSelect(data, filePath);
+            this.onLsSelect(data, filePath, setStorage);
         } else {
             fetchPost("/api/filetree/listDocsByPath", {
                 notebook: notebookId,
                 path: currentPath
             }, response => {
-                this.onLsSelect(response.data, filePath);
+                this.onLsSelect(response.data, filePath, setStorage);
             });
         }
     }
 
+    private getOpenPaths() {
+        const filesPaths: filesPath[] = [];
+        this.element.querySelectorAll('.b3-list[data-url]').forEach((item: HTMLElement) => {
+            const notebookPaths: filesPath = {
+                notebookId: item.getAttribute("data-url"),
+                openPaths: []
+            }
+            item.querySelectorAll(".b3-list-item__arrow--open").forEach((openItem) => {
+                const liElement = hasClosestByTag(openItem, "LI");
+                if (liElement) {
+                    notebookPaths.openPaths.push(liElement.getAttribute("data-path"));
+                }
+            })
+            if (notebookPaths.openPaths.length > 0) {
+                for (let i = 0; i < notebookPaths.openPaths.length; i++) {
+                    for (let j = i + 1; j < notebookPaths.openPaths.length; j++) {
+                        if (notebookPaths.openPaths[j].startsWith(notebookPaths.openPaths[i].replace(".sy", ""))) {
+                            notebookPaths.openPaths.splice(i, 1);
+                            j--;
+                        }
+                    }
+                }
+                notebookPaths.openPaths.forEach((openPath, index) => {
+                    const nextPath = this.element.querySelector(`[data-url="${notebookPaths.notebookId}"] li[data-path="${openPath}"]`)?.nextElementSibling?.firstElementChild?.getAttribute("data-path");
+                    if (nextPath) {
+                        notebookPaths.openPaths[index] = nextPath;
+                    }
+                });
+                filesPaths.push(notebookPaths);
+            }
+        });
+        window.siyuan.storage[Constants.LOCAL_FILESPATHS] = filesPaths;
+        setStorageVal(Constants.LOCAL_FILESPATHS, filesPaths);
+    }
+
     private genFileHTML = (item: IFile) => {
         let countHTML = "";
         if (item.count && item.count > 0) {

+ 2 - 1
app/src/protyle/util/compatibility.ts

@@ -231,6 +231,7 @@ export const getLocalStorage = (cb: () => void) => {
             id: "",
         };
         defaultStorage[Constants.LOCAL_FONTSTYLES] = [];
+        defaultStorage[Constants.LOCAL_FILESPATHS] = [];
         defaultStorage[Constants.LOCAL_SEARCHDATA] = {
             page: 1,
             sort: 0,
@@ -265,7 +266,7 @@ export const getLocalStorage = (cb: () => void) => {
             Constants.LOCAL_SEARCHDATA, Constants.LOCAL_ZOOM, Constants.LOCAL_LAYOUTS, Constants.LOCAL_AI,
             Constants.LOCAL_PLUGINTOPUNPIN, Constants.LOCAL_SEARCHASSET, Constants.LOCAL_FLASHCARD,
             Constants.LOCAL_DIALOGPOSITION, Constants.LOCAL_SEARCHUNREF, Constants.LOCAL_HISTORY,
-            Constants.LOCAL_OUTLINE, Constants.LOCAL_FILEPOSITION].forEach((key) => {
+            Constants.LOCAL_OUTLINE, Constants.LOCAL_FILEPOSITION, Constants.LOCAL_FILESPATHS].forEach((key) => {
             if (typeof response.data[key] === "string") {
                 try {
                     const parseData = JSON.parse(response.data[key]);