diff --git a/package.json b/package.json
index 1c055073..ec62a1b4 100644
--- a/package.json
+++ b/package.json
@@ -61,7 +61,6 @@
"react-map-gl": "7.1.9",
"react-qrcode-logo": "^3.0.0",
"rfc4648": "^1.5.4",
- "timeago-react": "^3.0.6",
"vite-plugin-node-polyfills": "^0.23.0",
"zustand": "5.0.3"
},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f0d0f030..7a2029e9 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -113,9 +113,6 @@ importers:
rfc4648:
specifier: ^1.5.4
version: 1.5.4
- timeago-react:
- specifier: ^3.0.6
- version: 3.0.6(react@19.0.0)
vite-plugin-node-polyfills:
specifier: ^0.23.0
version: 0.23.0(rollup@4.29.1)(vite@5.3.6(@types/node@22.12.0))
@@ -2817,14 +2814,6 @@ packages:
through2@4.0.2:
resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==}
- timeago-react@3.0.6:
- resolution: {integrity: sha512-4ywnCX3iFjdp84WPK7gt8s4n0FxXbYM+xv8hYL73p83dpcMxzmO+0W4xJuxflnkWNvum5aEaqTe6LZ3lUIudjQ==}
- peerDependencies:
- react: ^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
-
- timeago.js@4.0.2:
- resolution: {integrity: sha512-a7wPxPdVlQL7lqvitHGGRsofhdwtkoSXPGATFuSOA2i1ZNQEPLrGnj68vOp2sOJTCFAQVXPeNMX/GctBaO9L2w==}
-
timers-browserify@2.0.12:
resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==}
engines: {node: '>=0.6.0'}
@@ -6498,13 +6487,6 @@ snapshots:
dependencies:
readable-stream: 3.6.2
- timeago-react@3.0.6(react@19.0.0):
- dependencies:
- react: 19.0.0
- timeago.js: 4.0.2
-
- timeago.js@4.0.2: {}
-
timers-browserify@2.0.12:
dependencies:
setimmediate: 1.0.5
diff --git a/src/components/PageComponents/Map/NodeDetail.tsx b/src/components/PageComponents/Map/NodeDetail.tsx
index 5203d077..0aba4d4b 100644
--- a/src/components/PageComponents/Map/NodeDetail.tsx
+++ b/src/components/PageComponents/Map/NodeDetail.tsx
@@ -3,7 +3,7 @@ import { H5 } from "@app/components/UI/Typography/H5.tsx";
import { Subtle } from "@app/components/UI/Typography/Subtle.tsx";
import { Avatar } from "@components/UI/Avatar";
import { Mono } from "@components/generic/Mono.tsx";
-import { TimeAgo } from "@components/generic/Table/tmp/TimeAgo.tsx";
+import { TimeAgo } from "@components/generic/TimeAgo.tsx";
import { Protobuf } from "@meshtastic/js";
import type { Protobuf as ProtobufType } from "@meshtastic/js";
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
diff --git a/src/components/generic/Table/tmp/TimeAgo.tsx b/src/components/generic/Table/tmp/TimeAgo.tsx
deleted file mode 100755
index 32a766ff..00000000
--- a/src/components/generic/Table/tmp/TimeAgo.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import TimeAgoReact from "timeago-react";
-
-export interface TimeAgoProps {
- timestamp: number;
-}
-
-export const TimeAgo = ({ timestamp }: TimeAgoProps): JSX.Element => {
- return ;
-};
diff --git a/src/components/generic/TimeAgo.tsx b/src/components/generic/TimeAgo.tsx
new file mode 100755
index 00000000..723ebf19
--- /dev/null
+++ b/src/components/generic/TimeAgo.tsx
@@ -0,0 +1,34 @@
+import { type JSX, useEffect, useState } from "react";
+
+export interface TimeAgoProps {
+ timestamp: number;
+}
+
+const getTimeAgo = (timestamp: number): string => {
+ const now = Date.now();
+ const secondsPast = Math.floor((now - timestamp) / 1000);
+
+ if (secondsPast < 10) return "now";
+ if (secondsPast < 60) return `${secondsPast} seconds ago`;
+ if (secondsPast < 3600) return `${Math.floor(secondsPast / 60)} minutes ago`;
+ if (secondsPast < 86400) return `${Math.floor(secondsPast / 3600)} hours ago`;
+ return `${Math.floor(secondsPast / 86400)} days ago`;
+};
+
+export const TimeAgo = ({ timestamp }: TimeAgoProps): JSX.Element => {
+ const [timeAgo, setTimeAgo] = useState(getTimeAgo(timestamp));
+
+ useEffect(() => {
+ const interval = setInterval(() => {
+ setTimeAgo(getTimeAgo(timestamp));
+ }, 10000);
+
+ return () => clearInterval(interval);
+ }, [timestamp]);
+
+ useEffect(() => {
+ setTimeAgo(getTimeAgo(timestamp));
+ }, [timestamp]);
+
+ return {timeAgo};
+};
diff --git a/src/pages/Nodes.tsx b/src/pages/Nodes.tsx
index 5e3b48ac..7b0973f5 100644
--- a/src/pages/Nodes.tsx
+++ b/src/pages/Nodes.tsx
@@ -6,7 +6,7 @@ import { Sidebar } from "@components/Sidebar.tsx";
import { Button } from "@components/UI/Button.tsx";
import { Mono } from "@components/generic/Mono.tsx";
import { Table } from "@components/generic/Table/index.tsx";
-import { TimeAgo } from "@components/generic/Table/tmp/TimeAgo.tsx";
+import { TimeAgo } from "@components/generic/TimeAgo.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf, type Types } from "@meshtastic/js";
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
@@ -77,8 +77,8 @@ const NodesPage = (): JSX.Element => {
{node.user?.longName ??
(node.user?.macaddr
? `Meshtastic ${base16
- .stringify(node.user?.macaddr.subarray(4, 6) ?? [])
- .toLowerCase()}`
+ .stringify(node.user?.macaddr.subarray(4, 6) ?? [])
+ .toLowerCase()}`
: `!${numberToHexUnpadded(node.num)}`)}
,
@@ -114,9 +114,8 @@ const NodesPage = (): JSX.Element => {
{node.lastHeard !== 0
? node.viaMqtt === false && node.hopsAway === 0
? "Direct"
- : `${node.hopsAway.toString()} ${
- node.hopsAway > 1 ? "hops" : "hop"
- } away`
+ : `${node.hopsAway.toString()} ${node.hopsAway > 1 ? "hops" : "hop"
+ } away`
: "-"}
{node.viaMqtt === true ? ", via MQTT" : ""}
,