feat: fix Telegram controls and add GitHub links

This commit is contained in:
zhoujun0601
2026-08-13 12:20:30 -04:00
parent a027edda88
commit 5c8a3ffb8f
14 changed files with 477 additions and 16 deletions
+56 -1
View File
@@ -11,6 +11,7 @@ import {
Activity,
Bell,
Check,
Github,
LogOut,
Moon,
RefreshCw,
@@ -24,6 +25,7 @@ import { AuthProvider, useAuth } from "./auth";
import { ThemeProvider, useTheme } from "./theme";
import {
decodeAccounts,
decodeAction,
decodeCode,
decodeDashboard,
decodeDeviceLogin,
@@ -42,6 +44,23 @@ import {
import "./styles.css";
const UsageChart = lazy(() => import("./usage-chart"));
const repositoryURL = "https://github.com/zhoujun0601/codex-helper";
function GitHubLink({ className = "" }: { className?: string }) {
return (
<a
className={`github-link ${className}`.trim()}
href={repositoryURL}
target="_blank"
rel="noopener noreferrer"
aria-label="在 GitHub 上查看 Codex Helper"
title="GitHub"
>
<Github />
<span>GitHub</span>
</a>
);
}
function App() {
const { status, authenticated } = useAuth();
@@ -158,6 +177,7 @@ function Login({ version }: { version: string }) {
}
}}
>
<GitHubLink className="login-github" />
<Brand version={version} />
<h2>欢迎回来</h2>
<label>
@@ -198,6 +218,7 @@ function Shell({ version }: { version: string }) {
</button>
</nav>
<div className="aside-bottom">
<GitHubLink />
<button
onClick={() =>
void saveTheme(theme === "dark" ? "light" : "dark").catch(
@@ -987,7 +1008,7 @@ function Telegram() {
body: JSON.stringify(v),
});
setV({ ...x, token: "" });
setMsg("Bot 已验证并保存");
setMsg(x.warning || "Bot 已验证并保存");
} catch (x) {
setMsg(toErrorMessage(x));
}
@@ -1023,6 +1044,10 @@ function Telegram() {
启用查询菜单
</label>
</div>
<p className="hint telegram-switch-hint">
“启用提醒”只控制自动额度提醒,关闭后仍可发送测试消息;“启用查询菜单”关闭后将停止查询并移除
Telegram 键盘。
</p>
<button>验证并保存</button>
<div className="actions">
<button
@@ -1053,6 +1078,36 @@ function Telegram() {
>
发送测试
</button>
<button
type="button"
className="danger"
disabled={!v.configured}
onClick={async () => {
if (
!confirm(
"确定要解除 Telegram Bot 绑定吗?这会删除 Bot Token、Chat ID、开关和未使用的绑定码。",
)
)
return;
try {
const x = await del("settings/telegram", decodeAction);
setV({
chatId: 0,
enabled: false,
menuEnabled: false,
configured: false,
token: "",
});
setCode("");
setMsg(x.warning || "Telegram Bot 配置已删除");
} catch (error) {
setMsg(toErrorMessage(error));
}
}}
>
<Trash2 />
解除绑定
</button>
</div>
{code && (
<div className="codebox">
+43
View File
@@ -174,6 +174,38 @@ button svg {
}
.login {
width: min(420px, 90vw);
position: relative;
}
.github-link {
border-radius: 10px;
padding: 11px 16px;
color: var(--muted);
display: inline-flex;
align-items: center;
justify-content: flex-start;
gap: 8px;
font-weight: 600;
text-decoration: none;
}
.github-link:hover {
background: var(--track);
color: var(--text);
}
.github-link svg {
width: 17px;
}
.login-github {
position: absolute;
top: 16px;
right: 16px;
padding: 9px;
}
.login-github span {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
}
.error {
color: var(--danger) !important;
@@ -445,6 +477,11 @@ header p {
.checks input {
width: auto;
}
.telegram-switch-hint {
margin: -8px 0 0;
font-size: 12px;
line-height: 1.6;
}
.actions {
display: flex;
gap: 10px;
@@ -616,6 +653,12 @@ a {
margin: 0;
flex-direction: row;
}
aside .github-link {
padding: 10px;
}
aside .github-link span {
display: none;
}
aside button {
font-size: 0;
padding: 10px;
+22 -1
View File
@@ -1,6 +1,12 @@
import { describe, expect, it } from "vitest";
import { ApiError, toErrorMessage } from "./api";
import { decodeDashboard, decodeDeviceLogin, decodeGeneral } from "./types";
import {
decodeAction,
decodeDashboard,
decodeDeviceLogin,
decodeGeneral,
decodeTelegram,
} from "./types";
describe("API decoders", () => {
it("保留 Dashboard 的 null 与 optional 摘要", () => {
@@ -46,6 +52,21 @@ describe("API decoders", () => {
}).userCode,
).toBe("ABC");
});
it("保留 Telegram 操作警告", () => {
expect(
decodeTelegram({
chatId: 1,
enabled: false,
menuEnabled: false,
configured: true,
warning: "菜单同步失败",
}).warning,
).toBe("菜单同步失败");
expect(decodeAction({ ok: true, warning: "键盘清理失败" }).warning).toBe(
"键盘清理失败",
);
});
});
describe("errors", () => {
+13
View File
@@ -83,8 +83,13 @@ export interface TelegramSettingsResponse {
menuEnabled: boolean;
configured: boolean;
botName?: string;
warning?: string;
}
export type TelegramSettingsForm = TelegramSettingsResponse & { token: string };
export interface ActionResponse {
ok: boolean;
warning?: string;
}
export interface SMTPSettingsResponse {
host: string;
port: number;
@@ -223,6 +228,14 @@ export const decodeTelegram: Decoder<TelegramSettingsResponse> = (value) => {
menuEnabled: boolean(x.menuEnabled, "menuEnabled"),
configured: boolean(x.configured, "configured"),
...(typeof x.botName === "string" ? { botName: x.botName } : {}),
...(typeof x.warning === "string" ? { warning: x.warning } : {}),
};
};
export const decodeAction: Decoder<ActionResponse> = (value) => {
const x = record(value);
return {
ok: boolean(x.ok, "ok"),
...(typeof x.warning === "string" ? { warning: x.warning } : {}),
};
};
export const decodeSMTP: Decoder<SMTPSettingsResponse> = (value) => {