diff --git a/web/src/components/TimeAgo.jsx b/web/src/components/TimeAgo.jsx
deleted file mode 100644
index eafce61db..000000000
--- a/web/src/components/TimeAgo.jsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import { h } from 'preact';
-
-const timeAgo = ({ time, dense = false }) => {
- if (!time) return 'Invalid Time';
- try {
- const currentTime = new Date();
- const pastTime = new Date(time);
- const elapsedTime = currentTime - pastTime;
- if (elapsedTime < 0) return 'Invalid Time';
-
- const timeUnits = [
- { unit: 'ye', full: 'year', value: 31536000 },
- { unit: 'mo', full: 'month', value: 0 },
- { unit: 'day', full: 'day', value: 86400 },
- { unit: 'h', full: 'hour', value: 3600 },
- { unit: 'm', full: 'minute', value: 60 },
- { unit: 's', full: 'second', value: 1 },
- ];
-
- let elapsed = elapsedTime / 1000;
- if (elapsed < 60) {
- return 'just now';
- }
-
- for (let i = 0; i < timeUnits.length; i++) {
- // if months
- if (i === 1) {
- // Get the month and year for the time provided
- const pastMonth = pastTime.getUTCMonth();
- const pastYear = pastTime.getUTCFullYear();
-
- // get current month and year
- const currentMonth = currentTime.getUTCMonth();
- const currentYear = currentTime.getUTCFullYear();
-
- let monthDiff = (currentYear - pastYear) * 12 + (currentMonth - pastMonth);
-
- // check if the time provided is the previous month but not exceeded 1 month ago.
- if (currentTime.getUTCDate() < pastTime.getUTCDate()) {
- monthDiff--;
- }
-
- if (monthDiff > 0) {
- const unitAmount = monthDiff;
- return `${unitAmount}${dense ? timeUnits[i].unit[0] : ` ${timeUnits[i].full}`}${dense ? '' : 's'} ago`;
- }
- } else if (elapsed >= timeUnits[i].value) {
- const unitAmount = Math.floor(elapsed / timeUnits[i].value);
- return `${unitAmount}${dense ? timeUnits[i].unit[0] : ` ${timeUnits[i].full}`}${dense ? '' : 's'} ago`;
- }
- }
- } catch {
- return 'Invalid Time';
- }
-};
-
-const TimeAgo = (props) => {
- return {timeAgo({ ...props })};
-};
-
-export default TimeAgo;
diff --git a/web/src/components/TimeAgo.tsx b/web/src/components/TimeAgo.tsx
new file mode 100644
index 000000000..7b5a914e7
--- /dev/null
+++ b/web/src/components/TimeAgo.tsx
@@ -0,0 +1,84 @@
+import { h, FunctionComponent } from 'preact';
+import { useEffect, useMemo, useState } from 'preact/hooks';
+
+interface IProp {
+ /** The time to calculate time-ago from */
+ time: Date;
+ /** OPTIONAL: overwrite current time */
+ currentTime?: Date;
+ /** OPTIONAL: boolean that determines whether to show the time-ago text in dense format */
+ dense?: boolean;
+ /** OPTIONAL: set custom refresh interval in milliseconds, default 1000 (1 sec) */
+ refreshInterval?: number;
+}
+
+type TimeUnit = {
+ unit: string;
+ full: string;
+ value: number;
+};
+
+const timeAgo = ({ time, currentTime = new Date(), dense = false }: IProp): string => {
+ if (typeof time !== 'number' || time < 0) return 'Invalid Time Provided';
+
+ const pastTime: Date = new Date(time);
+ const elapsedTime: number = currentTime.getTime() - pastTime.getTime();
+
+ const timeUnits: TimeUnit[] = [
+ { unit: 'ye', full: 'year', value: 31536000 },
+ { unit: 'mo', full: 'month', value: 0 },
+ { unit: 'day', full: 'day', value: 86400 },
+ { unit: 'h', full: 'hour', value: 3600 },
+ { unit: 'm', full: 'minute', value: 60 },
+ { unit: 's', full: 'second', value: 1 },
+ ];
+
+ const elapsed: number = elapsedTime / 1000;
+ if (elapsed < 10) {
+ return 'just now';
+ }
+
+ for (let i = 0; i < timeUnits.length; i++) {
+ // if months
+ if (i === 1) {
+ // Get the month and year for the time provided
+ const pastMonth = pastTime.getUTCMonth();
+ const pastYear = pastTime.getUTCFullYear();
+
+ // get current month and year
+ const currentMonth = currentTime.getUTCMonth();
+ const currentYear = currentTime.getUTCFullYear();
+
+ let monthDiff = (currentYear - pastYear) * 12 + (currentMonth - pastMonth);
+
+ // check if the time provided is the previous month but not exceeded 1 month ago.
+ if (currentTime.getUTCDate() < pastTime.getUTCDate()) {
+ monthDiff--;
+ }
+
+ if (monthDiff > 0) {
+ const unitAmount = monthDiff;
+ return `${unitAmount}${dense ? timeUnits[i].unit[0] : ` ${timeUnits[i].full}`}${dense ? '' : 's'} ago`;
+ }
+ } else if (elapsed >= timeUnits[i].value) {
+ const unitAmount: number = Math.floor(elapsed / timeUnits[i].value);
+ return `${unitAmount}${dense ? timeUnits[i].unit[0] : ` ${timeUnits[i].full}`}${dense ? '' : 's'} ago`;
+ }
+ }
+ return 'Invalid Time';
+};
+
+const TimeAgo: FunctionComponent = ({ refreshInterval = 1000, ...rest }): JSX.Element => {
+ const [currentTime, setCurrentTime] = useState(new Date());
+ useEffect(() => {
+ const intervalId: NodeJS.Timeout = setInterval(() => {
+ setCurrentTime(new Date());
+ }, refreshInterval);
+ return () => clearInterval(intervalId);
+ }, [refreshInterval]);
+
+ const timeAgoValue = useMemo(() => timeAgo({ currentTime, ...rest }), [currentTime, rest]);
+
+ return {timeAgoValue};
+};
+export default TimeAgo;