Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 33 additions & 27 deletions extension/background.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,45 @@
const API_BASE_URL = "https://leetcodeai-backend.onrender.com";
//const API_BASE_URL = "http://localhost:10000";
function getUserEmail() {
return new Promise(resolve => {
chrome.storage.local.get({ userEmail: null }, ({ userEmail }) => resolve(userEmail));
});

async function parseApiResponse(response) {
const data = await response.json().catch(() => ({}));
if (!response.ok) {
throw new Error(data.detail || data.message || `Request failed with status ${response.status}`);
}
return data;
}

function getUserEmail() {
return new Promise(resolve => {
chrome.storage.local.get({ userEmail: null }, ({ userEmail }) => resolve(userEmail));
});
function authHeaders(userEmail, sessionToken) {
return {
"Content-Type": "application/json",
"X-User-Email": userEmail,
"Authorization": `Bearer ${sessionToken}`
};
}

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.type === 'GENERATE_BLOG') {
const { title, description, code, author, client_time, custom_prompt, difficulty, topics } = request.payload;
const { title, description, code, author, client_time, custom_prompt, difficulty, language, topics } = request.payload;
chrome.storage.local.get({
publishingPlatforms: ['devto'],
publishAsDraft: false,
userEmail: null,
}, async ({ publishingPlatforms, publishAsDraft, userEmail }) => {
if (!userEmail) {
chrome.runtime.sendMessage({ type: 'STATUS_UPDATE', message: 'Please set your email in the extension settings before publishing.', status: 'error' });
sessionToken: null,
}, async ({ publishingPlatforms, publishAsDraft, userEmail, sessionToken }) => {
if (!userEmail || !sessionToken) {
chrome.runtime.sendMessage({ type: 'STATUS_UPDATE', message: 'Please log in before publishing.', status: 'error' });
return;
}
fetch(`${API_BASE_URL}/generate-blog`, {
method: "POST",
headers: { "Content-Type": "application/json", "X-User-Email": userEmail },
headers: authHeaders(userEmail, sessionToken),
body: JSON.stringify({
title, description, code, author, client_time, custom_prompt, difficulty,
title, description, code, author, client_time, custom_prompt, difficulty, language,
tags: (topics && topics.length > 0) ? topics : null,
platforms: publishingPlatforms,
publish_as_draft: publishAsDraft
})
})
.then(r => r.json())
.then(parseApiResponse)
.then(data => {
if (data.status === 'success' || data.status === 'partial_success') {
const generatedBlog =
Expand All @@ -55,15 +61,14 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
const platforms = data.data?.platforms || [];
const postedPlatforms = platforms
.filter(result => result.status === 'success')
.map(result => result.platform)
.join(', ');
.map(result => result.platform);
const devtoResult = platforms.find(r => r.platform === 'devto' && r.status === 'success');
chrome.storage.local.get({ publishHistory: [] }, (res) => {
const entry = {
title: title,
url: devtoResult?.url || null,
publishedAt: client_time || new Date().toISOString(),
platforms: postedPlatforms ? postedPlatforms.split(', ').filter(p => p) : []
platforms: postedPlatforms
};
const history = res.publishHistory;
history.unshift(entry);
Expand All @@ -90,7 +95,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {

fetch(`${API_BASE_URL}/dashboard/record`, {
method: "POST",
headers: { "Content-Type": "application/json", "X-User-Email": userEmail },
headers: authHeaders(userEmail, sessionToken),
body: JSON.stringify(entry)
}).catch(() => { });
chrome.runtime.sendMessage({
Expand Down Expand Up @@ -127,17 +132,18 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
publishingPlatforms: ['devto'],
publishAsDraft: false,
userEmail: null,
sessionToken: null,
generatedProblemTitle: 'leetcode-blog'
}, async ({ publishingPlatforms, publishAsDraft, userEmail, generatedProblemTitle }) => {
if (!userEmail) {
const errMsg = 'Please set your email in the extension settings before publishing.';
}, async ({ publishingPlatforms, publishAsDraft, userEmail, sessionToken, generatedProblemTitle }) => {
if (!userEmail || !sessionToken) {
const errMsg = 'Please log in before publishing.';
chrome.runtime.sendMessage({ type: 'STATUS_UPDATE', message: errMsg, status: 'error' });
sendResponse({ success: false, error: errMsg });
return;
}
fetch(`${API_BASE_URL}/publish-blog`, {
method: "POST",
headers: { "Content-Type": "application/json", "X-User-Email": userEmail },
headers: authHeaders(userEmail, sessionToken),
body: JSON.stringify({
title: generatedProblemTitle,
content: blog,
Expand All @@ -146,7 +152,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
author: "Anonymous Developer"
})
})
.then(r => r.json())
.then(parseApiResponse)
.then(data => {
if (data.status === 'success' || data.status === 'partial_success') {
const platforms = data.data?.platforms || [];
Expand All @@ -173,7 +179,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {

fetch(`${API_BASE_URL}/dashboard/record`, {
method: "POST",
headers: { "Content-Type": "application/json", "X-User-Email": userEmail },
headers: authHeaders(userEmail, sessionToken),
body: JSON.stringify(entry)
}).catch(() => { });

Expand Down Expand Up @@ -208,4 +214,4 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
});
return true;
}
});
});
6 changes: 6 additions & 0 deletions extension/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@
document.querySelector('.ant-select-selection-item');
const language = langElement ? langElement.innerText.trim().toLowerCase() : "unknown";

const difficultyElement = document.querySelector('[diff]') ||
document.querySelector('div[class*="text-difficulty-"]') ||
document.querySelector('span[class*="text-difficulty-"]');
const difficulty = difficultyElement
? (difficultyElement.getAttribute('diff') || difficultyElement.innerText || "").trim()
: null;
// Extract topic tags (e.g. Array, Dynamic Programming, Hash Table)
const tagElements = document.querySelectorAll('a[href*="/tag/"]');
const topics = Array.from(tagElements)
Expand Down
42 changes: 22 additions & 20 deletions extension/dashboard.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//const API_BASE_URL = "https://leetcodeai-backend.onrender.com";
const API_BASE_URL = "http://localhost:10000";
const API_BASE_URL = "https://leetcodeai-backend.onrender.com";

//fixes timezone for IST and all non-UTC users
function getLocalDateStr(date) {
Expand All @@ -10,11 +9,11 @@ function getLocalDateStr(date) {
return `${year}-${month}-${day}`;
}

const colors = { devto:'#3b49df', hashnode:'#2962ff', medium:'#00ab6c', webhook:'#f7a01a' };
const colors = { devto: '#3b49df', hashnode: '#2962ff', medium: '#00ab6c', webhook: '#f7a01a' };

function escapeHTML(str) {
if (!str) return '';
return String(str).replace(/[&<>'"]/g,
return String(str).replace(/[&<>'"]/g,
tag => ({
'&': '&amp;',
'<': '&lt;',
Expand All @@ -26,13 +25,13 @@ function escapeHTML(str) {
}

document.addEventListener('DOMContentLoaded', () => {
chrome.storage.local.get({ userEmail: null }, ({ userEmail }) => {
if (!userEmail) {
chrome.storage.local.get({ userEmail: null, sessionToken: null }, ({ userEmail, sessionToken }) => {
if (!userEmail || !sessionToken) {
showBanner('No email set — open the extension and enter your email first.');
return;
}
setLoading(true);
fetchStatsFromBackend(userEmail)
fetchStatsFromBackend(userEmail, sessionToken)
.catch(() => {
showBanner("Couldn't reach backend — showing local data.");
return loadFromLocalStorage(userEmail);
Expand All @@ -41,9 +40,12 @@ document.addEventListener('DOMContentLoaded', () => {
});
});

async function fetchStatsFromBackend(email) {
async function fetchStatsFromBackend(email, sessionToken) {
const res = await fetch(`${API_BASE_URL}/dashboard/stats`, {
headers: { 'X-User-Email': email }
headers: {
'X-User-Email': email,
'Authorization': `Bearer ${sessionToken}`
}
});
if (!res.ok) throw new Error('Bad response');
const { total_posts, platform_counts, week_activity, recent, current_streak } = await res.json();
Expand All @@ -60,10 +62,10 @@ async function fetchStatsFromBackend(email) {
if (Array.isArray(platform_counts)) {
platform_counts.forEach(item => countsMap[item.name] = item.value);
} else {
countsMap = platform_counts || {};
countsMap = platform_counts || {};
}
renderPlatformBarsFromMap(countsMap);

renderHistory(recent);
}

Expand Down Expand Up @@ -95,14 +97,14 @@ function calculateStreakFromWeekMap(weekMap) {
d.setDate(d.getDate() - i);
const key = getLocalDateStr(d);
if (weekMap[key]) streak++;
else if (i > 0) break;
else if (i > 0) break;
}
return streak;
}

// --- Render from backend shapes ---
function renderWeekGridFromMap(weekMap) {
const days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
const grid = document.getElementById('weekGrid');
grid.innerHTML = Array.from({ length: 7 }, (_, i) => {
const d = new Date();
Expand All @@ -128,7 +130,7 @@ function renderPlatformBarsFromMap(counts) {
<div class="platform-bar">
<span class="platform-name">${escapeHTML(name)}</span>
<div class="bar-track">
<div class="bar-fill" style="width:${(count/max)*100}%;background:${colors[name]||'#f7a01a'}"></div>
<div class="bar-fill" style="width:${(count / max) * 100}%;background:${colors[name] || '#f7a01a'}"></div>
</div>
<span class="bar-count">${count}</span>
</div>`).join('');
Expand All @@ -151,8 +153,8 @@ function calculateStreak(history) {
const uniqueDates = [...new Set(history.map(h => getDateStr(h.date)))].sort().reverse();
let streak = 0;
for (const dateStr of uniqueDates) {
const diff = Math.floor((new Date().setHours(0,0,0,0) - new Date(dateStr)) / 86400000);
if (diff === streak) streak++;
const diff = Math.floor((new Date().setHours(0, 0, 0, 0) - new Date(dateStr)) / 86400000);
if (diff === streak) streak++;
else break;
}
return streak;
Expand All @@ -164,7 +166,7 @@ function countThisWeek(history) {
}

function renderWeekGrid(history) {
const days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
const grid = document.getElementById('weekGrid');
grid.innerHTML = Array.from({ length: 7 }, (_, i) => {
const d = new Date(); d.setDate(d.getDate() - (6 - i));
Expand All @@ -190,13 +192,13 @@ function renderHistory(history) {
return;
}
container.innerHTML = history.slice(0, 10).map(h => {
const dateStr = new Date(h.date).toLocaleDateString('en-US', { month:'short', day:'numeric', year:'numeric' });
const dateStr = new Date(h.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
return `<div class="history-item">
<div>
<div class="history-title">${escapeHTML(h.title || 'Unknown Problem')}</div>
<div class="history-platforms"> ${escapeHTML((h.platforms||[]).join(', ') || 'unknown')}</div>
<div class="history-platforms"> ${escapeHTML((h.platforms || []).join(', ') || 'unknown')}</div>
</div>
<div class="history-date">${dateStr}</div>
</div>`;
}).join('');
}
}
Loading
Loading