Skip to content

Commit 2388f85

Browse files
authored
Merge pull request ChatGPTNextWeb#2193 from Yidadaa/bugfix-0629
feat: improve ux & auto updater
2 parents 3adca26 + 1e8d476 commit 2388f85

13 files changed

+624
-80
lines changed

README_CN.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ BASE_URL=https://chatgpt1.nextweb.fun/api/proxy
121121

122122
1. 安装 nodejs 18 和 yarn,具体细节请询问 ChatGPT;
123123
2. 执行 `yarn install && yarn dev` 即可。⚠️ 注意:此命令仅用于本地开发,不要用于部署!
124-
3. 如果你想本地部署,请使用 `yarn install && yarn start` 命令,你可以配合 pm2 来守护进程,防止被杀死,详情询问 ChatGPT。
124+
3. 如果你想本地部署,请使用 `yarn install && yarn build && yarn start` 命令,你可以配合 pm2 来守护进程,防止被杀死,详情询问 ChatGPT。
125125

126126
## 部署
127127

app/components/chat.module.scss

+31-23
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,19 @@
239239
&:last-child {
240240
animation: slide-in ease 0.3s;
241241
}
242+
243+
&:hover {
244+
.chat-message-actions {
245+
opacity: 1;
246+
transform: translateY(0px);
247+
max-width: 100%;
248+
height: 40px;
249+
}
250+
251+
.chat-message-action-date {
252+
opacity: 0.2;
253+
}
254+
}
242255
}
243256

244257
.chat-message-user {
@@ -305,45 +318,40 @@
305318
position: relative;
306319
transition: all ease 0.3s;
307320

308-
&:hover {
309-
.chat-message-actions {
310-
opacity: 1;
311-
transform: translateY(0px);
312-
max-width: 100%;
313-
height: 40px;
314-
315-
.chat-message-action-date {
316-
opacity: 0.3;
317-
}
318-
}
319-
}
320-
321321
.chat-message-actions {
322322
display: flex;
323323
box-sizing: border-box;
324324
font-size: 12px;
325325
align-items: flex-end;
326326
justify-content: space-between;
327-
transition: all ease 0.1s;
328-
transform: translateY(10px);
327+
transition: all ease 0.3s 0.15s;
328+
transform: translateX(-5px) scale(0.9) translateY(30px);
329329
opacity: 0;
330330
height: 0;
331331
max-width: 0;
332+
position: absolute;
333+
left: 0;
334+
z-index: 2;
332335

333336
.chat-input-actions {
334337
display: flex;
335338
flex-wrap: nowrap;
336339
}
337340
}
341+
}
338342

339-
.chat-message-action-date {
340-
white-space: nowrap;
341-
transition: all ease 0.6s;
342-
color: var(--black);
343-
opacity: 0;
344-
text-align: right;
345-
margin-left: 20px;
346-
}
343+
.chat-message-action-date {
344+
font-size: 12px;
345+
opacity: 0.2;
346+
white-space: nowrap;
347+
transition: all ease 0.6s;
348+
color: var(--black);
349+
text-align: right;
350+
width: 100%;
351+
box-sizing: border-box;
352+
padding-right: 10px;
353+
pointer-events: none;
354+
z-index: 1;
347355
}
348356

349357
.chat-message-user > .chat-message-container > .chat-message-item {

app/components/chat.tsx

+6-4
Original file line numberDiff line numberDiff line change
@@ -992,13 +992,15 @@ export function Chat() {
992992
</>
993993
)}
994994
</div>
995-
996-
<div className={styles["chat-message-action-date"]}>
997-
{message.date.toLocaleString()}
998-
</div>
999995
</div>
1000996
)}
1001997
</div>
998+
999+
{showActions && (
1000+
<div className={styles["chat-message-action-date"]}>
1001+
{message.date.toLocaleString()}
1002+
</div>
1003+
)}
10021004
</div>
10031005
</div>
10041006
{shouldShowClearContextDivider && <ClearContextDivider />}

app/components/settings.tsx

+7-25
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import Locale, {
4040
} from "../locales";
4141
import { copyToClipboard } from "../utils";
4242
import Link from "next/link";
43-
import { Path, UPDATE_URL } from "../constant";
43+
import { Path, RELEASE_URL, UPDATE_URL } from "../constant";
4444
import { Prompt, SearchService, usePromptStore } from "../store/prompt";
4545
import { ErrorBoundary } from "./error";
4646
import { InputRange } from "./input-range";
@@ -310,19 +310,6 @@ function SyncItems() {
310310
);
311311
}
312312

313-
function formatVersionDate(t: string) {
314-
const d = new Date(+t);
315-
const year = d.getUTCFullYear();
316-
const month = d.getUTCMonth() + 1;
317-
const day = d.getUTCDate();
318-
319-
return [
320-
year.toString(),
321-
month.toString().padStart(2, "0"),
322-
day.toString().padStart(2, "0"),
323-
].join("");
324-
}
325-
326313
export function Settings() {
327314
const navigate = useNavigate();
328315
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
@@ -332,24 +319,19 @@ export function Settings() {
332319

333320
const updateStore = useUpdateStore();
334321
const [checkingUpdate, setCheckingUpdate] = useState(false);
335-
const currentVersion = formatVersionDate(updateStore.version);
336-
const remoteId = formatVersionDate(updateStore.remoteVersion);
322+
const currentVersion = updateStore.formatVersion(updateStore.version);
323+
const remoteId = updateStore.formatVersion(updateStore.remoteVersion);
337324
const hasNewVersion = currentVersion !== remoteId;
325+
const updateUrl = getClientConfig()?.isApp ? RELEASE_URL : UPDATE_URL;
338326

339327
function checkUpdate(force = false) {
340328
setCheckingUpdate(true);
341329
updateStore.getLatestVersion(force).then(() => {
342330
setCheckingUpdate(false);
343331
});
344332

345-
console.log(
346-
"[Update] local version ",
347-
new Date(+updateStore.version).toLocaleString(),
348-
);
349-
console.log(
350-
"[Update] remote version ",
351-
new Date(+updateStore.remoteVersion).toLocaleString(),
352-
);
333+
console.log("[Update] local version ", updateStore.version);
334+
console.log("[Update] remote version ", updateStore.remoteVersion);
353335
}
354336

355337
const usage = {
@@ -460,7 +442,7 @@ export function Settings() {
460442
{checkingUpdate ? (
461443
<LoadingIcon />
462444
) : hasNewVersion ? (
463-
<Link href={UPDATE_URL} target="_blank" className="link">
445+
<Link href={updateUrl} target="_blank" className="link">
464446
{Locale.Settings.Update.GoToUpdate}
465447
</Link>
466448
) : (

app/config/build.ts

+22-6
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,43 @@
1+
import tauriConfig from "../../src-tauri/tauri.conf.json";
2+
13
export const getBuildConfig = () => {
24
if (typeof process === "undefined") {
35
throw Error(
46
"[Server Config] you are importing a nodejs-only module outside of nodejs",
57
);
68
}
79

8-
const COMMIT_ID: string = (() => {
10+
const buildMode = process.env.BUILD_MODE ?? "standalone";
11+
const isApp = !!process.env.BUILD_APP;
12+
const version = tauriConfig.package.version;
13+
14+
const commitInfo = (() => {
915
try {
1016
const childProcess = require("child_process");
11-
return childProcess
17+
const commitDate: string = childProcess
1218
.execSync('git log -1 --format="%at000" --date=unix')
1319
.toString()
1420
.trim();
21+
const commitHash: string = childProcess
22+
.execSync('git log --pretty=format:"%H" -n 1')
23+
.toString()
24+
.trim();
25+
26+
return { commitDate, commitHash };
1527
} catch (e) {
1628
console.error("[Build Config] No git or not from git repo.");
17-
return "unknown";
29+
return {
30+
commitDate: "unknown",
31+
commitHash: "unknown",
32+
};
1833
}
1934
})();
2035

2136
return {
22-
commitId: COMMIT_ID,
23-
buildMode: process.env.BUILD_MODE ?? "standalone",
24-
isApp: !!process.env.BUILD_APP,
37+
version,
38+
...commitInfo,
39+
buildMode,
40+
isApp,
2541
};
2642
};
2743

app/constant.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export const REPO = "ChatGPT-Next-Web";
33
export const REPO_URL = `https://github.com/${OWNER}/${REPO}`;
44
export const ISSUE_URL = `https://github.com/${OWNER}/${REPO}/issues`;
55
export const UPDATE_URL = `${REPO_URL}#keep-updated`;
6+
export const RELEASE_URL = `${REPO_URL}/releases`;
67
export const FETCH_COMMIT_URL = `https://api.github.com/repos/${OWNER}/${REPO}/commits?per_page=1`;
78
export const FETCH_TAG_URL = `https://api.github.com/repos/${OWNER}/${REPO}/tags?per_page=1`;
89
export const RUNTIME_CONFIG_DOM = "danger-runtime-config";

app/store/update.ts

+57-9
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,96 @@
11
import { create } from "zustand";
22
import { persist } from "zustand/middleware";
3-
import { FETCH_COMMIT_URL, StoreKey } from "../constant";
3+
import { FETCH_COMMIT_URL, FETCH_TAG_URL, StoreKey } from "../constant";
44
import { api } from "../client/api";
55
import { getClientConfig } from "../config/client";
66

77
export interface UpdateStore {
8+
versionType: "date" | "tag";
89
lastUpdate: number;
10+
version: string;
911
remoteVersion: string;
1012

1113
used?: number;
1214
subscription?: number;
1315
lastUpdateUsage: number;
1416

15-
version: string;
1617
getLatestVersion: (force?: boolean) => Promise<void>;
1718
updateUsage: (force?: boolean) => Promise<void>;
19+
20+
formatVersion: (version: string) => string;
1821
}
1922

2023
const ONE_MINUTE = 60 * 1000;
2124

25+
function formatVersionDate(t: string) {
26+
const d = new Date(+t);
27+
const year = d.getUTCFullYear();
28+
const month = d.getUTCMonth() + 1;
29+
const day = d.getUTCDate();
30+
31+
return [
32+
year.toString(),
33+
month.toString().padStart(2, "0"),
34+
day.toString().padStart(2, "0"),
35+
].join("");
36+
}
37+
38+
async function getVersion(type: "date" | "tag") {
39+
if (type === "date") {
40+
const data = (await (await fetch(FETCH_COMMIT_URL)).json()) as {
41+
commit: {
42+
author: { name: string; date: string };
43+
};
44+
sha: string;
45+
}[];
46+
const remoteCommitTime = data[0].commit.author.date;
47+
const remoteId = new Date(remoteCommitTime).getTime().toString();
48+
return remoteId;
49+
} else if (type === "tag") {
50+
const data = (await (await fetch(FETCH_TAG_URL)).json()) as {
51+
commit: { sha: string; url: string };
52+
name: string;
53+
}[];
54+
return data.at(0)?.name;
55+
}
56+
}
57+
2258
export const useUpdateStore = create<UpdateStore>()(
2359
persist(
2460
(set, get) => ({
61+
versionType: "tag",
2562
lastUpdate: 0,
63+
version: "unknown",
2664
remoteVersion: "",
2765

2866
lastUpdateUsage: 0,
2967

30-
version: "unknown",
68+
formatVersion(version: string) {
69+
if (get().versionType === "date") {
70+
version = formatVersionDate(version);
71+
}
72+
return version;
73+
},
3174

3275
async getLatestVersion(force = false) {
33-
set(() => ({ version: getClientConfig()?.commitId ?? "unknown" }));
76+
const versionType = get().versionType;
77+
let version =
78+
versionType === "date"
79+
? getClientConfig()?.commitDate
80+
: getClientConfig()?.version;
81+
82+
set(() => ({ version }));
3483

35-
const overTenMins = Date.now() - get().lastUpdate > 10 * ONE_MINUTE;
36-
if (!force && !overTenMins) return;
84+
const shouldCheck =
85+
Date.now() - get().lastUpdate > 24 * 60 * ONE_MINUTE;
86+
if (!force && !shouldCheck) return;
3787

3888
set(() => ({
3989
lastUpdate: Date.now(),
4090
}));
4191

4292
try {
43-
const data = await (await fetch(FETCH_COMMIT_URL)).json();
44-
const remoteCommitTime = data[0].commit.committer.date;
45-
const remoteId = new Date(remoteCommitTime).getTime().toString();
93+
const remoteId = await getVersion(versionType);
4694
set(() => ({
4795
remoteVersion: remoteId,
4896
}));

docs/faq-cn.md

+17
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,23 @@ keepalive_timeout 300; # 设定keep-alive超时时间为65秒
105105

106106
API KEY 有问题。余额不足。
107107

108+
## 使用时遇到 "Error: Loading CSS chunk xxx failed..."
109+
110+
为了减少首屏白屏时间,默认启用了分块编译,技术原理见下:
111+
112+
- https://nextjs.org/docs/app/building-your-application/optimizing/lazy-loading
113+
- https://stackoverflow.com/questions/55993890/how-can-i-disable-chunkcode-splitting-with-webpack4
114+
- https://github.com/vercel/next.js/issues/38507
115+
- https://stackoverflow.com/questions/55993890/how-can-i-disable-chunkcode-splitting-with-webpack4
116+
117+
然而 NextJS 的兼容性比较差,在比较老的浏览器上会导致此报错,可以在编译时关闭分块编译。
118+
119+
对于 Vercel 平台,在环境变量中增加 `DISABLE_CHUNK=1`,然后重新部署即可;
120+
对于自行编译部署的项目,在构建时使用 `DISABLE_CHUNK=1 yarn build` 构建即可;
121+
对于 Docker 用户,由于 Docker 打包时已经构建完毕,所以暂不支持关闭此特性。
122+
123+
注意,关闭此特性后,用户会在第一次访问网站时加载所有资源,如果用户网络状况较差,可能会引起较长时间的白屏,从而影响用户使用体验,所以自行考虑。
124+
108125
# 网络服务相关问题
109126

110127
## Cloudflare 是什么?

0 commit comments

Comments
 (0)