import { atom, computed, type MapStore, map, type WritableAtom } from 'nanostores'; import type { Section } from '~/types/actions'; import type { DocumentProperties, Editor } from '~/types/editor'; import type { PageMap, PagesStore } from './pages'; /** * 编辑器文档,结构为 */ export type EditorDocuments = Record; type SelectedDocument = WritableAtom; export type EditorSection = Record; // 编辑器命令类型 export type EditorCommandType = 'scrollToElement' | 'exportToZip'; // 编辑器命令接口 export interface EditorCommand { type: EditorCommandType; payload: any; } // 创建一个用于发送编辑器命令的atom export const editorCommands = atom(null); /** * 与 Editor 进行对接的 store。 * 其内部保存的数据可以直接由 editor 使用与操作,并且当前数据与 editor 实时同步。 */ export class EditorStore { private readonly pagesStore: PagesStore; editorInstance: WritableAtom = import.meta.hot?.data?.editorInstance ?? atom(null); // 编辑器中当前选中的文档。 selectedDocument: SelectedDocument = import.meta.hot?.data?.selectedPage ?? atom(); // 编辑器文档数据,始终是与编辑器所保持的最新数据,但此数据不一定执行了保存。 editorDocuments: MapStore = import.meta.hot?.data?.documents ?? map({}); // 当前编辑器文档,基于 editorDocuments 和 selectedDocument 计算而来。始终是与编辑器所保持的最新数据,但此数据不一定执行了保存。 currentDocument = computed([this.editorDocuments, this.selectedDocument], (documents, selectedDocument) => { if (!selectedDocument) { return undefined; } return documents[selectedDocument]; }); // 当前编辑器未保存的页面 unsavedDocuments: WritableAtom> = import.meta.hot?.data?.unsavedDocuments ?? atom(new Set()); // 编辑器文档最后保存时间 documentLastSaved: WritableAtom> = import.meta.hot?.data?.documentLastSaved ?? atom>({}); constructor(pagesStore: PagesStore) { this.pagesStore = pagesStore; if (import.meta.hot && import.meta.hot.data) { import.meta.hot.data.unsavedDocuments = this.unsavedDocuments; import.meta.hot.data.selectedDocument = this.selectedDocument; import.meta.hot.data.editorDocuments = this.editorDocuments; import.meta.hot.data.documentLastSaved = this.documentLastSaved; } this.setupCoordination(); } private setupCoordination() { // 监听 pagesStore 的 pages 变化 this.pagesStore.pages.listen((pages) => { this.setDocuments(pages); }); // 监听 pagesStore 的 activePage 变化 this.pagesStore.activePage.listen((pageName) => { this.selectedDocument.set(pageName); }); } setEditorInstance(editor: Editor) { this.editorInstance.set(editor); } getEditorInstance() { return this.editorInstance.get(); } setDocuments(pages: PageMap, updateContent: boolean = false) { const documents = this.editorDocuments.get(); this.editorDocuments.set( Object.fromEntries( Object.entries(pages) .map(([pageName, page]) => { if (page === undefined) { return undefined; } const oldDocument = documents[pageName]; if (oldDocument && !updateContent) { return [pageName, { ...oldDocument, name: pageName, title: page.title }]; } return [ pageName, { name: pageName, title: page.title, content: page.content, }, ] as [string, DocumentProperties]; }) .filter(Boolean) as Array<[string, DocumentProperties]>, ), ); } updatePageState(pageName: string, page: Omit) { const documents = this.editorDocuments.get(); const oldDocumentState = documents[pageName]; if (!oldDocumentState) { return; } const content = oldDocumentState.content; this.editorDocuments.setKey(pageName, { ...oldDocumentState, ...page, content }); } updateDocumentContent(pageName: string, newContent: string) { const documents = this.editorDocuments.get(); const oldDocumentState = documents[pageName]; if (!oldDocumentState) { return; } const oldContent = oldDocumentState.content; const contentChanged = oldContent !== newContent; if (contentChanged) { this.editorDocuments.setKey(pageName, { ...oldDocumentState, content: newContent, }); } this.updateUnsavedDocuments(pageName, newContent); } private updateUnsavedDocuments(pageName: string, newContent: string) { const savedContent = this.pagesStore.getPage(pageName)?.content; // 是否存在未保存的更改 const unsavedChanges = savedContent === undefined || savedContent !== newContent; const currentDocument = this.currentDocument.get(); if (!currentDocument) { return; } // 保存数据至未保存中 const previousUnsavedPages = this.unsavedDocuments.get(); // 如果已经将此页面标记为未保存,则不进行更新。 if (unsavedChanges && previousUnsavedPages.has(pageName)) { return; } const newUnsavedPages = new Set(previousUnsavedPages); // 如果存在未保存的更改,则将此页面标记为未保存。否则,将此页面从未保存中移除。 if (unsavedChanges) { newUnsavedPages.add(pageName); } else { newUnsavedPages.delete(pageName); } this.unsavedDocuments.set(newUnsavedPages); } removeUnsavedDocument(pageName: string, saved: boolean = false) { const newUnsavedPages = new Set(this.unsavedDocuments.get()); newUnsavedPages.delete(pageName); this.unsavedDocuments.set(newUnsavedPages); if (!saved) { return; } // 记录保存时间 const currentTime = Date.now(); const lastSavedTimes = this.documentLastSaved.get(); this.documentLastSaved.set({ ...lastSavedTimes, [pageName]: currentTime, }); } scrollToElement(domId: string) { editorCommands.set({ type: 'scrollToElement', payload: { domId }, }); } }