04/20 Update extension UI and functions: add item, form filler
This commit is contained in:
+91
-10
@@ -341,7 +341,11 @@ async function signOut() {
|
||||
}
|
||||
} catch {}
|
||||
await chrome.storage.session.clear();
|
||||
await chrome.storage.local.remove(["refresh_token", "enc_key_salt"]);
|
||||
await chrome.storage.local.remove([
|
||||
"refresh_token",
|
||||
"enc_key_salt",
|
||||
"vault_items_cs",
|
||||
]);
|
||||
_vaultKey = null;
|
||||
_items = [];
|
||||
showView("login");
|
||||
@@ -411,8 +415,27 @@ async function fetchAndDecryptVault() {
|
||||
}),
|
||||
);
|
||||
|
||||
// Write to session for the popup's own use (badge, rendering).
|
||||
await chrome.storage.session.set({ vault_items: _items });
|
||||
chrome.runtime.sendMessage({ type: "VAULT_UPDATED" }).catch(() => {});
|
||||
|
||||
// Write a lightweight copy to local storage — this is what content scripts
|
||||
// read, since chrome.storage.local works reliably across all Chrome versions
|
||||
// and does not require message delivery from the background service worker.
|
||||
const itemsForContentScript = _items.map((item) => ({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
item_type: item.item_type,
|
||||
plain: item.plain,
|
||||
}));
|
||||
await chrome.storage.local.set({ vault_items_cs: itemsForContentScript });
|
||||
|
||||
// Notify background to refresh badges and forward to content scripts.
|
||||
chrome.runtime
|
||||
.sendMessage({
|
||||
type: "VAULT_UPDATED",
|
||||
vault_items: itemsForContentScript,
|
||||
})
|
||||
.catch(() => {});
|
||||
} catch (err) {
|
||||
console.error("fetchAndDecryptVault:", err);
|
||||
} finally {
|
||||
@@ -581,6 +604,30 @@ function initTabs() {
|
||||
|
||||
// ── Save-prompt modal ─────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Populate the folder <select> inside the save modal.
|
||||
* Fetches /api/folders and rebuilds the option list.
|
||||
* The first option ("— No folder —") with value "" is always kept.
|
||||
*/
|
||||
async function loadFoldersIntoSaveModal() {
|
||||
const select = $("save-folder");
|
||||
// Reset to just the placeholder option.
|
||||
select.innerHTML = '<option value="">— No folder —</option>';
|
||||
try {
|
||||
const res = await apiFetch("/api/folders");
|
||||
if (!res?.ok) return;
|
||||
const folders = await res.json();
|
||||
folders.forEach((f) => {
|
||||
const opt = document.createElement("option");
|
||||
opt.value = f.id;
|
||||
opt.textContent = f.name;
|
||||
select.appendChild(opt);
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("[PassKeeper] loadFoldersIntoSaveModal failed:", err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the save-prompt overlay. It is a blocking modal — no backdrop click,
|
||||
* no ✕ button — so the user MUST click "Save" or "Not now".
|
||||
@@ -602,6 +649,10 @@ async function checkPendingSave() {
|
||||
? "Update in PassKeeper?"
|
||||
: "Save to PassKeeper?";
|
||||
|
||||
// Load folders (non-blocking — modal shows immediately, folders populate async).
|
||||
$("save-folder").value = "";
|
||||
loadFoldersIntoSaveModal();
|
||||
|
||||
// Show the overlay.
|
||||
$("save-prompt-overlay").classList.remove("hidden");
|
||||
$("save-name").focus();
|
||||
@@ -635,6 +686,9 @@ async function saveCredential(data) {
|
||||
notes: "",
|
||||
};
|
||||
const name = $("save-name").value.trim() || data.siteName || "Untitled";
|
||||
// Read selected folder — empty string means no folder (null).
|
||||
const folderVal = $("save-folder").value;
|
||||
const folder_id = folderVal ? parseInt(folderVal, 10) : null;
|
||||
const { enc_data, iv } = await ExtCrypto.encryptItem(_vaultKey, plain);
|
||||
try {
|
||||
const res = await apiFetch("/api/vault", {
|
||||
@@ -642,12 +696,16 @@ async function saveCredential(data) {
|
||||
body: JSON.stringify({
|
||||
name,
|
||||
item_type: "password",
|
||||
folder_id: null,
|
||||
folder_id,
|
||||
enc_data,
|
||||
iv,
|
||||
}),
|
||||
});
|
||||
if (res?.ok) {
|
||||
console.log(
|
||||
"[PassKeeper] Credential saved to vault, folder_id:",
|
||||
folder_id,
|
||||
);
|
||||
await fetchAndDecryptVault();
|
||||
renderList();
|
||||
}
|
||||
@@ -821,14 +879,37 @@ async function init() {
|
||||
const sessionState = await restoreSessionIfAvailable();
|
||||
|
||||
if (sessionState === "vault") {
|
||||
showView("vault");
|
||||
await checkPendingSave();
|
||||
if (_items.length) {
|
||||
renderList();
|
||||
fetchAndDecryptVault().then(() => renderList());
|
||||
// Check whether the content script requested a specific view (e.g. generator).
|
||||
const { popup_nav } = await chrome.storage.session.get("popup_nav");
|
||||
if (popup_nav) {
|
||||
await chrome.storage.session.remove("popup_nav");
|
||||
if (popup_nav === "generator") {
|
||||
showView("generator");
|
||||
initGenerator();
|
||||
// Still load vault data in background so the Vault tab is ready.
|
||||
fetchAndDecryptVault().then(() => {});
|
||||
// Wire event listeners below, then return early from vault-specific setup.
|
||||
} else {
|
||||
showView("vault");
|
||||
await checkPendingSave();
|
||||
if (_items.length) {
|
||||
renderList();
|
||||
fetchAndDecryptVault().then(() => renderList());
|
||||
} else {
|
||||
await fetchAndDecryptVault();
|
||||
renderList();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await fetchAndDecryptVault();
|
||||
renderList();
|
||||
showView("vault");
|
||||
await checkPendingSave();
|
||||
if (_items.length) {
|
||||
renderList();
|
||||
fetchAndDecryptVault().then(() => renderList());
|
||||
} else {
|
||||
await fetchAndDecryptVault();
|
||||
renderList();
|
||||
}
|
||||
}
|
||||
} else if (sessionState === "unlock") {
|
||||
showView("unlock");
|
||||
|
||||
Reference in New Issue
Block a user