diff --git a/messages/en-US.json b/messages/en-US.json
index 1b5c5e87..3466edf4 100644
--- a/messages/en-US.json
+++ b/messages/en-US.json
@@ -1279,6 +1279,8 @@
"settingsErrorUpdateDescription": "An error occurred while updating settings",
"sidebarCollapse": "Collapse",
"sidebarExpand": "Expand",
+ "productUpdateMoreInfo": "{noOfUpdates} more updates",
+ "productUpdateInfo": "{noOfUpdates} updates",
"pangolinUpdateAvailable": "New version available",
"pangolinUpdateAvailableInfo": "Version {version} is ready to install",
"pangolinUpdateAvailableReleaseNotes": "View release notes",
diff --git a/src/components/ProductUpdates.tsx b/src/components/ProductUpdates.tsx
index 75590c84..0b9e35de 100644
--- a/src/components/ProductUpdates.tsx
+++ b/src/components/ProductUpdates.tsx
@@ -3,29 +3,59 @@
import { useEnvContext } from "@app/hooks/useEnvContext";
import { useLocalStorage } from "@app/hooks/useLocalStorage";
import { cn } from "@app/lib/cn";
-import { versionsQueries } from "@app/lib/queries";
-import { useQuery } from "@tanstack/react-query";
+import { productUpdatesQueries } from "@app/lib/queries";
+import { useQueries } from "@tanstack/react-query";
import { ArrowRight, BellIcon, XIcon } from "lucide-react";
import { useTranslations } from "next-intl";
-interface ProductUpdatesProps {}
+export default function ProductUpdates() {
+ const data = useQueries({
+ queries: [
+ productUpdatesQueries.list,
+ productUpdatesQueries.latestVersion
+ ],
+ combine(result) {
+ return {
+ updates: result[0].data?.data ?? [],
+ latestVersion: result[1].data
+ };
+ }
+ });
+
+ const t = useTranslations();
-export default function ProductUpdates({}: ProductUpdatesProps) {
return (
-
- {/*
-
- 3 more updates
- */}
-
+
+ {data.updates.length > 0 && (
+
+
+
+ {t("productUpdateMoreInfo", {
+ noOfUpdates: data.updates.length
+ })}
+
+
+ )}
+
);
}
-function NewVersionAvailable() {
+type NewVersionAvailableProps = {
+ version:
+ | Awaited<
+ ReturnType<
+ NonNullable<
+ typeof productUpdatesQueries.latestVersion.queryFn
+ >
+ >
+ >
+ | undefined;
+};
+
+function NewVersionAvailable({ version }: NewVersionAvailableProps) {
const { env } = useEnvContext();
const t = useTranslations();
- const { data: version } = useQuery(versionsQueries.latestVersion());
const [ignoredVersionUpdate, setIgnoredVersionUpdate] = useLocalStorage<
string | null
@@ -33,8 +63,8 @@ function NewVersionAvailable() {
const showNewVersionPopup =
version?.data &&
- ignoredVersionUpdate !== version.data.pangolin.latestVersion &&
- env.app.version !== version.data.pangolin.latestVersion;
+ ignoredVersionUpdate !== version.data?.pangolin.latestVersion &&
+ env.app.version !== version.data?.pangolin.latestVersion;
if (!showNewVersionPopup) return null;
diff --git a/src/lib/queries.ts b/src/lib/queries.ts
index 37ec0e76..9f1ca81c 100644
--- a/src/lib/queries.ts
+++ b/src/lib/queries.ts
@@ -1,38 +1,58 @@
-import {
- type InfiniteData,
- type QueryClient,
- keepPreviousData,
- queryOptions,
- type skipToken
-} from "@tanstack/react-query";
+import { keepPreviousData, queryOptions } from "@tanstack/react-query";
import { durationToMs } from "./durationToMs";
import { build } from "@server/build";
import { remote } from "./api";
import type ResponseT from "@server/types/Response";
-export const versionsQueries = {
- latestVersion: () =>
- queryOptions({
- queryKey: ["LATEST_VERSION"] as const,
- queryFn: async ({ signal }) => {
- const data = await remote.get<
- ResponseT<{
- pangolin: {
- latestVersion: string;
- releaseNotes: string;
- };
- }>
- >("/latest-version");
- return data.data;
- },
- placeholderData: keepPreviousData,
- refetchInterval: (query) => {
- if (query.state.data) {
- return durationToMs(30, "minutes");
- }
- return false;
- },
- enabled: build === "oss" || build === "enterprise" // disabled in cloud version
- // because we don't need to listen for new versions there
- })
+type ProductUpdate = {
+ link: string | null;
+ edition: "enterprise" | "community" | "cloud" | null;
+ id: number;
+ priority: "CRITICAL" | "IMPORTANT" | "NORMAL" | null;
+ title: string;
+ contents: string;
+ publishedAt: Date;
+ showUntil: Date;
+};
+
+export const productUpdatesQueries = {
+ list: queryOptions({
+ queryKey: ["PRODUCT_UPDATES"] as const,
+ queryFn: async ({ signal }) => {
+ const data = await remote.get
>(
+ "/product-updates",
+ { signal }
+ );
+ return data.data;
+ },
+ refetchInterval: (query) => {
+ if (query.state.data) {
+ return durationToMs(5, "minutes");
+ }
+ return false;
+ }
+ }),
+ latestVersion: queryOptions({
+ queryKey: ["LATEST_VERSION"] as const,
+ queryFn: async ({ signal }) => {
+ const data = await remote.get<
+ ResponseT<{
+ pangolin: {
+ latestVersion: string;
+ releaseNotes: string;
+ };
+ }>
+ >("/versions", { signal });
+ return data.data;
+ },
+ placeholderData: keepPreviousData,
+ refetchInterval: (query) => {
+ if (query.state.data) {
+ return durationToMs(30, "minutes");
+ }
+ return false;
+ },
+ enabled: build === "oss" || build === "enterprise" // disabled in cloud version
+ // because we don't need to listen for new versions there
+ })
};