diff --git a/src/js/api/Client.js b/src/js/api/Client.js
index c35e59f..1cee6c2 100644
--- a/src/js/api/Client.js
+++ b/src/js/api/Client.js
@@ -46,12 +46,12 @@ export default class Client {
params: { date: `${formatDate(fromDate)}:${formatDate(toDate)}` },
})
- bookedHours = service => {
+ activitiesStatus = service => {
if (!service) {
return Promise.resolve({ data: { hours: 0 } })
}
- return this.#client.get("activities/tags", {
- params: { selection: [service.id], remote_service: service.name },
+ return this.#client.get("activities/status", {
+ params: { remote_id: service.id, remote_service: service.name },
})
}
diff --git a/src/js/background.js b/src/js/background.js
index 3eb9617..70ac8cb 100644
--- a/src/js/background.js
+++ b/src/js/background.js
@@ -33,11 +33,12 @@ chrome.runtime.onMessage.addListener(action => {
.createActivity(activity)
.then(() => {
messenger.postMessage(tab, { type: "closePopup" })
- apiClient.bookedHours(service).then(({ data }) => {
+ apiClient.activitiesStatus(service).then(({ data }) => {
messenger.postMessage(tab, {
type: "showBubble",
payload: {
- bookedHours: parseFloat(data[0]?.hours) || 0,
+ bookedHours: parseFloat(data.hours),
+ timedActivity: data.timed_activity,
service,
},
})
diff --git a/src/js/components/Bubble.js b/src/js/components/Bubble.js
index 1e680d1..b80e0b5 100644
--- a/src/js/components/Bubble.js
+++ b/src/js/components/Bubble.js
@@ -1,18 +1,36 @@
import React from "react"
import PropTypes from "prop-types"
-import logoUrl from "images/moco-159x159.png"
+import mocoLogo from "images/moco-32x32.png"
+import mocoTimerLogo from "images/moco-timer-32x32.png"
+import { parseISO } from "date-fns"
+import Timer from "./shared/Timer"
-const Bubble = ({ bookedHours }) => (
-
-
})
- {bookedHours > 0 ? (
-
{bookedHours.toFixed(2)}
- ) : null}
-
-)
+const Bubble = ({ bookedHours, timedActivity }) => {
+ const logo = timedActivity ? mocoTimerLogo : mocoLogo
+
+ return (
+
+
})
+ {!timedActivity && bookedHours > 0 && (
+
{bookedHours.toFixed(2)}
+ )}
+ {timedActivity && (
+
+ )}
+
+ )
+}
Bubble.propTypes = {
bookedHours: PropTypes.number,
+ timedActivity: PropTypes.shape({
+ timer_started_at: PropTypes.string.isRequired,
+ seconds: PropTypes.number.isRequired,
+ }),
}
Bubble.defaultProps = {
diff --git a/src/js/components/shared/Timer.js b/src/js/components/shared/Timer.js
new file mode 100644
index 0000000..f454821
--- /dev/null
+++ b/src/js/components/shared/Timer.js
@@ -0,0 +1,28 @@
+import React, { useState } from "react"
+import PropTypes from "prop-types"
+import { useInterval } from "./hooks"
+import { differenceInSeconds, addSeconds, startOfDay, format } from "date-fns"
+
+Timer.propTypes = {
+ startedAt: PropTypes.instanceOf(Date).isRequired,
+ offset: PropTypes.number,
+ onTick: PropTypes.func,
+}
+
+function Timer({ startedAt, offset = 0, onTick, ...domProps }) {
+ const [timerLabel, setTimerLabel] = useState(formattedTimerLabel(startedAt, offset))
+
+ useInterval(() => {
+ setTimerLabel(formattedTimerLabel(startedAt, offset))
+ onTick && onTick()
+ }, 1000)
+
+ return {timerLabel}
+}
+
+function formattedTimerLabel(startedAt, offset) {
+ const seconds = differenceInSeconds(new Date(), startedAt) + offset
+ return format(addSeconds(startOfDay(new Date()), seconds), "HH:mm:ss")
+}
+
+export default Timer
diff --git a/src/js/components/shared/hooks.js b/src/js/components/shared/hooks.js
new file mode 100644
index 0000000..dfb6f90
--- /dev/null
+++ b/src/js/components/shared/hooks.js
@@ -0,0 +1,18 @@
+import { useEffect, useRef } from "react"
+
+export function useInterval(callback, delay) {
+ const savedCallback = useRef()
+
+ useEffect(() => {
+ savedCallback.current = callback
+ })
+
+ useEffect(() => {
+ function tick() {
+ savedCallback.current()
+ }
+
+ let id = setInterval(tick, delay)
+ return () => clearInterval(id)
+ }, [delay])
+}
diff --git a/src/js/content.js b/src/js/content.js
index 33bbdda..097822d 100644
--- a/src/js/content.js
+++ b/src/js/content.js
@@ -25,7 +25,7 @@ chrome.runtime.onConnect.addListener(function(port) {
document.removeEventListener("click", clickHandler, true)
})
- function updateBubble({ service, bookedHours } = {}) {
+ function updateBubble({ service, bookedHours, timedActivity } = {}) {
if (!document.getElementById("moco-bx-root")) {
const domRoot = document.createElement("div")
domRoot.setAttribute("id", "moco-bx-root")
@@ -47,7 +47,7 @@ chrome.runtime.onConnect.addListener(function(port) {
// eslint-disable-next-line react/display-name
(props => (
-
+
))
}
@@ -86,8 +86,8 @@ chrome.runtime.onConnect.addListener(function(port) {
})
})
- messenger.on("showBubble", ({ payload: { service, bookedHours } }) => {
- updateBubble({ service, bookedHours })
+ messenger.on("showBubble", ({ payload }) => {
+ updateBubble(payload)
})
messenger.on("hideBubble", () => {
diff --git a/src/js/utils/messageHandlers.js b/src/js/utils/messageHandlers.js
index d9eb3a7..538c03e 100644
--- a/src/js/utils/messageHandlers.js
+++ b/src/js/utils/messageHandlers.js
@@ -25,12 +25,13 @@ export function tabUpdated(tab, { messenger, settings }) {
messenger.once("newService", ({ payload: { service } }) => {
apiClient
- .bookedHours(service)
+ .activitiesStatus(service)
.then(({ data }) => {
messenger.postMessage(tab, {
type: "showBubble",
payload: {
- bookedHours: parseFloat(data[0]?.hours) || 0,
+ bookedHours: parseFloat(data.hours),
+ timedActivity: data.timed_activity,
service,
},
})
@@ -89,6 +90,7 @@ async function openPopup(tab, { service, messenger }) {
apiClient.activities(fromDate, toDate),
apiClient.schedules(fromDate, toDate),
])
+
const action = {
type: "openPopup",
payload: {