Skip to content

Commit

Permalink
Check if an item is currently running on start
Browse files Browse the repository at this point in the history
  • Loading branch information
b263 committed Jan 13, 2024
1 parent 0cd603f commit 79ba261
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 21 deletions.
9 changes: 7 additions & 2 deletions src/js/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { initTrackerAction } from "./lib/action/tracker-action";
import { KimaiApi } from "./lib/api/kimai-api";
import { AppEvent, StateKey } from "./lib/constants";
import { registerGlobalErrorReporter } from "./lib/error-reporter";
import { Store } from "./lib/store/store";
import { AppState, GlobalSettings } from "./lib/types";
import { AppState, GlobalSettings, SDConnectionInfo } from "./lib/types";

const store = new Store<AppState>();

$SD.onConnected(() => {
$SD.onConnected((args: SDConnectionInfo) => {
registerGlobalErrorReporter(args);
$SD.getGlobalSettings();
$SD.onDidReceiveGlobalSettings(
(event: { payload: { settings: GlobalSettings } }) => {
Expand All @@ -23,6 +25,9 @@ $SD.onConnected(() => {
}
}
);
// setTimeout(() => {
// throw new Error("test error " + new Date().toISOString());
// }, 1000);
});

EventEmitter.on(AppEvent.actionSuccess, (context: string) =>
Expand Down
4 changes: 1 addition & 3 deletions src/js/src/lib/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@ export type ApiResponse<T> =

export type TrackingItem = {
id: string | number;
};

export type TimeEntry = {
duration: number;
begin: string;
};

export type Category = {
Expand Down
32 changes: 31 additions & 1 deletion src/js/src/lib/api/kimai-api-tracker-connector.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { parse } from "date-fns";
import { AppEvent, StateKey } from "../constants";
import { Store } from "../store/store";
import { Tracker, TrackerEvent } from "../tracker";
import { AppState, KimaiBackendProviderPluginConfig } from "../types";
import { ApiTrackerConnector } from "./api";
import { KimaiApi } from "./kimai-api";
import { KimaiApi, kimaiDateFormatTz } from "./kimai-api";

export class KimaiApiTrackerConnector implements ApiTrackerConnector {
#api: KimaiApi;
Expand All @@ -13,6 +14,7 @@ export class KimaiApiTrackerConnector implements ApiTrackerConnector {
onStart: this.onStart.bind(this),
onStop: this.onStop.bind(this),
onRequestWorkedToday: this.onRequestWorkedToday.bind(this),
onCheckIfCurrentlyActive: this.onCheckIfCurrentlyActive.bind(this),
};

backendProvider = "kimai" as const;
Expand Down Expand Up @@ -70,6 +72,30 @@ export class KimaiApiTrackerConnector implements ApiTrackerConnector {
}
}

async onCheckIfCurrentlyActive() {
console.log(
"KimaiApiTrackerConnector.onCheckIfCurrentlyActive()",
this.settings(this.#tracker)
);
const projectId = this.settings(this.#tracker)?.projectId;
const activityId = this.settings(this.#tracker)?.activityId;
if (this.#tracker.running) {
throw new Error(
"Tracker has already been started in Stream Deck. Check if there's an active event in the backend is too late."
);
}
if (projectId && activityId) {
const event = await this.#api.getCurrentlyActive(projectId, activityId);
if (event) {
this.#store.patchState({
[StateKey.currentEvent]: event,
});
const startTime = parse(event.begin, kimaiDateFormatTz, new Date());
this.#tracker.start(startTime);
}
}
}

connect(tracker: Tracker) {
this.#tracker = tracker;

Expand All @@ -79,6 +105,10 @@ export class KimaiApiTrackerConnector implements ApiTrackerConnector {
TrackerEvent.requestWorkedToday,
this.#bound.onRequestWorkedToday
);
tracker.addEventListener(
TrackerEvent.checkIfCurrentlyActive,
this.#bound.onCheckIfCurrentlyActive
);

return this;
}
Expand Down
32 changes: 21 additions & 11 deletions src/js/src/lib/api/kimai-api.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
import { format, startOfToday } from "date-fns";
import {
ApiResponse,
Category,
TimeEntry,
TrackingItem,
tryFetch,
} from "./api";
import { ApiResponse, Category, TrackingItem, tryFetch } from "./api";
import { KimaiBackendProviderPluginConfig } from "../types";

type ApiConfig = {
Expand All @@ -14,6 +8,9 @@ type ApiConfig = {
token: string;
};

export const kimaiDateFormat = "yyyy-MM-dd'T'HH:mm:ss";
export const kimaiDateFormatTz = "yyyy-MM-dd'T'HH:mm:ssxx";

export class KimaiApi {
static instance: KimaiApi | null = null;

Expand Down Expand Up @@ -87,7 +84,7 @@ export class KimaiApi {
this.assertValidConfig();
const url = `${this.#baseUrl}api/timesheets`;
const body = {
begin: format(new Date(), "yyyy-MM-dd'T'HH:mm:ss"),
begin: format(new Date(), kimaiDateFormat),
project: projectId,
activity: activityId,
description: "",
Expand All @@ -110,6 +107,19 @@ export class KimaiApi {
return tryFetch<any>(url, options);
}

async getCurrentlyActive(projectId: number, activityId: number) {
this.assertValidConfig();
const params = new URLSearchParams({
active: "1",
project: String(projectId),
activity: String(activityId),
});
const url = `${this.#baseUrl}api/timesheets?${params.toString()}`;
const response = await tryFetch<TrackingItem[]>(url, this.fetchOptions);
if (!response.success) return null;
return response.body.length ? response.body[0] : null;
}

async getProjects() {
this.assertValidConfig();
const url = `${this.#baseUrl}api/projects`;
Expand All @@ -128,7 +138,7 @@ export class KimaiApi {
async listTodaysTimeEntries(
projectId: number,
activityId: number
): Promise<ApiResponse<TimeEntry[]>> {
): Promise<ApiResponse<TrackingItem[]>> {
this.assertValidConfig();
if (!projectId || !activityId) {
return {
Expand All @@ -139,11 +149,11 @@ export class KimaiApi {
};
}
const params = new URLSearchParams({
begin: format(startOfToday(), "yyyy-MM-dd'T'HH:mm:ss"),
begin: format(startOfToday(), kimaiDateFormat),
"projects[]": String(projectId),
"activities[]": String(activityId),
});
const url = `${this.#baseUrl}api/timesheets?${params.toString()}`;
return tryFetch<TimeEntry[]>(url, this.fetchOptions);
return tryFetch<TrackingItem[]>(url, this.fetchOptions);
}
}
9 changes: 9 additions & 0 deletions src/js/src/lib/error-reporter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { SDConnectionInfo } from "./types";

export function registerGlobalErrorReporter(info: SDConnectionInfo) {
window.addEventListener("error", ({ error }: ErrorEvent) => {
const message = `Error in ${info?.appInfo?.plugin?.uuid} (${info?.appInfo?.plugin?.version}): ${error?.message}\n${error?.stack}`;
console.log("Writing error log:", message);
$SD.logMessage(message);
});
}
19 changes: 15 additions & 4 deletions src/js/src/lib/tracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const TrackerEvent = {
start: "start",
stop: "stop",
requestWorkedToday: "requestWorkedToday",
checkIfCurrentlyActive: "checkIfCurrentlyActive",
} as const;

export class Tracker extends EventTarget {
Expand Down Expand Up @@ -37,6 +38,7 @@ export class Tracker extends EventTarget {
public running: boolean = false;
public workedToday: number | undefined;
private interval: number | undefined;
private checkedIfCurrentlyActive: boolean = false;

constructor(context: string, running: boolean) {
console.log("Tracker.constructor(context, running)", { context, running });
Expand All @@ -52,6 +54,10 @@ export class Tracker extends EventTarget {
if (typeof this.workedToday !== "number") {
this.dispatchEvent(new Event(TrackerEvent.requestWorkedToday));
}
if (!this.checkedIfCurrentlyActive) {
this.dispatchEvent(new Event(TrackerEvent.checkIfCurrentlyActive));
this.checkedIfCurrentlyActive = true;
}
}
public get settings(): PluginSettings | undefined {
return this.#settings;
Expand All @@ -62,14 +68,19 @@ export class Tracker extends EventTarget {
return Math.floor((new Date().getTime() - this.startTime?.getTime()) / 1e3);
}

start() {
start(startTime?: Date) {
console.log("Tracker.start(startTime)", { startTime });
this.running = true;
this.startTime = new Date();
this.startTime = startTime ?? new Date();
this.render();
Tracker.pauseOtherTrackers(this);
this.dispatchEvent(new Event(TrackerEvent.requestWorkedToday));
this.dispatchEvent(new Event(TrackerEvent.start));
this.interval = window.setInterval(this.tick(), 6e4);
if (!startTime) {
this.dispatchEvent(new Event(TrackerEvent.requestWorkedToday));
this.dispatchEvent(new Event(TrackerEvent.start));
} else {
// A given start time means that the tracker was started in the backend.
}
}

tick() {
Expand Down

0 comments on commit 79ba261

Please sign in to comment.