feat: fix Telegram controls and add GitHub links
This commit is contained in:
+56
-1
@@ -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">
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user