mirror of
https://github.com/Yidadaa/ChatGPT-Next-Web.git
synced 2025-08-08 16:51:54 +08:00
feat: add check update
This commit is contained in:
@@ -26,6 +26,7 @@ import { copyToClipboard, downloadAs, isIOS, selectOrCopy } from "../utils";
|
||||
import Locale from "../locales";
|
||||
|
||||
import dynamic from "next/dynamic";
|
||||
import { REPO_URL } from "../constant";
|
||||
|
||||
export function Loading(props: { noLogo?: boolean }) {
|
||||
return (
|
||||
@@ -466,10 +467,7 @@ export function Home() {
|
||||
/>
|
||||
</div>
|
||||
<div className={styles["sidebar-action"]}>
|
||||
<a
|
||||
href="https://github.com/Yidadaa/ChatGPT-Next-Web"
|
||||
target="_blank"
|
||||
>
|
||||
<a href={REPO_URL} target="_blank">
|
||||
<IconButton icon={<GithubIcon />} />
|
||||
</a>
|
||||
</div>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
import EmojiPicker, { Theme as EmojiTheme } from "emoji-picker-react";
|
||||
|
||||
@@ -15,6 +15,9 @@ import { SubmitKey, useChatStore, Theme, ALL_MODELS } from "../store";
|
||||
import { Avatar } from "./home";
|
||||
|
||||
import Locale, { changeLang, getLang } from "../locales";
|
||||
import { checkUpstreamLatestCommitId, getCurrentCommitId } from "../utils";
|
||||
import Link from "next/link";
|
||||
import { UPDATE_URL } from "../constant";
|
||||
|
||||
function SettingItem(props: {
|
||||
title: string;
|
||||
@@ -45,6 +48,23 @@ export function Settings(props: { closeSettings: () => void }) {
|
||||
]
|
||||
);
|
||||
|
||||
const currentId = getCurrentCommitId();
|
||||
const [checkingUpdate, setCheckingUpdate] = useState(false);
|
||||
const [remoteId, setRemoteId] = useState<string>();
|
||||
const hasNewVersion = currentId !== remoteId;
|
||||
|
||||
function checkUpdate(force = false) {
|
||||
setCheckingUpdate(true);
|
||||
checkUpstreamLatestCommitId(force).then((id) => {
|
||||
setRemoteId(id);
|
||||
setCheckingUpdate(false);
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
checkUpdate();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles["window-header"]}>
|
||||
@@ -109,6 +129,31 @@ export function Settings(props: { closeSettings: () => void }) {
|
||||
</Popover>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem
|
||||
title={Locale.Settings.Update.Version(currentId)}
|
||||
subTitle={
|
||||
checkingUpdate
|
||||
? Locale.Settings.Update.IsChecking
|
||||
: hasNewVersion
|
||||
? Locale.Settings.Update.FoundUpdate(remoteId ?? "ERROR")
|
||||
: Locale.Settings.Update.IsLatest
|
||||
}
|
||||
>
|
||||
{checkingUpdate ? (
|
||||
<div />
|
||||
) : hasNewVersion ? (
|
||||
<Link href={UPDATE_URL} target="_blank" className="link">
|
||||
{Locale.Settings.Update.GoToUpdate}
|
||||
</Link>
|
||||
) : (
|
||||
<IconButton
|
||||
icon={<ResetIcon></ResetIcon>}
|
||||
text={Locale.Settings.Update.CheckUpdate}
|
||||
onClick={() => checkUpdate(true)}
|
||||
/>
|
||||
)}
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem title={Locale.Settings.SendKey}>
|
||||
<select
|
||||
value={config.submitKey}
|
||||
|
3
app/constant.ts
Normal file
3
app/constant.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const REPO_URL = "https://github.com/Yidadaa/ChatGPT-Next-Web";
|
||||
export const UPDATE_URL =
|
||||
"https://github.com/Yidadaa/ChatGPT-Next-Web#%E4%BF%9D%E6%8C%81%E6%9B%B4%E6%96%B0-keep-updated";
|
@@ -4,9 +4,11 @@ import "./styles/prism.scss";
|
||||
|
||||
export const metadata = {
|
||||
title: "ChatGPT Next Web",
|
||||
description: "Your personal ChatGPT Chat Bot.",
|
||||
description: "Your personal ChatGPT Chat Bot."
|
||||
};
|
||||
|
||||
const COMMIT_ID = process.env.COMMIT_ID
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
@@ -19,7 +21,14 @@ export default function RootLayout({
|
||||
name="viewport"
|
||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
|
||||
/>
|
||||
<meta name="version" content={COMMIT_ID} />
|
||||
<link rel="manifest" href="/site.webmanifest"></link>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com"></link>
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com"></link>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;700;900&display=swap"
|
||||
rel="stylesheet"
|
||||
></link>
|
||||
</head>
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
|
@@ -43,6 +43,14 @@ const cn = {
|
||||
},
|
||||
},
|
||||
Avatar: "头像",
|
||||
Update: {
|
||||
Version: (x: string) => `当前版本:${x}`,
|
||||
IsLatest: "已是最新版本",
|
||||
CheckUpdate: "检查更新",
|
||||
IsChecking: "正在检查更新...",
|
||||
FoundUpdate: (x: string) => `发现新版本:${x}`,
|
||||
GoToUpdate: "前往更新",
|
||||
},
|
||||
SendKey: "发送键",
|
||||
Theme: "主题",
|
||||
TightBorder: "紧凑边框",
|
||||
|
@@ -46,6 +46,14 @@ const en: LocaleType = {
|
||||
},
|
||||
},
|
||||
Avatar: "Avatar",
|
||||
Update: {
|
||||
Version: (x: string) => `Version: ${x}`,
|
||||
IsLatest: "Latest version",
|
||||
CheckUpdate: "Check Update",
|
||||
IsChecking: "Checking update...",
|
||||
FoundUpdate: (x: string) => `Found new version: ${x}`,
|
||||
GoToUpdate: "Update",
|
||||
},
|
||||
SendKey: "Send Key",
|
||||
Theme: "Theme",
|
||||
TightBorder: "Tight Border",
|
||||
|
@@ -83,6 +83,8 @@ body {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
font-family: "Noto Sans SC", "SF Pro SC", "SF Pro Text", "SF Pro Icons",
|
||||
"PingFang SC", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
@@ -192,3 +194,14 @@ div.math {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.link {
|
||||
font-size: 12px;
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
padding: 5px 10px;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
@@ -96,8 +96,6 @@
|
||||
margin: 0;
|
||||
color: var(--color-fg-default);
|
||||
background-color: var(--color-canvas-default);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans",
|
||||
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
word-wrap: break-word;
|
||||
@@ -319,7 +317,7 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.markdown-body details:not([open])>*:not(summary) {
|
||||
.markdown-body details:not([open]) > *:not(summary) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@@ -489,11 +487,11 @@
|
||||
content: "";
|
||||
}
|
||||
|
||||
.markdown-body>*:first-child {
|
||||
.markdown-body > *:first-child {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
.markdown-body>*:last-child {
|
||||
.markdown-body > *:last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
@@ -529,11 +527,11 @@
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.markdown-body blockquote> :first-child {
|
||||
.markdown-body blockquote > :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.markdown-body blockquote> :last-child {
|
||||
.markdown-body blockquote > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@@ -632,7 +630,7 @@
|
||||
list-style-type: decimal;
|
||||
}
|
||||
|
||||
.markdown-body div>ol:not([type]) {
|
||||
.markdown-body div > ol:not([type]) {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
|
||||
@@ -644,11 +642,11 @@
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.markdown-body li>p {
|
||||
.markdown-body li > p {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.markdown-body li+li {
|
||||
.markdown-body li + li {
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
|
||||
@@ -711,7 +709,7 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.markdown-body span.frame>span {
|
||||
.markdown-body span.frame > span {
|
||||
display: block;
|
||||
float: left;
|
||||
width: auto;
|
||||
@@ -739,7 +737,7 @@
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown-body span.align-center>span {
|
||||
.markdown-body span.align-center > span {
|
||||
display: block;
|
||||
margin: 13px auto 0;
|
||||
overflow: hidden;
|
||||
@@ -757,7 +755,7 @@
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown-body span.align-right>span {
|
||||
.markdown-body span.align-right > span {
|
||||
display: block;
|
||||
margin: 13px 0 0;
|
||||
overflow: hidden;
|
||||
@@ -787,7 +785,7 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.markdown-body span.float-right>span {
|
||||
.markdown-body span.float-right > span {
|
||||
display: block;
|
||||
margin: 13px auto 0;
|
||||
overflow: hidden;
|
||||
@@ -821,7 +819,7 @@
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
.markdown-body pre>code {
|
||||
.markdown-body pre > code {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
word-break: normal;
|
||||
@@ -1085,7 +1083,7 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.markdown-body .task-list-item+.task-list-item {
|
||||
.markdown-body .task-list-item + .task-list-item {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
@@ -1107,7 +1105,9 @@
|
||||
}
|
||||
|
||||
.markdown-body .contains-task-list:hover .task-list-item-convert-container,
|
||||
.markdown-body .contains-task-list:focus-within .task-list-item-convert-container {
|
||||
.markdown-body
|
||||
.contains-task-list:focus-within
|
||||
.task-list-item-convert-container {
|
||||
display: block;
|
||||
width: auto;
|
||||
height: 24px;
|
||||
@@ -1117,4 +1117,4 @@
|
||||
|
||||
.markdown-body ::-webkit-calendar-picker-indicator {
|
||||
filter: invert(50%);
|
||||
}
|
||||
}
|
||||
|
39
app/utils.ts
39
app/utils.ts
@@ -56,3 +56,42 @@ export function selectOrCopy(el: HTMLElement, content: string) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
let currentId: string;
|
||||
export function getCurrentCommitId() {
|
||||
if (currentId) {
|
||||
return currentId;
|
||||
}
|
||||
|
||||
if (document) {
|
||||
const meta = document.head.querySelector(
|
||||
"meta[name='version']"
|
||||
) as HTMLMetaElement;
|
||||
currentId = meta?.content ?? "";
|
||||
} else {
|
||||
currentId = process.env.COMMIT_ID ?? "";
|
||||
}
|
||||
|
||||
return currentId;
|
||||
}
|
||||
|
||||
let remoteId: string;
|
||||
export async function checkUpstreamLatestCommitId(force = false) {
|
||||
if (!force && remoteId) {
|
||||
return remoteId;
|
||||
}
|
||||
|
||||
const owner = "Yidadaa";
|
||||
const repo = "ChatGPT-Next-Web";
|
||||
const url = `https://api.github.com/repos/${owner}/${repo}/commits?per_page=1`;
|
||||
|
||||
try {
|
||||
const data = await (await fetch(url)).json();
|
||||
const sha = data[0].sha as string;
|
||||
remoteId = sha.substring(0, 7);
|
||||
return remoteId;
|
||||
} catch (error) {
|
||||
console.error("[Fetch Upstream Commit Id]", error);
|
||||
return getCurrentCommitId();
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user