Files
auto-login/src/popup/lock.ts
T
2026-09-17 23:00:47 +08:00

160 lines
5.0 KiB
TypeScript

/**
* 锁定 / 解锁界面与顶栏锁定按钮。
* 解锁成功后通过 ctx 进入主界面(恢复草稿 + 渲染环境与配置列表)。
*/
import { t } from "../i18n.js";
import { sendMessage } from "../messaging.js";
import type { LockMode } from "../types.js";
import { $ } from "./dom";
import type { PopupCtx } from "./context";
const lockScreen = $("lockScreen");
const mainContainer = $("mainContainer");
const lockTitle = $("lockTitle");
const lockHint = $("lockHint");
const lockPassword = $("lockPassword");
const lockPasswordConfirm = $("lockPasswordConfirm");
const lockConfirmGroup = $("lockConfirmGroup");
const lockSubmitBtn = $("lockSubmitBtn");
const lockWarn = $("lockWarn");
const lockBtn = $("lockBtn");
export function initLock(ctx: PopupCtx) {
let lockMode: LockMode = "setup"; // "setup" | "unlock"
let lockAfterSetup = false;
function showLockScreen(mode: LockMode): void {
lockMode = mode;
lockScreen.style.display = "flex";
mainContainer.style.display = "none";
lockPassword.value = "";
lockPasswordConfirm.value = "";
lockWarn.textContent = "";
lockWarn.style.display = "none";
if (mode === "setup") {
lockTitle.textContent = t("lockSetupTitle");
lockHint.textContent = t("lockSetupHint");
lockConfirmGroup.style.display = "block";
lockSubmitBtn.textContent = t("lockSetupBtn");
} else {
lockTitle.textContent = t("lockUnlockTitle");
lockHint.textContent = t("lockUnlockHint");
lockConfirmGroup.style.display = "none";
lockSubmitBtn.textContent = t("lockUnlockBtn");
}
setTimeout(() => lockPassword.focus(), 50);
}
function showMainUI() {
lockScreen.style.display = "none";
mainContainer.style.display = "flex";
}
async function enterMain() {
showMainUI();
await ctx.loadDraft();
await ctx.renderEnvSelect();
await ctx.renderConfigList();
}
async function initLockState() {
try {
const resp = await sendMessage({ action: "getVaultStatus" });
lockBtn.hidden = false;
if (!(resp as any).hasVault) {
// 无 vault:明文模式,直接进入,不锁定
await enterMain();
} else if (!(resp as any).unlocked) {
showLockScreen("unlock");
} else {
await enterMain();
}
} catch (err) {
console.warn("[AutoLogin]", t("logGetLockStatusFail"), err);
lockBtn.hidden = false;
await enterMain();
}
}
lockSubmitBtn.addEventListener("click", async () => {
const pwd = lockPassword.value;
if (!pwd) {
lockWarn.textContent = t("lockWarnEmpty");
lockWarn.style.display = "block";
return;
}
if (lockMode === "setup") {
if (pwd.length < 6) {
lockWarn.textContent = t("lockWarnShort");
lockWarn.style.display = "block";
return;
}
if (pwd !== lockPasswordConfirm.value) {
lockWarn.textContent = t("lockWarnMismatch");
lockWarn.style.display = "block";
return;
}
try {
await sendMessage({ action: "setupVault", password: pwd });
ctx.showToast(t("toastPasswordSet"));
if (lockAfterSetup) {
lockAfterSetup = false;
await sendMessage({ action: "lock" });
showLockScreen("unlock");
ctx.showToast(t("toastLocked"));
} else {
await enterMain();
}
} catch (err) {
lockWarn.textContent = t("lockWarnSetupFail", (err as Error).message || String(err));
lockWarn.style.display = "block";
}
} else {
try {
const resp = await sendMessage({ action: "unlock", password: pwd });
if ((resp as any).success) {
ctx.showToast(t("toastUnlocked"));
await enterMain();
} else {
lockWarn.textContent = (resp as any).error || t("lockWarnUnlockFail", "");
lockWarn.style.display = "block";
}
} catch (err) {
lockWarn.textContent = t("lockWarnUnlockFail", (err as Error).message || String(err));
lockWarn.style.display = "block";
}
}
});
lockPassword.addEventListener("keydown", (e: KeyboardEvent) => {
if (e.key === "Enter") lockSubmitBtn.click();
});
lockPasswordConfirm.addEventListener("keydown", (e: KeyboardEvent) => {
if (e.key === "Enter") lockSubmitBtn.click();
});
lockBtn.addEventListener("click", async () => {
const resp = await sendMessage({ action: "getVaultStatus" }).catch(() => null);
if (resp && !(resp as any).hasVault) {
// 明文模式:先设密码再锁定
lockAfterSetup = true;
showLockScreen("setup");
return;
}
if (!confirm(t("lockConfirmPrompt"))) return;
await sendMessage({ action: "lock" });
showLockScreen("unlock");
ctx.showToast(t("toastLocked"));
});
// 弹窗内的点击视为用户活动,重置 background 自动锁定计时
document.addEventListener("click", () => {
if (lockScreen.style.display === "none") {
sendMessage({ action: "ping" }, 1).catch(() => {});
}
});
return { initLockState };
}