Skip to content

Commit 9c0019a

Browse files
authored
Merge pull request #5 from kiing-dom/fix/issues-2-persist-problem-solved
Fix/issues 2 persist problem solved
2 parents a6af142 + 0dd8f64 commit 9c0019a

File tree

2 files changed

+161
-142
lines changed

2 files changed

+161
-142
lines changed

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"manifest_version": 2,
33
"name": "KeepCode",
4-
"version": "0.1.0",
4+
"version": "0.2.0",
55
"description": "Focus on answering problems, let KeepCode handle the rest",
66
"permissions": ["storage", "tabs", "activeTab", "https://leetcode.com/*"],
77
"background": {

popup/popup.js

Lines changed: 160 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,166 +1,185 @@
11
document.addEventListener("DOMContentLoaded", () => {
22
browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => {
33
const tab = tabs[0];
4-
// Get the problem slug from the URL
4+
55
const url = new URL(tab.url);
66
const pathParts = url.pathname.split("/").filter(Boolean);
77
const slug = pathParts[1] || null;
88
if (!slug) {
9-
document.body.innerHTML = "<p>Not a leetcode problem page</p>";
9+
document.getElementById("popupContent").innerHTML = "<p>To track a problem, please visit a leetcode problem page</p>";
1010
return;
1111
}
12-
// Ask background for the latest problem data
13-
browser.runtime.sendMessage({ type: "GET_PROBLEM_DATA", slug })
14-
.then((data) => {
15-
if (!data) {
16-
document.body.innerHTML = "<p>Not a leetcode problem page</p>";
17-
return;
18-
}
19-
const container = document.getElementById("popupContent");
20-
21-
container.innerHTML = "";
22-
const titleEl = document.createElement("h3");
23-
titleEl.textContent = `Problem: ${data.title}`;
24-
const diffEl = document.createElement("p");
25-
diffEl.textContent = `Difficulty: ${data.difficulty} `;
26-
const statusEl = document.createElement("p");
27-
statusEl.id = "status";
28-
const strongEl = document.createElement("strong");
29-
strongEl.textContent = data.status || "Unsolved";
30-
statusEl.appendChild(strongEl);
31-
container.appendChild(titleEl);
32-
container.appendChild(diffEl);
33-
container.appendChild(statusEl);
34-
35-
// Listen for problem solved message
36-
browser.runtime.onMessage.addListener((msg) => {
37-
if (msg.type === "PROBLEM_SOLVED" && msg.slug === data.slug) {
38-
const statusEl = document.getElementById("status");
39-
if (statusEl) statusEl.textContent = "Solved ✅";
12+
13+
const container = document.getElementById("popupContent");
14+
15+
browser.storage.local.get(slug).then((result) => {
16+
let data = result[slug];
17+
if (data) {
18+
renderCurrentProblem(data);
19+
} else {
20+
21+
browser.runtime
22+
.sendMessage({ type: "GET_PROBLEM_DATA", slug })
23+
.then((data) => {
24+
if (data) {
25+
renderCurrentProblem(data);
26+
} else {
27+
container.innerHTML = "<p>To track a problem please visit a leetcode problem page</p>";
4028
}
41-
});
42-
})
43-
.catch((err) => {
44-
document.body.innerHTML = "<p>Unable to read this page</p>";
45-
console.error(err);
29+
})
30+
.catch((err) => {
31+
container.innerHTML = "<p>Unable to read this page.</p>";
32+
console.error(err);
33+
});
34+
}
35+
});
36+
37+
function renderCurrentProblem(data) {
38+
container.innerHTML = "";
39+
const titleEl = document.createElement("h3");
40+
titleEl.textContent = `Problem: ${data.title}`;
41+
const diffEl = document.createElement("p");
42+
diffEl.textContent = `Difficulty: ${data.difficulty} `;
43+
const statusEl = document.createElement("p");
44+
statusEl.id = "status";
45+
const strongEl = document.createElement("strong");
46+
strongEl.textContent = data.status || "Unsolved";
47+
statusEl.appendChild(strongEl);
48+
container.appendChild(titleEl);
49+
container.appendChild(diffEl);
50+
container.appendChild(statusEl);
51+
52+
// Listen for problem solved message
53+
browser.runtime.onMessage.addListener((msg) => {
54+
if (msg.type === "PROBLEM_SOLVED" && msg.slug === data.slug) {
55+
const statusEl = document.getElementById("status");
56+
if (statusEl) statusEl.textContent = "Solved ✅";
57+
}
4658
});
59+
}
4760
});
4861
});
4962

5063
document.addEventListener("DOMContentLoaded", () => {
51-
browser.storage.local.get(null).then((allData) => {
52-
// Filter out invalid/undefined problems before displaying
53-
const problems = Object.values(allData).filter(p =>
54-
p &&
55-
typeof p.title === 'string' && p.title !== 'Unknown Title' &&
56-
typeof p.slug === 'string' && p.slug !== 'unknown-problem' &&
57-
typeof p.difficulty === 'string' && p.difficulty !== 'Unknown Difficulty' &&
58-
p.status === "Solved"
59-
);
60-
// Sort by solvedAt descending (most recent first)
61-
problems.sort((a, b) => (b.solvedAt || 0) - (a.solvedAt || 0));
62-
63-
// Collect all unique tags (flattened)
64-
const tagSet = new Set();
65-
problems.forEach(p => {
66-
if (Array.isArray(p.tags)) {
67-
p.tags.forEach(tag => tagSet.add(tag));
68-
}
69-
});
70-
const tags = Array.from(tagSet);
71-
72-
// Populate tag filter dropdown
73-
const tagFilter = document.getElementById("tagFilter");
74-
if (tagFilter) {
75-
// Remove old options except 'All'
76-
tagFilter.innerHTML = '<option value="all">All</option>';
77-
if (tags.length > 0) {
78-
tags.forEach(tag => {
79-
const opt = document.createElement("option");
80-
opt.value = tag;
81-
opt.textContent = tag;
82-
tagFilter.appendChild(opt);
83-
});
84-
} else {
85-
const opt = document.createElement("option");
86-
opt.value = "none";
87-
opt.textContent = "No tags";
88-
tagFilter.appendChild(opt);
89-
}
90-
}
64+
browser.storage.local.get(null).then((allData) => {
65+
// Filter out invalid/undefined problems before displaying
66+
const problems = Object.values(allData).filter(
67+
(p) =>
68+
p &&
69+
typeof p.title === "string" &&
70+
p.title !== "Unknown Title" &&
71+
typeof p.slug === "string" &&
72+
p.slug !== "unknown-problem" &&
73+
typeof p.difficulty === "string" &&
74+
p.difficulty !== "Unknown Difficulty" &&
75+
p.status === "Solved"
76+
);
9177

92-
// Render problems (filtered and limited)
93-
function renderProblems() {
94-
const list = document.getElementById("solvedList");
95-
list.innerHTML = "";
96-
let filtered = problems;
97-
const selectedTag = tagFilter ? tagFilter.value : "all";
98-
if (selectedTag && selectedTag !== "all" && selectedTag !== "none") {
99-
filtered = problems.filter(p => Array.isArray(p.tags) && p.tags.includes(selectedTag));
100-
}
101-
const toShow = filtered.slice(0, 5);
102-
if (toShow.length === 0) {
103-
list.innerHTML = "<p>No solved problems yet</p>";
104-
}
105-
toShow.forEach(problem => {
106-
const item = document.createElement("div");
107-
item.className = "problem-item";
108-
const difficultyClass = problem.difficulty ? problem.difficulty.toLowerCase() : "";
109-
// Use DOM methods instead of innerHTML for safety
110-
const link = document.createElement('a');
111-
link.href = problem.url;
112-
link.target = '_blank';
113-
link.textContent = problem.title;
114-
115-
const diffSpan = document.createElement('span');
116-
diffSpan.className = `difficulty ${difficultyClass}`;
117-
diffSpan.textContent = problem.difficulty;
118-
119-
const tagsSpan = document.createElement('span');
120-
tagsSpan.style.fontSize = '0.85em';
121-
tagsSpan.style.color = problem.tags && problem.tags.length > 0 ? '#666' : '#bbb';
122-
tagsSpan.style.marginLeft = '8px';
123-
tagsSpan.textContent = problem.tags && problem.tags.length > 0
124-
? `[${problem.tags.join(', ')}]`
125-
: '[No tags]';
126-
127-
item.appendChild(link);
128-
item.appendChild(diffSpan);
129-
item.appendChild(tagsSpan);
130-
list.appendChild(item);
131-
});
132-
}
78+
problems.sort((a, b) => (b.solvedAt || 0) - (a.solvedAt || 0));
13379

134-
if (tagFilter) {
135-
tagFilter.addEventListener("change", renderProblems);
136-
}
137-
renderProblems();
80+
const tagSet = new Set();
81+
problems.forEach((p) => {
82+
if (Array.isArray(p.tags)) {
83+
p.tags.forEach((tag) => tagSet.add(tag));
84+
}
13885
});
86+
const tags = Array.from(tagSet);
13987

140-
// View All link opens options page
141-
const viewAllLink = document.getElementById("viewAllLink");
142-
if (viewAllLink) {
143-
viewAllLink.addEventListener("click", (e) => {
144-
e.preventDefault();
145-
if (browser.runtime && browser.runtime.openOptionsPage) {
146-
browser.runtime.openOptionsPage();
147-
} else {
148-
window.open("../options/options.html", "_blank");
149-
}
88+
// Populate tag filter dropdown
89+
const tagFilter = document.getElementById("tagFilter");
90+
if (tagFilter) {
91+
// Remove old options except 'All'
92+
tagFilter.innerHTML = '<option value="all">All</option>';
93+
if (tags.length > 0) {
94+
tags.forEach((tag) => {
95+
const opt = document.createElement("option");
96+
opt.value = tag;
97+
opt.textContent = tag;
98+
tagFilter.appendChild(opt);
15099
});
100+
} else {
101+
const opt = document.createElement("option");
102+
opt.value = "none";
103+
opt.textContent = "No tags";
104+
tagFilter.appendChild(opt);
105+
}
106+
}
107+
108+
function renderProblems() {
109+
const list = document.getElementById("solvedList");
110+
list.innerHTML = "";
111+
let filtered = problems;
112+
const selectedTag = tagFilter ? tagFilter.value : "all";
113+
if (selectedTag && selectedTag !== "all" && selectedTag !== "none") {
114+
filtered = problems.filter(
115+
(p) => Array.isArray(p.tags) && p.tags.includes(selectedTag)
116+
);
117+
}
118+
const toShow = filtered.slice(0, 5);
119+
if (toShow.length === 0) {
120+
list.innerHTML = "<p>No solved problems yet</p>";
121+
}
122+
toShow.forEach((problem) => {
123+
const item = document.createElement("div");
124+
item.className = "problem-item";
125+
const difficultyClass = problem.difficulty
126+
? problem.difficulty.toLowerCase()
127+
: "";
128+
129+
const link = document.createElement("a");
130+
link.href = problem.url;
131+
link.target = "_blank";
132+
link.textContent = problem.title;
133+
134+
const diffSpan = document.createElement("span");
135+
diffSpan.className = `difficulty ${difficultyClass}`;
136+
diffSpan.textContent = problem.difficulty;
137+
138+
const tagsSpan = document.createElement("span");
139+
tagsSpan.style.fontSize = "0.85em";
140+
tagsSpan.style.color =
141+
problem.tags && problem.tags.length > 0 ? "#666" : "#bbb";
142+
tagsSpan.style.marginLeft = "8px";
143+
tagsSpan.textContent =
144+
problem.tags && problem.tags.length > 0
145+
? `[${problem.tags.join(", ")}]`
146+
: "[No tags]";
147+
148+
item.appendChild(link);
149+
item.appendChild(diffSpan);
150+
item.appendChild(tagsSpan);
151+
list.appendChild(item);
152+
});
151153
}
154+
155+
if (tagFilter) {
156+
tagFilter.addEventListener("change", renderProblems);
157+
}
158+
renderProblems();
159+
});
160+
161+
const viewAllLink = document.getElementById("viewAllLink");
162+
if (viewAllLink) {
163+
viewAllLink.addEventListener("click", (e) => {
164+
e.preventDefault();
165+
if (browser.runtime?.openOptionsPage) {
166+
browser.runtime.openOptionsPage();
167+
} else {
168+
window.open("../options/options.html", "_blank");
169+
}
170+
});
171+
}
152172
});
153173

154174
document.addEventListener("DOMContentLoaded", () => {
155-
const optionsBtn = document.getElementById("optionsBtn");
156-
if (optionsBtn) {
157-
optionsBtn.addEventListener("click", () => {
158-
if (browser.runtime && browser.runtime.openOptionsPage) {
159-
browser.runtime.openOptionsPage();
160-
} else {
161-
// fallback for browsers that don't support openOptionsPage
162-
window.open("../options/options.html", "_blank");
163-
}
164-
});
165-
}
175+
const optionsBtn = document.getElementById("optionsBtn");
176+
if (optionsBtn) {
177+
optionsBtn.addEventListener("click", () => {
178+
if (browser.runtime?.openOptionsPage) {
179+
browser.runtime.openOptionsPage();
180+
} else {
181+
window.open("../options/options.html", "_blank");
182+
}
183+
});
184+
}
166185
});

0 commit comments

Comments
 (0)