2023-09-08 13:59:50 +02:00
|
|
|
import {moment, App, MarkdownSectionInformation, ButtonComponent, TextComponent, TFile} from "obsidian";
|
|
|
|
import {SimpleTimeTrackerSettings} from "./settings";
|
2022-09-27 16:06:40 +02:00
|
|
|
|
2022-09-29 16:01:20 +02:00
|
|
|
export interface Tracker {
|
2022-09-27 17:03:44 +02:00
|
|
|
entries: Entry[];
|
|
|
|
}
|
2022-09-27 16:06:40 +02:00
|
|
|
|
2022-09-27 17:03:44 +02:00
|
|
|
export interface Entry {
|
|
|
|
name: string;
|
2023-09-08 13:59:50 +02:00
|
|
|
startTime: string;
|
|
|
|
endTime: string;
|
2022-10-19 16:35:21 +02:00
|
|
|
subEntries: Entry[];
|
2022-09-27 17:03:44 +02:00
|
|
|
}
|
2022-09-27 16:06:40 +02:00
|
|
|
|
2023-05-23 18:06:29 +02:00
|
|
|
export async function saveTracker(tracker: Tracker, app: App, fileName: string, section: MarkdownSectionInformation): Promise<void> {
|
|
|
|
let file = app.vault.getAbstractFileByPath(fileName) as TFile;
|
2022-09-29 16:05:39 +02:00
|
|
|
if (!file)
|
|
|
|
return;
|
|
|
|
let content = await app.vault.read(file);
|
2022-09-27 16:06:40 +02:00
|
|
|
|
2022-09-27 17:03:44 +02:00
|
|
|
// figure out what part of the content we have to edit
|
|
|
|
let lines = content.split("\n");
|
|
|
|
let prev = lines.filter((_, i) => i <= section.lineStart).join("\n");
|
|
|
|
let next = lines.filter((_, i) => i >= section.lineEnd).join("\n");
|
|
|
|
// edit only the code block content, leave the rest untouched
|
|
|
|
content = `${prev}\n${JSON.stringify(tracker)}\n${next}`;
|
2022-09-27 16:06:40 +02:00
|
|
|
|
2022-09-27 17:03:44 +02:00
|
|
|
await app.vault.modify(file, content);
|
|
|
|
}
|
2022-09-27 16:06:40 +02:00
|
|
|
|
2022-09-27 17:03:44 +02:00
|
|
|
export function loadTracker(json: string): Tracker {
|
|
|
|
if (json) {
|
|
|
|
try {
|
2023-09-08 13:59:50 +02:00
|
|
|
let ret = JSON.parse(json);
|
|
|
|
fixLegacyTimestamps(ret.entries);
|
|
|
|
return ret;
|
2022-09-27 17:03:44 +02:00
|
|
|
} catch (e) {
|
|
|
|
console.log(`Failed to parse Tracker from ${json}`);
|
2022-09-27 16:06:40 +02:00
|
|
|
}
|
|
|
|
}
|
2023-05-22 20:01:50 +02:00
|
|
|
return {entries: []};
|
2022-09-27 16:06:40 +02:00
|
|
|
}
|
|
|
|
|
2023-05-23 18:06:29 +02:00
|
|
|
export function displayTracker(tracker: Tracker, element: HTMLElement, file: string, getSectionInfo: () => MarkdownSectionInformation, settings: SimpleTimeTrackerSettings): void {
|
2023-09-08 13:59:50 +02:00
|
|
|
element.classList.add("simple-time-tracker-container");
|
2022-09-27 21:44:28 +02:00
|
|
|
// add start/stop controls
|
|
|
|
let running = isRunning(tracker);
|
|
|
|
let btn = new ButtonComponent(element)
|
2022-10-10 14:51:11 +02:00
|
|
|
.setClass("clickable-icon")
|
|
|
|
.setIcon(`lucide-${running ? "stop" : "play"}-circle`)
|
|
|
|
.setTooltip(running ? "End" : "Start")
|
2022-09-27 21:44:28 +02:00
|
|
|
.onClick(async () => {
|
|
|
|
if (running) {
|
2022-10-19 16:35:21 +02:00
|
|
|
endRunningEntry(tracker);
|
2022-09-27 21:44:28 +02:00
|
|
|
} else {
|
2022-10-19 16:35:21 +02:00
|
|
|
startNewEntry(tracker, newSegmentNameBox.getValue());
|
2022-09-27 21:44:28 +02:00
|
|
|
}
|
2023-05-23 18:06:29 +02:00
|
|
|
await saveTracker(tracker, this.app, file, getSectionInfo());
|
2022-09-27 21:44:28 +02:00
|
|
|
});
|
|
|
|
btn.buttonEl.addClass("simple-time-tracker-btn");
|
2022-10-19 16:35:21 +02:00
|
|
|
let newSegmentNameBox = new TextComponent(element)
|
2022-09-27 21:44:28 +02:00
|
|
|
.setPlaceholder("Segment name")
|
|
|
|
.setDisabled(running);
|
2022-10-19 16:35:21 +02:00
|
|
|
newSegmentNameBox.inputEl.addClass("simple-time-tracker-txt");
|
2022-09-27 21:44:28 +02:00
|
|
|
|
2022-09-27 21:23:36 +02:00
|
|
|
// add timers
|
2023-05-22 20:01:50 +02:00
|
|
|
let timer = element.createDiv({cls: "simple-time-tracker-timers"});
|
|
|
|
let currentDiv = timer.createEl("div", {cls: "simple-time-tracker-timer"});
|
|
|
|
let current = currentDiv.createEl("span", {cls: "simple-time-tracker-timer-time"});
|
|
|
|
currentDiv.createEl("span", {text: "Current"});
|
|
|
|
let totalDiv = timer.createEl("div", {cls: "simple-time-tracker-timer"});
|
|
|
|
let total = totalDiv.createEl("span", {cls: "simple-time-tracker-timer-time", text: "0s"});
|
|
|
|
totalDiv.createEl("span", {text: "Total"});
|
2022-09-27 21:23:36 +02:00
|
|
|
|
2022-09-27 21:52:02 +02:00
|
|
|
if (tracker.entries.length > 0) {
|
2022-09-28 14:09:21 +02:00
|
|
|
// add table
|
2023-05-22 20:01:50 +02:00
|
|
|
let table = element.createEl("table", {cls: "simple-time-tracker-table"});
|
2022-09-27 21:52:02 +02:00
|
|
|
table.createEl("tr").append(
|
2023-05-22 20:01:50 +02:00
|
|
|
createEl("th", {text: "Segment"}),
|
|
|
|
createEl("th", {text: "Start time"}),
|
|
|
|
createEl("th", {text: "End time"}),
|
|
|
|
createEl("th", {text: "Duration"}),
|
2022-10-10 14:40:36 +02:00
|
|
|
createEl("th"));
|
2022-09-27 21:52:02 +02:00
|
|
|
|
2024-02-26 14:24:21 +01:00
|
|
|
for (let entry of orderedEntries(tracker.entries, settings))
|
2023-05-23 18:06:29 +02:00
|
|
|
addEditableTableRow(tracker, entry, table, newSegmentNameBox, running, file, getSectionInfo, settings, 0);
|
2022-09-28 14:09:21 +02:00
|
|
|
|
|
|
|
// add copy buttons
|
2023-05-22 20:01:50 +02:00
|
|
|
let buttons = element.createEl("div", {cls: "simple-time-tracker-bottom"});
|
2022-09-28 14:09:21 +02:00
|
|
|
new ButtonComponent(buttons)
|
|
|
|
.setButtonText("Copy as table")
|
|
|
|
.onClick(() => navigator.clipboard.writeText(createMarkdownTable(tracker, settings)));
|
|
|
|
new ButtonComponent(buttons)
|
2022-09-28 14:11:49 +02:00
|
|
|
.setButtonText("Copy as CSV")
|
2022-09-28 14:09:21 +02:00
|
|
|
.onClick(() => navigator.clipboard.writeText(createCsv(tracker, settings)));
|
2022-09-27 21:23:36 +02:00
|
|
|
}
|
|
|
|
|
2022-09-28 14:09:21 +02:00
|
|
|
|
2023-05-23 18:18:28 +02:00
|
|
|
setCountdownValues(tracker, current, total, currentDiv, settings);
|
2022-09-27 21:23:36 +02:00
|
|
|
let intervalId = window.setInterval(() => {
|
|
|
|
// we delete the interval timer when the element is removed
|
|
|
|
if (!element.isConnected) {
|
|
|
|
window.clearInterval(intervalId);
|
|
|
|
return;
|
|
|
|
}
|
2023-05-23 18:18:28 +02:00
|
|
|
setCountdownValues(tracker, current, total, currentDiv, settings);
|
2022-09-27 21:23:36 +02:00
|
|
|
}, 1000);
|
2022-09-28 00:52:52 +02:00
|
|
|
}
|
2022-09-27 16:06:40 +02:00
|
|
|
|
2023-09-08 13:59:50 +02:00
|
|
|
function startSubEntry(entry: Entry, name: string): void {
|
2022-10-19 16:35:21 +02:00
|
|
|
// if this entry is not split yet, we add its time as a sub-entry instead
|
|
|
|
if (!entry.subEntries) {
|
2023-05-22 20:01:50 +02:00
|
|
|
entry.subEntries = [{...entry, name: `Part 1`}];
|
2022-10-19 16:35:21 +02:00
|
|
|
entry.startTime = null;
|
|
|
|
entry.endTime = null;
|
2022-09-28 14:09:21 +02:00
|
|
|
}
|
2022-10-19 16:35:21 +02:00
|
|
|
|
|
|
|
if (!name)
|
|
|
|
name = `Part ${entry.subEntries.length + 1}`;
|
2023-09-08 13:59:50 +02:00
|
|
|
entry.subEntries.push({name: name, startTime: moment().toISOString(), endTime: null, subEntries: null});
|
2022-09-28 14:09:21 +02:00
|
|
|
}
|
|
|
|
|
2022-10-19 16:35:21 +02:00
|
|
|
function startNewEntry(tracker: Tracker, name: string): void {
|
|
|
|
if (!name)
|
|
|
|
name = `Segment ${tracker.entries.length + 1}`;
|
2023-09-08 13:59:50 +02:00
|
|
|
let entry: Entry = {name: name, startTime: moment().toISOString(), endTime: null, subEntries: null};
|
2022-10-19 16:35:21 +02:00
|
|
|
tracker.entries.push(entry);
|
2023-05-22 20:01:50 +02:00
|
|
|
}
|
2022-10-19 16:35:21 +02:00
|
|
|
|
|
|
|
function endRunningEntry(tracker: Tracker): void {
|
|
|
|
let entry = getRunningEntry(tracker.entries);
|
2023-09-08 13:59:50 +02:00
|
|
|
entry.endTime = moment().toISOString();
|
2022-10-19 16:35:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function removeEntry(entries: Entry[], toRemove: Entry): boolean {
|
|
|
|
if (entries.contains(toRemove)) {
|
|
|
|
entries.remove(toRemove);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
for (let entry of entries) {
|
|
|
|
if (entry.subEntries && removeEntry(entry.subEntries, toRemove)) {
|
|
|
|
// if we only have one sub entry remaining, we can merge back into our main entry
|
|
|
|
if (entry.subEntries.length == 1) {
|
|
|
|
let single = entry.subEntries[0];
|
|
|
|
entry.startTime = single.startTime;
|
|
|
|
entry.endTime = single.endTime;
|
|
|
|
entry.subEntries = null;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isRunning(tracker: Tracker): boolean {
|
|
|
|
return !!getRunningEntry(tracker.entries);
|
|
|
|
}
|
|
|
|
|
|
|
|
function getRunningEntry(entries: Entry[]): Entry {
|
|
|
|
for (let entry of entries) {
|
|
|
|
// if this entry has sub entries, check if one of them is running
|
|
|
|
if (entry.subEntries) {
|
|
|
|
let running = getRunningEntry(entry.subEntries);
|
|
|
|
if (running)
|
|
|
|
return running;
|
|
|
|
} else {
|
|
|
|
// if this entry has no sub entries and no end time, it's running
|
|
|
|
if (!entry.endTime)
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2023-09-08 13:59:50 +02:00
|
|
|
function getDuration(entry: Entry): number {
|
2022-10-19 16:35:21 +02:00
|
|
|
if (entry.subEntries) {
|
|
|
|
return getTotalDuration(entry.subEntries);
|
|
|
|
} else {
|
2023-09-08 13:59:50 +02:00
|
|
|
let endTime = entry.endTime ? moment(entry.endTime) : moment();
|
|
|
|
return endTime.diff(moment(entry.startTime));
|
2022-09-28 14:09:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-19 16:35:21 +02:00
|
|
|
function getTotalDuration(entries: Entry[]): number {
|
|
|
|
let ret = 0;
|
|
|
|
for (let entry of entries)
|
|
|
|
ret += getDuration(entry);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-09-08 13:59:50 +02:00
|
|
|
function setCountdownValues(tracker: Tracker, current: HTMLElement, total: HTMLElement, currentDiv: HTMLDivElement, settings: SimpleTimeTrackerSettings): void {
|
2022-10-19 16:35:21 +02:00
|
|
|
let running = getRunningEntry(tracker.entries);
|
|
|
|
if (running && !running.endTime) {
|
2023-05-23 18:18:28 +02:00
|
|
|
current.setText(formatDuration(getDuration(running), settings));
|
2022-10-19 16:35:21 +02:00
|
|
|
currentDiv.hidden = false;
|
|
|
|
} else {
|
|
|
|
currentDiv.hidden = true;
|
|
|
|
}
|
2023-05-23 18:18:28 +02:00
|
|
|
total.setText(formatDuration(getTotalDuration(tracker.entries), settings));
|
2022-09-28 14:09:21 +02:00
|
|
|
}
|
|
|
|
|
2023-09-08 13:59:50 +02:00
|
|
|
function formatTimestamp(timestamp: string, settings: SimpleTimeTrackerSettings): string {
|
|
|
|
return moment(timestamp).format(settings.timestampFormat);
|
2022-09-28 14:09:21 +02:00
|
|
|
}
|
|
|
|
|
2023-09-08 13:59:50 +02:00
|
|
|
function formatEditableTimestamp(timestamp: string, settings: SimpleTimeTrackerSettings): string {
|
|
|
|
return moment(timestamp).format(settings.editableTimestampFormat);
|
2023-07-05 15:57:58 +02:00
|
|
|
}
|
|
|
|
|
2023-09-08 13:59:50 +02:00
|
|
|
function unformatEditableTimestamp(formatted: string, settings: SimpleTimeTrackerSettings): string {
|
|
|
|
return moment(formatted, settings.editableTimestampFormat).toISOString();
|
2023-07-03 17:23:47 +02:00
|
|
|
}
|
|
|
|
|
2023-05-23 18:18:28 +02:00
|
|
|
function formatDuration(totalTime: number, settings: SimpleTimeTrackerSettings): string {
|
2022-09-27 17:03:44 +02:00
|
|
|
let ret = "";
|
2023-05-23 18:18:28 +02:00
|
|
|
let duration = moment.duration(totalTime);
|
|
|
|
let hours: number;
|
|
|
|
if (settings.fineGrainedDurations) {
|
|
|
|
if (duration.years() > 0)
|
|
|
|
ret += duration.years() + "y ";
|
|
|
|
if (duration.months() > 0)
|
|
|
|
ret += duration.months() + "M ";
|
|
|
|
if (duration.days() > 0)
|
|
|
|
ret += duration.days() + "d ";
|
|
|
|
hours = duration.hours();
|
|
|
|
} else {
|
|
|
|
hours = Math.floor(duration.asHours());
|
|
|
|
}
|
|
|
|
if (hours > 0)
|
|
|
|
ret += hours + "h ";
|
2022-09-27 21:35:26 +02:00
|
|
|
if (duration.minutes() > 0)
|
|
|
|
ret += duration.minutes() + "m ";
|
|
|
|
ret += duration.seconds() + "s";
|
2022-09-27 21:23:36 +02:00
|
|
|
return ret;
|
|
|
|
}
|
2022-09-27 16:06:40 +02:00
|
|
|
|
2023-09-08 13:59:50 +02:00
|
|
|
function fixLegacyTimestamps(entries: Entry[]): void {
|
|
|
|
for (let entry of entries) {
|
|
|
|
if (!isNaN(+entry.startTime))
|
|
|
|
entry.startTime = moment.unix(+entry.startTime).toISOString();
|
|
|
|
if (entry.endTime && !isNaN(+entry.endTime))
|
|
|
|
entry.endTime = moment.unix(+entry.endTime).toISOString();
|
|
|
|
|
|
|
|
if (entry.subEntries)
|
|
|
|
fixLegacyTimestamps(entry.subEntries);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-28 14:09:21 +02:00
|
|
|
function createMarkdownTable(tracker: Tracker, settings: SimpleTimeTrackerSettings): string {
|
|
|
|
let table = [["Segment", "Start time", "End time", "Duration"]];
|
2024-02-26 14:24:21 +01:00
|
|
|
for (let entry of orderedEntries(tracker.entries, settings))
|
2022-10-19 16:35:21 +02:00
|
|
|
table.push(...createTableSection(entry, settings));
|
2023-05-23 18:18:28 +02:00
|
|
|
table.push(["**Total**", "", "", `**${formatDuration(getTotalDuration(tracker.entries), settings)}**`]);
|
2022-09-27 16:06:40 +02:00
|
|
|
|
2022-09-28 14:09:21 +02:00
|
|
|
let ret = "";
|
|
|
|
// calculate the width every column needs to look neat when monospaced
|
|
|
|
let widths = Array.from(Array(4).keys()).map(i => Math.max(...table.map(a => a[i].length)));
|
|
|
|
for (let r = 0; r < table.length; r++) {
|
|
|
|
// add separators after first row
|
|
|
|
if (r == 1)
|
2023-05-23 16:48:18 +02:00
|
|
|
ret += "| " + Array.from(Array(4).keys()).map(i => "-".repeat(widths[i])).join(" | ") + " |\n";
|
2022-09-28 14:09:21 +02:00
|
|
|
|
|
|
|
let row: string[] = [];
|
|
|
|
for (let i = 0; i < 4; i++)
|
|
|
|
row.push(table[r][i].padEnd(widths[i], " "));
|
2023-05-23 16:48:18 +02:00
|
|
|
ret += "| " + row.join(" | ") + " |\n";
|
2022-09-27 16:06:40 +02:00
|
|
|
}
|
2022-09-28 14:09:21 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
function createCsv(tracker: Tracker, settings: SimpleTimeTrackerSettings): string {
|
|
|
|
let ret = "";
|
2024-02-26 14:24:21 +01:00
|
|
|
for (let entry of orderedEntries(tracker.entries, settings)) {
|
2022-10-19 16:35:21 +02:00
|
|
|
for (let row of createTableSection(entry, settings))
|
|
|
|
ret += row.join(settings.csvDelimiter) + "\n";
|
|
|
|
}
|
2022-09-28 14:09:21 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-10-19 16:35:21 +02:00
|
|
|
function createTableSection(entry: Entry, settings: SimpleTimeTrackerSettings): string[][] {
|
2023-09-08 13:59:50 +02:00
|
|
|
let ret = [[
|
2022-09-28 14:09:21 +02:00
|
|
|
entry.name,
|
2022-10-19 16:35:21 +02:00
|
|
|
entry.startTime ? formatTimestamp(entry.startTime, settings) : "",
|
2022-09-28 14:09:21 +02:00
|
|
|
entry.endTime ? formatTimestamp(entry.endTime, settings) : "",
|
2023-05-23 18:18:28 +02:00
|
|
|
entry.endTime || entry.subEntries ? formatDuration(getDuration(entry), settings) : ""]];
|
2022-10-19 16:35:21 +02:00
|
|
|
if (entry.subEntries) {
|
2024-02-26 14:24:21 +01:00
|
|
|
for (let sub of orderedEntries(entry.subEntries, settings))
|
2022-10-19 16:35:21 +02:00
|
|
|
ret.push(...createTableSection(sub, settings));
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2024-02-26 14:24:21 +01:00
|
|
|
function orderedEntries(entries: Entry[], settings: SimpleTimeTrackerSettings): Entry[] {
|
|
|
|
return settings.reverseSegmentOrder ? entries.slice().reverse() : entries;
|
|
|
|
}
|
|
|
|
|
2023-07-03 17:23:47 +02:00
|
|
|
class EditableField {
|
|
|
|
cell: HTMLTableCellElement;
|
|
|
|
label: HTMLSpanElement;
|
|
|
|
box: TextComponent;
|
2023-09-08 13:59:50 +02:00
|
|
|
|
2023-07-03 17:23:47 +02:00
|
|
|
constructor(row: HTMLTableRowElement, indent: number, value: string) {
|
|
|
|
this.cell = row.createEl("td");
|
2023-09-08 13:59:50 +02:00
|
|
|
this.label = this.cell.createEl("span", {text: value});
|
2023-07-03 17:23:47 +02:00
|
|
|
this.label.style.marginLeft = `${indent}em`;
|
|
|
|
this.box = new TextComponent(this.cell).setValue(value);
|
2023-09-08 13:59:50 +02:00
|
|
|
this.box.inputEl.classList.add("simple-time-tracker-input");
|
2023-07-03 17:23:47 +02:00
|
|
|
this.box.inputEl.hide();
|
|
|
|
}
|
2023-09-08 13:59:50 +02:00
|
|
|
|
2023-07-03 17:23:47 +02:00
|
|
|
editing(): boolean {
|
|
|
|
return this.label.hidden;
|
|
|
|
}
|
2023-09-08 13:59:50 +02:00
|
|
|
|
|
|
|
beginEdit(value: string): void {
|
2023-07-03 17:23:47 +02:00
|
|
|
this.label.hidden = true;
|
|
|
|
this.box.setValue(value);
|
|
|
|
this.box.inputEl.show();
|
|
|
|
}
|
2023-09-08 13:59:50 +02:00
|
|
|
|
2023-07-03 17:23:47 +02:00
|
|
|
endEdit(): string {
|
2023-07-05 15:57:58 +02:00
|
|
|
const value = this.box.getValue();
|
2023-07-03 17:23:47 +02:00
|
|
|
this.label.setText(value);
|
|
|
|
this.box.inputEl.hide();
|
|
|
|
this.label.hidden = false;
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-05 15:57:58 +02:00
|
|
|
class EditableTimestampField extends EditableField {
|
|
|
|
settings: SimpleTimeTrackerSettings;
|
2023-09-08 13:59:50 +02:00
|
|
|
|
2023-07-05 15:57:58 +02:00
|
|
|
constructor(row: HTMLTableRowElement, indent: number, value: string, settings: SimpleTimeTrackerSettings) {
|
2023-09-08 13:59:50 +02:00
|
|
|
super(row, indent, value ? formatTimestamp(value, settings) : "");
|
2023-07-05 15:57:58 +02:00
|
|
|
this.settings = settings;
|
|
|
|
}
|
2023-09-08 13:59:50 +02:00
|
|
|
|
|
|
|
beginEdit(value: string): void {
|
|
|
|
super.beginEdit(value ? formatEditableTimestamp(value, this.settings) : "");
|
2023-07-05 15:57:58 +02:00
|
|
|
}
|
2023-09-08 13:59:50 +02:00
|
|
|
|
2023-07-05 15:57:58 +02:00
|
|
|
endEdit(): string {
|
|
|
|
const value = this.box.getValue();
|
2023-07-12 04:17:42 +02:00
|
|
|
let displayValue = value;
|
|
|
|
if (value) {
|
|
|
|
const timestamp = unformatEditableTimestamp(value, this.settings);
|
|
|
|
displayValue = formatTimestamp(timestamp, this.settings);
|
|
|
|
}
|
2023-07-05 15:57:58 +02:00
|
|
|
this.label.setText(displayValue);
|
|
|
|
this.box.inputEl.hide();
|
|
|
|
this.label.hidden = false;
|
|
|
|
return value;
|
|
|
|
}
|
2023-09-08 13:59:50 +02:00
|
|
|
|
|
|
|
getTimestamp(): string {
|
2023-07-12 04:17:42 +02:00
|
|
|
if (this.box.getValue()) {
|
|
|
|
return unformatEditableTimestamp(this.box.getValue(), this.settings);
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
2023-07-05 15:57:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-08 13:59:50 +02:00
|
|
|
function addEditableTableRow(tracker: Tracker, entry: Entry, table: HTMLTableElement, newSegmentNameBox: TextComponent, running: boolean, file: string, getSectionInfo: () => MarkdownSectionInformation, settings: SimpleTimeTrackerSettings, indent: number): void {
|
2022-10-19 16:35:21 +02:00
|
|
|
let row = table.createEl("tr");
|
|
|
|
|
2023-07-03 17:23:47 +02:00
|
|
|
let nameField = new EditableField(row, indent, entry.name);
|
2023-09-08 13:59:50 +02:00
|
|
|
let startField = new EditableTimestampField(row, indent, (entry.startTime), settings);
|
|
|
|
let endField = new EditableTimestampField(row, indent, (entry.endTime), settings);
|
2023-07-03 17:23:47 +02:00
|
|
|
|
2023-09-08 13:59:50 +02:00
|
|
|
row.createEl("td", {text: entry.endTime || entry.subEntries ? formatDuration(getDuration(entry), settings) : ""});
|
2022-10-19 16:35:21 +02:00
|
|
|
|
|
|
|
let entryButtons = row.createEl("td");
|
|
|
|
if (!running) {
|
|
|
|
new ButtonComponent(entryButtons)
|
|
|
|
.setClass("clickable-icon")
|
|
|
|
.setIcon(`lucide-play`)
|
|
|
|
.setTooltip("Continue")
|
|
|
|
.onClick(async () => {
|
|
|
|
startSubEntry(entry, newSegmentNameBox.getValue());
|
2023-05-23 18:06:29 +02:00
|
|
|
await saveTracker(tracker, this.app, file, getSectionInfo());
|
2022-10-19 16:35:21 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
let editButton = new ButtonComponent(entryButtons)
|
|
|
|
.setClass("clickable-icon")
|
|
|
|
.setTooltip("Edit")
|
|
|
|
.setIcon("lucide-pencil")
|
|
|
|
.onClick(async () => {
|
2023-07-03 17:23:47 +02:00
|
|
|
if (nameField.editing()) {
|
|
|
|
entry.name = nameField.endEdit();
|
2023-07-05 15:57:58 +02:00
|
|
|
startField.endEdit();
|
2023-09-08 14:02:15 +02:00
|
|
|
entry.startTime = startField.getTimestamp();
|
2023-07-05 15:57:58 +02:00
|
|
|
endField.endEdit();
|
2023-09-08 14:02:15 +02:00
|
|
|
entry.endTime = endField.getTimestamp();
|
2023-07-03 17:23:47 +02:00
|
|
|
await saveTracker(tracker, this.app, file, getSectionInfo());
|
2022-10-19 16:35:21 +02:00
|
|
|
editButton.setIcon("lucide-pencil");
|
|
|
|
} else {
|
2023-07-03 17:23:47 +02:00
|
|
|
nameField.beginEdit(entry.name);
|
2023-09-08 13:59:50 +02:00
|
|
|
startField.beginEdit((entry.startTime));
|
|
|
|
endField.beginEdit((entry.endTime));
|
2022-10-19 16:35:21 +02:00
|
|
|
editButton.setIcon("lucide-check");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
new ButtonComponent(entryButtons)
|
|
|
|
.setClass("clickable-icon")
|
|
|
|
.setTooltip("Remove")
|
|
|
|
.setIcon("lucide-trash")
|
|
|
|
.onClick(async () => {
|
|
|
|
removeEntry(tracker.entries, entry);
|
2023-05-23 18:06:29 +02:00
|
|
|
await saveTracker(tracker, this.app, file, getSectionInfo());
|
2022-10-19 16:35:21 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
if (entry.subEntries) {
|
2024-02-26 14:24:21 +01:00
|
|
|
for (let sub of orderedEntries(entry.subEntries, settings))
|
2023-05-23 18:06:29 +02:00
|
|
|
addEditableTableRow(tracker, sub, table, newSegmentNameBox, running, file, getSectionInfo, settings, indent + 1);
|
2022-10-19 16:35:21 +02:00
|
|
|
}
|
2022-09-27 16:06:40 +02:00
|
|
|
}
|