Skip to content

Commit 9cafe1d

Browse files
authored
feat: set post title separately on post-configuration panel (#136)
1 parent 4942a22 commit 9cafe1d

File tree

7 files changed

+116
-22
lines changed

7 files changed

+116
-22
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ export const saveLocalDraftToCnblogs = async (localDraft: LocalDraft) => {
9797
post.categoryIds ??= [];
9898
void postConfigurationPanel.open({
9999
panelTitle: '',
100+
localFileUri: localDraft.filePathUri,
100101
breadcrumbs: ['新建博文', '博文设置', post.title],
101102
post,
102103
successCallback: async savedPost => {
@@ -145,7 +146,6 @@ export const savePostToCnblogs = async (input: Post | PostTreeItem | PostEditDto
145146

146147
await saveFilePendingChanges(localFilePath);
147148
post.postBody = (await workspace.fs.readFile(Uri.file(localFilePath))).toString();
148-
post.title = await PostTitleSanitizer.unSanitize(post);
149149
post.isMarkdown = path.extname(localFilePath).endsWith('md') || post.isMarkdown;
150150

151151
if (!validatePost(post)) return false;

src/models/webview-commands.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export namespace webviewCommands {
1212
savePost = 'savePost',
1313
disposePanel = 'disposePanel',
1414
uploadImage = 'uploadImage',
15+
refreshPost = 'refreshPost',
1516
}
1617

1718
export namespace ingCommands {

src/models/webview-message.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export namespace webviewMessage {
1919
siteCategories: SiteCategories;
2020
tags: PostTags;
2121
breadcrumbs?: string[];
22+
fileName: string;
2223
}
2324

2425
export interface SavePostMessage extends Message {

src/services/post-configuration-panel.service.ts

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { uploadImage } from '../commands/upload-image/upload-image';
1313
import { ImageUploadStatusId } from '../models/image-upload-status';
1414
import { openPostFile } from '../commands/posts-list/open-post-file';
1515
import { parseWebviewHtml } from 'src/services/parse-webview-html';
16+
import path from 'path';
1617

1718
const panels: Map<string, vscode.WebviewPanel> = new Map();
1819

@@ -35,7 +36,7 @@ export namespace postConfigurationPanel {
3536
export const buildPanelId = (postId: number, postTitle: string): string => `${postId}-${postTitle}`;
3637
export const findPanelById = (panelId: string) => panels.get(panelId);
3738
export const open = async (option: PostConfigurationPanelOpenOption) => {
38-
const { post, breadcrumbs } = option;
39+
const { post, breadcrumbs, localFileUri } = option;
3940
const panelTitle = option.panelTitle ? option.panelTitle : `博文设置 - ${post.title}`;
4041
await openPostFile(post, {
4142
viewColumn: vscode.ViewColumn.One,
@@ -47,22 +48,28 @@ export namespace postConfigurationPanel {
4748
const disposables: (vscode.Disposable | undefined)[] = [];
4849
panel = await createPanel(panelTitle, post);
4950
const { webview } = panel;
50-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
51-
webview.postMessage({
52-
command: webviewCommands.UiCommands.setFluentIconBaseUrl,
53-
baseUrl: webview.asWebviewUri(Uri.joinPath(resourceRootUri(), 'fonts')).toString() + '/',
54-
} as webviewMessage.SetFluentIconBaseUrlMessage);
55-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
56-
webview.postMessage({
57-
command: webviewCommands.UiCommands.editPostConfiguration,
58-
post: cloneDeep(post),
59-
activeTheme: vscode.window.activeColorTheme.kind,
60-
personalCategories: cloneDeep(await postCategoryService.fetchCategories()),
61-
siteCategories: cloneDeep(await siteCategoryService.fetchAll()),
62-
tags: cloneDeep(await postTagService.fetchTags()),
63-
breadcrumbs,
64-
} as webviewMessage.EditPostConfigurationMessage);
51+
6552
disposables.push(
53+
webview.onDidReceiveMessage(async ({ command }: webviewMessage.Message) => {
54+
if (command === webviewCommands.ExtensionCommands.refreshPost) {
55+
await webview.postMessage({
56+
command: webviewCommands.UiCommands.setFluentIconBaseUrl,
57+
baseUrl: webview.asWebviewUri(Uri.joinPath(resourceRootUri(), 'fonts')).toString() + '/',
58+
} as webviewMessage.SetFluentIconBaseUrlMessage);
59+
await webview.postMessage({
60+
command: webviewCommands.UiCommands.editPostConfiguration,
61+
post: cloneDeep(post),
62+
activeTheme: vscode.window.activeColorTheme.kind,
63+
personalCategories: cloneDeep(await postCategoryService.fetchCategories()),
64+
siteCategories: cloneDeep(await siteCategoryService.fetchAll()),
65+
tags: cloneDeep(await postTagService.fetchTags()),
66+
breadcrumbs,
67+
fileName: localFileUri
68+
? path.basename(localFileUri.fsPath, path.extname(localFileUri?.fsPath))
69+
: '',
70+
} as webviewMessage.EditPostConfigurationMessage);
71+
}
72+
}),
6673
observeWebviewMessages(panel, option),
6774
observeActiveColorSchemaChange(panel),
6875
observerPanelDisposeEvent(panel, disposables)

ui/post-configuration/App.tsx

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,46 @@ import { webviewCommands } from '@models/webview-commands';
1111
import { PostFormContextProvider } from './components/PostFormContextProvider';
1212
import { activeThemeProvider } from 'share/active-theme-provider';
1313
import { darkTheme, lightTheme } from 'share/theme';
14+
import { vsCodeApi } from 'share/vscode-api';
1415

1516
interface AppState {
1617
post?: Post;
1718
theme?: Theme | PartialTheme;
1819
breadcrumbs?: string[];
20+
fileName: string;
1921
}
2022

2123
export interface AppProps extends Record<string, never> {}
2224

2325
class App extends Component<AppProps, AppState> {
2426
constructor(props: AppProps) {
2527
super(props);
26-
this.state = { theme: activeThemeProvider.activeTheme() };
28+
this.state = { theme: activeThemeProvider.activeTheme(), fileName: '' };
2729
this.observerMessages();
30+
vsCodeApi.getInstance().postMessage({ command: webviewCommands.ExtensionCommands.refreshPost });
2831
}
2932

3033
render() {
31-
const isReady = !!this.state.post;
34+
const { post, fileName } = this.state;
35+
const isReady = post != null;
3236
const content = (
3337
<>
3438
{this.renderBreadcrumbs()}
3539
<Stack tokens={{ padding: '8px 10px 16px 10px' }}>
3640
<PostFormContextProvider>
37-
<PostForm post={this.state.post} />
41+
<PostForm
42+
post={this.state.post}
43+
onTitleChange={title =>
44+
this.state.breadcrumbs && this.state.breadcrumbs.length > 1
45+
? this.setState({
46+
breadcrumbs: this.state.breadcrumbs
47+
.slice(0, this.state.breadcrumbs.length - 1)
48+
.concat(title),
49+
})
50+
: undefined
51+
}
52+
fileName={fileName}
53+
/>
3854
</PostFormContextProvider>
3955
</Stack>
4056
</>
@@ -68,16 +84,17 @@ class App extends Component<AppProps, AppState> {
6884
const message: webviewMessage.Message = ev.data ?? {};
6985
const { command } = message;
7086
if (command === webviewCommands.UiCommands.editPostConfiguration) {
71-
const { post, activeTheme, personalCategories, siteCategories, tags, breadcrumbs } =
87+
const { post, activeTheme, personalCategories, siteCategories, tags, breadcrumbs, fileName } =
7288
message as webviewMessage.EditPostConfigurationMessage;
7389
personalCategoriesStore.set(personalCategories);
7490
siteCategoriesStore.set(siteCategories);
7591
tagsStore.set(tags);
7692

7793
this.setState({
7894
theme: activeTheme === 2 ? darkTheme : lightTheme,
79-
post: post,
95+
post,
8096
breadcrumbs,
97+
fileName,
8198
});
8299
} else if (command === webviewCommands.UiCommands.updateBreadcrumbs) {
83100
const { breadcrumbs } = message as webviewMessage.UpdateBreadcrumbsMessage;

ui/post-configuration/components/PostForm.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@ import { webviewMessage } from '@models/webview-message';
1818
import { InputSummary } from './InputSummary';
1919
import { IPostFormContext, PostFormContext } from './PostFormContext';
2020
import PostEntryNameInput from './PostEntryNameInput';
21+
import PostTitleInput from 'post-configuration/components/PostTitleInput';
2122

2223
export interface IPostFormProps {
2324
post?: Post;
25+
fileName?: string;
2426
onConfirm?: (postConfiguration: PostConfiguration) => void;
27+
onTitleChange?: (title: string) => void;
2528
}
2629

2730
export interface IPostFormState extends PostConfiguration {}
@@ -42,6 +45,14 @@ export class PostForm extends React.Component<IPostFormProps, IPostFormState> {
4245
return (
4346
<form>
4447
<Stack tokens={{ childrenGap: 16 }}>
48+
<PostTitleInput
49+
value={this.state.title ?? ''}
50+
fileName={this.props.fileName ?? ''}
51+
onChange={v => {
52+
this.setState({ title: v ?? '' });
53+
this.props.onTitleChange?.(v ?? '');
54+
}}
55+
></PostTitleInput>
4556
<Stack tokens={{ childrenGap: 8 }}>
4657
<Label>个人分类</Label>
4758
<Stack>
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { ActionButton, Label, Stack, Text, TextField } from '@fluentui/react';
2+
import React from 'react';
3+
4+
export interface IPostTitleInputProps {
5+
value: string;
6+
fileName: string;
7+
onChange: (value: string | null | undefined) => unknown;
8+
}
9+
10+
export interface IPostTitleInputState {
11+
value: IPostTitleInputProps['value'];
12+
}
13+
14+
export default class PostTitleInput extends React.Component<IPostTitleInputProps, IPostTitleInputState> {
15+
constructor(props: IPostTitleInputProps) {
16+
super(props);
17+
this.state = {
18+
value: props.value,
19+
};
20+
}
21+
22+
render() {
23+
return (
24+
<Stack tokens={{ childrenGap: 8 }}>
25+
<Stack horizontal tokens={{ childrenGap: 8 }} wrap>
26+
<Label styles={{ root: { whiteSpace: 'nowrap' } }}>博文标题</Label>
27+
{this.props.fileName && this.props.fileName !== this.state.value ? (
28+
<ActionButton
29+
onClick={() => {
30+
this.setState({ value: this.props.fileName });
31+
this.props.onChange(this.state.value);
32+
}}
33+
styles={{ root: { height: 'auto', whiteSpace: 'nowrap' } }}
34+
secondaryText={this.props.fileName}
35+
>
36+
使用本地文件名:&nbsp;
37+
<code>
38+
<Text>"{this.props.fileName}"</Text>
39+
</code>
40+
</ActionButton>
41+
) : (
42+
<></>
43+
)}
44+
</Stack>
45+
<Stack>
46+
<TextField
47+
value={this.state.value}
48+
onChange={(_, v) => {
49+
this.setState({ value: v ?? '' });
50+
this.props.onChange(v);
51+
}}
52+
></TextField>
53+
</Stack>
54+
</Stack>
55+
);
56+
}
57+
}

0 commit comments

Comments
 (0)