Skip to content

Commit 2294819

Browse files
committed
implement #405
1 parent c87d7b4 commit 2294819

File tree

11 files changed

+96
-1
lines changed

11 files changed

+96
-1
lines changed

exampleVault/Buttons/Button Example.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ actions:
188188
folderPath: templates
189189
fileName: asdasd
190190
openNote: false
191+
openIfAlreadyExists: true
191192
192193
```
193194

packages/core/src/api/InternalAPI.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ export abstract class InternalAPI<Plugin extends IPlugin> {
247247
*/
248248
abstract createFile(folderPath: string, fileName: string, extension: string, open?: boolean): Promise<string>;
249249

250+
abstract existsFilePath(filePath: string): Promise<boolean>;
251+
250252
abstract createContextMenu(items: ContextMenuItemDefinition[]): IContextMenu;
251253

252254
abstract evaluateTemplaterTemplate(templateFilePath: string, targetFilePath: string): Promise<string>;

packages/core/src/config/ButtonConfig.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export interface TemplaterCreateNoteButtonAction {
5656
folderPath?: string;
5757
fileName?: string;
5858
openNote?: boolean;
59+
openIfAlreadyExists?: boolean;
5960
}
6061

6162
export interface UpdateMetadataButtonAction {
@@ -70,6 +71,7 @@ export interface CreateNoteButtonAction {
7071
folderPath?: string;
7172
fileName: string;
7273
openNote?: boolean;
74+
openIfAlreadyExists?: boolean;
7375
}
7476

7577
export interface ReplaceInNoteButtonAction {

packages/core/src/config/ButtonConfigValidators.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ export const V_TemplaterCreateNoteButtonAction = schemaForType<TemplaterCreateNo
8888
folderPath: stringValidator('templaterCreateNote', 'folderPath', 'folder path').optional(),
8989
fileName: stringValidator('templaterCreateNote', 'fileName', 'file name').optional(),
9090
openNote: booleanValidator('templaterCreateNote', 'openNote', 'value for whether to open the note').optional(),
91+
openIfAlreadyExists: booleanValidator(
92+
'templaterCreateNote',
93+
'openIfAlreadyExists',
94+
'value for whether to open the note if it already exists',
95+
).optional(),
9196
}),
9297
);
9398
export const V_UpdateMetadataButtonAction = schemaForType<UpdateMetadataButtonAction>()(
@@ -112,6 +117,11 @@ export const V_CreateNoteButtonAction = schemaForType<CreateNoteButtonAction>()(
112117
folderPath: stringValidator('createNote', 'folderPath', 'folder path').optional(),
113118
fileName: stringValidator('createNote', 'fileName', 'file name'),
114119
openNote: booleanValidator('createNote', 'openNote', 'value for whether to open the note').optional(),
120+
openIfAlreadyExists: booleanValidator(
121+
'createNote',
122+
'openIfAlreadyExists',
123+
'value for whether to open the note if it already exists',
124+
).optional(),
115125
}),
116126
);
117127

packages/core/src/fields/button/ButtonActionRunner.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import type { IPlugin } from 'packages/core/src/IPlugin';
2121
import { MDLinkParser } from 'packages/core/src/parsers/MarkdownLinkParser';
2222
import { ErrorLevel, MetaBindJsError, MetaBindParsingError } from 'packages/core/src/utils/errors/MetaBindErrors';
2323
import { parseLiteral } from 'packages/core/src/utils/Literal';
24-
import { expectType } from 'packages/core/src/utils/Utils';
24+
import { ensureFileExtension, expectType, joinPath } from 'packages/core/src/utils/Utils';
2525

2626
export class ButtonActionRunner {
2727
plugin: IPlugin;
@@ -108,6 +108,7 @@ export class ButtonActionRunner {
108108
folderPath: '/',
109109
fileName: '',
110110
openNote: true,
111+
openIfAlreadyExists: false,
111112
} satisfies TemplaterCreateNoteButtonAction;
112113
} else if (type === ButtonActionType.UPDATE_METADATA) {
113114
return {
@@ -122,6 +123,7 @@ export class ButtonActionRunner {
122123
folderPath: '/',
123124
fileName: 'Untitled',
124125
openNote: true,
126+
openIfAlreadyExists: false,
125127
} satisfies CreateNoteButtonAction;
126128
} else if (type === ButtonActionType.REPLACE_IN_NOTE) {
127129
return {
@@ -265,6 +267,15 @@ export class ButtonActionRunner {
265267
}
266268

267269
async runTemplaterCreateNoteAction(action: TemplaterCreateNoteButtonAction): Promise<void> {
270+
if (action.openIfAlreadyExists && action.fileName) {
271+
const filePath = ensureFileExtension(joinPath(action.folderPath ?? '', action.fileName), 'md');
272+
// if the file already exists, open it in the same tab
273+
if (await this.plugin.internal.existsFilePath(filePath)) {
274+
this.plugin.internal.openFile(filePath, '', false);
275+
return;
276+
}
277+
}
278+
268279
await this.plugin.internal.createNoteWithTemplater(
269280
action.templateFile,
270281
action.folderPath,
@@ -302,6 +313,15 @@ export class ButtonActionRunner {
302313
}
303314

304315
async runCreateNoteAction(action: CreateNoteButtonAction): Promise<void> {
316+
if (action.openIfAlreadyExists) {
317+
const filePath = ensureFileExtension(joinPath(action.folderPath ?? '', action.fileName), 'md');
318+
// if the file already exists, open it in the same tab
319+
if (await this.plugin.internal.existsFilePath(filePath)) {
320+
this.plugin.internal.openFile(filePath, '', false);
321+
return;
322+
}
323+
}
324+
305325
await this.plugin.internal.createFile(action.folderPath ?? '', action.fileName, 'md', action.openNote ?? false);
306326
}
307327

packages/core/src/modals/modalContents/buttonBuilder/CreateNoteActionSettings.svelte

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,10 @@
3636
<SettingComponent name="Open Note" description="Whether to open the new note after this action ran.">
3737
<Toggle bind:checked={action.openNote}></Toggle>
3838
</SettingComponent>
39+
40+
<SettingComponent
41+
name="Open If Note Already Exists"
42+
description="Whether to open the note instead of creating a new one if the note already exists."
43+
>
44+
<Toggle bind:checked={action.openIfAlreadyExists}></Toggle>
45+
</SettingComponent>

packages/core/src/modals/modalContents/buttonBuilder/TemplaterCreateNoteActionSettings.svelte

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,10 @@
5757
<SettingComponent name="Open Note" description="Whether to open the new note after this action ran.">
5858
<Toggle bind:checked={action.openNote}></Toggle>
5959
</SettingComponent>
60+
61+
<SettingComponent
62+
name="Open If Note Already Exists"
63+
description="Whether to open the note instead of creating a new one if the note already exists."
64+
>
65+
<Toggle bind:checked={action.openIfAlreadyExists}></Toggle>
66+
</SettingComponent>

packages/core/src/utils/Utils.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,3 +313,37 @@ export function getFolderPathFromFilePath(filePath: string): string {
313313
}
314314
return filePath.substring(0, pathSeparator);
315315
}
316+
317+
/**
318+
* Joins the given paths together without duplicate slashes.
319+
*/
320+
export function joinPath(...paths: string[]): string {
321+
let result = paths[0];
322+
for (let i = 1; i < paths.length; i++) {
323+
const endsWithSlash = result.endsWith('/');
324+
const startsWithSlash = paths[i].startsWith('/');
325+
326+
if (endsWithSlash && startsWithSlash) {
327+
result = result.substring(0, result.length - 1);
328+
} else if (!endsWithSlash && !startsWithSlash) {
329+
result += '/';
330+
}
331+
332+
result += paths[i];
333+
}
334+
335+
return result;
336+
}
337+
338+
/**
339+
* Ensures that the file path has the given extension.
340+
*/
341+
export function ensureFileExtension(filePath: string, extension: string): string {
342+
extension = extension.startsWith('.') ? extension : '.' + extension;
343+
344+
if (filePath.endsWith(extension)) {
345+
return filePath;
346+
}
347+
348+
return filePath + extension;
349+
}

packages/obsidian/src/ObsidianInternalAPI.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,10 @@ export class ObsidianInternalAPI extends InternalAPI<MetaBindPlugin> {
215215
return this.app.vault.adapter.write(filePath, content);
216216
}
217217

218+
public existsFilePath(filePath: string): Promise<boolean> {
219+
return this.app.vault.adapter.exists(filePath);
220+
}
221+
218222
public createContextMenu(items: ContextMenuItemDefinition[]): IContextMenu {
219223
const menu = new ObsidianContextMenu();
220224
menu.setItems(items);

packages/publish/src/PublishInternalAPI.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ export class PublishInternalAPI extends InternalAPI<MetaBindPublishPlugin> {
142142
return Promise.resolve('');
143143
}
144144

145+
public existsFilePath(filePath: string): Promise<boolean> {
146+
return Promise.resolve(this.getAllFiles().contains(filePath));
147+
}
148+
145149
public createContextMenu(_items: ContextMenuItemDefinition[]): IContextMenu {
146150
throw new Error('not implemented');
147151
}

0 commit comments

Comments
 (0)