From c27525d338448262aadaaae5eac8d1390110c9c9 Mon Sep 17 00:00:00 2001 From: Dominion Gbadamosi Date: Fri, 30 May 2025 05:24:33 +0100 Subject: [PATCH] fix: resolved issue with popup content users can now access the options from the popup at any time and the recently solved problems list is also visible --- manifest.json | 2 +- popup/popup.js | 239 ++++++++++++++++++++++++++----------------------- 2 files changed, 126 insertions(+), 115 deletions(-) diff --git a/manifest.json b/manifest.json index f5bc8ba..4d11143 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "KeepCode", - "version": "0.1.0", + "version": "0.2.0", "description": "Focus on answering problems, let KeepCode handle the rest", "permissions": ["storage", "tabs", "activeTab", "https://leetcode.com/*"], "background": { diff --git a/popup/popup.js b/popup/popup.js index f4aabcf..1cede5a 100644 --- a/popup/popup.js +++ b/popup/popup.js @@ -1,22 +1,23 @@ document.addEventListener("DOMContentLoaded", () => { browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => { const tab = tabs[0]; - // Get the problem slug from the URL + const url = new URL(tab.url); const pathParts = url.pathname.split("/").filter(Boolean); const slug = pathParts[1] || null; if (!slug) { - document.body.innerHTML = "

Not a leetcode problem page

"; + document.getElementById("popupContent").innerHTML = "

To track a problem, please visit a leetcode problem page

"; return; } - // Ask background for the latest problem data - browser.runtime.sendMessage({ type: "GET_PROBLEM_DATA", slug }) + + browser.runtime + .sendMessage({ type: "GET_PROBLEM_DATA", slug }) .then((data) => { + const container = document.getElementById("popupContent"); if (!data) { - document.body.innerHTML = "

Not a leetcode problem page

"; + container.innerHTML = "

To track a problem please visit a leetcode problem page

"; return; } - const container = document.getElementById("popupContent"); container.innerHTML = ""; const titleEl = document.createElement("h3"); @@ -34,133 +35,143 @@ document.addEventListener("DOMContentLoaded", () => { // Listen for problem solved message browser.runtime.onMessage.addListener((msg) => { - if (msg.type === "PROBLEM_SOLVED" && msg.slug === data.slug) { - const statusEl = document.getElementById("status"); - if (statusEl) statusEl.textContent = "Solved ✅"; - } + if (msg.type === "PROBLEM_SOLVED" && msg.slug === data.slug) { + const statusEl = document.getElementById("status"); + if (statusEl) statusEl.textContent = "Solved ✅"; + } }); }) .catch((err) => { - document.body.innerHTML = "

Unable to read this page

"; + document.getElementById("popupContent").innerHTML = "

Unable to read this page

"; console.error(err); }); }); }); document.addEventListener("DOMContentLoaded", () => { - browser.storage.local.get(null).then((allData) => { - // Filter out invalid/undefined problems before displaying - const problems = Object.values(allData).filter(p => - p && - typeof p.title === 'string' && p.title !== 'Unknown Title' && - typeof p.slug === 'string' && p.slug !== 'unknown-problem' && - typeof p.difficulty === 'string' && p.difficulty !== 'Unknown Difficulty' && - p.status === "Solved" - ); - // Sort by solvedAt descending (most recent first) - problems.sort((a, b) => (b.solvedAt || 0) - (a.solvedAt || 0)); + browser.storage.local.get(null).then((allData) => { + // Filter out invalid/undefined problems before displaying + const problems = Object.values(allData).filter( + (p) => + p && + typeof p.title === "string" && + p.title !== "Unknown Title" && + typeof p.slug === "string" && + p.slug !== "unknown-problem" && + typeof p.difficulty === "string" && + p.difficulty !== "Unknown Difficulty" && + p.status === "Solved" + ); + // Sort by solvedAt descending (most recent first) + problems.sort((a, b) => (b.solvedAt || 0) - (a.solvedAt || 0)); - // Collect all unique tags (flattened) - const tagSet = new Set(); - problems.forEach(p => { - if (Array.isArray(p.tags)) { - p.tags.forEach(tag => tagSet.add(tag)); - } + // Collect all unique tags (flattened) + const tagSet = new Set(); + problems.forEach((p) => { + if (Array.isArray(p.tags)) { + p.tags.forEach((tag) => tagSet.add(tag)); + } + }); + const tags = Array.from(tagSet); + + // Populate tag filter dropdown + const tagFilter = document.getElementById("tagFilter"); + if (tagFilter) { + // Remove old options except 'All' + tagFilter.innerHTML = ''; + if (tags.length > 0) { + tags.forEach((tag) => { + const opt = document.createElement("option"); + opt.value = tag; + opt.textContent = tag; + tagFilter.appendChild(opt); }); - const tags = Array.from(tagSet); + } else { + const opt = document.createElement("option"); + opt.value = "none"; + opt.textContent = "No tags"; + tagFilter.appendChild(opt); + } + } - // Populate tag filter dropdown - const tagFilter = document.getElementById("tagFilter"); - if (tagFilter) { - // Remove old options except 'All' - tagFilter.innerHTML = ''; - if (tags.length > 0) { - tags.forEach(tag => { - const opt = document.createElement("option"); - opt.value = tag; - opt.textContent = tag; - tagFilter.appendChild(opt); - }); - } else { - const opt = document.createElement("option"); - opt.value = "none"; - opt.textContent = "No tags"; - tagFilter.appendChild(opt); - } - } + // Render problems (filtered and limited) + function renderProblems() { + const list = document.getElementById("solvedList"); + list.innerHTML = ""; + let filtered = problems; + const selectedTag = tagFilter ? tagFilter.value : "all"; + if (selectedTag && selectedTag !== "all" && selectedTag !== "none") { + filtered = problems.filter( + (p) => Array.isArray(p.tags) && p.tags.includes(selectedTag) + ); + } + const toShow = filtered.slice(0, 5); + if (toShow.length === 0) { + list.innerHTML = "

No solved problems yet

"; + } + toShow.forEach((problem) => { + const item = document.createElement("div"); + item.className = "problem-item"; + const difficultyClass = problem.difficulty + ? problem.difficulty.toLowerCase() + : ""; + // Use DOM methods instead of innerHTML for safety + const link = document.createElement("a"); + link.href = problem.url; + link.target = "_blank"; + link.textContent = problem.title; - // Render problems (filtered and limited) - function renderProblems() { - const list = document.getElementById("solvedList"); - list.innerHTML = ""; - let filtered = problems; - const selectedTag = tagFilter ? tagFilter.value : "all"; - if (selectedTag && selectedTag !== "all" && selectedTag !== "none") { - filtered = problems.filter(p => Array.isArray(p.tags) && p.tags.includes(selectedTag)); - } - const toShow = filtered.slice(0, 5); - if (toShow.length === 0) { - list.innerHTML = "

No solved problems yet

"; - } - toShow.forEach(problem => { - const item = document.createElement("div"); - item.className = "problem-item"; - const difficultyClass = problem.difficulty ? problem.difficulty.toLowerCase() : ""; - // Use DOM methods instead of innerHTML for safety - const link = document.createElement('a'); - link.href = problem.url; - link.target = '_blank'; - link.textContent = problem.title; + const diffSpan = document.createElement("span"); + diffSpan.className = `difficulty ${difficultyClass}`; + diffSpan.textContent = problem.difficulty; - const diffSpan = document.createElement('span'); - diffSpan.className = `difficulty ${difficultyClass}`; - diffSpan.textContent = problem.difficulty; + const tagsSpan = document.createElement("span"); + tagsSpan.style.fontSize = "0.85em"; + tagsSpan.style.color = + problem.tags && problem.tags.length > 0 ? "#666" : "#bbb"; + tagsSpan.style.marginLeft = "8px"; + tagsSpan.textContent = + problem.tags && problem.tags.length > 0 + ? `[${problem.tags.join(", ")}]` + : "[No tags]"; - const tagsSpan = document.createElement('span'); - tagsSpan.style.fontSize = '0.85em'; - tagsSpan.style.color = problem.tags && problem.tags.length > 0 ? '#666' : '#bbb'; - tagsSpan.style.marginLeft = '8px'; - tagsSpan.textContent = problem.tags && problem.tags.length > 0 - ? `[${problem.tags.join(', ')}]` - : '[No tags]'; + item.appendChild(link); + item.appendChild(diffSpan); + item.appendChild(tagsSpan); + list.appendChild(item); + }); + } - item.appendChild(link); - item.appendChild(diffSpan); - item.appendChild(tagsSpan); - list.appendChild(item); - }); - } + if (tagFilter) { + tagFilter.addEventListener("change", renderProblems); + } + renderProblems(); + }); - if (tagFilter) { - tagFilter.addEventListener("change", renderProblems); - } - renderProblems(); + // View All link opens options page + const viewAllLink = document.getElementById("viewAllLink"); + if (viewAllLink) { + viewAllLink.addEventListener("click", (e) => { + e.preventDefault(); + if (browser.runtime && browser.runtime.openOptionsPage) { + browser.runtime.openOptionsPage(); + } else { + window.open("../options/options.html", "_blank"); + } }); - - // View All link opens options page - const viewAllLink = document.getElementById("viewAllLink"); - if (viewAllLink) { - viewAllLink.addEventListener("click", (e) => { - e.preventDefault(); - if (browser.runtime && browser.runtime.openOptionsPage) { - browser.runtime.openOptionsPage(); - } else { - window.open("../options/options.html", "_blank"); - } - }); - } + } }); document.addEventListener("DOMContentLoaded", () => { - const optionsBtn = document.getElementById("optionsBtn"); - if (optionsBtn) { - optionsBtn.addEventListener("click", () => { - if (browser.runtime && browser.runtime.openOptionsPage) { - browser.runtime.openOptionsPage(); - } else { - // fallback for browsers that don't support openOptionsPage - window.open("../options/options.html", "_blank"); - } - }); - } + const optionsBtn = document.getElementById("optionsBtn"); + if (optionsBtn) { + optionsBtn.addEventListener("click", () => { + if (browser.runtime && browser.runtime.openOptionsPage) { + browser.runtime.openOptionsPage(); + } else { + // fallback for browsers that don't support openOptionsPage + window.open("../options/options.html", "_blank"); + } + }); + } });