Show timer in bubble if timed activity exists
This commit is contained in:
@@ -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 },
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -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 }) => (
|
||||
<div className="moco-bx-bubble-inner">
|
||||
<img className="moco-bx-logo" src={chrome.extension.getURL(logoUrl)} />
|
||||
{bookedHours > 0 ? (
|
||||
<span className="moco-bx-booked-hours">{bookedHours.toFixed(2)}</span>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
const Bubble = ({ bookedHours, timedActivity }) => {
|
||||
const logo = timedActivity ? mocoTimerLogo : mocoLogo
|
||||
|
||||
return (
|
||||
<div className="moco-bx-bubble-inner">
|
||||
<img className="moco-bx-logo" src={chrome.extension.getURL(logo)} />
|
||||
{!timedActivity && bookedHours > 0 && (
|
||||
<span className="moco-bx-booked-hours">{bookedHours.toFixed(2)}</span>
|
||||
)}
|
||||
{timedActivity && (
|
||||
<Timer
|
||||
startedAt={parseISO(timedActivity.timer_started_at)}
|
||||
offset={timedActivity.seconds}
|
||||
style={{ color: "red", marginBottom: "3px", fontSize: "12px" }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Bubble.propTypes = {
|
||||
bookedHours: PropTypes.number,
|
||||
timedActivity: PropTypes.shape({
|
||||
timer_started_at: PropTypes.string.isRequired,
|
||||
seconds: PropTypes.number.isRequired,
|
||||
}),
|
||||
}
|
||||
|
||||
Bubble.defaultProps = {
|
||||
|
||||
28
src/js/components/shared/Timer.js
Normal file
28
src/js/components/shared/Timer.js
Normal file
@@ -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 <span {...domProps}>{timerLabel}</span>
|
||||
}
|
||||
|
||||
function formattedTimerLabel(startedAt, offset) {
|
||||
const seconds = differenceInSeconds(new Date(), startedAt) + offset
|
||||
return format(addSeconds(startOfDay(new Date()), seconds), "HH:mm:ss")
|
||||
}
|
||||
|
||||
export default Timer
|
||||
18
src/js/components/shared/hooks.js
Normal file
18
src/js/components/shared/hooks.js
Normal file
@@ -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])
|
||||
}
|
||||
@@ -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 => (
|
||||
<animated.div className="moco-bx-bubble" style={{ ...props, ...service.position }}>
|
||||
<Bubble key={service.url} bookedHours={bookedHours} />
|
||||
<Bubble key={service.url} bookedHours={bookedHours} timedActivity={timedActivity} />
|
||||
</animated.div>
|
||||
))
|
||||
}
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
Reference in New Issue
Block a user