Skip to content

Commit 94e2603

Browse files
authored
feat(posts-list): support search posts (#79)
* feat(posts-list): support search posts * feat(posts-list/search): allow clear search result * feat(posts-list/search): allow refresh search results * fix(posts-list/result): fix delete not working * fix(posts-list): make commands work for the search-result-item * docs: add document about search posts
1 parent bec9dbf commit 94e2603

25 files changed

+528
-238
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
- [登录 / 授权](#登录--授权)
1212
- [将本地 markdown 文件发布到博客园](#将本地-markdown-文件发布到博客园)
1313
- [博客园博文列表](#博客园博文列表)
14+
- [搜索博文](#搜索博文)
1415
- [将本地文件关联到博客园博文](#将本地文件关联到博客园博文)
1516
- [拉取远程博文内容更新本地文件](#拉取远程博文内容更新本地文件)
1617
- [图片上传](#图片上传)
@@ -51,6 +52,16 @@
5152

5253
<kbd><img src="https://img2020.cnblogs.com/blog/3/202112/3-20211227184342642-1938639868.png" height="550"></kbd>
5354

55+
### 搜索博文
56+
57+
在博文列表的工具栏中, 包含一个搜索的图标, 点击这个图标可以触发搜索功能, 点击后会先要求输入关键词, 输入完成后按回车确认, 搜索结果将在列表中进行展示
58+
59+
<kbd><img src="https://img2022.cnblogs.com/blog/35695/202210/35695-20221027141735814-276823372.png"></kbd>
60+
61+
列表中的 `搜索结果` 那一项的工具栏包含两个可以使用的命令, 分别是 `刷新搜索结果``清除搜索结果`; 也可以通过右键上下文菜单调用这两个命令
62+
63+
<kbd><img src="https://img2022.cnblogs.com/blog/35695/202210/35695-20221027142035784-2094216229.png"></kbd>
64+
5465
### 将本地文件关联到博客园博文
5566

5667
一个本地文件可以关联到一篇博客园博文,本地文件必须在 `vscode-cnb.workspace` 配置的工作目录中

package.json

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,20 @@
7575
"fontPath": "dist/assets/icons.woff2",
7676
"fontCharacter": "\\e65b"
7777
}
78+
},
79+
"vscode-cnb-posts-list-search": {
80+
"description": "posts list search",
81+
"default": {
82+
"fontPath": "dist/assets/icons.woff2",
83+
"fontCharacter": "\\e610"
84+
}
85+
},
86+
"vscode-cnb-posts-list-search-result-summary": {
87+
"description": "posts list search result summary",
88+
"default": {
89+
"fontPath": "dist/assets/icons.woff2",
90+
"fontCharacter": "\\e674"
91+
}
7892
}
7993
},
8094
"commands": [
@@ -254,6 +268,27 @@
254268
"title": "提取图片",
255269
"category": "Cnblogs",
256270
"enablement": "vscode-cnb.isAuthorized"
271+
},
272+
{
273+
"command": "vscode-cnb.search-posts",
274+
"title": "搜索博文",
275+
"category": "Cnblogs",
276+
"icon": "$(vscode-cnb-posts-list-search)",
277+
"enablement": "vscode-cnb.isAuthorized && !vscode-cnb.posts-list.refreshing"
278+
},
279+
{
280+
"command": "vscode-cnb.clear-posts-search-results",
281+
"title": "清除随笔搜索结果",
282+
"category": "Cnblogs",
283+
"icon": "$(clear-all)",
284+
"enablement": "vscode-cnb.isAuthorized && !vscode-cnb.posts-list.refreshing"
285+
},
286+
{
287+
"command": "vscode-cnb.refresh-posts-search-results",
288+
"title": "刷新随笔搜索结果",
289+
"category": "Cnblogs",
290+
"icon": "$(refresh)",
291+
"enablement": "vscode-cnb.isAuthorized && !vscode-cnb.posts-list-refreshing"
257292
}
258293
],
259294
"configuration": [
@@ -400,9 +435,14 @@
400435
"when": "view == cnblogs-posts-list || view == cnblogs-posts-list-another",
401436
"group": "navigation@4"
402437
},
438+
{
439+
"command": "vscode-cnb.search-posts",
440+
"when": "view == cnblogs-posts-list || view == cnblogs-posts-list-another",
441+
"group": "navigation@5"
442+
},
403443
{
404444
"command": "vscode-cnb.create-local-draft",
405-
"group": "navigation@5",
445+
"group": "navigation@6",
406446
"when": "view == cnblogs-posts-list || view == cnblogs-posts-list-another"
407447
},
408448
{
@@ -581,6 +621,26 @@
581621
{
582622
"command": "vscode-cnb.update-post-category",
583623
"when": "viewItem == cnb-post-category"
624+
},
625+
{
626+
"command": "vscode-cnb.refresh-posts-search-results",
627+
"when": "viewItem == cnblogs-posts-search-results-entry",
628+
"group": "inline@1"
629+
},
630+
{
631+
"command": "vscode-cnb.clear-posts-search-results",
632+
"when": "viewItem == cnblogs-posts-search-results-entry",
633+
"group": "inline@2"
634+
},
635+
{
636+
"command": "vscode-cnb.refresh-posts-search-results",
637+
"when": "viewItem == cnblogs-posts-search-results-entry",
638+
"group": "cnblogs-posts-search-results-entry@1"
639+
},
640+
{
641+
"command": "vscode-cnb.clear-posts-search-results",
642+
"when": "viewItem == cnblogs-posts-search-results-entry",
643+
"group": "cnblogs-posts-search-results-entry@2"
584644
}
585645
],
586646
"editor/context": [

src/assets/icons.woff2

340 Bytes
Binary file not shown.

src/commands/commands-registration.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { viewPostOnline } from './view-post-online';
3333
import { exportPostToPdf } from './pdf/export-pdf.command';
3434
import { pullPostRemoteUpdates } from './pull-post-remote-updates';
3535
import { extractImages } from './extract-images';
36+
import { clearPostsSearchResults, refreshPostsSearchResults, searchPosts } from './posts-list/search';
3637

3738
export const registerCommands = () => {
3839
const context = globalState.extensionContext;
@@ -73,6 +74,9 @@ export const registerCommands = () => {
7374
commands.registerCommand(`${appName}.view-post-online`, viewPostOnline),
7475
commands.registerCommand(`${appName}.export-post-to-pdf`, exportPostToPdf),
7576
commands.registerCommand(`${appName}.extract-images`, extractImages),
77+
commands.registerCommand(`${appName}.search-posts`, searchPosts),
78+
commands.registerCommand(`${appName}.clear-posts-search-results`, clearPostsSearchResults),
79+
commands.registerCommand(`${appName}.refresh-posts-search-results`, refreshPostsSearchResults),
7680
];
7781
context?.subscriptions.push(...disposables);
7882
};

src/commands/open-post-in-blog-admin.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { commands, Uri } from 'vscode';
22
import { Post } from '../models/post';
33
import { PostFileMapManager } from '../services/post-file-map';
4+
import { PostTreeItem } from '../tree-view-providers/models/post-tree-item';
45

5-
export const openPostInBlogAdmin = (item: Post | Uri) => {
6+
export const openPostInBlogAdmin = (item: Post | PostTreeItem | Uri) => {
67
if (!item) {
78
return;
89
}
910

11+
item = item instanceof PostTreeItem ? item.post : item;
1012
const postId = item instanceof Post ? item.id : PostFileMapManager.getPostId(item.fsPath) ?? -1;
1113

1214
void commands.executeCommand('vscode.open', Uri.parse(`https://i.cnblogs.com/posts/edit;postId=${postId}`));

src/commands/pdf/export-pdf.command.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { chromiumPathProvider } from '../../utils/chromium-path-provider';
1212
import { Settings } from '../../services/settings.service';
1313
import { accountService } from '../../services/account.service';
1414
import { AlertService } from '../../services/alert.service';
15+
import { PostTreeItem } from '../../tree-view-providers/models/post-tree-item';
1516

1617
const launchBrowser = async (
1718
chromiumPath: string
@@ -142,9 +143,10 @@ const inputTargetFolder = async (): Promise<Uri | undefined> => {
142143
})) ?? [])[0];
143144
};
144145

145-
const handlePostInput = (post: Post): Promise<Post[]> => {
146-
const posts: Post[] = [post];
146+
const handlePostInput = (post: Post | PostTreeItem): Promise<Post[]> => {
147+
const posts: Post[] = [post instanceof PostTreeItem ? post.post : post];
147148
extensionViews.visiblePostsList()?.selection.map(item => {
149+
item = item instanceof PostTreeItem ? item.post : item;
148150
if (item instanceof Post && !posts.includes(item)) {
149151
posts.push(item);
150152
}
@@ -182,7 +184,7 @@ const reportErrors = (errors: string[] | undefined) => {
182184
}
183185
};
184186

185-
const exportPostToPdf = async (input: Post | Uri): Promise<void> => {
187+
const exportPostToPdf = async (input: Post | PostTreeItem | Uri): Promise<void> => {
186188
const chromiumPath = await retrieveChromiumPath();
187189
if (!chromiumPath) {
188190
return;
@@ -203,7 +205,9 @@ const exportPostToPdf = async (input: Post | Uri): Promise<void> => {
203205
async progress => {
204206
const errors: string[] = [];
205207
progress.report({ message: '导出pdf - 处理博文数据' });
206-
let selectedPosts = await (input instanceof Post ? handlePostInput(input) : handleUriInput(input));
208+
let selectedPosts = await (input instanceof Post || input instanceof PostTreeItem
209+
? handlePostInput(input)
210+
: handleUriInput(input));
207211
if (selectedPosts.length <= 0) {
208212
return;
209213
}

src/commands/posts-list/delete-post-to-local-file-map.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { MessageOptions, window } from 'vscode';
22
import { Post } from '../../models/post';
33
import { PostFileMap, PostFileMapManager } from '../../services/post-file-map';
44
import { revealPostsListItem } from '../../services/posts-list-view';
5+
import { PostTreeItem } from '../../tree-view-providers/models/post-tree-item';
56
import { extensionViews } from '../../tree-view-providers/tree-view-registration';
67

78
const confirm = async (posts: Post[]): Promise<boolean> => {
@@ -17,9 +18,12 @@ const confirm = async (posts: Post[]): Promise<boolean> => {
1718
return input === options[0];
1819
};
1920

20-
export const deletePostToLocalFileMap = async (post: Post) => {
21+
export const deletePostToLocalFileMap = async (post: Post | PostTreeItem) => {
22+
post = post instanceof PostTreeItem ? post.post : post;
2123
const view = extensionViews.postsList!;
22-
let selectedPosts = view.selection.filter(x => x instanceof Post).map(x => x as Post);
24+
let selectedPosts = view.selection
25+
.map(x => (x instanceof Post ? x : x instanceof PostTreeItem ? x.post : null))
26+
.filter((x): x is Post => x != null);
2327
if (!selectedPosts.includes(post)) {
2428
await revealPostsListItem(post);
2529
selectedPosts = post ? [post] : [];

src/commands/posts-list/delete-post.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { PostFileMap, PostFileMapManager } from '../../services/post-file-map';
66
import { postsDataProvider } from '../../tree-view-providers/posts-data-provider';
77
import { extensionViews } from '../../tree-view-providers/tree-view-registration';
88
import { refreshPostsList } from './refresh-posts-list';
9+
import { PostTreeItem } from '../../tree-view-providers/models/post-tree-item';
910

1011
let deleting = false;
1112

@@ -38,9 +39,19 @@ const confirmDelete = async (
3839
return result;
3940
};
4041

41-
export const deleteSelectedPosts = async (post: Post) => {
42+
export const deleteSelectedPosts = async (arg: unknown) => {
43+
let post: Post;
44+
if (arg instanceof Post) {
45+
post = arg;
46+
} else if (arg instanceof PostTreeItem) {
47+
post = arg.post;
48+
} else {
49+
return;
50+
}
51+
4252
const selectedPosts: Post[] = post ? [post] : [];
43-
extensionViews.visiblePostsList()?.selection.map(post => {
53+
extensionViews.visiblePostsList()?.selection.map(item => {
54+
const post = item instanceof PostTreeItem ? item.post : item;
4455
if (post instanceof Post && !selectedPosts.includes(post)) {
4556
postsDataProvider.pagedPosts?.items.find(item => item === post);
4657
selectedPosts.push(post);

src/commands/posts-list/modify-post-settings.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@ import fs from 'fs';
1010
import { LocalDraft } from '../../services/local-draft.service';
1111
import { saveFilePendingChanges } from '../../utils/save-file-pending-changes';
1212
import { postsDataProvider } from '../../tree-view-providers/posts-data-provider';
13+
import { PostTreeItem } from '../../tree-view-providers/models/post-tree-item';
1314

14-
export const modifyPostSettings = async (input: Post | Uri) => {
15+
export const modifyPostSettings = async (input: Post | PostTreeItem | Uri) => {
1516
let post: Post | undefined;
1617
let postId = -1;
18+
input = input instanceof PostTreeItem ? input.post : input;
19+
1720
if (input instanceof Post) {
1821
post = input;
1922
postId = input.id;

src/commands/posts-list/refresh-posts-list.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ export const refreshPostsList = ({ queue = false } = {}): Promise<boolean> => {
4040
)
4141
)
4242
.catch(() => false)
43+
.then(x =>
44+
postsDataProvider.refreshSearch().then(
45+
() => x,
46+
() => x
47+
)
48+
)
4349
.then(x =>
4450
setRefreshing(false).then(
4551
() => x,

src/commands/posts-list/rename-post.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { postService } from '../../services/post.service';
66
import { PostFileMapManager } from '../../services/post-file-map';
77
import { postsDataProvider } from '../../tree-view-providers/posts-data-provider';
88
import { revealPostsListItem } from '../../services/posts-list-view';
9+
import { PostTreeItem } from '../../tree-view-providers/models/post-tree-item';
910

1011
const renameLinkedFile = async (post: Post): Promise<void> => {
1112
const filePath = PostFileMapManager.getFilePath(post.id);
@@ -33,7 +34,8 @@ const renameLinkedFile = async (post: Post): Promise<void> => {
3334
}
3435
};
3536

36-
export const renamePost = async (post: Post) => {
37+
export const renamePost = async (arg: Post | PostTreeItem) => {
38+
const post = arg instanceof PostTreeItem ? arg.post : arg;
3739
if (!post) {
3840
return;
3941
}

src/commands/posts-list/save-post.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { postConfigurationPanel } from '../../services/post-configuration-panel.
1616
import { saveFilePendingChanges } from '../../utils/save-file-pending-changes';
1717
import { extractImages } from '../extract-images';
1818
import { Settings } from '../../services/settings.service';
19+
import { PostTreeItem } from '../../tree-view-providers/models/post-tree-item';
1920

2021
const parseFileUri = async (fileUri: Uri | undefined): Promise<Uri | undefined> => {
2122
if (fileUri && fileUri.scheme !== 'file') {
@@ -129,7 +130,8 @@ export const saveLocalDraftToCnblogs = async (localDraft: LocalDraft) => {
129130
});
130131
};
131132

132-
export const savePostToCnblogs = async (input: Post | PostEditDto | undefined, isNewPost = false) => {
133+
export const savePostToCnblogs = async (input: Post | PostTreeItem | PostEditDto | undefined, isNewPost = false) => {
134+
input = input instanceof PostTreeItem ? input.post : input;
133135
const post =
134136
input instanceof PostEditDto
135137
? input.post

src/commands/posts-list/search.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { window } from 'vscode';
2+
import { postsDataProvider } from '../../tree-view-providers/posts-data-provider';
3+
4+
export const searchPosts = async () => {
5+
const searchKey = await window.showInputBox({
6+
ignoreFocusOut: true,
7+
title: '搜索博文',
8+
prompt: '输入关键词搜索博文',
9+
placeHolder: '在此输入关键词',
10+
validateInput: value => (value.length <= 30 ? null : '最多输入30个字符'),
11+
});
12+
if (!searchKey) {
13+
return;
14+
}
15+
16+
await postsDataProvider.search({ key: searchKey });
17+
};
18+
19+
export const clearPostsSearchResults = () => postsDataProvider.clearSearch();
20+
21+
export const refreshPostsSearchResults = () => postsDataProvider.refreshSearch();

src/commands/pull-post-remote-updates.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ import { postService } from '../services/post.service';
77
import { AlertService } from '../services/alert.service';
88
import path from 'path';
99
import { revealPostsListItem } from '../services/posts-list-view';
10+
import { PostTreeItem } from '../tree-view-providers/models/post-tree-item';
1011

11-
const pullPostRemoteUpdates = async (input: Post | Uri | undefined | null): Promise<void> => {
12+
const pullPostRemoteUpdates = async (input: Post | PostTreeItem | Uri | undefined | null): Promise<void> => {
1213
let ctxs: CommandContext[] = [];
1314
let uri: Uri | undefined;
15+
input = input instanceof PostTreeItem ? input.post : input;
1416
if (parsePostInput(input) && input.id > 0) {
1517
await handlePostInput(input, ctxs);
1618
} else if ((uri = parseUriInput(input))) {

src/commands/view-post-online.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import { commands, Uri, window } from 'vscode';
22
import { Post } from '../models/post';
33
import { postService } from '../services/post.service';
44
import { PostFileMapManager } from '../services/post-file-map';
5+
import { PostTreeItem } from '../tree-view-providers/models/post-tree-item';
56

6-
export const viewPostOnline = async (input?: Post | Uri) => {
7-
let post: Post | undefined = input instanceof Post ? input : undefined;
7+
export const viewPostOnline = async (input?: Post | PostTreeItem | Uri) => {
8+
let post: Post | undefined = input instanceof Post ? input : input instanceof PostTreeItem ? input.post : undefined;
89
if (!input) {
910
input = window.activeTextEditor?.document.uri;
1011
}

src/models/zzk-search-result.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export class ZzkSearchResult {
2+
constructor(public readonly postIds: number[] = []) {}
3+
4+
get count() {
5+
return this.postIds.length;
6+
}
7+
8+
static parse<T extends { postIds?: number[] }>(obj: T | undefined | null) {
9+
return obj == null ? null : new ZzkSearchResult(obj.postIds);
10+
}
11+
}

src/services/post-file-map.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,7 @@ export class PostFileMapManager {
3131
maps.push([postId, filePath]);
3232
}
3333
await globalState.storage.update(this.storageKey, maps.filter(validatePostFileMap));
34-
const treeViewItem = postsDataProvider.pagedPosts?.items.find(x => x.id === postId);
35-
if (treeViewItem) {
36-
postsDataProvider.fireTreeDataChangedEvent(treeViewItem);
37-
}
34+
postsDataProvider.fireTreeDataChangedEvent(postId);
3835
}
3936

4037
static findByPostId(postId: number) {

0 commit comments

Comments
 (0)