Separated Opportunitues from Applied using headers, Added Date Posted to jobs.

This commit is contained in:
2026-07-07 20:58:42 +00:00
parent dcd00af6bb
commit 8155b121da
5 changed files with 410 additions and 173 deletions

View File

@@ -324,6 +324,41 @@ async function notifyNewJob(job) {
await sendNtfyNotification(job, companyName); await sendNtfyNotification(job, companyName);
} }
// Opens a job's detail page briefly just to read its "Posted X ago" text,
// then closes the tab. Only called for genuinely new jobs, not on every
// 5-minute check, to avoid needlessly opening tabs for jobs we already know about.
async function fetchPostedDate(url) {
if (!url) return null;
let tab;
try {
tab = await chrome.tabs.create({ url: url, active: false });
await new Promise((resolve) => setTimeout(resolve, 3500));
const postedDate = await new Promise((resolve) => {
chrome.tabs.sendMessage(
tab.id,
{ action: 'extractPostedDate' },
(response) => {
if (chrome.runtime.lastError) {
log(
'Error extracting posted date:',
chrome.runtime.lastError.message,
);
resolve(null);
} else {
resolve((response && response.postedDate) || null);
}
},
);
});
return postedDate;
} catch (err) {
log('Error in fetchPostedDate:', err.message);
return null;
} finally {
if (tab) chrome.tabs.remove(tab.id).catch(() => {});
}
}
let keepAliveInterval = null; let keepAliveInterval = null;
function startKeepAlive() { function startKeepAlive() {
@@ -354,24 +389,61 @@ async function checkForNewJobs() {
const allJobs = await fetchJobsViaContentScript(); const allJobs = await fetchJobsViaContentScript();
const sortedJobs = sortJobsByRecency(allJobs); const sortedJobs = sortJobsByRecency(allJobs);
const result = await getStoredData(['notifiedJobIds']); const result = await getStoredData([
'notifiedJobIds',
'postedDates',
'backfillDone',
]);
const notifiedJobIds = new Set(result.notifiedJobIds || []); const notifiedJobIds = new Set(result.notifiedJobIds || []);
const postedDates = result.postedDates || {};
const backfillDone = result.backfillDone || false;
log('Previously notified jobs: ' + notifiedJobIds.size); sortedJobs.forEach((job) => {
if (postedDates[job.id]) job.posted_ago = postedDates[job.id];
});
const newJobs = sortedJobs.filter((job) => !notifiedJobIds.has(job.id)); if (!backfillDone) {
const toBackfill = sortedJobs.filter(
(job) => job.kind === 'opportunity' && !postedDates[job.id],
);
log(
'Backfilling posted dates for ' +
toBackfill.length +
' existing jobs...',
);
for (const job of toBackfill) {
const postedDate = await fetchPostedDate(job.url);
if (postedDate) {
job.posted_ago = postedDate;
postedDates[job.id] = postedDate;
}
}
}
const newJobs = sortedJobs.filter(
(job) => job.kind === 'opportunity' && !notifiedJobIds.has(job.id),
);
log('Found ' + newJobs.length + ' new jobs'); log('Found ' + newJobs.length + ' new jobs');
for (const job of newJobs) { for (const job of newJobs) {
const postedDate = await fetchPostedDate(job.url);
if (postedDate) {
job.posted_ago = postedDate;
postedDates[job.id] = postedDate;
}
await notifyNewJob(job); await notifyNewJob(job);
notifiedJobIds.add(job.id); notifiedJobIds.add(job.id);
} }
await setStoredData({ await setStoredData({
notifiedJobIds: Array.from(notifiedJobIds), notifiedJobIds: Array.from(notifiedJobIds),
postedDates: postedDates,
backfillDone: true,
lastAndelaJobs: sortedJobs, lastAndelaJobs: sortedJobs,
lastAndelaUpdated: new Date().toISOString(), lastAndelaUpdated: new Date().toISOString(),
totalAndelaMatching: sortedJobs.length, totalAndelaMatching: sortedJobs.filter((j) => j.kind === 'opportunity')
.length,
}); });
log('Notifications stored successfully'); log('Notifications stored successfully');

View File

@@ -8,7 +8,12 @@ function isLoginPage() {
var hasLoginForm = !!(emailInput && emailLabel); var hasLoginForm = !!(emailInput && emailLabel);
var isLoginUrl = window.location.pathname.indexOf('/login') === 0; var isLoginUrl = window.location.pathname.indexOf('/login') === 0;
console.log('[Andela Content Script] Login check - hasForm:', hasLoginForm, 'isLoginUrl:', isLoginUrl); console.log(
'[Andela Content Script] Login check - hasForm:',
hasLoginForm,
'isLoginUrl:',
isLoginUrl,
);
return hasLoginForm || (isLoginUrl && !!emailInput); return hasLoginForm || (isLoginUrl && !!emailInput);
} }
@@ -28,19 +33,23 @@ function enterEmailAndClickLogin(email) {
emailInput.dispatchEvent(new Event('change', { bubbles: true })); emailInput.dispatchEvent(new Event('change', { bubbles: true }));
emailInput.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true })); emailInput.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true }));
console.log('[Andela Content Script] Email entered, looking for submit button...'); console.log(
'[Andela Content Script] Email entered, looking for submit button...',
);
setTimeout(function() { setTimeout(function () {
var submitButton = document.querySelector('button[type="submit"]'); var submitButton = document.querySelector('button[type="submit"]');
if (!submitButton) { if (!submitButton) {
var buttons = document.querySelectorAll('button'); var buttons = document.querySelectorAll('button');
for (var i = 0; i < buttons.length; i++) { for (var i = 0; i < buttons.length; i++) {
var btnText = buttons[i].textContent.toLowerCase(); var btnText = buttons[i].textContent.toLowerCase();
if (btnText.indexOf('login') !== -1 || if (
btnText.indexOf('sign in') !== -1 || btnText.indexOf('login') !== -1 ||
btnText.indexOf('continue') !== -1 || btnText.indexOf('sign in') !== -1 ||
btnText.indexOf('submit') !== -1) { btnText.indexOf('continue') !== -1 ||
btnText.indexOf('submit') !== -1
) {
submitButton = buttons[i]; submitButton = buttons[i];
break; break;
} }
@@ -48,10 +57,15 @@ function enterEmailAndClickLogin(email) {
} }
if (submitButton) { if (submitButton) {
console.log('[Andela Content Script] Clicking submit button:', submitButton.textContent); console.log(
'[Andela Content Script] Clicking submit button:',
submitButton.textContent,
);
submitButton.click(); submitButton.click();
} else { } else {
console.log('[Andela Content Script] Submit button not found, trying form submit'); console.log(
'[Andela Content Script] Submit button not found, trying form submit',
);
var form = emailInput.closest('form'); var form = emailInput.closest('form');
if (form) { if (form) {
if (form.requestSubmit) form.requestSubmit(); if (form.requestSubmit) form.requestSubmit();
@@ -67,17 +81,61 @@ function enterEmailAndClickLogin(email) {
} }
} }
// A section counts as "opportunities" if its heading contains this word —
// matches "Open Full-Stack Engineer opportunities", "Open AI Engineer
// opportunities", etc. Anything else (e.g. "Your applications") is
// treated as already-applied.
function isOpportunitySection(headingText) {
return /opportunit/i.test(headingText || '');
}
// Walks the page in DOM order and tags every "See more" button and every
// job-posting link with the heading text of the section it falls under.
function getPageSections() {
var elements = document.querySelectorAll(
'h3, button, a[href*="/job-postings/"]',
);
var currentHeading = '';
var seeMoreButtons = []; // { button, heading }
var jobLinks = []; // { link, heading }
elements.forEach(function (el) {
if (el.tagName === 'H3') {
currentHeading = el.textContent.trim();
console.log(
'[Andela Content Script] Section heading found:',
currentHeading,
);
return;
}
if (el.tagName === 'BUTTON') {
var text = (el.textContent || '').trim().toLowerCase();
if (text.indexOf('see') === 0 && text.indexOf('more') !== -1) {
seeMoreButtons.push({ button: el, heading: currentHeading });
}
return;
}
if (el.tagName === 'A') {
jobLinks.push({ link: el, heading: currentHeading });
}
});
return { seeMoreButtons: seeMoreButtons, jobLinks: jobLinks };
}
// Only expands "See more" under opportunity sections — leaves the
// applied-jobs section collapsed as-is.
function expandAllSeeMoreButtons(maxRounds) { function expandAllSeeMoreButtons(maxRounds) {
maxRounds = maxRounds || 25; maxRounds = maxRounds || 25;
return new Promise(function(resolve) { return new Promise(function (resolve) {
var rounds = 0; var rounds = 0;
function findSeeMoreButton() { function findSeeMoreButton() {
var buttons = document.querySelectorAll('button'); var sections = getPageSections();
for (var i = 0; i < buttons.length; i++) { for (var i = 0; i < sections.seeMoreButtons.length; i++) {
var text = (buttons[i].textContent || '').trim().toLowerCase(); var entry = sections.seeMoreButtons[i];
if (text.indexOf('see') === 0 && text.indexOf('more') !== -1) { if (isOpportunitySection(entry.heading)) {
return buttons[i]; return entry.button;
} }
} }
return null; return null;
@@ -87,7 +145,11 @@ function expandAllSeeMoreButtons(maxRounds) {
rounds++; rounds++;
var btn = findSeeMoreButton(); var btn = findSeeMoreButton();
if (!btn || rounds > maxRounds) { if (!btn || rounds > maxRounds) {
console.log('[Andela Content Script] Done expanding "See more" buttons after', rounds - 1, 'click(s)'); console.log(
'[Andela Content Script] Done expanding opportunity "See more" buttons after',
rounds - 1,
'click(s)',
);
resolve(); resolve();
return; return;
} }
@@ -105,11 +167,15 @@ function extractJobFromLink(link) {
var idMatch = href.match(/\/job-postings\/(\d+)/); var idMatch = href.match(/\/job-postings\/(\d+)/);
var id = idMatch ? idMatch[1] : href; var id = idMatch ? idMatch[1] : href;
var card = link.closest('div.rounded-lg.border.border-neutral-200.bg-white') || link.parentElement; var card =
link.closest('div.rounded-lg.border.border-neutral-200.bg-white') ||
link.parentElement;
var title = ''; var title = '';
var company = ''; var company = '';
var titleEl = card ? card.querySelector('.text-primary-900.font-semibold') : null; var titleEl = card
? card.querySelector('.text-primary-900.font-semibold')
: null;
if (titleEl) { if (titleEl) {
var clone = titleEl.cloneNode(true); var clone = titleEl.cloneNode(true);
@@ -125,8 +191,12 @@ function extractJobFromLink(link) {
var startInfo = paragraphs[0] ? paragraphs[0].textContent.trim() : ''; var startInfo = paragraphs[0] ? paragraphs[0].textContent.trim() : '';
var locationInfo = paragraphs[1] ? paragraphs[1].textContent.trim() : ''; var locationInfo = paragraphs[1] ? paragraphs[1].textContent.trim() : '';
var skillEls = card ? card.querySelectorAll('.flex.flex-wrap.gap-2 span') : []; var skillEls = card
var skills = Array.prototype.map.call(skillEls, function(s) { return s.textContent.trim(); }); ? card.querySelectorAll('.flex.flex-wrap.gap-2 span')
: [];
var skills = Array.prototype.map.call(skillEls, function (s) {
return s.textContent.trim();
});
var url = href; var url = href;
if (url && url.indexOf('http') !== 0) { if (url && url.indexOf('http') !== 0) {
@@ -140,7 +210,7 @@ function extractJobFromLink(link) {
posted_date: startInfo || '', posted_date: startInfo || '',
location: locationInfo || '', location: locationInfo || '',
skills: skills, skills: skills,
url: url url: url,
}; };
} }
@@ -148,33 +218,50 @@ function extractJobsFromPage() {
console.log('[Andela Content Script] Extracting jobs...'); console.log('[Andela Content Script] Extracting jobs...');
var jobs = []; var jobs = [];
var seen = {}; var seen = {};
var links = document.querySelectorAll('a[href*="/job-postings/"]'); var jobLinks = getPageSections().jobLinks;
console.log('[Andela Content Script] Found ' + links.length + ' job-posting links'); console.log(
'[Andela Content Script] Found ' + jobLinks.length + ' job-posting links',
);
links.forEach(function(link) { jobLinks.forEach(function (entry) {
try { try {
var job = extractJobFromLink(link); var job = extractJobFromLink(entry.link);
if (!job.id || seen[job.id]) return; if (!job.id || seen[job.id]) return;
seen[job.id] = true; seen[job.id] = true;
job.section = entry.heading;
job.kind = isOpportunitySection(entry.heading)
? 'opportunity'
: 'applied';
if (job.title) { if (job.title) {
jobs.push(job); jobs.push(job);
console.log('[Andela Content Script] Extracted job:', job.title, '(' + job.id + ')'); console.log(
'[Andela Content Script] Extracted job:',
job.title,
'(' + job.id + ')',
'-',
job.kind,
);
} }
} catch (err) { } catch (err) {
console.error('[Andela Content Script] Error extracting a job card:', err); console.error(
'[Andela Content Script] Error extracting a job card:',
err,
);
} }
}); });
console.log('[Andela Content Script] Extracted ' + jobs.length + ' jobs total'); console.log(
'[Andela Content Script] Extracted ' + jobs.length + ' jobs total',
);
return jobs; return jobs;
} }
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
console.log('[Andela Content Script] Received message:', request); console.log('[Andela Content Script] Received message:', request);
if (request.action === 'extractJobs') { if (request.action === 'extractJobs') {
expandAllSeeMoreButtons().then(function() { expandAllSeeMoreButtons().then(function () {
setTimeout(function() { setTimeout(function () {
try { try {
var jobs = extractJobsFromPage(); var jobs = extractJobsFromPage();
sendResponse({ jobs: jobs }); sendResponse({ jobs: jobs });
@@ -198,5 +285,15 @@ chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
sendResponse({ success: success }); sendResponse({ success: success });
} }
if (request.action === 'extractPostedDate') {
var el = document.querySelector('span.text-success-500');
var postedText = el ? el.textContent.trim() : '';
console.log(
'[Andela Content Script] Extracted posted date text:',
postedText,
);
sendResponse({ postedDate: postedText });
}
return true; return true;
}); });

View File

@@ -1,7 +1,7 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "Andela Job Notifier", "name": "Andela Job Notifier",
"version": "2.0", "version": "2.1",
"description": "Checks for new job opportunities on Andela with auto-login", "description": "Checks for new job opportunities on Andela with auto-login",
"permissions": [ "permissions": [
"notifications", "notifications",
@@ -40,8 +40,10 @@
"48": "icon48.png", "48": "icon48.png",
"128": "icon128.png" "128": "icon128.png"
}, },
"web_accessible_resources": [{ "web_accessible_resources": [
"resources": ["notify.wav"], {
"matches": ["<all_urls>"] "resources": ["notify.wav"],
}] "matches": ["<all_urls>"]
}
]
} }

View File

@@ -1,104 +1,117 @@
<!DOCTYPE html> <!doctype html>
<html> <html>
<head> <head>
<title>Andela Job Notifier</title> <title>Andela Job Notifier</title>
<style> <style>
body { body {
width: 350px; width: 350px;
padding: 15px; padding: 15px;
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
} }
h2 { h2 {
color: #333; color: #333;
margin-bottom: 15px; margin-bottom: 15px;
} }
.job-list { .job-list {
margin-top: 15px; margin-top: 15px;
} }
.job-item { .section-heading {
padding: 10px; font-size: 12px;
margin-bottom: 10px; font-weight: bold;
border-bottom: 1px solid #eee; text-transform: uppercase;
cursor: pointer; letter-spacing: 0.03em;
transition: background-color 0.2s; margin: 12px 0 6px;
} }
.job-item:hover { .section-heading-opportunities {
background-color: #f5f5f5; color: #111111;
} }
.job-title { .section-heading-applied {
font-weight: bold; color: #c0392b;
color: #333; }
margin-bottom: 5px; .job-item {
} padding: 10px;
.job-company { margin-bottom: 10px;
color: #666; border-bottom: 1px solid #eee;
font-size: 14px; cursor: pointer;
} transition: background-color 0.2s;
.job-location { }
color: #888; .job-item:hover {
font-size: 12px; background-color: #f5f5f5;
margin-top: 3px; }
} .job-title {
.job-posted { font-weight: bold;
color: #0066cc; color: #333;
font-size: 11px; margin-bottom: 5px;
margin-top: 5px; }
font-weight: 500; .job-company {
} color: #666;
.status { font-size: 14px;
color: #666; }
font-size: 12px; .job-location {
margin-top: 15px; color: #888;
padding-top: 10px; font-size: 12px;
border-top: 1px solid #eee; margin-top: 3px;
} }
.job-count { .job-posted {
font-size: 14px; color: #0066cc;
color: #333; font-size: 11px;
margin-bottom: 10px; margin-top: 5px;
} font-weight: 500;
.no-jobs { }
color: #999; .status {
text-align: center; color: #666;
padding: 20px; font-size: 12px;
} margin-top: 15px;
.button-row { padding-top: 10px;
display: flex; border-top: 1px solid #eee;
gap: 10px; }
margin: 10px 0; .job-count {
} font-size: 14px;
.btn { color: #333;
padding: 8px 16px; margin-bottom: 10px;
border: none; }
border-radius: 4px; .no-jobs {
cursor: pointer; color: #999;
font-size: 14px; text-align: center;
} padding: 20px;
.btn-primary { }
background: #0066cc; .button-row {
color: white; display: flex;
} gap: 10px;
.btn-primary:hover { margin: 10px 0;
background: #0055aa; }
} .btn {
.btn-secondary { padding: 8px 16px;
background: #f0f0f0; border: none;
color: #333; border-radius: 4px;
} cursor: pointer;
.btn-secondary:hover { font-size: 14px;
background: #e0e0e0; }
} .btn-primary {
</style> background: #0066cc;
</head> color: white;
<body> }
<h2>Andela Job Notifier</h2> .btn-primary:hover {
<div id="jobCount" class="job-count"></div> background: #0055aa;
<div class="button-row"> }
<button id="checkNow" class="btn btn-primary">Check Now</button> .btn-secondary {
<button id="openSettings" class="btn btn-secondary">Settings</button> background: #f0f0f0;
</div> color: #333;
<div id="jobList" class="job-list"></div> }
<div id="status" class="status"></div> .btn-secondary:hover {
<script src="popup.js"></script> background: #e0e0e0;
</body> }
</style>
</head>
<body>
<h2>Andela Job Notifier</h2>
<div id="jobCount" class="job-count"></div>
<div class="button-row">
<button id="checkNow" class="btn btn-primary">Check Now</button>
<button id="openSettings" class="btn btn-secondary">Settings</button>
</div>
<div id="jobList" class="job-list"></div>
<div id="status" class="status"></div>
<script src="popup.js"></script>
</body>
</html> </html>

View File

@@ -1,7 +1,7 @@
// popup.js // popup.js
function sortJobsById(jobs) { function sortJobsById(jobs) {
return jobs.slice().sort(function(a, b) { return jobs.slice().sort(function (a, b) {
return (parseInt(b.id, 10) || 0) - (parseInt(a.id, 10) || 0); return (parseInt(b.id, 10) || 0) - (parseInt(a.id, 10) || 0);
}); });
} }
@@ -11,66 +11,119 @@ function updateUI(data) {
var lastAndelaUpdated = data.lastAndelaUpdated; var lastAndelaUpdated = data.lastAndelaUpdated;
var totalAndelaMatching = data.totalAndelaMatching || 0; var totalAndelaMatching = data.totalAndelaMatching || 0;
document.getElementById('jobCount').textContent = totalAndelaMatching + ' jobs found'; document.getElementById('jobCount').textContent =
totalAndelaMatching + ' open opportunities';
var jobList = document.getElementById('jobList'); var jobList = document.getElementById('jobList');
jobList.innerHTML = ''; jobList.innerHTML = '';
if (lastAndelaJobs.length === 0) { if (lastAndelaJobs.length === 0) {
jobList.innerHTML = '<div class="no-jobs">No jobs found yet. Click "Check Now" to scan for jobs.</div>'; jobList.innerHTML =
'<div class="no-jobs">No jobs found yet. Click "Check Now" to scan for jobs.</div>';
} else { } else {
var sortedJobs = sortJobsById(lastAndelaJobs); var opportunities = sortJobsById(
lastAndelaJobs.filter(function (j) {
return j.kind === 'opportunity';
}),
);
var applied = sortJobsById(
lastAndelaJobs.filter(function (j) {
return j.kind !== 'opportunity';
}),
);
sortedJobs.slice(0, 10).forEach(function(job) { function renderJob(job) {
var jobElement = document.createElement('div'); var jobElement = document.createElement('div');
jobElement.className = 'job-item'; jobElement.className = 'job-item';
var postedDate = job.posted_date || 'Recently posted'; var startInfo = job.posted_date || '';
var companyName = typeof job.company === 'object' ? (job.company && job.company.name || 'Company') : (job.company || 'Company'); var postedAgo = job.posted_ago || '';
var companyName =
typeof job.company === 'object'
? (job.company && job.company.name) || 'Company'
: job.company || 'Company';
jobElement.innerHTML = jobElement.innerHTML =
'<div class="job-title">' + job.title + '</div>' + '<div class="job-title">' +
'<div class="job-company">' + companyName + '</div>' + job.title +
'<div class="job-location">' + (job.location || 'Remote') + '</div>' + '</div>' +
'<div class="job-posted">' + postedDate + '</div>'; '<div class="job-company">' +
companyName +
'</div>' +
'<div class="job-location">' +
(job.location || 'Remote') +
'</div>' +
'<div class="job-posted">' +
startInfo +
(postedAgo ? ' · ' + postedAgo : '') +
'</div>';
jobElement.addEventListener('click', function() { jobElement.addEventListener('click', function () {
var url = job.url || ('https://app.andela.com/job-postings/' + job.id); var url = job.url || 'https://app.andela.com/job-postings/' + job.id;
chrome.tabs.create({ url: url }); chrome.tabs.create({ url: url });
}); });
jobList.appendChild(jobElement); jobList.appendChild(jobElement);
}); }
if (opportunities.length) {
var oppHeading = document.createElement('div');
oppHeading.className = 'section-heading section-heading-opportunities';
oppHeading.textContent = 'Open Opportunities';
jobList.appendChild(oppHeading);
opportunities.slice(0, 10).forEach(renderJob);
}
if (applied.length) {
var appliedHeading = document.createElement('div');
appliedHeading.className = 'section-heading section-heading-applied';
appliedHeading.textContent = 'Applied';
jobList.appendChild(appliedHeading);
applied.slice(0, 10).forEach(renderJob);
}
} }
var status = document.getElementById('status'); var status = document.getElementById('status');
status.textContent = lastAndelaUpdated ? status.textContent = lastAndelaUpdated
'Last checked: ' + new Date(lastAndelaUpdated).toLocaleString() : ? 'Last checked: ' + new Date(lastAndelaUpdated).toLocaleString()
'Not checked yet'; : 'Not checked yet';
} }
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function () {
document.getElementById('checkNow').addEventListener('click', function() { document.getElementById('checkNow').addEventListener('click', function () {
console.log('Manual check triggered'); console.log('Manual check triggered');
chrome.runtime.sendMessage({ action: 'checkNow' }, function(response) { chrome.runtime.sendMessage({ action: 'checkNow' }, function (response) {
console.log('Check initiated'); console.log('Check initiated');
}); });
}); });
document.getElementById('openSettings').addEventListener('click', function() { document
chrome.tabs.create({ url: 'settings.html' }); .getElementById('openSettings')
}); .addEventListener('click', function () {
chrome.tabs.create({ url: 'settings.html' });
});
chrome.storage.local.get(['lastAndelaJobs', 'lastAndelaUpdated', 'totalAndelaMatching'], function(result) { chrome.storage.local.get(
updateUI(result); ['lastAndelaJobs', 'lastAndelaUpdated', 'totalAndelaMatching'],
}); function (result) {
updateUI(result);
},
);
chrome.storage.onChanged.addListener(function(changes, areaName) { chrome.storage.onChanged.addListener(function (changes, areaName) {
if (areaName === 'local' && (changes.lastAndelaJobs || changes.lastAndelaUpdated || changes.totalAndelaMatching)) { if (
areaName === 'local' &&
(changes.lastAndelaJobs ||
changes.lastAndelaUpdated ||
changes.totalAndelaMatching)
) {
console.log('Storage updated, refreshing UI...'); console.log('Storage updated, refreshing UI...');
chrome.storage.local.get(['lastAndelaJobs', 'lastAndelaUpdated', 'totalAndelaMatching'], function(result) { chrome.storage.local.get(
updateUI(result); ['lastAndelaJobs', 'lastAndelaUpdated', 'totalAndelaMatching'],
}); function (result) {
updateUI(result);
},
);
} }
}); });
}); });