mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
fix: check if update is available based on auto-detected version channel.
This commit is contained in:
@@ -7,10 +7,10 @@ import Link from "next/link";
|
|||||||
|
|
||||||
|
|
||||||
export const UpdateNotification = () => {
|
export const UpdateNotification = () => {
|
||||||
const {isUpdateAvailable, latestRelease, dismissUpdate} = useUpdateCheck();
|
const {isUpdateAvailable, newRelease, dismissUpdate} = useUpdateCheck();
|
||||||
const {state} = useSidebar();
|
const {state} = useSidebar();
|
||||||
|
|
||||||
if (!isUpdateAvailable || !latestRelease || state !== "expanded") {
|
if (!isUpdateAvailable || !newRelease || state !== "expanded") {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,21 +29,21 @@ export const UpdateNotification = () => {
|
|||||||
<span className="sr-only">Dismiss</span>
|
<span className="sr-only">Dismiss</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div className="flex items-center gap-2.5">
|
<div className="flex items-center gap-2">
|
||||||
<div
|
<div
|
||||||
className="flex size-7 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary">
|
className="flex size-7 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary">
|
||||||
<ArrowUpCircle className="size-4"/>
|
<ArrowUpCircle className="size-4"/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col min-w-0">
|
<div className="flex flex-col min-w-0">
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1">
|
||||||
<span className="text-[12px] font-semibold leading-none">Update available</span>
|
<span className="text-[10px] font-semibold leading-none">Update available</span>
|
||||||
<span
|
<span
|
||||||
className="text-[10px] text-muted-foreground font-medium px-1.5 py-0.5 bg-primary/10 rounded-full">
|
className="text-[10px] text-muted-foreground font-medium px-1 py-0.5 bg-primary/10 rounded-full">
|
||||||
v{latestRelease.tag_name.replace(/^v/, "")}
|
v{newRelease.tag_name.replace(/^v/, "")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<Link
|
<Link
|
||||||
href={latestRelease.html_url}
|
href={newRelease.html_url}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="group inline-flex items-center gap-1 text-[11px] text-primary hover:underline font-medium mt-1"
|
className="group inline-flex items-center gap-1 text-[11px] text-primary hover:underline font-medium mt-1"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import {useQuery} from "@tanstack/react-query";
|
import {useQuery} from "@tanstack/react-query";
|
||||||
import {getLatestRelease} from "../services/github";
|
import {getNewRelease} from "../services/github";
|
||||||
import {env} from "@/env.mjs";
|
import {env} from "@/env.mjs";
|
||||||
import {useEffect, useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
|
|
||||||
@@ -9,53 +9,51 @@ const DISMISS_KEY = "portabase-update-dismissed";
|
|||||||
const DISMISS_DURATION = 1000 * 60 * 60 * 24;
|
const DISMISS_DURATION = 1000 * 60 * 60 * 24;
|
||||||
|
|
||||||
export const useUpdateCheck = () => {
|
export const useUpdateCheck = () => {
|
||||||
const [isDismissed, setIsDismissed] = useState(true);
|
const [isDismissed, setIsDismissed] = useState(true);
|
||||||
|
|
||||||
const currentVersion = env.NEXT_PUBLIC_PROJECT_VERSION!;
|
const currentVersion = env.NEXT_PUBLIC_PROJECT_VERSION!;
|
||||||
|
|
||||||
const {data: latestRelease, isLoading} = useQuery({
|
const {data: newRelease, isLoading} = useQuery({
|
||||||
queryKey: ["latest-release"],
|
queryKey: ["new-release"],
|
||||||
queryFn: () => getLatestRelease(currentVersion),
|
queryFn: () => getNewRelease(currentVersion),
|
||||||
staleTime: 1000 * 60 * 60,
|
staleTime: 1000 * 60 * 60,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log("newRelease", newRelease);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!latestRelease) return;
|
if (!newRelease) return;
|
||||||
|
|
||||||
const latestVersion = latestRelease.tag_name.replace(/^v/, "");
|
|
||||||
const cleanCurrentVersion = currentVersion?.replace(/^v/, "");
|
|
||||||
|
|
||||||
if (latestVersion && cleanCurrentVersion && latestVersion !== cleanCurrentVersion) {
|
|
||||||
const dismissedData = localStorage.getItem(DISMISS_KEY);
|
const dismissedData = localStorage.getItem(DISMISS_KEY);
|
||||||
if (dismissedData) {
|
if (dismissedData) {
|
||||||
const {version, timestamp} = JSON.parse(dismissedData);
|
const {version, timestamp} = JSON.parse(dismissedData);
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (version === latestRelease.tag_name && now - timestamp < DISMISS_DURATION) {
|
if (version === newRelease.name && now - timestamp < DISMISS_DURATION) {
|
||||||
setIsDismissed(true);
|
setIsDismissed(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setIsDismissed(false);
|
setIsDismissed(false);
|
||||||
} else {
|
|
||||||
setIsDismissed(true);
|
|
||||||
}
|
|
||||||
}, [latestRelease, currentVersion]);
|
|
||||||
|
|
||||||
const dismissUpdate = () => {
|
}, [newRelease])
|
||||||
if (latestRelease) {
|
|
||||||
localStorage.setItem(DISMISS_KEY, JSON.stringify({
|
|
||||||
version: latestRelease.tag_name,
|
|
||||||
timestamp: Date.now()
|
|
||||||
}));
|
|
||||||
setIsDismissed(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
latestRelease,
|
const dismissUpdate = () => {
|
||||||
isLoading,
|
if (newRelease) {
|
||||||
isUpdateAvailable: !isDismissed && latestRelease,
|
localStorage.setItem(DISMISS_KEY, JSON.stringify({
|
||||||
dismissUpdate
|
version: newRelease.name,
|
||||||
};
|
timestamp: Date.now()
|
||||||
};
|
}));
|
||||||
|
setIsDismissed(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
newRelease,
|
||||||
|
isLoading,
|
||||||
|
isUpdateAvailable: !isDismissed && newRelease,
|
||||||
|
dismissUpdate
|
||||||
|
};
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|||||||
@@ -6,26 +6,79 @@ export interface GitHubRelease {
|
|||||||
body: string;
|
body: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getLatestRelease = async (currentVersion: string): Promise<GitHubRelease | null> => {
|
type ParsedVersion = {
|
||||||
|
major: number;
|
||||||
|
minor: number;
|
||||||
|
patch: number;
|
||||||
|
rc?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
function parseVersion(version: string): ParsedVersion {
|
||||||
|
const match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-rc\.(\d+))?$/);
|
||||||
|
if (!match) return {
|
||||||
|
major: 0,
|
||||||
|
minor: 0,
|
||||||
|
patch: 0,
|
||||||
|
rc: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
major: Number(match[1]),
|
||||||
|
minor: Number(match[2]),
|
||||||
|
patch: Number(match[3]),
|
||||||
|
rc: match[4] ? Number(match[4]) : undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getNewRelease = async (currentVersion: string): Promise<GitHubRelease | null> => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch("https://api.github.com/repos/Portabase/portabase/releases");
|
const response = await fetch("https://api.github.com/repos/Portabase/portabase/releases");
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const releases: GitHubRelease[] = await response.json();
|
const releases: GitHubRelease[] = await response.json();
|
||||||
|
|
||||||
const isStable = /^\d+\.\d+\.\d+$/.test(currentVersion);
|
const isStable = /^\d+\.\d+\.\d+$/.test(currentVersion);
|
||||||
const isRc = /^\d+\.\d+\.\d+-rc\.\d+$/.test(currentVersion);
|
const isRc = /^\d+\.\d+\.\d+-rc\.\d+$/.test(currentVersion);
|
||||||
|
|
||||||
if (isStable) {
|
console.log("isStable", isStable)
|
||||||
return releases.find(r => !r.prerelease) || null;
|
console.log("isRc", isRc)
|
||||||
}
|
|
||||||
|
|
||||||
|
const currentParsedVersion = parseVersion(currentVersion)
|
||||||
|
|
||||||
|
const latestStableVersion = releases.find(r => !r.prerelease)
|
||||||
|
const latestRcVersion = releases.find(r => r.name.includes("rc"))!
|
||||||
|
|
||||||
if (isRc) {
|
if (isRc) {
|
||||||
return releases.find(r => r.tag_name.includes("rc") || !r.prerelease) || null;
|
if (!latestRcVersion) return null
|
||||||
|
const latestRcParsedVersion = parseVersion(latestRcVersion.name)
|
||||||
|
if (currentParsedVersion.major < latestRcParsedVersion.major) return latestRcVersion;
|
||||||
|
if (currentParsedVersion.minor < latestRcParsedVersion.minor) return latestRcVersion;
|
||||||
|
if (currentParsedVersion.patch < latestRcParsedVersion.patch) return latestRcVersion;
|
||||||
|
if (currentParsedVersion.rc! < latestRcParsedVersion.rc!) return latestRcVersion;
|
||||||
|
|
||||||
|
if (!latestStableVersion) return null
|
||||||
|
const latestStableParsedVersion = parseVersion(latestStableVersion.name)
|
||||||
|
if (currentParsedVersion.major < latestStableParsedVersion.major) return latestStableVersion;
|
||||||
|
if (currentParsedVersion.minor < latestStableParsedVersion.minor) return latestStableVersion;
|
||||||
|
if (currentParsedVersion.patch < latestStableParsedVersion.patch) return latestStableVersion;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
} else if (isStable) {
|
||||||
|
if (!latestStableVersion) return null
|
||||||
|
const latestStableParsedVersion = parseVersion(latestStableVersion.name)
|
||||||
|
if (currentParsedVersion.major < latestStableParsedVersion.major) return latestStableVersion;
|
||||||
|
if (currentParsedVersion.minor < latestStableParsedVersion.minor) return latestStableVersion;
|
||||||
|
if (currentParsedVersion.patch < latestStableParsedVersion.patch) return latestStableVersion;
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return releases[0] || null;
|
return null;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch latest release", error);
|
console.error("Failed to fetch latest release", error);
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
Reference in New Issue
Block a user