diff --git a/src/auth/auth-manager.ts b/src/auth/auth-manager.ts index af38d9bd..8c60ffe1 100644 --- a/src/auth/auth-manager.ts +++ b/src/auth/auth-manager.ts @@ -19,7 +19,7 @@ authProvider.onDidChangeSessions(async e => { await AuthManager.updateAuthStatus() accountViewDataProvider.fireTreeDataChangedEvent() - postCategoryDataProvider.refresh() + await postCategoryDataProvider.refreshAsync() if (e.removed != null) postDataProvider.refresh() else await PostListView.refresh() diff --git a/src/cmd/pdf/post-pdf-template-builder.ts b/src/cmd/pdf/post-pdf-template-builder.ts index a6a6cfb9..108b6b26 100644 --- a/src/cmd/pdf/post-pdf-template-builder.ts +++ b/src/cmd/pdf/post-pdf-template-builder.ts @@ -1,11 +1,12 @@ +/* eslint-disable prettier/prettier */ import { Post } from '@/model/post' import { PostFileMapManager } from '@/service/post/post-file-map' import fs from 'fs' import { BlogSettingService } from '@/service/blog-setting' -import { PostCatService } from '@/service/post/post-cat' import { PostCat } from '@/model/post-cat' import { markdownItFactory } from '@cnblogs/markdown-it-presets' import { UserService } from '@/service/user.service' +import { PostCateStore } from '@/stores/post-cate-store' export namespace PostPdfTemplateBuilder { export const HighlightedMessage = 'markdown-highlight-finished' @@ -19,11 +20,11 @@ export namespace PostPdfTemplateBuilder { const html = isMarkdown ? markdownItFactory({ - codeHighlight: false, - math: true, - disableRules: [], - html: true, - }).render(postBody) + codeHighlight: false, + math: true, + disableRules: [], + html: true, + }).render(postBody) : postBody const buildTagHtml = (): Promise => { @@ -36,7 +37,7 @@ export namespace PostPdfTemplateBuilder { } const buildCategoryHtml = async (): Promise => { - const categories = await PostCatService.getAll() + const categories = (await PostCateStore.createAsync()).getFlatAll() const postCategories = post.categoryIds ?.map(categoryId => categories.find(x => x.categoryId === categoryId)) @@ -44,11 +45,11 @@ export namespace PostPdfTemplateBuilder { let html = postCategories.length > 0 ? postCategories - .map( - c => - `${c?.title}` - ) - .join(', ') + .map( + c => + `${c?.title}` + ) + .join(', ') : '' html = html !== '' ? `
分类: ${html}
` : '' return html diff --git a/src/cmd/post-cat/del-selected-cat.ts b/src/cmd/post-cat/del-selected-cat.ts index c3c6ae65..839c8595 100644 --- a/src/cmd/post-cat/del-selected-cat.ts +++ b/src/cmd/post-cat/del-selected-cat.ts @@ -56,7 +56,7 @@ export async function delSelectedCat(input?: PostCatTreeItem | PostCat) { } p.report({ increment: 100 }) - postCategoryDataProvider.refresh() + await postCategoryDataProvider.refreshAsync() } ) } diff --git a/src/cmd/post-cat/new-post-cat.ts b/src/cmd/post-cat/new-post-cat.ts index f87e9a46..c2211d96 100644 --- a/src/cmd/post-cat/new-post-cat.ts +++ b/src/cmd/post-cat/new-post-cat.ts @@ -3,6 +3,7 @@ import { PostCatService } from '@/service/post/post-cat' import { extTreeViews } from '@/tree-view/tree-view-register' import { inputPostCat } from './input-post-cat' import { postCategoryDataProvider } from '@/tree-view/provider/post-category-tree-data-provider' +import { PostCateStore } from '@/stores/post-cate-store' export async function newPostCat() { const input = await inputPostCat('新建分类') @@ -22,9 +23,9 @@ export async function newPostCat() { increment: 70, }) - postCategoryDataProvider.refresh() + await postCategoryDataProvider.refreshAsync() - const allCategory = await PostCatService.getAll() + const allCategory = (await PostCateStore.createAsync()).getFlatAll() const newCategory = allCategory.find(x => x.title === input.title) if (newCategory !== undefined) await extTreeViews.postCategoriesList.reveal(newCategory) diff --git a/src/cmd/post-cat/update-post-cat-treeview.ts b/src/cmd/post-cat/update-post-cat-treeview.ts index a8bce650..be16095a 100644 --- a/src/cmd/post-cat/update-post-cat-treeview.ts +++ b/src/cmd/post-cat/update-post-cat-treeview.ts @@ -32,7 +32,7 @@ export async function updatePostCatTreeView(arg?: PostCat | PostCatTreeItem) { p.report({ increment: 10 }) try { await PostCatService.update(updateDto) - postCategoryDataProvider.refresh() + await postCategoryDataProvider.refreshAsync() p.report({ increment: 100 }) } catch (e) { void Alert.err(`更新博文失败: ${e}`) diff --git a/src/cmd/show-local-file-to-post-info.ts b/src/cmd/show-local-file-to-post-info.ts index ac67ea20..ae2fd144 100644 --- a/src/cmd/show-local-file-to-post-info.ts +++ b/src/cmd/show-local-file-to-post-info.ts @@ -1,12 +1,13 @@ +/* eslint-disable prettier/prettier */ import path from 'path' import { Uri } from 'vscode' import { Alert } from '@/infra/alert' import { PostService } from '@/service/post/post' -import { PostCatService } from '@/service/post/post-cat' import { PostFileMapManager } from '@/service/post/post-file-map' import { searchPostByTitle } from '@/service/post/search-post-by-title' import { viewPostOnline } from './view-post-online' import format from 'date-fns/format' +import { PostCateStore } from '@/stores/post-cate-store' /** * 本地文件所关联的博文信息 @@ -64,7 +65,7 @@ export async function showLocalFileToPostInfo(input: Uri | number): Promise post.categoryIds?.includes(x.categoryId)) const categoryDesc = categories.length > 0 ? `博文分类: ${categories.map(c => c.title).join(', ')}\n` : '' const tagsDesc = (post.tags?.length ?? 0) > 0 ? `博文标签: ${post.tags?.join(', ')}\n` : '' @@ -77,9 +78,8 @@ export async function showLocalFileToPostInfo(input: Uri | number): PromiseJSON.parse(resp) if (categories == null) return [] - return categories.map(x => Object.assign(new PostCat(), x)) - } catch (e) { - if (await UserService.hasBlog()) void Alert.err(`查询随笔分类失败: ${e}`) - throw e - } - } - - export async function getFlatAll() { - const categories = await getAll() - if (categories == null || categories.length === 0) return [] - - const flat = [] - const queue = categories - while (queue.length > 0) { - const current = queue.pop() - if (current == null) continue - flat.push(current) - if (current.children != null) for (const child of current.children) queue.unshift(child) - } - - return flat.sort((x, y) => { - const order1 = x.order ?? DEFAULT_ORDER - const order2 = y.order ?? DEFAULT_ORDER - if (order1 > order2) return 1 - else if (order1 < order2) return -1 - else return x.title.localeCompare(y.title) - }) - } - - export async function getOne(categoryId: number) { - const req = await getAuthedPostCatReq() - try { - const resp = await req.getOne(categoryId) - const { parent } = <{ parent: PostCat | null }>JSON.parse(resp) - return Object.assign(new PostCat(), parent) - } catch (e) { - if (await UserService.hasBlog()) void Alert.err(`查询随笔分类失败: ${e}`) - throw e - } - } - - export async function getAllUnder(parentId: number) { - const req = await getAuthedPostCatReq() - try { - const resp = await req.getOne(parentId) - const { categories } = <{ categories: PostCat[] }>JSON.parse(resp) - if (categories == null) return [] - return categories.map(x => Object.assign(new PostCat(), x)) + return categories } catch (e) { if (await UserService.hasBlog()) void Alert.err(`查询随笔分类失败: ${e}`) throw e diff --git a/src/service/post/post-cfg-panel.ts b/src/service/post/post-cfg-panel.ts index 20329463..622eef0e 100644 --- a/src/service/post/post-cfg-panel.ts +++ b/src/service/post/post-cfg-panel.ts @@ -17,6 +17,7 @@ import { Token } from '@/wasm' import { PostTagReq } from '@/wasm' import { PostTag } from '@/wasm' import { AuthManager } from '@/auth/auth-manager' +import { PostCateStore } from '@/stores/post-cate-store' async function getAuthedPostTagReq() { const token = await AuthManager.acquireToken() @@ -72,11 +73,13 @@ export namespace PostCfgPanel { baseUrl: webview.asWebviewUri(Uri.joinPath(globalCtx.assetsUri, 'fonts')).toString() + '/', } as WebviewMsg.SetFluentIconBaseUrlMsg) + const postCateStore = await PostCateStore.createAsync() + await webview.postMessage({ command: Webview.Cmd.Ui.editPostCfg, post: cloneDeep(post), activeTheme: vscode.window.activeColorTheme.kind, - userCats: cloneDeep(await PostCatService.getFlatAll()), + userCats: cloneDeep(postCateStore.getFlatAll()), siteCats: cloneDeep(await PostCatService.getSitePresetList()), tags, breadcrumbs, @@ -161,10 +164,11 @@ const observeWebviewMsg = (panel: WebviewPanel, options: PostCfgPanelOpenOption) await doUploadImg(webview, message) } else if (command === Webview.Cmd.Ext.getChildCategories) { const { payload } = message as WebviewCommonCmd + const cateStore = await PostCateStore.createAsync() await webview.postMessage({ command: Webview.Cmd.Ui.updateChildCategories, payload: { - value: await PostCatService.getAllUnder(payload.parentId).catch(() => []), + value: cateStore.getChildren(payload.parentId), parentId: payload.parentId, }, }) diff --git a/src/setup/setup-cmd.ts b/src/setup/setup-cmd.ts index 978848de..08137394 100644 --- a/src/setup/setup-cmd.ts +++ b/src/setup/setup-cmd.ts @@ -92,7 +92,7 @@ export function setupCmd() { // post category regCmd(extName`.post-category.new`, newPostCat), regCmd(extName`.post-category.del-select`, delSelectedCat), - regCmd(extName`.post-category.refresh`, () => postCategoryDataProvider.refresh()), + regCmd(extName`.post-category.refresh`, () => postCategoryDataProvider.refreshAsync()), regCmd(extName`.post-category.update`, updatePostCatTreeView), // workspace regCmd(extName`.workspace.set`, Workspace.set), diff --git a/src/setup/setup-watch.ts b/src/setup/setup-watch.ts index a2886b5b..efca6dd4 100644 --- a/src/setup/setup-watch.ts +++ b/src/setup/setup-watch.ts @@ -8,10 +8,10 @@ import { PostListView } from '@/cmd/post-list/post-list-view' import { postCategoryDataProvider } from '@/tree-view/provider/post-category-tree-data-provider' export const setupCfgWatch = () => - workspace.onDidChangeConfiguration(ev => { + workspace.onDidChangeConfiguration(async ev => { if (ev.affectsConfiguration('cnblogsClient')) isTargetWorkspace() - if (ev.affectsConfiguration('workbench.iconTheme')) postCategoryDataProvider.refresh() + if (ev.affectsConfiguration('workbench.iconTheme')) await postCategoryDataProvider.refreshAsync() if (ev.affectsConfiguration('cnblogsClient.pageSize.postList')) void PostListView.refresh({ queue: true }) diff --git a/src/stores/post-cate-store.ts b/src/stores/post-cate-store.ts new file mode 100644 index 00000000..1140bf6a --- /dev/null +++ b/src/stores/post-cate-store.ts @@ -0,0 +1,95 @@ +import { PostCat } from '@/model/post-cat' +import { PostCatService } from '@/service/post/post-cat' + +const DEFAULT_ORDER: number = 999999 + +export class PostCateStore { + // eslint-disable-next-line prettier/prettier + constructor(private categories: ReadonlyArray) { } + + get isNullOrEmpty() { + return this.categories == null || this.categories.length === 0 + } + + static async createAsync() { + const categories = await PostCatService.getAll() + return new PostCateStore(categories) + } + + private static clonePostCat(categories: ReadonlyArray) { + return categories.map(x => { + const cate = Object.assign(new PostCat(), x) + return cate + }) + } + + async refreshAsync() { + this.categories = await PostCatService.getAll() + } + + getFlatAll() { + if (this.isNullOrEmpty) return [] + + const flat: PostCat[] = [] + const queue = this.getRoots() + while (queue.length > 0) { + const current = queue.pop() + if (current == null) continue + flat.push(Object.assign(new PostCat(), current)) + if (current.children != null) for (const child of current.children) queue.unshift(child) + } + + return flat.sort((x, y) => { + const order1 = x.order ?? DEFAULT_ORDER + const order2 = y.order ?? DEFAULT_ORDER + if (order1 > order2) return 1 + else if (order1 < order2) return -1 + else return x.title.localeCompare(y.title) + }) + } + + getRoots() { + if (this.isNullOrEmpty) return [] + return PostCateStore.clonePostCat(this.categories) + } + + getChildren(categoryId: number) { + if (this.isNullOrEmpty) return [] + + let children: PostCat[] = [] + const queue = this.getRoots() + while (queue.length > 0) { + const current = queue.pop() + if (current == null) continue + if (current.categoryId === categoryId) { + if (current.children != null) children = current.children + break + } + + if (current.children != null) for (const child of current.children) queue.unshift(child) + } + + children = PostCateStore.clonePostCat(children) + return children + } + + getOne(categoryId: number) { + if (this.isNullOrEmpty) return null + + let category: PostCat | null = null + const queue = this.getRoots() + while (queue.length > 0) { + const current = queue.pop() + if (current == null) continue + if (current.categoryId === categoryId) { + category = Object.assign(new PostCat(), current) + break + } + + if (current.children != null) for (const child of current.children) queue.unshift(child) + } + + if (category != null) category.children = [] + return category + } +} diff --git a/src/tree-view/model/post-metadata.ts b/src/tree-view/model/post-metadata.ts index 498aecb0..90033d57 100644 --- a/src/tree-view/model/post-metadata.ts +++ b/src/tree-view/model/post-metadata.ts @@ -1,3 +1,4 @@ +/* eslint-disable prettier/prettier */ import differenceInSeconds from 'date-fns/differenceInSeconds' import differenceInYears from 'date-fns/differenceInYears' import format from 'date-fns/format' @@ -6,12 +7,12 @@ import zhCN from 'date-fns/locale/zh-CN' import { TreeItem, TreeItemCollapsibleState, ThemeIcon } from 'vscode' import { AccessPermission, Post, formatAccessPermission } from '@/model/post' import { PostEditDto } from '@/model/post-edit-dto' -import { PostCatService } from '@/service/post/post-cat' import { PostService } from '@/service/post/post' import { BaseEntryTreeItem } from './base-entry-tree-item' import { BaseTreeItemSource } from './base-tree-item-source' import { PostTreeItem } from './post-tree-item' import { PostCat } from '@/model/post-cat' +import { PostCateStore } from '@/stores/post-cate-store' export enum RootPostMetadataType { categoryEntry = 'categoryEntry', @@ -81,8 +82,8 @@ export abstract class PostMetadata extends BaseTreeItemSource { export abstract class PostEntryMetadata extends PostMetadata - implements BaseEntryTreeItem -{ + // eslint-disable-next-line prettier/prettier + implements BaseEntryTreeItem { constructor( parent: Post, public readonly children: T[] @@ -138,8 +139,10 @@ export class PostCatMetadata extends PostMetadata { static async parse(parent: Post, editDto?: PostEditDto): Promise { if (editDto === undefined) editDto = await PostService.getPostEditDto(parent.id) + const cateStore = await PostCateStore.createAsync() + const categoryIds = editDto.post.categoryIds ?? [] - const futList = categoryIds.map(PostCatService.getOne) + const futList = categoryIds.map(x => cateStore.getOne(x)) const categoryList = await Promise.all(futList) return categoryList @@ -214,8 +217,8 @@ export abstract class PostDateMetadata extends PostMetadata { toTreeItem = (): TreeItem => Object.assign( new TreeItem( - `${this.label}: ${ - this.shouldUseDistance() ? this.distance + `(${this.formattedDate})` : this.formattedDate + // eslint-disable-next-line prettier/prettier + `${this.label}: ${this.shouldUseDistance() ? this.distance + `(${this.formattedDate})` : this.formattedDate }` ), { @@ -267,7 +270,7 @@ export class PostAccessPermissionMetadata extends PostMetadata { Object.assign>( new TreeItem( `访问权限: ${formatAccessPermission(this.parent.accessPermission)}` + - (isPasswordRequired ? '(需密码)' : '') + (isPasswordRequired ? '(需密码)' : '') ), { iconPath: PostAccessPermissionMetadata.parseIcon(this.parent.accessPermission, isPasswordRequired), diff --git a/src/tree-view/provider/post-category-tree-data-provider.ts b/src/tree-view/provider/post-category-tree-data-provider.ts index b9df630f..404f88ac 100644 --- a/src/tree-view/provider/post-category-tree-data-provider.ts +++ b/src/tree-view/provider/post-category-tree-data-provider.ts @@ -1,6 +1,5 @@ import { flattenDepth, take } from 'lodash-es' import { EventEmitter, ProviderResult, TreeDataProvider, TreeItem } from 'vscode' -import { PostCatService } from '@/service/post/post-cat' import { PostService } from '@/service/post/post' import { toTreeItem } from '@/tree-view/convert' import { PostCatListTreeItem } from '@/tree-view/model/category-list-tree-item' @@ -9,11 +8,13 @@ import { PostEntryMetadata, PostMetadata, RootPostMetadataType } from '@/tree-vi import { PostTreeItem } from '@/tree-view/model/post-tree-item' import { Alert } from '@/infra/alert' import { setCtx } from '@/ctx/global-ctx' +import { PostCateStore } from '@/stores/post-cate-store' export class PostCatTreeDataProvider implements TreeDataProvider { private _treeDataChanged = new EventEmitter() private _isLoading = false private _roots: PostCatTreeItem[] | null = null + private _postCateStore: PostCateStore | null = null get isLoading() { return this._isLoading @@ -38,6 +39,11 @@ export class PostCatTreeDataProvider implements TreeDataProvider list.map(c => new PostCatTreeItem(c))) + const roots = (await this.getPostCateStore()).getRoots() + this._roots = roots.map(c => new PostCatTreeItem(c)) return this._roots } @@ -119,7 +127,7 @@ export class PostCatTreeDataProvider implements TreeDataProvider new PostCatTreeItem(x)) } catch (e) {