mirror of
https://github.com/Ellpeck/ObsidianSimpleTimeTracker.git
synced 2024-11-21 17:23:28 +01:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
3186f1ba4d
3 changed files with 37 additions and 18 deletions
|
@ -17,10 +17,6 @@ A time tracker is really just a special code block that stores information about
|
||||||
|
|
||||||
The tracker's information is stored in the code block as JSON data. The names, start times and end times of each segment are stored. They're displayed neatly in the code block in preview or reading mode.
|
The tracker's information is stored in the code block as JSON data. The names, start times and end times of each segment are stored. They're displayed neatly in the code block in preview or reading mode.
|
||||||
|
|
||||||
# 🛣️ Roadmap
|
|
||||||
Super Simple Time Tracker is still in its early stages! There are a lot of plans for it, including:
|
|
||||||
- A setting to link segments to corresponding daily notes automatically
|
|
||||||
|
|
||||||
# 🙏 Acknowledgements
|
# 🙏 Acknowledgements
|
||||||
If you like this plugin and want to support its development, you can do so through my website by clicking this fancy image!
|
If you like this plugin and want to support its development, you can do so through my website by clicking this fancy image!
|
||||||
|
|
||||||
|
|
21
src/main.ts
21
src/main.ts
|
@ -1,4 +1,4 @@
|
||||||
import {MarkdownRenderChild, Plugin} from "obsidian";
|
import {MarkdownRenderChild, Plugin, TFile} from "obsidian";
|
||||||
import { defaultSettings, SimpleTimeTrackerSettings } from "./settings";
|
import { defaultSettings, SimpleTimeTrackerSettings } from "./settings";
|
||||||
import { SimpleTimeTrackerSettingsTab } from "./settings-tab";
|
import { SimpleTimeTrackerSettingsTab } from "./settings-tab";
|
||||||
import { displayTracker, loadTracker } from "./tracker";
|
import { displayTracker, loadTracker } from "./tracker";
|
||||||
|
@ -16,7 +16,24 @@ export default class SimpleTimeTrackerPlugin extends Plugin {
|
||||||
e.empty();
|
e.empty();
|
||||||
let component = new MarkdownRenderChild(e)
|
let component = new MarkdownRenderChild(e)
|
||||||
let tracker = loadTracker(s);
|
let tracker = loadTracker(s);
|
||||||
displayTracker(tracker, e, i.sourcePath, () => i.getSectionInfo(e), this.settings, component);
|
|
||||||
|
// Initial file name
|
||||||
|
let filePath = i.sourcePath;
|
||||||
|
|
||||||
|
// Getter passed to displayTracker since the file name can change
|
||||||
|
const getFile = () => filePath;
|
||||||
|
|
||||||
|
// Hook rename events to update the file path
|
||||||
|
const renameEventRef = this.app.vault.on("rename", (file, oldPath) => {
|
||||||
|
if (file instanceof TFile && oldPath === filePath) {
|
||||||
|
filePath = file.path;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Register the event to remove on unload
|
||||||
|
component.registerEvent(renameEventRef);
|
||||||
|
|
||||||
|
displayTracker(tracker, e, getFile, () => i.getSectionInfo(e), this.settings, component);
|
||||||
i.addChild(component)
|
i.addChild(component)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,10 @@ export function loadTracker(json: string): Tracker {
|
||||||
return {entries: []};
|
return {entries: []};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function displayTracker(tracker: Tracker, element: HTMLElement, file: string, getSectionInfo: () => MarkdownSectionInformation, settings: SimpleTimeTrackerSettings, component: MarkdownRenderChild): void {
|
type GetFile = () => string;
|
||||||
|
|
||||||
|
export function displayTracker(tracker: Tracker, element: HTMLElement, getFile: GetFile, getSectionInfo: () => MarkdownSectionInformation, settings: SimpleTimeTrackerSettings, component: MarkdownRenderChild): void {
|
||||||
|
|
||||||
element.addClass("simple-time-tracker-container");
|
element.addClass("simple-time-tracker-container");
|
||||||
// add start/stop controls
|
// add start/stop controls
|
||||||
let running = isRunning(tracker);
|
let running = isRunning(tracker);
|
||||||
|
@ -55,7 +58,7 @@ export function displayTracker(tracker: Tracker, element: HTMLElement, file: str
|
||||||
} else {
|
} else {
|
||||||
startNewEntry(tracker, newSegmentNameBox.getValue());
|
startNewEntry(tracker, newSegmentNameBox.getValue());
|
||||||
}
|
}
|
||||||
await saveTracker(tracker, this.app, file, getSectionInfo());
|
await saveTracker(tracker, this.app, getFile(), getSectionInfo());
|
||||||
});
|
});
|
||||||
btn.buttonEl.addClass("simple-time-tracker-btn");
|
btn.buttonEl.addClass("simple-time-tracker-btn");
|
||||||
let newSegmentNameBox = new TextComponent(element)
|
let newSegmentNameBox = new TextComponent(element)
|
||||||
|
@ -83,7 +86,7 @@ export function displayTracker(tracker: Tracker, element: HTMLElement, file: str
|
||||||
createEl("th"));
|
createEl("th"));
|
||||||
|
|
||||||
for (let entry of orderedEntries(tracker.entries, settings))
|
for (let entry of orderedEntries(tracker.entries, settings))
|
||||||
addEditableTableRow(tracker, entry, table, newSegmentNameBox, running, file, getSectionInfo, settings, 0, component);
|
addEditableTableRow(tracker, entry, table, newSegmentNameBox, running, getFile, getSectionInfo, settings, 0, component);
|
||||||
|
|
||||||
// add copy buttons
|
// add copy buttons
|
||||||
let buttons = element.createEl("div", {cls: "simple-time-tracker-bottom"});
|
let buttons = element.createEl("div", {cls: "simple-time-tracker-bottom"});
|
||||||
|
@ -303,7 +306,7 @@ function orderedEntries(entries: Entry[], settings: SimpleTimeTrackerSettings):
|
||||||
return settings.reverseSegmentOrder ? entries.slice().reverse() : entries;
|
return settings.reverseSegmentOrder ? entries.slice().reverse() : entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addEditableTableRow(tracker: Tracker, entry: Entry, table: HTMLTableElement, newSegmentNameBox: TextComponent, trackerRunning: boolean, file: string, getSectionInfo: () => MarkdownSectionInformation, settings: SimpleTimeTrackerSettings, indent: number, component: MarkdownRenderChild): void {
|
function addEditableTableRow(tracker: Tracker, entry: Entry, table: HTMLTableElement, newSegmentNameBox: TextComponent, trackerRunning: boolean, getFile: GetFile, getSectionInfo: () => MarkdownSectionInformation, settings: SimpleTimeTrackerSettings, indent: number, component: MarkdownRenderChild): void {
|
||||||
let entryRunning = getRunningEntry(tracker.entries) == entry;
|
let entryRunning = getRunningEntry(tracker.entries) == entry;
|
||||||
let row = table.createEl("tr");
|
let row = table.createEl("tr");
|
||||||
|
|
||||||
|
@ -313,7 +316,7 @@ function addEditableTableRow(tracker: Tracker, entry: Entry, table: HTMLTableEle
|
||||||
|
|
||||||
row.createEl("td", {text: entry.endTime || entry.subEntries ? formatDuration(getDuration(entry), settings) : ""});
|
row.createEl("td", {text: entry.endTime || entry.subEntries ? formatDuration(getDuration(entry), settings) : ""});
|
||||||
|
|
||||||
renderNameAsMarkdown(nameField.label, file, component);
|
renderNameAsMarkdown(nameField.label, getFile, component);
|
||||||
|
|
||||||
let entryButtons = row.createEl("td");
|
let entryButtons = row.createEl("td");
|
||||||
entryButtons.addClass("simple-time-tracker-table-buttons");
|
entryButtons.addClass("simple-time-tracker-table-buttons");
|
||||||
|
@ -324,7 +327,7 @@ function addEditableTableRow(tracker: Tracker, entry: Entry, table: HTMLTableEle
|
||||||
.setDisabled(trackerRunning)
|
.setDisabled(trackerRunning)
|
||||||
.onClick(async () => {
|
.onClick(async () => {
|
||||||
startSubEntry(entry, newSegmentNameBox.getValue());
|
startSubEntry(entry, newSegmentNameBox.getValue());
|
||||||
await saveTracker(tracker, this.app, file, getSectionInfo());
|
await saveTracker(tracker, this.app, getFile(), getSectionInfo());
|
||||||
});
|
});
|
||||||
let editButton = new ButtonComponent(entryButtons)
|
let editButton = new ButtonComponent(entryButtons)
|
||||||
.setClass("clickable-icon")
|
.setClass("clickable-icon")
|
||||||
|
@ -339,10 +342,10 @@ function addEditableTableRow(tracker: Tracker, entry: Entry, table: HTMLTableEle
|
||||||
endField.endEdit();
|
endField.endEdit();
|
||||||
entry.endTime = endField.getTimestamp();
|
entry.endTime = endField.getTimestamp();
|
||||||
}
|
}
|
||||||
await saveTracker(tracker, this.app, file, getSectionInfo());
|
await saveTracker(tracker, this.app, getFile(), getSectionInfo());
|
||||||
editButton.setIcon("lucide-pencil");
|
editButton.setIcon("lucide-pencil");
|
||||||
|
|
||||||
renderNameAsMarkdown(nameField.label, file, component);
|
renderNameAsMarkdown(nameField.label, getFile, component);
|
||||||
} else {
|
} else {
|
||||||
nameField.beginEdit(entry.name);
|
nameField.beginEdit(entry.name);
|
||||||
// only allow editing start and end times if we don't have sub entries
|
// only allow editing start and end times if we don't have sub entries
|
||||||
|
@ -360,18 +363,21 @@ function addEditableTableRow(tracker: Tracker, entry: Entry, table: HTMLTableEle
|
||||||
.setIcon("lucide-trash")
|
.setIcon("lucide-trash")
|
||||||
.setDisabled(entryRunning)
|
.setDisabled(entryRunning)
|
||||||
.onClick(async () => {
|
.onClick(async () => {
|
||||||
|
if (!confirm("Are you sure you want to delete this entry?")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
removeEntry(tracker.entries, entry);
|
removeEntry(tracker.entries, entry);
|
||||||
await saveTracker(tracker, this.app, file, getSectionInfo());
|
await saveTracker(tracker, this.app, getFile(), getSectionInfo());
|
||||||
});
|
});
|
||||||
|
|
||||||
if (entry.subEntries) {
|
if (entry.subEntries) {
|
||||||
for (let sub of orderedEntries(entry.subEntries, settings))
|
for (let sub of orderedEntries(entry.subEntries, settings))
|
||||||
addEditableTableRow(tracker, sub, table, newSegmentNameBox, trackerRunning, file, getSectionInfo, settings, indent + 1, component);
|
addEditableTableRow(tracker, sub, table, newSegmentNameBox, trackerRunning, getFile, getSectionInfo, settings, indent + 1, component);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderNameAsMarkdown(label: HTMLSpanElement, path: string, component: Component): void {
|
function renderNameAsMarkdown(label: HTMLSpanElement, getFile: GetFile, component: Component): void {
|
||||||
void MarkdownRenderer.renderMarkdown(label.innerHTML, label, path, component);
|
void MarkdownRenderer.renderMarkdown(label.innerHTML, label, getFile(), component);
|
||||||
// rendering wraps it in a paragraph
|
// rendering wraps it in a paragraph
|
||||||
label.innerHTML = label.querySelector("p").innerHTML;
|
label.innerHTML = label.querySelector("p").innerHTML;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue