Skip to content

Commit dfc9b4a

Browse files
committed
feat: support pull remote post updates to local mapped file
1 parent 4483916 commit dfc9b4a

File tree

5 files changed

+197
-60
lines changed

5 files changed

+197
-60
lines changed

package.json

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,13 @@
154154
"enablement": "vscode-cnb.isAuthorized",
155155
"category": "Cnblogs"
156156
},
157+
{
158+
"command": "vscode-cnb.pull-post-remote-updates",
159+
"title": "拉取远程更新",
160+
"category": "Cnblogs",
161+
"enablement": "vscode-cnb.isAuthorized",
162+
"icon": "$(cloud-download)"
163+
},
157164
{
158165
"command": "vscode-cnb.show-post-to-local-file-info",
159166
"title": "博客园关联博文",
@@ -424,17 +431,22 @@
424431
"view/item/context": [
425432
{
426433
"command": "vscode-cnb.save-post",
427-
"group": "inline",
434+
"group": "inline@1",
428435
"when": "viewItem == cnb-post-cached"
429436
},
430437
{
431-
"command": "vscode-cnb.delete-post",
432-
"group": "inline",
433-
"when": "viewItem =~ /^cnb-post/ && viewItem != cnb-post-category"
438+
"command": "vscode-cnb.pull-post-remote-updates",
439+
"group": "inline@2",
440+
"when": "viewItem == cnb-post-cached"
434441
},
435442
{
436443
"command": "vscode-cnb.modify-post-settings",
437-
"group": "inline",
444+
"group": "inline@3",
445+
"when": "viewItem =~ /^cnb-post/ && viewItem != cnb-post-category"
446+
},
447+
{
448+
"command": "vscode-cnb.delete-post",
449+
"group": "inline@4",
438450
"when": "viewItem =~ /^cnb-post/ && viewItem != cnb-post-category"
439451
},
440452
{
@@ -475,20 +487,30 @@
475487
"when": "viewItem =~ /^cnb-post-cached/",
476488
"group": "delete@2"
477489
},
490+
{
491+
"command": "vscode-cnb.save-post",
492+
"group": "0@1",
493+
"when": "viewItem == cnb-post-cached"
494+
},
495+
{
496+
"command": "vscode-cnb.pull-post-remote-updates",
497+
"group": "0@2",
498+
"when": "viewItem == cnb-post-cached"
499+
},
478500
{
479501
"command": "vscode-cnb.reveal-local-post-file-in-os",
480502
"when": "viewItem =~ /^cnb-post-cached/",
481-
"group": "0@1"
503+
"group": "0@3"
482504
},
483505
{
484506
"command": "vscode-cnb.view-post-online",
485507
"when": "viewItem =~ /^cnb-post/ && viewItem != cnb-post-category",
486-
"group": "0@2"
508+
"group": "0@4"
487509
},
488510
{
489511
"command": "vscode-cnb.export-post-to-pdf",
490-
"when": "viewItem =~ /^cnb-post/",
491-
"group": "0@3"
512+
"when": "viewItem =~ /^cnb-post/ && viewItem != cnb-post-category",
513+
"group": "0@5"
492514
},
493515
{
494516
"command": "vscode-cnb.delete-selected-post-categories",
@@ -526,29 +548,34 @@
526548
"group": "cnblogs@2"
527549
},
528550
{
529-
"command": "vscode-cnb.modify-post-settings",
551+
"command": "vscode-cnb.pull-post-remote-updates",
530552
"when": "resourceLangId == markdown",
531553
"group": "cnblogs@3"
532554
},
533555
{
534-
"command": "vscode-cnb.open-post-in-blog-admin",
556+
"command": "vscode-cnb.modify-post-settings",
535557
"when": "resourceLangId == markdown",
536558
"group": "cnblogs@4"
537559
},
538560
{
539-
"command": "vscode-cnb.upload-clipboard-image",
561+
"command": "vscode-cnb.open-post-in-blog-admin",
540562
"when": "resourceLangId == markdown",
541563
"group": "cnblogs@5"
542564
},
543565
{
544-
"command": "vscode-cnb.upload-local-disk-image",
566+
"command": "vscode-cnb.upload-clipboard-image",
545567
"when": "resourceLangId == markdown",
546568
"group": "cnblogs@6"
547569
},
548570
{
549-
"command": "vscode-cnb.export-post-to-pdf",
571+
"command": "vscode-cnb.upload-local-disk-image",
550572
"when": "resourceLangId == markdown",
551573
"group": "cnblogs@7"
574+
},
575+
{
576+
"command": "vscode-cnb.export-post-to-pdf",
577+
"when": "resourceLangId == markdown",
578+
"group": "cnblogs@8"
552579
}
553580
],
554581
"editor/title": [
@@ -575,24 +602,29 @@
575602
"group": "cnblogs@2"
576603
},
577604
{
578-
"command": "vscode-cnb.modify-post-settings",
605+
"command": "vscode-cnb.pull-post-remote-updates",
579606
"when": "resourceLangId == markdown",
580607
"group": "cnblogs@3"
581608
},
582609
{
583-
"command": "vscode-cnb.show-post-to-local-file-info",
610+
"command": "vscode-cnb.modify-post-settings",
584611
"when": "resourceLangId == markdown",
585612
"group": "cnblogs@4"
586613
},
587614
{
588-
"command": "vscode-cnb.open-post-in-blog-admin",
615+
"command": "vscode-cnb.show-post-to-local-file-info",
589616
"when": "resourceLangId == markdown",
590617
"group": "cnblogs@5"
591618
},
592619
{
593-
"command": "vscode-cnb.export-post-to-pdf",
620+
"command": "vscode-cnb.open-post-in-blog-admin",
594621
"when": "resourceLangId == markdown",
595622
"group": "cnblogs@6"
623+
},
624+
{
625+
"command": "vscode-cnb.export-post-to-pdf",
626+
"when": "resourceLangId == markdown",
627+
"group": "cnblogs@7"
596628
}
597629
]
598630
},

src/commands/commands-registration.ts

Lines changed: 38 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import vscode from 'vscode';
1+
import { commands } from 'vscode';
22
import { openMyAccountSettings } from './open-my-account-settings';
33
import { openMyWebBlogConsole } from './open-my-blog-management-background';
44
import { openMyHomePage } from './open-my-home-page';
@@ -32,46 +32,48 @@ import { setWorkspace } from './set-workspace';
3232
import { revealWorkspaceInOs } from './reveal-workspace-in-os';
3333
import { viewPostOnline } from './view-post-online';
3434
import { exportPostToPdf } from './pdf/export-pdf.command';
35+
import { pullPostRemoteUpdates } from './pull-post-remote-updates';
3536

3637
export const registerCommands = () => {
3738
const context = globalState.extensionContext;
3839
const appName = globalState.extensionName;
3940
const disposables = [
40-
vscode.commands.registerCommand(`${appName}.login`, login),
41-
vscode.commands.registerCommand(`${appName}.open-my-blog`, openMyBlog),
42-
vscode.commands.registerCommand(`${appName}.open-my-home-page`, openMyHomePage),
43-
vscode.commands.registerCommand(`${appName}.open-my-blog-management-background`, openMyWebBlogConsole),
44-
vscode.commands.registerCommand(`${appName}.open-my-account-settings`, openMyAccountSettings),
45-
vscode.commands.registerCommand(`${appName}.logout`, logout),
46-
vscode.commands.registerCommand(`${appName}.refresh-posts-list`, refreshPostsList),
47-
vscode.commands.registerCommand(`${appName}.previous-posts-list`, gotoPreviousPostsList),
48-
vscode.commands.registerCommand(`${appName}.seek-posts-list`, seekPostsList),
49-
vscode.commands.registerCommand(`${appName}.next-posts-list`, gotoNextPostsList),
50-
vscode.commands.registerCommand(`${appName}.edit-post`, openPostInVscode),
51-
vscode.commands.registerCommand(`${appName}.save-post`, savePostToCnblogs),
52-
vscode.commands.registerCommand(`${appName}.modify-post-settings`, modifyPostSettings),
53-
vscode.commands.registerCommand(`${appName}.delete-post`, deleteSelectedPosts),
54-
vscode.commands.registerCommand(`${appName}.create-local-draft`, createLocalDraft),
55-
vscode.commands.registerCommand(`${appName}.delete-local-draft`, deleteLocalDraft),
56-
vscode.commands.registerCommand(`${appName}.save-local-draft-to-cnblogs`, saveLocalDraftToCnblogs),
57-
vscode.commands.registerCommand(`${appName}.save-post-file-to-cnblogs`, savePostFileToCnblogs),
58-
vscode.commands.registerCommand(`${appName}.upload-clipboard-image`, () => uploadImage(true, 'clipboard')),
59-
vscode.commands.registerCommand(`${appName}.upload-local-disk-image`, () => uploadImage(true, 'local')),
60-
vscode.commands.registerCommand(`${appName}.upload-image`, () => uploadImage(true)),
61-
vscode.commands.registerCommand(`${appName}.reveal-local-post-file-in-os`, revealLocalPostFileInOs),
62-
vscode.commands.registerCommand(`${appName}.show-post-to-local-file-info`, showLocalFileToPostInfo),
63-
vscode.commands.registerCommand(`${appName}.new-post-category`, newPostCategory),
64-
vscode.commands.registerCommand(`${appName}.delete-selected-post-categories`, deleteSelectedCategories),
65-
vscode.commands.registerCommand(`${appName}.refresh-post-categories-list`, refreshPostCategoriesList),
66-
vscode.commands.registerCommand(`${appName}.update-post-category`, updatePostCategory),
67-
vscode.commands.registerCommand(`${appName}.delete-post-to-local-file-map`, deletePostToLocalFileMap),
68-
vscode.commands.registerCommand(`${appName}.rename-post`, renamePost),
69-
vscode.commands.registerCommand(`${appName}.open-post-in-blog-admin`, openPostInBlogAdmin),
70-
vscode.commands.registerCommand(`${appName}.open-workspace`, openWorkspace),
71-
vscode.commands.registerCommand(`${appName}.set-workspace`, setWorkspace),
72-
vscode.commands.registerCommand(`${appName}.reveal-workspace-in-os`, revealWorkspaceInOs),
73-
vscode.commands.registerCommand(`${appName}.view-post-online`, viewPostOnline),
74-
vscode.commands.registerCommand(`${appName}.export-post-to-pdf`, exportPostToPdf),
41+
commands.registerCommand(`${appName}.login`, login),
42+
commands.registerCommand(`${appName}.open-my-blog`, openMyBlog),
43+
commands.registerCommand(`${appName}.open-my-home-page`, openMyHomePage),
44+
commands.registerCommand(`${appName}.open-my-blog-management-background`, openMyWebBlogConsole),
45+
commands.registerCommand(`${appName}.open-my-account-settings`, openMyAccountSettings),
46+
commands.registerCommand(`${appName}.logout`, logout),
47+
commands.registerCommand(`${appName}.refresh-posts-list`, refreshPostsList),
48+
commands.registerCommand(`${appName}.previous-posts-list`, gotoPreviousPostsList),
49+
commands.registerCommand(`${appName}.seek-posts-list`, seekPostsList),
50+
commands.registerCommand(`${appName}.next-posts-list`, gotoNextPostsList),
51+
commands.registerCommand(`${appName}.edit-post`, openPostInVscode),
52+
commands.registerCommand(`${appName}.save-post`, savePostToCnblogs),
53+
commands.registerCommand(`${appName}.modify-post-settings`, modifyPostSettings),
54+
commands.registerCommand(`${appName}.delete-post`, deleteSelectedPosts),
55+
commands.registerCommand(`${appName}.create-local-draft`, createLocalDraft),
56+
commands.registerCommand(`${appName}.delete-local-draft`, deleteLocalDraft),
57+
commands.registerCommand(`${appName}.save-local-draft-to-cnblogs`, saveLocalDraftToCnblogs),
58+
commands.registerCommand(`${appName}.save-post-file-to-cnblogs`, savePostFileToCnblogs),
59+
commands.registerCommand(`${appName}.pull-post-remote-updates`, pullPostRemoteUpdates),
60+
commands.registerCommand(`${appName}.upload-clipboard-image`, () => uploadImage(true, 'clipboard')),
61+
commands.registerCommand(`${appName}.upload-local-disk-image`, () => uploadImage(true, 'local')),
62+
commands.registerCommand(`${appName}.upload-image`, () => uploadImage(true)),
63+
commands.registerCommand(`${appName}.reveal-local-post-file-in-os`, revealLocalPostFileInOs),
64+
commands.registerCommand(`${appName}.show-post-to-local-file-info`, showLocalFileToPostInfo),
65+
commands.registerCommand(`${appName}.new-post-category`, newPostCategory),
66+
commands.registerCommand(`${appName}.delete-selected-post-categories`, deleteSelectedCategories),
67+
commands.registerCommand(`${appName}.refresh-post-categories-list`, refreshPostCategoriesList),
68+
commands.registerCommand(`${appName}.update-post-category`, updatePostCategory),
69+
commands.registerCommand(`${appName}.delete-post-to-local-file-map`, deletePostToLocalFileMap),
70+
commands.registerCommand(`${appName}.rename-post`, renamePost),
71+
commands.registerCommand(`${appName}.open-post-in-blog-admin`, openPostInBlogAdmin),
72+
commands.registerCommand(`${appName}.open-workspace`, openWorkspace),
73+
commands.registerCommand(`${appName}.set-workspace`, setWorkspace),
74+
commands.registerCommand(`${appName}.reveal-workspace-in-os`, revealWorkspaceInOs),
75+
commands.registerCommand(`${appName}.view-post-online`, viewPostOnline),
76+
commands.registerCommand(`${appName}.export-post-to-pdf`, exportPostToPdf),
7577
];
7678
context?.subscriptions.push(...disposables);
7779
};

src/commands/posts-list/open-post-in-vscode.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import fs from 'fs';
2-
import path = require('path');
2+
import path from 'path';
33
import { FileSystemError, MessageOptions, Uri, window, workspace } from 'vscode';
44
import { Post } from '../../models/post';
55
import { AlertService } from '../../services/alert.service';
@@ -17,12 +17,12 @@ const buildLocalPostFileUri = async (post: Post, includePostId = false): Promise
1717
return Uri.joinPath(workspaceUri, `${postTitle}${postIdSegment}${ext}`);
1818
};
1919

20-
export const openPostInVscode = async (postId: number, forceUpdateLocalPostFile = false) => {
20+
export const openPostInVscode = async (postId: number, forceUpdateLocalPostFile = false): Promise<Uri | false> => {
2121
let mappedPostFilePath = PostFileMapManager.getFilePath(postId);
2222
const isFileExist = mappedPostFilePath ? fs.existsSync(mappedPostFilePath) : false;
2323
if (isFileExist && !forceUpdateLocalPostFile) {
2424
await openPostFile(mappedPostFilePath!);
25-
return;
25+
return Uri.file(mappedPostFilePath!);
2626
}
2727
// 本地文件已经被删除了, 确保重新生成博文与本地文件的关联
2828
if (mappedPostFilePath && !isFileExist) {
@@ -32,7 +32,7 @@ export const openPostInVscode = async (postId: number, forceUpdateLocalPostFile
3232

3333
const postEditDto = await postService.fetchPostEditDto(postId);
3434
if (!postEditDto) {
35-
return;
35+
return false;
3636
}
3737
const post = postEditDto.post;
3838

@@ -59,7 +59,7 @@ export const openPostInVscode = async (postId: number, forceUpdateLocalPostFile
5959
break;
6060
// 取消, 直接返回, 不进行任何操作
6161
case undefined:
62-
return;
62+
return false;
6363
}
6464
}
6565
}
@@ -68,6 +68,7 @@ export const openPostInVscode = async (postId: number, forceUpdateLocalPostFile
6868
await workspace.fs.writeFile(fileUri, new TextEncoder().encode(postEditDto.post.postBody));
6969
await PostFileMapManager.updateOrCreate(postId, fileUri.fsPath);
7070
await openPostFile(post);
71+
return fileUri;
7172
};
7273

7374
const createDirectoryIfNotExist = async (uri: Uri) => {
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { MessageOptions, Uri, window, workspace } from 'vscode';
2+
import { Post } from '../models/post';
3+
import { PostFileMapManager } from '../services/post-file-map';
4+
import { openPostInVscode } from './posts-list/open-post-in-vscode';
5+
import fs from 'fs';
6+
import { postService } from '../services/post.service';
7+
import { AlertService } from '../services/alert.service';
8+
import path from 'path';
9+
import { revealPostsListItem } from '../services/posts-list-view';
10+
11+
const pullPostRemoteUpdates = async (input: Post | Uri | undefined | null): Promise<void> => {
12+
let ctxs: CommandContext[] = [];
13+
let uri: Uri | undefined;
14+
if (parsePostInput(input) && input.id > 0) {
15+
await handlePostInput(input, ctxs);
16+
} else if ((uri = parseUriInput(input))) {
17+
await handleUriInput(uri, ctxs);
18+
}
19+
20+
if (ctxs.length <= 0 || !(await confirmOperation(ctxs))) {
21+
return;
22+
}
23+
24+
await update(ctxs);
25+
26+
AlertService.info(`本地文件${resolveFileNames(ctxs)}已更新`);
27+
};
28+
29+
export { pullPostRemoteUpdates };
30+
31+
type InputType = Post | Uri | undefined | null;
32+
type CommandContext = { postId: number; fileUri: Uri };
33+
34+
const parsePostInput = (input: InputType): input is Post => input instanceof Post;
35+
36+
const handlePostInput = async (post: Post, contexts: CommandContext[]) => {
37+
const { id: postId } = post;
38+
let filePath = PostFileMapManager.getFilePath(postId);
39+
if (filePath && !fs.existsSync(filePath)) {
40+
// 博文关联了本地不存在文件, 此时需要删除这个关联
41+
42+
filePath = '';
43+
await PostFileMapManager.updateOrCreate(postId, filePath);
44+
}
45+
if (!filePath) {
46+
// 本地没有这篇博文, 直接将博文下载到本地即可
47+
return void (await openPostInVscode(postId, false));
48+
}
49+
50+
await revealPostsListItem(post);
51+
contexts.push({ postId: postId, fileUri: Uri.file(filePath) });
52+
};
53+
54+
const parseUriInput = (input: InputType): Uri | undefined => {
55+
if (input instanceof Uri) {
56+
return input;
57+
}
58+
const { document } = window.activeTextEditor ?? {};
59+
if (document && !document.isUntitled) {
60+
return document.uri;
61+
}
62+
};
63+
64+
const handleUriInput = (fileUri: Uri, contexts: CommandContext[]): Promise<void> => {
65+
const postId = PostFileMapManager.getPostId(fileUri.fsPath);
66+
if (!postId) {
67+
AlertService.warning(
68+
`本地文件"${path.basename(fileUri.fsPath, path.extname(fileUri.fsPath))}"未关联博客园博文`
69+
);
70+
return Promise.resolve();
71+
}
72+
contexts.push({ postId, fileUri });
73+
return Promise.resolve();
74+
};
75+
76+
const confirmOperation = async (ctxs: CommandContext[]): Promise<boolean> => {
77+
const options = ['确定'];
78+
return (
79+
(await window.showWarningMessage(
80+
'确定要拉取远程博文内容更新本地文件吗?',
81+
{
82+
modal: true,
83+
detail: `本地文件${resolveFileNames(ctxs)}的内容将被覆盖, 数据无价, 请谨慎操作`,
84+
} as MessageOptions,
85+
...options
86+
)) === options[0]
87+
);
88+
};
89+
90+
const update = async (contexts: CommandContext[]) => {
91+
for (const ctx of contexts) {
92+
const { fileUri, postId } = ctx;
93+
const { post } = (await postService.fetchPostEditDto(postId)) ?? {};
94+
if (post) {
95+
const textEditors = window.visibleTextEditors.filter(x => x.document.uri.fsPath === fileUri.fsPath);
96+
await Promise.all(textEditors.map(editor => editor.document.save()));
97+
await workspace.fs.writeFile(fileUri, new TextEncoder().encode(post.postBody));
98+
}
99+
}
100+
};
101+
102+
const resolveFileNames = (ctxs: CommandContext[]) => `"${ctxs.map(x => path.basename(x.fileUri.fsPath)).join('", ')}"`;

src/services/posts-list-view.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ export const revealPostsListItem = async (
99
return;
1010
}
1111

12-
const view = [extensionViews.postsList, extensionViews.anotherPostsList].find(x => x?.visible);
12+
const view = extensionViews.visiblePostList();
1313
await view?.reveal(post, options);
1414
};

0 commit comments

Comments
 (0)