From 88730b6dcd6fc0cd91c29d71e69c6105b61dca07 Mon Sep 17 00:00:00 2001 From: Thomas Churchman Date: Tue, 19 Mar 2024 22:49:53 +0100 Subject: [PATCH] feat: add badge indicating kit active status --- src/Components/KitActiveBadge.module.css | 23 ++++++++++++ src/Components/KitActiveBadge.tsx | 45 ++++++++++++++++++++++++ src/hooks.ts | 10 ++++++ src/scenes/kit/index.tsx | 2 ++ src/scenes/user/index.tsx | 2 ++ 5 files changed, 82 insertions(+) create mode 100644 src/Components/KitActiveBadge.module.css create mode 100644 src/Components/KitActiveBadge.tsx diff --git a/src/Components/KitActiveBadge.module.css b/src/Components/KitActiveBadge.module.css new file mode 100644 index 0000000..a3c49a2 --- /dev/null +++ b/src/Components/KitActiveBadge.module.css @@ -0,0 +1,23 @@ + +.active { + --color1: oklch(76% 0.12 151.19); + --color2: oklch(72% 0.12 151.19); +} + +.recentlyActive { + --color1: oklch(76% 0.12 70.5); + --color2: oklch(72% 0.12 70.5); +} + +.inactive, +.neverSeen { + --color1: oklch(76% 0.0 70.5); + --color2: oklch(72% 0.0 70.5); +} + +.badge { + border-radius: 100%; + width: 1rem; + height: 1rem; + background: linear-gradient(225deg, var(--color1), var(--color2)); +} diff --git a/src/Components/KitActiveBadge.tsx b/src/Components/KitActiveBadge.tsx new file mode 100644 index 0000000..359dc76 --- /dev/null +++ b/src/Components/KitActiveBadge.tsx @@ -0,0 +1,45 @@ +import { DateTime, Duration } from "luxon"; +import { useMemo } from "react"; + +import { schemas } from "~/api"; +import { useTime } from "~/hooks"; + +import style from "./KitActiveBadge.module.css"; +import clsx from "clsx"; + +export function KitActiveBadge({ kit }: { kit: schemas["Kit"] }) { + const now = useTime(Duration.fromMillis(10000)); + + const [activeClass, title] = useMemo(() => { + if (kit.lastSeen === undefined || kit.lastSeen === null) { + return ["neverSeen", "Never seen"]; + } else { + const lastSeen = DateTime.fromISO(kit.lastSeen); + const ago = now.diff(lastSeen); + + let activeClass; + if (ago.as("minutes") < 10) { + activeClass = "active"; + } else if (ago.as("hours") < 4) { + activeClass = "recentlyActive"; + } else { + activeClass = "inactive"; + } + + return [ + activeClass, + `Last seen ${lastSeen.toLocaleString(DateTime.DATETIME_SHORT)}`, + ]; + } + }, [now, kit]); + + return ( + <> + + + ); +} diff --git a/src/hooks.ts b/src/hooks.ts index 5fd6943..952864d 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -1,6 +1,7 @@ import { useEffect, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import type { TypedUseSelectorHook } from "react-redux"; +import { DateTime, Duration } from "luxon"; import type { RootState, AppDispatch } from "./store"; @@ -27,3 +28,12 @@ export function useDebounce(value: T, delayMs: number): T { return debounced; } + +/** + * Get the current time and keep it updated by the specified interval. + */ +export function useTime(updateInterval: Duration): DateTime { + const [time, setTime] = useState(DateTime.now()); + + return time; +} diff --git a/src/scenes/kit/index.tsx b/src/scenes/kit/index.tsx index 7447451..96847d0 100644 --- a/src/scenes/kit/index.tsx +++ b/src/scenes/kit/index.tsx @@ -41,6 +41,7 @@ import style from "./index.module.css"; import { rtkApi } from "~/services/astroplant"; import { skipToken } from "@reduxjs/toolkit/query"; import { selectMe } from "~/modules/me/reducer"; +import { KitActiveBadge } from "~/Components/KitActiveBadge"; type Params = { kitSerial: string }; @@ -62,6 +63,7 @@ function KitHeader({
{kit.name || "Unnamed kit"} / {kit.serial} + {kit.privacyPublicDashboard && ( )} diff --git a/src/scenes/user/index.tsx b/src/scenes/user/index.tsx index dcfbc90..c0dfd6f 100644 --- a/src/scenes/user/index.tsx +++ b/src/scenes/user/index.tsx @@ -16,6 +16,7 @@ import { Badge } from "~/Components/Badge"; import commonStyle from "~/Common.module.css"; import style from "./index.module.css"; +import { KitActiveBadge } from "~/Components/KitActiveBadge"; export default function User({ username }: { username: string }) { // This component is used to render both the user profile of the logged-in @@ -160,6 +161,7 @@ export default function User({ username }: { username: string }) {

{kit.name}

+ {kit.privacyPublicDashboard && (