mirror of
https://github.com/jacobtread/obsidian-timekeep.git
synced 2026-07-22 10:10:27 +00:00
feat: shared create new timekeep file logic, create new timekeep file
command
This commit is contained in:
parent
3da8d5415c
commit
7a169be170
5 changed files with 129 additions and 29 deletions
|
|
@ -24,7 +24,7 @@ export abstract class MockTAbstractFile {
|
|||
|
||||
constructor(vault: Vault, path: string, parent: MockTFolder | null) {
|
||||
const nameIndex = path.lastIndexOf("/");
|
||||
const name = path.substring(nameIndex);
|
||||
const name = path.substring(nameIndex + 1);
|
||||
|
||||
this.vault = vault;
|
||||
this.path = path;
|
||||
|
|
@ -44,6 +44,7 @@ export class MockTFolder extends MockTAbstractFile {
|
|||
) {
|
||||
super(vault, path, parent);
|
||||
this.children = children;
|
||||
this.path = path + "/";
|
||||
}
|
||||
|
||||
isRoot(): boolean {
|
||||
|
|
@ -144,6 +145,13 @@ export class MockVault {
|
|||
}
|
||||
});
|
||||
|
||||
create = vi.fn(async (path, data: string) => {
|
||||
const file = new MockTFile(this.asVault(), path, data);
|
||||
this._files[path] = file;
|
||||
this._cache[file.path] = data;
|
||||
this.emitEvent("create", file);
|
||||
});
|
||||
|
||||
cachedRead = vi.fn(async (file: TFile) => {
|
||||
if (!(file instanceof MockTFile)) {
|
||||
throw new Error("can only read files");
|
||||
|
|
@ -158,6 +166,16 @@ export class MockVault {
|
|||
|
||||
addFile(path: string, value: string): TFile {
|
||||
const file = new MockTFile(this.asVault(), path, value);
|
||||
|
||||
const nameIndex = path.lastIndexOf("/");
|
||||
const folderPath = path.substring(0, nameIndex);
|
||||
|
||||
const folder = this._files[folderPath];
|
||||
|
||||
if (folder && folder instanceof MockTFolder) {
|
||||
folder.children.push(file);
|
||||
}
|
||||
|
||||
this._files[path] = file;
|
||||
|
||||
this.emitEvent("create", file);
|
||||
|
|
|
|||
11
src/commands/newTimekeepFile.ts
Normal file
11
src/commands/newTimekeepFile.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { App, Command } from "obsidian";
|
||||
|
||||
import { createNewTimekeepFile } from "@/timekeep/createNewTimekeepFile";
|
||||
|
||||
export default function (app: App): Command {
|
||||
return {
|
||||
id: "new-timekeep-file",
|
||||
name: `Create new timekeep file`,
|
||||
callback: () => createNewTimekeepFile(app, app.vault.getRoot()),
|
||||
};
|
||||
}
|
||||
44
src/main.ts
44
src/main.ts
|
|
@ -1,5 +1,5 @@
|
|||
import type { Moment } from "moment";
|
||||
import type { Vault, TFile, PluginManifest, App } from "obsidian";
|
||||
import type { Vault, TFile, PluginManifest, App, TAbstractFile, Menu } from "obsidian";
|
||||
|
||||
import { Plugin, TFolder } from "obsidian";
|
||||
|
||||
|
|
@ -11,6 +11,7 @@ import createMerged from "@/commands/createMerged";
|
|||
import exportMergedPdf from "@/commands/exportMergedPdf";
|
||||
import findRunningTrackers from "@/commands/findRunningTrackers";
|
||||
import insertTracker from "@/commands/insertTracker";
|
||||
import newTimekeepFile from "@/commands/newTimekeepFile";
|
||||
import stopAllTimekeepsCommand from "@/commands/stopAllTimekeeps";
|
||||
import stopFileTimekeepsCommand from "@/commands/stopFileTimekeeps";
|
||||
import { TimesheetStatusBar } from "@/components/TimesheetStatusBar";
|
||||
|
|
@ -32,6 +33,8 @@ import { stopFileTimekeeps } from "@/timekeep/stopFileTimekeeps";
|
|||
import TimekeepFileView from "@/views/TimekeepFileView";
|
||||
import TimekeepMarkdownView from "@/views/TimekeepMarkdownView";
|
||||
|
||||
import { createNewTimekeepFile } from "./timekeep/createNewTimekeepFile";
|
||||
|
||||
export default class TimekeepPlugin extends Plugin {
|
||||
/** Store containing the plugin settings */
|
||||
settingsStore: Store<TimekeepSettings> = createStore(defaultSettings);
|
||||
|
|
@ -126,6 +129,7 @@ export default class TimekeepPlugin extends Plugin {
|
|||
this.addCommand(exportMergedPdf(this.app, this.registry, this.settingsStore));
|
||||
this.addCommand(stopAllTimekeepsCommand(this.app));
|
||||
this.addCommand(stopFileTimekeepsCommand(this.app));
|
||||
this.addCommand(newTimekeepFile(this.app));
|
||||
|
||||
// Custom timekeep file format
|
||||
this.registerView("timekeep", (leaf) => {
|
||||
|
|
@ -139,36 +143,20 @@ export default class TimekeepPlugin extends Plugin {
|
|||
|
||||
this.registerExtensions(["timekeep"], "timekeep");
|
||||
|
||||
this.app.workspace.on("file-menu", (menu, parent) => {
|
||||
if (!(parent instanceof TFolder)) return;
|
||||
this.app.workspace.on("file-menu", this.onFileMenu.bind(this));
|
||||
}
|
||||
|
||||
const folder = parent;
|
||||
private onFileMenu(menu: Menu, parent: TAbstractFile) {
|
||||
if (!(parent instanceof TFolder)) return;
|
||||
|
||||
menu.addItem((item) => {
|
||||
item.setTitle("New Timekeep")
|
||||
.setIcon("clock")
|
||||
.onClick(async () => {
|
||||
const folderPath = folder.path;
|
||||
const folder = parent;
|
||||
|
||||
const isNameTaken = (name: string) =>
|
||||
folder.children.find((child) => child.name === name) !== undefined;
|
||||
|
||||
let name = "Untitled.timekeep";
|
||||
let index = 1;
|
||||
|
||||
while (isNameTaken(name)) {
|
||||
name = `Untitled ${index}.timekeep`;
|
||||
index += 1;
|
||||
}
|
||||
|
||||
const filePath = `${folderPath}${name}`;
|
||||
const file = await this.app.vault.create(filePath, "");
|
||||
|
||||
// Open the created file
|
||||
const leaf = this.app.workspace.getLeaf();
|
||||
await leaf.openFile(file);
|
||||
});
|
||||
});
|
||||
menu.addItem((item) => {
|
||||
item.setTitle("New Timekeep")
|
||||
.setIcon("clock")
|
||||
.onClick(async () => {
|
||||
await createNewTimekeepFile(this.app, folder);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
54
src/timekeep/createNewTimekeepFile.test.ts
Normal file
54
src/timekeep/createNewTimekeepFile.test.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import { App } from "obsidian";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { MockVault } from "@/__mocks__/obsidian";
|
||||
|
||||
import { createNewTimekeepFile } from "./createNewTimekeepFile";
|
||||
|
||||
describe("createNewTimekeepFile", () => {
|
||||
it("should use Untitled.timekeep when the file name is not in use", async () => {
|
||||
const vault = new MockVault();
|
||||
const openFile = vi.fn();
|
||||
const app = {
|
||||
vault,
|
||||
workspace: {
|
||||
getLeaf() {
|
||||
return { openFile };
|
||||
},
|
||||
},
|
||||
} as any as App;
|
||||
|
||||
const folder = vault.addFolder("test");
|
||||
await createNewTimekeepFile(app, folder);
|
||||
|
||||
const files = vault.getFiles();
|
||||
const file = files.find((file) => file.name === "Untitled.timekeep");
|
||||
expect(file).toBeDefined();
|
||||
});
|
||||
|
||||
it("should increment the name counter until a unused name is found", async () => {
|
||||
const vault = new MockVault();
|
||||
const openFile = vi.fn();
|
||||
const app = {
|
||||
vault,
|
||||
workspace: {
|
||||
getLeaf() {
|
||||
return { openFile };
|
||||
},
|
||||
},
|
||||
} as any as App;
|
||||
|
||||
const folder = vault.addFolder("test");
|
||||
vault.addFile("test/Untitled.timekeep", "");
|
||||
|
||||
for (let i = 1; i <= 10; i++) {
|
||||
vault.addFile(`test/Untitled ${i}.timekeep`, "");
|
||||
}
|
||||
|
||||
await createNewTimekeepFile(app, folder);
|
||||
|
||||
const files = vault.getFiles();
|
||||
const file = files.find((file) => file.name === "Untitled 11.timekeep");
|
||||
expect(file).toBeDefined();
|
||||
});
|
||||
});
|
||||
29
src/timekeep/createNewTimekeepFile.ts
Normal file
29
src/timekeep/createNewTimekeepFile.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import type { App, TFolder } from "obsidian";
|
||||
|
||||
/**
|
||||
* Create a new timekeep file within the provided folder
|
||||
*
|
||||
* @param app App to use for the vault and workspace
|
||||
* @param folder
|
||||
*/
|
||||
export async function createNewTimekeepFile(app: App, folder: TFolder) {
|
||||
const folderPath = folder.path;
|
||||
|
||||
const isNameTaken = (name: string) =>
|
||||
folder.children.find((child) => child.name === name) !== undefined;
|
||||
|
||||
let name = "Untitled.timekeep";
|
||||
let index = 1;
|
||||
|
||||
while (isNameTaken(name)) {
|
||||
name = `Untitled ${index}.timekeep`;
|
||||
index += 1;
|
||||
}
|
||||
|
||||
const filePath = `${folderPath}${name}`;
|
||||
const file = await app.vault.create(filePath, "");
|
||||
|
||||
// Open the created file
|
||||
const leaf = app.workspace.getLeaf();
|
||||
await leaf.openFile(file);
|
||||
}
|
||||
Loading…
Reference in a new issue