Skip to content

Commit 3ac1698

Browse files
committed
Enhance file creation handling: update logic to track file creation and improve date validation with new utility functions for better organization and error handling.
1 parent 5e5514f commit 3ac1698

File tree

3 files changed

+128
-73
lines changed

3 files changed

+128
-73
lines changed

src/command-handler.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,20 +58,20 @@ export class TimeTreeHandler {
5858
}
5959
}
6060

61-
async handleFileCreation(file: TFile, useTitleDate = true): Promise<void> {
62-
const targetFolder = this.settings.TimeFolderPath;
63-
if (file.path.startsWith(`${targetFolder}/`) && file.extension === "md") {
61+
async handleFileCreation(file: TFile, updateRootNoteTracker = true): Promise<void> {
62+
const timeFolder = this.settings.TimeFolderPath;
63+
if (file.path.startsWith(`${timeFolder}/`) && file.extension === "md") {
6464
const isRegistred = await this.frontMatterManager.getProperty(file, "Registred");
6565
const taskName = await this.frontMatterManager.getProperty(file, "Task");
6666
if (isRegistred === false && !taskName) {
6767
const fileStat = await this.app.vault.adapter.stat(file.path) as Stat;
68-
await organizeSingleFile(this.app, targetFolder, file);
68+
await organizeSingleFile(this.app, timeFolder, file);
6969
const rootNote = this.app.vault.getAbstractFileByPath(this.settings.rootNotePath) as TFile;
70-
if (rootNote) {
71-
const dateToUse = useTitleDate
72-
? formatDateToISO(formatStringToDate(file.name))
73-
: formatDateToISO(new Date(fileStat.ctime));
74-
console.log(file.name, dateToUse);
70+
if (rootNote && updateRootNoteTracker) {
71+
let dateToUse = formatDateToISO(formatStringToDate(file.name));
72+
if (!dateToUse) {
73+
dateToUse = formatDateToISO(new Date(fileStat.ctime));
74+
}
7575
await replaceSimpleTimeTrackerBlock(this.app, rootNote, "", dateToUse);
7676
const runningNote = await this.frontMatterManager.findDoingNote(rootNote) as TFile;
7777
if (runningNote !== rootNote) {
@@ -121,7 +121,7 @@ export class TimeTreeHandler {
121121
await ensureFolderStructure(this.app, entryFilePath);
122122
await this.app.vault.create(entryFilePath, content);
123123

124-
replaceSimpleTimeTrackerBlock(this.app, rootFile, runningValue, lastTrackerTime);
124+
await replaceSimpleTimeTrackerBlock(this.app, rootFile, runningValue, lastTrackerTime);
125125
if (runningNote != rootFile && runningNote != activeFile && activeFile != rootFile) {
126126
await this.updateTrackerBlocks(runningNote, lastTrackerTime, status(isEnd));
127127
}
@@ -175,7 +175,7 @@ export class TimeTreeHandler {
175175
rootFileContent: string,
176176
trackerBlockRegex: RegExp,
177177
trackerBlockMatch: RegExpMatchArray,
178-
entry: any,
178+
entry: { name: string; startTime: string; endTime: string | null },
179179
status: string,
180180
lastTrackerTime: string
181181
): Promise<string> {
@@ -215,7 +215,7 @@ export class TimeTreeHandler {
215215

216216
private async createNewTrackerBlock(
217217
rootFileContent: string,
218-
entry: any,
218+
entry: { name: string; startTime: string; endTime: string | null },
219219
status: string
220220
): Promise<string> {
221221
const yamlEnd = this.frontMatterManager.getYamlEnd(rootFileContent.split("\n"));

src/main.ts

Lines changed: 101 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { Plugin, TFile, App } from "obsidian";
1+
import { Plugin, TFile, App, TFolder } from "obsidian";
22
import { defaultSettings, TimeTreeSettings } from "./settings";
33
import { TimeTreeSettingsTab } from "./settings-tab";
44
import { FrontMatterManager } from "./front-matter-manager";
55
import { TimeTreeHandler } from "./command-handler";
6-
import { delay } from "./utils";
6+
import { delay, isValidDateFormat } from "./utils";
77

88
interface AppWithPlugins extends App {
99
plugins: {
@@ -21,6 +21,20 @@ export default class TimeTreePlugin extends Plugin {
2121
private commandHandler: TimeTreeHandler;
2222
private pluginLoadTime = 0;
2323

24+
private scheduleComputeTimeTree(): void {
25+
// Clear any existing interval
26+
if (this.computeIntervalHandle) {
27+
clearInterval(this.computeIntervalHandle);
28+
}
29+
// Only schedule if the compute interval is greater than 0 (enabled)
30+
if (this.settings.computeIntervalMinutes > 0) {
31+
const intervalMs = this.settings.computeIntervalMinutes * 60 * 1000;
32+
this.computeIntervalHandle = setInterval(async () => {
33+
await this.commandHandler.computeTimeTree();
34+
}, intervalMs);
35+
}
36+
}
37+
2438
private async loadPlugins(verbose = false): Promise<void> {
2539
// Load Simple Time Tracker API
2640
const simpleTimeTrackerPlugin = (this.app as AppWithPlugins).plugins.plugins["simple-time-tracker"];
@@ -41,6 +55,89 @@ export default class TimeTreePlugin extends Plugin {
4155
}
4256
}
4357

58+
private async getAllFilesInTimeFolder(): Promise<string[]> {
59+
const timeFolder = this.app.vault.getAbstractFileByPath(this.settings.TimeFolderPath);
60+
if (timeFolder && timeFolder instanceof TFolder) {
61+
const files: string[] = [];
62+
timeFolder.children.forEach((file) => {
63+
if (file instanceof TFile && file.extension === "md" && isValidDateFormat(file.name, true)) {
64+
files.push(file.path);
65+
}
66+
});
67+
files.sort((a, b) => a.localeCompare(b));
68+
return files;
69+
} else {
70+
console.error("TimeFolderPath is not a valid folder:", this.settings.TimeFolderPath);
71+
return [];
72+
}
73+
}
74+
75+
private async logRootFileCreatedDate(rootFile: TFile): Promise<void> {
76+
const lastTrackerTime = await this.frontMatterManager.getLastTrackerTimeRegex(rootFile) as string;
77+
this.pluginLoadTime = lastTrackerTime ? new Date(lastTrackerTime).getTime() : Date.now();
78+
const files = await this.getAllFilesInTimeFolder();
79+
for (let i = 0; i < files.length; i++) {
80+
const filePath = files[i];
81+
const file = this.app.vault.getAbstractFileByPath(filePath) as TFile;
82+
await this.handleFileCreation(file, i === files.length - 1);
83+
}
84+
this.registerEvent(this.app.vault.on("create", this.handleFileCreation.bind(this)));
85+
}
86+
87+
private async initializeRootFileChecker(rootNotePath: string): Promise<void> {
88+
const tryGetRootFile = (): TFile | null => {
89+
if (!rootNotePath) {
90+
return null;
91+
}
92+
const rootFile = this.app.vault.getAbstractFileByPath(rootNotePath);
93+
return rootFile instanceof TFile ? rootFile : null;
94+
};
95+
96+
let rootFile: TFile | null = null;
97+
while (!rootFile) {
98+
rootFile = tryGetRootFile();
99+
if (rootFile) {
100+
await this.logRootFileCreatedDate(rootFile);
101+
console.log("Root file found:", rootFile.path);
102+
break;
103+
}
104+
await delay(1000);
105+
}
106+
}
107+
108+
private async handleFileCreation(file: TFile, updateRootNoteTracker = true): Promise<void> {
109+
if (file instanceof TFile && file.stat) {
110+
if (file.stat.ctime > this.pluginLoadTime) {
111+
await delay(0);
112+
await this.commandHandler.handleFileCreation(file, updateRootNoteTracker);
113+
console.log("File handled:", file.path);
114+
}
115+
}
116+
}
117+
118+
private initializeButtonObserver(): void {
119+
this.buttonObserver = new MutationObserver((mutations) => {
120+
mutations.forEach((mutation) => {
121+
mutation.addedNodes.forEach((node) => {
122+
if (node instanceof HTMLElement) {
123+
const btn = node.querySelector(
124+
".simple-time-tracker-btn"
125+
) as HTMLButtonElement | null;
126+
if (btn) {
127+
btn.addEventListener("click", async () => {
128+
await this.commandHandler.handleTrackerButtonClick(btn);
129+
});
130+
}
131+
}
132+
});
133+
});
134+
});
135+
this.buttonObserver.observe(document.body, {
136+
childList: true,
137+
subtree: true,
138+
});
139+
}
140+
44141
async onload(): Promise<void> {
45142
await this.loadSettings();
46143
await this.loadPlugins();
@@ -163,46 +260,8 @@ export default class TimeTreePlugin extends Plugin {
163260
},
164261
});
165262

166-
this.buttonObserver = new MutationObserver((mutations) => {
167-
mutations.forEach((mutation) => {
168-
mutation.addedNodes.forEach((node) => {
169-
if (node instanceof HTMLElement) {
170-
const btn = node.querySelector(
171-
".simple-time-tracker-btn"
172-
) as HTMLButtonElement | null;
173-
if (btn) {
174-
btn.addEventListener("click", async () => {
175-
await this.commandHandler.handleTrackerButtonClick(btn);
176-
});
177-
}
178-
}
179-
});
180-
});
181-
});
182-
this.buttonObserver.observe(document.body, {
183-
childList: true,
184-
subtree: true,
185-
});
186-
187-
// const rootFile = await getRootFile(this.app, this.settings.rootNotePath) as TFile; // | null;
188-
// if (!rootFile) {
189-
// // TODO: file exists but was not loaded yet
190-
// console.error("Root file not found at path:", this.settings.rootNotePath);
191-
// return; // Exit early if the root file is not found
192-
// }
193-
const lastTrackerTime = null; // await this.frontMatterManager.getLastTrackerTimeRegex(rootFile) as string;
194-
this.pluginLoadTime = lastTrackerTime ? new Date(lastTrackerTime).getTime() : Date.now();
195-
this.registerEvent(
196-
this.app.vault.on("create", async (file: TFile) => {
197-
if (file instanceof TFile && file.stat) {
198-
if (file.stat.ctime > this.pluginLoadTime) {
199-
await delay(0);
200-
await this.commandHandler.handleFileCreation(file);
201-
}
202-
}
203-
})
204-
);
205-
263+
this.initializeButtonObserver();
264+
this.initializeRootFileChecker(this.settings.rootNotePath);
206265
this.scheduleComputeTimeTree();
207266
}
208267

@@ -227,18 +286,4 @@ export default class TimeTreePlugin extends Plugin {
227286
await this.saveData(this.settings);
228287
this.scheduleComputeTimeTree();
229288
}
230-
231-
scheduleComputeTimeTree(): void {
232-
// Clear any existing interval
233-
if (this.computeIntervalHandle) {
234-
clearInterval(this.computeIntervalHandle);
235-
}
236-
// Only schedule if the compute interval is greater than 0 (enabled)
237-
if (this.settings.computeIntervalMinutes > 0) {
238-
const intervalMs = this.settings.computeIntervalMinutes * 60 * 1000;
239-
this.computeIntervalHandle = setInterval(async () => {
240-
await this.commandHandler.computeTimeTree();
241-
}, intervalMs);
242-
}
243-
}
244289
}

src/utils.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,18 +136,28 @@ export function createPathSetting(
136136
});
137137
}
138138

139+
export function detectDatePattern(fileName: string, includeTime = false): RegExpMatchArray | null {
140+
return includeTime
141+
? fileName.match(/^(\d{4})-(\d{2})-(\d{2}) \d{2}_\d{2}_\d{2}/)
142+
: fileName.match(/^(\d{4})-(\d{2})-(\d{2})/);
143+
}
144+
145+
export function isValidDateFormat(fileName: string, includeTime = false): boolean {
146+
return detectDatePattern(fileName, includeTime) !== null;
147+
}
148+
139149
export function getCorrectFilePath(timeFolderPath: string, fileName: string, includeFilePath = true): string | null {
140-
const match = fileName.match(/(\d{4})-(\d{2})-(\d{2})/);
150+
const match = detectDatePattern(fileName, false);
141151
if (!match) {
142152
return null;
143153
}
144154

145155
const [, year, month, day] = match;
146156
let path = `${timeFolderPath}/${year}/${year}-${month}/${year}-${month}-${day}`;
147-
if (includeFilePath) {
148-
path = fileName.endsWith('.md') ? `${path}/${fileName}` : `${path}/${fileName}.md`;
149-
}
150-
return path;
157+
if (includeFilePath) {
158+
path = fileName.endsWith('.md') ? `${path}/${fileName}` : `${path}/${fileName}.md`;
159+
}
160+
return path;
151161
}
152162

153163
export async function ensureFolderStructure(app: App, folderPath: string): Promise<void> {

0 commit comments

Comments
 (0)