File size: 2,740 Bytes
c47ec10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
"use client";

import { useCallback, useMemo, useState } from "react";
import { toast } from "sonner";

import webConfig from "@/constants/common-env";
import { parseChangelog, type ReleaseInfo } from "@/lib/release";

const latestVersionUrl =
  "https://raw.githubusercontent.com/basketikun/chatgpt2api/main/VERSION";
const latestChangelogUrl =
  "https://raw.githubusercontent.com/basketikun/chatgpt2api/main/CHANGELOG.md";

function readLocalReleases(): ReleaseInfo[] {
  return JSON.parse(process.env.NEXT_PUBLIC_APP_RELEASES || "[]");
}

function toVersionParts(version: string) {
  const match = version.trim().match(/^v?(\d+)\.(\d+)\.(\d+)/);
  return match ? match.slice(1).map(Number) : null;
}

function isNewerVersion(latestVersion: string, currentVersion: string) {
  const latest = toVersionParts(latestVersion);
  const current = toVersionParts(currentVersion);
  if (!latest || !current) return false;
  return latest.some(
    (value, index) =>
      value > current[index] &&
      latest.slice(0, index).every((part, prevIndex) => part === current[prevIndex]),
  );
}

export function useVersionCheck() {
  const currentVersion = webConfig.appVersion;
  const localReleases = useMemo(readLocalReleases, []);
  const [latestVersion, setLatestVersion] = useState(currentVersion);
  const [releases, setReleases] = useState<ReleaseInfo[]>(localReleases);
  const [checking, setChecking] = useState(false);
  const [open, setOpen] = useState(false);
  const hasNewVersion = isNewerVersion(latestVersion, currentVersion);

  const checkLatestRelease = useCallback(
    async (showMessage = false) => {
      setChecking(true);
      try {
        const [versionResponse, changelogResponse] = await Promise.all([
          fetch(latestVersionUrl),
          fetch(latestChangelogUrl),
        ]);
        if (!versionResponse.ok || !changelogResponse.ok) throw new Error();
        const [version, changelog] = await Promise.all([
          versionResponse.text(),
          changelogResponse.text(),
        ]);
        setLatestVersion(version.trim() || currentVersion);
        if (changelog.trim()) setReleases(parseChangelog(changelog));
        if (showMessage) toast.success("已获取最新版本信息");
      } catch {
        setLatestVersion(currentVersion);
        setReleases(localReleases);
        if (showMessage) toast.error("获取最新版本信息失败");
      } finally {
        setChecking(false);
      }
    },
    [currentVersion, localReleases],
  );

  const openReleaseModal = () => {
    setOpen(true);
    void checkLatestRelease();
  };

  return {
    open,
    setOpen,
    openReleaseModal,
    latestVersion,
    releases,
    checking,
    hasNewVersion,
    checkLatestRelease,
  };
}