Skip to content

Commit 0f196f6

Browse files
authored
feat(categories-list): show post children of the category (#80)
1 parent 94e2603 commit 0f196f6

22 files changed

+521
-262
lines changed

src/commands/command-handler.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
export abstract class TreeViewCommandHandler<TData> {
2+
readonly input: unknown;
3+
4+
abstract handle(): Promise<void> | void;
5+
6+
abstract parseInput(): TData | null;
7+
}
8+
9+
export abstract class MultiSelectableTreeViewCommandHandler<TArgument, TData>
10+
implements TreeViewCommandHandler<TData[]>
11+
{
12+
private _selections: TData[] | null = null;
13+
constructor(public readonly input: TArgument) {}
14+
15+
get selections(): TData[] {
16+
if (this._selections == null) {
17+
this._selections = this.parseSelections();
18+
}
19+
20+
return this._selections;
21+
}
22+
23+
protected abstract parseSelections(): TData[];
24+
25+
abstract handle(): void | Promise<void>;
26+
27+
parseInput(): TData[] | null {
28+
return this.parseSelections();
29+
}
30+
}

src/commands/commands-registration.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@ import { uploadImage } from './upload-image/upload-image';
1919
import { revealLocalPostFileInOs } from './reveal-local-post-file-in-os';
2020
import { showLocalFileToPostInfo } from './show-local-file-to-post-info';
2121
import { newPostCategory } from './post-category/new-post-category';
22-
import { deleteSelectedCategories } from './post-category/delete-selected-categories';
2322
import { refreshPostCategoriesList } from './post-category/refresh-post-categories-list';
24-
import { updatePostCategory } from './post-category/update-post-category';
23+
import { handleUpdatePostCategory } from './post-category/update-post-category';
2524
import { openPostInVscode } from './posts-list/open-post-in-vscode';
2625
import { deletePostToLocalFileMap } from './posts-list/delete-post-to-local-file-map';
2726
import { renamePost } from './posts-list/rename-post';
@@ -34,6 +33,7 @@ import { exportPostToPdf } from './pdf/export-pdf.command';
3433
import { pullPostRemoteUpdates } from './pull-post-remote-updates';
3534
import { extractImages } from './extract-images';
3635
import { clearPostsSearchResults, refreshPostsSearchResults, searchPosts } from './posts-list/search';
36+
import { handleDeletePostCategories } from './post-category/delete-selected-categories';
3737

3838
export const registerCommands = () => {
3939
const context = globalState.extensionContext;
@@ -62,9 +62,9 @@ export const registerCommands = () => {
6262
commands.registerCommand(`${appName}.reveal-local-post-file-in-os`, revealLocalPostFileInOs),
6363
commands.registerCommand(`${appName}.show-post-to-local-file-info`, showLocalFileToPostInfo),
6464
commands.registerCommand(`${appName}.new-post-category`, newPostCategory),
65-
commands.registerCommand(`${appName}.delete-selected-post-categories`, deleteSelectedCategories),
65+
commands.registerCommand(`${appName}.delete-selected-post-categories`, handleDeletePostCategories),
6666
commands.registerCommand(`${appName}.refresh-post-categories-list`, refreshPostCategoriesList),
67-
commands.registerCommand(`${appName}.update-post-category`, updatePostCategory),
67+
commands.registerCommand(`${appName}.update-post-category`, handleUpdatePostCategory),
6868
commands.registerCommand(`${appName}.delete-post-to-local-file-map`, deletePostToLocalFileMap),
6969
commands.registerCommand(`${appName}.rename-post`, renamePost),
7070
commands.registerCommand(`${appName}.open-post-in-blog-admin`, openPostInBlogAdmin),
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { PostCategory } from '../../models/post-category';
2+
import { PostCategoriesListTreeItem } from '../../tree-view-providers/models/categories-list-tree-item';
3+
import { PostCategoryTreeItem } from '../../tree-view-providers/models/post-category-tree-item';
4+
import { extensionViews } from '../../tree-view-providers/tree-view-registration';
5+
import { MultiSelectableTreeViewCommandHandler, TreeViewCommandHandler } from '../command-handler';
6+
7+
export abstract class BasePostCategoryTreeViewCommandHandler implements TreeViewCommandHandler<PostCategory> {
8+
protected readonly view = extensionViews.postCategoriesList!;
9+
10+
constructor(public readonly input: unknown) {}
11+
12+
abstract handle(): void | Promise<void>;
13+
14+
parseInput(): PostCategory | null {
15+
const { input } = this;
16+
if (input instanceof PostCategory || input instanceof PostCategoryTreeItem) {
17+
const category = input instanceof PostCategoryTreeItem ? input.category : input;
18+
this.view.reveal(input).then(undefined, undefined);
19+
return category;
20+
}
21+
22+
return null;
23+
}
24+
}
25+
26+
export abstract class BaseMultiSelectablePostCategoryTreeViewCommandHandler extends MultiSelectableTreeViewCommandHandler<
27+
PostCategoriesListTreeItem,
28+
PostCategory
29+
> {
30+
protected get view() {
31+
return extensionViews.postCategoriesList;
32+
}
33+
34+
protected parseSelections() {
35+
const categories =
36+
this.view?.selection
37+
.map(x => (x instanceof PostCategoryTreeItem ? x.category : x instanceof PostCategory ? x : null))
38+
.filter((x): x is PostCategory => x != null) ?? [];
39+
const inputCategory =
40+
this.input instanceof PostCategoryTreeItem
41+
? this.input.category
42+
: this.input instanceof PostCategory
43+
? this.input
44+
: null;
45+
if (inputCategory && !categories.find(x => x.categoryId === inputCategory.categoryId)) {
46+
categories.unshift(inputCategory);
47+
}
48+
return categories;
49+
}
50+
}
Lines changed: 72 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,85 @@
11
import { MessageOptions, ProgressLocation, window } from 'vscode';
2-
import { PostCategories, PostCategory } from '../../models/post-category';
3-
import { AlertService } from '../../services/alert.service';
2+
import { PostCategory } from '../../models/post-category';
43
import { postCategoryService } from '../../services/post-category.service';
5-
import { extensionViews } from '../../tree-view-providers/tree-view-registration';
4+
import { PostCategoriesListTreeItem } from '../../tree-view-providers/models/categories-list-tree-item';
5+
import { BaseMultiSelectablePostCategoryTreeViewCommandHandler } from './base-tree-view-command-handler';
66
import { refreshPostCategoriesList } from './refresh-post-categories-list';
77

8-
const confirmDelete = async (categories: PostCategories): Promise<boolean> => {
9-
const options = ['确定'];
10-
const clicked = await window.showWarningMessage(
11-
'确定要删除这些博文分类吗',
12-
{
13-
detail: `${categories.map(x => `📂${x.title}`).join(', ')} 将被永久删除! 请谨慎操作!`,
14-
modal: true,
15-
} as MessageOptions,
16-
...options
17-
);
18-
if (clicked === options[0]) {
19-
return true;
8+
export class DeletePostCategoriesHandler extends BaseMultiSelectablePostCategoryTreeViewCommandHandler {
9+
constructor(input: PostCategoriesListTreeItem) {
10+
super(input);
2011
}
2112

22-
return false;
23-
};
13+
async handle(): Promise<void> {
14+
const {
15+
selections: { length },
16+
} = this;
2417

25-
export const deleteSelectedCategories = async (category?: PostCategory) => {
26-
const view = extensionViews.postCategoriesList;
27-
let selectedCategories = view?.selection ?? [];
28-
if (category && !selectedCategories.includes(category)) {
29-
selectedCategories = [category];
30-
}
31-
if (selectedCategories.length <= 0) {
32-
AlertService.warning('删除博文分类失败, 没有选中任何博文分类');
33-
return;
34-
}
35-
if (!(await confirmDelete(Array.from(selectedCategories)))) {
36-
return;
18+
if (length <= 0 || !(await this.confirm())) {
19+
return;
20+
}
21+
22+
await this.delete();
3723
}
3824

39-
await window.withProgress(
40-
{
41-
title: '正在删除博文分类',
42-
location: ProgressLocation.Notification,
43-
},
44-
async p => {
45-
p.report({ increment: 10 });
46-
let idx = 0;
47-
const errs: [PostCategory, any][] = [];
48-
for (const category of selectedCategories) {
49-
try {
50-
const increment = Math.round(10 + idx / selectedCategories.length / 90);
51-
p.report({ increment, message: `正在删除: 📂${category.title}` });
52-
await postCategoryService.deleteCategory(category.categoryId);
53-
idx++;
54-
} catch (err) {
55-
errs.push([category, err]);
25+
private delete() {
26+
const { selections: selectedCategories } = this;
27+
return window.withProgress(
28+
{
29+
title: '正在删除博文分类',
30+
location: ProgressLocation.Notification,
31+
},
32+
async p => {
33+
p.report({ increment: 10 });
34+
let idx = 0;
35+
const errs: [PostCategory, any][] = [];
36+
for (const category of selectedCategories) {
37+
try {
38+
const increment = Math.round(10 + idx / selectedCategories.length / 90);
39+
p.report({ increment, message: `正在删除: 📂${category.title}` });
40+
await postCategoryService.deleteCategory(category.categoryId);
41+
idx++;
42+
} catch (err) {
43+
errs.push([category, err]);
44+
}
5645
}
57-
}
5846

59-
p.report({ increment: 100 });
60-
if (errs.length > 0) {
61-
await window.showErrorMessage('删除博文分类时发生了一些错误', {
62-
detail: errs
63-
.map(
64-
err =>
65-
`${err[0].title} - ${err[1] instanceof Error ? err[1].message : JSON.stringify(err[1])}`
66-
)
67-
.join('\n'),
68-
} as MessageOptions);
69-
}
70-
if (errs.length < selectedCategories.length) {
71-
refreshPostCategoriesList();
47+
p.report({ increment: 100 });
48+
if (errs.length > 0) {
49+
await window.showErrorMessage('删除博文分类时发生了一些错误', {
50+
detail: errs
51+
.map(
52+
err =>
53+
`${err[0].title} - ${
54+
err[1] instanceof Error ? err[1].message : JSON.stringify(err[1])
55+
}`
56+
)
57+
.join('\n'),
58+
} as MessageOptions);
59+
}
60+
if (errs.length < selectedCategories.length) {
61+
refreshPostCategoriesList();
62+
}
7263
}
64+
);
65+
}
66+
67+
private async confirm() {
68+
const options = ['确定'];
69+
const clicked = await window.showWarningMessage(
70+
'确定要删除这些博文分类吗',
71+
{
72+
detail: `${this.selections.map(x => `📂${x.title}`).join(', ')} 将被永久删除! 请谨慎操作!`,
73+
modal: true,
74+
} as MessageOptions,
75+
...options
76+
);
77+
if (clicked === options[0]) {
78+
return true;
7379
}
74-
);
75-
};
80+
81+
return false;
82+
}
83+
}
84+
85+
export const handleDeletePostCategories = (arg: any) => new DeletePostCategoriesHandler(arg);
Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import { postCategoryService } from '../../services/post-category.service';
2-
import { postCategoriesDataProvider } from '../../tree-view-providers/categories-view-data-provider';
1+
import { postCategoriesDataProvider } from '../../tree-view-providers/post-categories-tree-data-provider';
32

43
export const refreshPostCategoriesList = () => {
5-
postCategoryService.clearCache();
6-
postCategoriesDataProvider.fireTreeDataChangedEvent();
4+
postCategoriesDataProvider.refresh();
75
};

src/commands/post-category/update-post-category.ts

Lines changed: 58 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,56 +2,68 @@ import fs from 'fs';
22
import { MessageOptions, ProgressLocation, window, Uri, workspace } from 'vscode';
33
import { PostCategory } from '../../models/post-category';
44
import { postCategoryService } from '../../services/post-category.service';
5-
import { extensionViews } from '../../tree-view-providers/tree-view-registration';
65
import { inputPostCategory } from './input-post-category';
76
import { refreshPostCategoriesList } from './refresh-post-categories-list';
87
import { Settings } from '../../services/settings.service';
8+
import { BasePostCategoryTreeViewCommandHandler } from './base-tree-view-command-handler';
99

10-
export const updatePostCategory = async (category?: PostCategory) => {
11-
if (!category) {
12-
return;
13-
}
14-
await extensionViews.postCategoriesList?.reveal(category);
15-
const addDto = await inputPostCategory({
16-
title: '编辑博文分类',
17-
category,
18-
});
19-
if (!addDto) {
20-
return;
21-
}
22-
const updateDto = Object.assign(new PostCategory(), category, addDto);
23-
if (!updateDto) {
24-
return;
25-
}
10+
class UpdatePostCategoryTreeViewCommandHandler extends BasePostCategoryTreeViewCommandHandler {
11+
async handle(): Promise<void> {
12+
const category = this.parseInput();
13+
if (category == null) {
14+
return;
15+
}
2616

27-
await window.withProgress(
28-
{
29-
title: `正在更新博文分类 - ${updateDto.title}`,
30-
location: ProgressLocation.Notification,
31-
},
32-
async p => {
33-
p.report({ increment: 10 });
34-
try {
35-
await postCategoryService.updateCategory(updateDto);
36-
refreshPostCategoriesList();
37-
// 如果选择了createLocalPostFileWithCategory模式且本地有该目录,则重命名该目录
38-
const workspaceUri = Settings.workspaceUri;
39-
const createLocalPostFileWithCategory = Settings.createLocalPostFileWithCategory;
40-
const uri = Uri.joinPath(workspaceUri, category.title).fsPath;
41-
const isFileExist = fs.existsSync(uri);
42-
if (createLocalPostFileWithCategory && isFileExist) {
43-
const oldUri = Uri.joinPath(workspaceUri, category.title);
44-
const newUri = Uri.joinPath(workspaceUri, addDto.title);
45-
await workspace.fs.rename(oldUri, newUri);
17+
const addDto = await inputPostCategory({
18+
title: '编辑博文分类',
19+
category,
20+
});
21+
if (!addDto) {
22+
return;
23+
}
24+
25+
const updateDto = Object.assign(new PostCategory(), category, addDto);
26+
if (!updateDto) {
27+
return;
28+
}
29+
30+
await window.withProgress(
31+
{
32+
title: `正在更新博文分类 - ${updateDto.title}`,
33+
location: ProgressLocation.Notification,
34+
},
35+
async p => {
36+
p.report({ increment: 10 });
37+
try {
38+
await postCategoryService.updateCategory(updateDto);
39+
refreshPostCategoriesList();
40+
// 如果选择了createLocalPostFileWithCategory模式且本地有该目录,则重命名该目录
41+
const workspaceUri = Settings.workspaceUri;
42+
const createLocalPostFileWithCategory = Settings.createLocalPostFileWithCategory;
43+
const uri = Uri.joinPath(workspaceUri, category.title).fsPath;
44+
const isFileExist = fs.existsSync(uri);
45+
if (createLocalPostFileWithCategory && isFileExist) {
46+
const oldUri = Uri.joinPath(workspaceUri, category.title);
47+
const newUri = Uri.joinPath(workspaceUri, addDto.title);
48+
await workspace.fs.rename(oldUri, newUri);
49+
}
50+
} catch (err) {
51+
this.notifyFailed(err);
52+
} finally {
53+
p.report({ increment: 100 });
4654
}
47-
} catch (err) {
48-
void window.showErrorMessage('更新博文分类失败', {
49-
detail: `服务器返回了错误, ${err instanceof Error ? err.message : JSON.stringify(err)}`,
50-
modal: true,
51-
} as MessageOptions);
52-
} finally {
53-
p.report({ increment: 100 });
5455
}
55-
}
56-
);
57-
};
56+
);
57+
}
58+
59+
private notifyFailed(err: unknown) {
60+
window
61+
.showErrorMessage('更新博文分类失败', {
62+
detail: `服务器返回了错误, ${err instanceof Error ? err.message : JSON.stringify(err)}`,
63+
modal: true,
64+
} as MessageOptions)
65+
.then(undefined, undefined);
66+
}
67+
}
68+
69+
export const handleUpdatePostCategory = (arg: unknown) => new UpdatePostCategoryTreeViewCommandHandler(arg).handle();

0 commit comments

Comments
 (0)