Show timer in bubble if timed activity exists

This commit is contained in:
manubo
2019-09-19 17:33:41 +02:00
parent 8a4cfccc6f
commit 714e9bd139
7 changed files with 87 additions and 20 deletions

View File

@@ -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 },
})
}

View File

@@ -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,
},
})

View File

@@ -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 }) => (
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(logoUrl)} />
{bookedHours > 0 ? (
<img className="moco-bx-logo" src={chrome.extension.getURL(logo)} />
{!timedActivity && bookedHours > 0 && (
<span className="moco-bx-booked-hours">{bookedHours.toFixed(2)}</span>
) : null}
)}
{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 = {

View 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

View 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])
}

View File

@@ -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", () => {

View File

@@ -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: {