160 lines
5.0 KiB
TypeScript
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 };
|
|
}
|