diff --git a/src/components/ProductUpdates.tsx b/src/components/ProductUpdates.tsx
index c5f48d68..83fa32e6 100644
--- a/src/components/ProductUpdates.tsx
+++ b/src/components/ProductUpdates.tsx
@@ -8,7 +8,6 @@ import { useQueries } from "@tanstack/react-query";
import {
ArrowRight,
BellIcon,
- CheckIcon,
ChevronRightIcon,
RocketIcon,
XIcon
@@ -57,7 +56,11 @@ export default function ProductUpdates({
const [ignoredVersionUpdate, setIgnoredVersionUpdate] = useLocalStorage<
string | null
- >("ignored-version", null);
+ >("product-updates:skip-version", null);
+
+ const [productUpdatesRead, setProductUpdatesRead] = useLocalStorage<
+ number[]
+ >("product-updates:read", []);
if (!data) return null;
@@ -68,6 +71,10 @@ export default function ProductUpdates({
env.app.version !== data.latestVersion.data?.pangolin.latestVersion
);
+ const filteredUpdates = data.updates.filter(
+ (update) => !productUpdatesRead.includes(update.id)
+ );
+
return (
- {data.updates.length > 0 && (
+ {filteredUpdates.length > 0 && (
<>
{showNewVersionPopup
? t("productUpdateMoreInfo", {
- noOfUpdates: data.updates.length
+ noOfUpdates: filteredUpdates.length
})
: t("productUpdateInfo", {
- noOfUpdates: data.updates.length
+ noOfUpdates: filteredUpdates.length
})}
>
)}
0}
+ updates={filteredUpdates}
+ show={filteredUpdates.length > 0}
+ onDimissAll={() =>
+ setProductUpdatesRead([
+ ...productUpdatesRead,
+ ...filteredUpdates.map((update) => update.id)
+ ])
+ }
+ onDimiss={(id) =>
+ setProductUpdatesRead([...productUpdatesRead, id])
+ }
/>
{
+ onDimiss={() => {
setIgnoredVersionUpdate(
data.latestVersion?.data?.pangolin.latestVersion ?? null
);
@@ -118,29 +134,44 @@ export default function ProductUpdates({
);
}
-type ProductUpdatesListPopupProps = { updates: ProductUpdate[]; show: boolean };
+type ProductUpdatesListPopupProps = {
+ updates: ProductUpdate[];
+ show: boolean;
+ onDimiss: (id: number) => void;
+ onDimissAll: () => void;
+};
function ProductUpdatesListPopup({
updates,
- show
+ show,
+ onDimiss,
+ onDimissAll
}: ProductUpdatesListPopupProps) {
- const [open, setOpen] = React.useState(false);
+ const [showContent, setShowContent] = React.useState(false);
+ const [popoverOpen, setPopoverOpen] = React.useState(false);
const t = useTranslations();
// we need to delay the initial opening state to have an animation on `appear`
React.useEffect(() => {
if (show) {
- requestAnimationFrame(() => setOpen(true));
+ requestAnimationFrame(() => setShowContent(true));
}
}, [show]);
+ React.useEffect(() => {
+ if (updates.length === 0) {
+ setShowContent(false);
+ setPopoverOpen(false);
+ }
+ }, [updates.length]);
+
return (
-
-
+
+
-
-
-
-
-
- {t("productUpdateTitle")}
- {updates.length}
-
-
-
- {updates.map((update) => (
- -
-
-
- {update.title}
-
- New
-
-
-
-
-
-
-
-
- Mark as read
-
-
-
-
-
- {update.contents}
-
-
-
- ))}
-
-
+
+
+
+
+ {t("productUpdateTitle")}
+ {updates.length > 0 && (
+ {updates.length}
+ )}
+
+
+
+
+ {updates.length === 0 && (
+
+ No updates
+
+ )}
+ {updates.map((update) => (
+ -
+
+
+ {update.title}
+ {/*
+ {t("new")}
+ */}
+
+
+
+
+
+
+
+ {t("dismiss")}
+
+
+
+
+
+ {update.contents}
+
+
+
+ ))}
+
+
);
}
type NewVersionAvailableProps = {
- onClose: () => void;
+ onDimiss: () => void;
show: boolean;
version:
| Awaited<
@@ -251,7 +294,7 @@ type NewVersionAvailableProps = {
function NewVersionAvailable({
version,
show,
- onClose
+ onDimiss
}: NewVersionAvailableProps) {
const t = useTranslations();
const [open, setOpen] = React.useState(false);
@@ -302,7 +345,7 @@ function NewVersionAvailable({
className="p-1 cursor-pointer"
onClick={() => {
setOpen(false);
- onClose();
+ onDimiss();
}}
>
diff --git a/src/lib/timeAgoFormatter.ts b/src/lib/timeAgoFormatter.ts
index 0aeff8bc..f6ae0175 100644
--- a/src/lib/timeAgoFormatter.ts
+++ b/src/lib/timeAgoFormatter.ts
@@ -39,10 +39,9 @@ export function timeAgoFormatter(
unit = "year";
}
- const rtf = new Intl.RelativeTimeFormat("en", {
+ const rtf = new Intl.RelativeTimeFormat(navigator.languages[0] ?? "en", {
numeric: "auto",
style: short ? "narrow" : "long"
});
- const formatedValue = rtf.format(-value, unit);
- return formatedValue === "now" ? "Just now" : formatedValue;
+ return rtf.format(-value, unit);
}