vadykoo / boykottRussianBrands

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Sweep: i want to save info about brands that was found on the current page and show the counter in the circle the same as Number of brands in popup

vadykoo opened this issue · comments

also, it has to be the counter in storage of all times that brand was found on the page, it has to add a counter of brands on this page after the page is loaded and the loop of searching brands is finished to storage counter

Checklist

Here's the PR! #6.

⚡ Sweep Free Trial: I'm creating this ticket using GPT-4. You have 2 GPT-4 tickets left for the month and 2 for the day. For more GPT-4 tickets, visit [our payment portal.](https://buy.stripe.com/6oE5npbGVbhC97afZ4)

Actions (click)

  • ↻ Restart Sweep

Step 1: 🔎 Searching

I found the following snippets in your repository. I will now analyze these snippets and come up with a plan.

Some code snippets I looked at (click to expand). If some file is missing from here, you can mention the path in the ticket description.

const brandForm = document.getElementById("brandForm");
// Function to create checkbox element for each brand category
function createCheckbox(name, enabled) {
const checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.name = name;
checkbox.checked = enabled;
const label = document.createElement("label");
label.textContent = name;
label.appendChild(checkbox);
brandForm.appendChild(label);
// Add event listener to toggle brand category and send message to background script
checkbox.addEventListener("change", () => {
const toggleData = {
name,
enabled: checkbox.checked,
};
chrome.runtime.sendMessage(toggleData, (response) => {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message);
} else {
console.log(`Brand category '${name}' toggled: ${checkbox.checked}`);
}
});
});
}
// Fetch brandData from local storage and create checkboxes
chrome.storage.local.get({ brandData: null }, ({ brandData }) => {
if (!brandData) {
brandData = defaultBrandData; // Use default brand data if not found in local storage
}
brandData.forEach(({ name, enabled }) => {
createCheckbox(name, enabled);
});
});
// Send a message to the background script when the popup is opened
document.addEventListener('DOMContentLoaded', () => {
chrome.runtime.sendMessage({ action: 'fetchBrandData' });
});
const fetchBrandDataButton = document.getElementById("fetchBrandDataButton");
const brandCount = document.getElementById("brandCount");
fetchBrandDataButton.addEventListener("click", () => {
chrome.storage.local.remove('brandData', function() {
console.log('brandData has been removed from local storage and updated from GitHub');
});
chrome.runtime.sendMessage({ action: 'fetchBrandData' }, (response) => {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message);
} else {
// Force fetch the brand data again and update the brand count
chrome.storage.local.set({ fetchTime: null }, () => {
brandCount.textContent = `Number of brands on server: ${response.brandCount}`;
});
}
});
});
chrome.storage.local.get({ brandData: null, fetchTime: null }, ({ brandData, fetchTime }) => {
if (brandData) {
const totalBrandsElement = document.getElementById("totalBrands");
const lastUpdatedElement = document.getElementById("lastUpdated");
// Calculate the total number of brands
let totalBrands = 0;
brandData.forEach(category => {
totalBrands += category.names.length;
});
document.getElementById('totalBrands').textContent = totalBrands;
if (fetchTime) {
const lastUpdatedDate = new Date(fetchTime);
lastUpdatedElement.textContent = `Last updated: ${lastUpdatedDate.toLocaleString()}`;
}
}
});
const toggleExtensionButton = document.getElementById("toggleExtension");
function updateToggleButton() {
chrome.storage.local.get({ extensionEnabled: true }, ({ extensionEnabled }) => {
toggleExtensionButton.textContent = extensionEnabled ? 'ON' : 'OFF';
});
}
toggleExtensionButton.addEventListener("click", () => {
chrome.storage.local.get({ extensionEnabled: true }, ({ extensionEnabled }) => {
// Toggle the extensionEnabled value
chrome.storage.local.set({ extensionEnabled: !extensionEnabled }, () => {
console.log(`Extension toggled: ${!extensionEnabled}`);
updateToggleButton();
});
});
});

this.root = new TrieNode();
}
insert(word, brand) {
let node = this.root;
for (let char of word) {
if (!node.children[char]) {
node.children[char] = new TrieNode();
}
node = node.children[char];
}
node.endOfWord = brand;
}
search(word) {
let node = this.root;
for (let char of word) {
if (!node.children[char]) {
return null;
}
node = node.children[char];
}
return node.endOfWord;
}
}
function addEmojisToTextNode(textNode, brandData) {
if (hasEmoji(textNode)) return;
const trie = new Trie();
brandData.forEach((brandCategory) => {
if (brandCategory.enabled) {
brandCategory.names.forEach((brand) => {
if (brand.names) {
brand.names.forEach((brandName) => {
// Split brand name into words
const brandWords = brandName.toLowerCase().split(' ');
// Insert each word into the trie
brandWords.forEach((word) => {
trie.insert(word, { name: brandName, category: brandCategory, brand: brand });
});
});
}
});
}
});
const words = textNode.nodeValue.split(' ');
let matchedBrandWords = [];
for (let i = 0; i < words.length; i++) {
const word = words[i];
const matchedBrand = trie.search(word.toLowerCase());
if (matchedBrand) {
matchedBrandWords.push(word);
if (matchedBrandWords.join(' ').toLowerCase() === matchedBrand.name.toLowerCase()) {
const parent = textNode.parentNode;
if (!parent) {
break;
}
const wordIndex = textNode.nodeValue.indexOf(matchedBrandWords.join(' '));
const preMatchTextNode = document.createTextNode(textNode.nodeValue.slice(0, wordIndex));
const postMatchTextNode = document.createTextNode(textNode.nodeValue.slice(wordIndex + matchedBrandWords.join(' ').length));
if (parent && matchedBrand) {
parent.insertBefore(preMatchTextNode, textNode);
const span = createBrandSpan(matchedBrandWords.join(' '), matchedBrand.category, matchedBrand.brand);
parent.insertBefore(span, textNode);
const remainingText = textNode.nodeValue.slice(wordIndex + matchedBrandWords.join(' ').length);
if (remainingText) {
const remainingTextNode = document.createTextNode(remainingText);
parent.insertBefore(remainingTextNode, textNode);
}
parent.removeChild(textNode);
}
textNode = postMatchTextNode;
matchedBrandWords = [];
}
} else {
matchedBrandWords = [];
}
}
}
// Function to check if the text node already contains an emoji
function hasEmoji(textNode) {
const emojiRegex = /(\p{Emoji_Presentation}|\p{Emoji}\uFE0F)/gu;
return emojiRegex.test(textNode.nodeValue);
}
// Function to create a tooltip
function createTooltip(brand) {
if (!brand.description || !brand.linkSource) {
return null;
}
const tooltip = document.createElement('div');
tooltip.style.all = 'initial'; // Reset all inherited styles
tooltip.style.display = 'none';
tooltip.style.position = 'fixed'; // Change this line
tooltip.style.top = '100%';
tooltip.style.left = '0';
tooltip.style.width = '240px';
tooltip.style.padding = '16px';
tooltip.style.background = '#e2f8ee';
tooltip.style.color = '#414141';
tooltip.style.fontSize = '14px';
tooltip.style.borderRadius = '8px';
tooltip.style.zIndex = '9999';
let tooltipHTML = '';
if (brand.description) {
tooltipHTML += `<p>${brand.description}</p>`;
}
if (brand.linkSource) {
tooltipHTML += `<a href="${brand.linkSource}" target="_blank">Дізнатись більше</a>`;
}
tooltip.innerHTML = tooltipHTML;
tooltip.classList.add('brand-tooltip'); // Add a class to the tooltip
// Create a close button
const closeButton = document.createElement('button');
closeButton.textContent = 'X';
closeButton.style.position = 'absolute';
closeButton.style.top = '0';
closeButton.style.right = '0';
closeButton.style.background = 'none';
closeButton.style.border = 'none';
closeButton.style.fontSize = '16px';
closeButton.style.cursor = 'pointer';
// Add an event listener to the close button to hide the tooltip when clicked
closeButton.addEventListener('click', (event) => {
event.stopPropagation();
tooltip.style.display = 'none';
});
// Add the close button to the tooltip
tooltip.appendChild(closeButton);
return tooltip;
}
// Function to create a span element for the brand name and emoji
function createBrandSpan(match, brandCategory, brand) {
const span = document.createElement('span');
span.textContent = `${match} ${brandCategory.emoji}`;
span.style.position = 'relative';
span.classList.add('brand-span'); // Add a class to the span for identification
span.dataset.brand = JSON.stringify(brand); // Store the brand data in the dataset
return span;
}
// Function to traverse and add emojis to all text nodes on the page
function traverseAndAddEmojis(node, brandData) {
if (node.nodeType === Node.TEXT_NODE) {
addEmojisToTextNode(node, brandData);
} else if (node.nodeType === Node.ELEMENT_NODE) {
let i = 0;
function processNextChild() {
if (i < node.childNodes.length) {
traverseAndAddEmojis(node.childNodes[i], brandData);
i++;
processNextChild();
}
}
requestIdleCallback(processNextChild);
}
}
// Retrieve brandData from local storage or use default values
chrome.storage.local.get({ brandData: null, extensionEnabled: true }, ({ brandData, extensionEnabled }) => {
if (!extensionEnabled) {
console.log('Extension is disabled');
return;
}
let observer;
function processPage() {
// Disconnect the old observer, if it exists
if (observer) {
observer.disconnect();
}
let isProcessing = false;
let pendingMutations = false;
requestIdleCallback(() => {
traverseAndAddEmojis(document.body, brandData);
});
observer = new MutationObserver((mutationsList) => {
if (isProcessing) {
pendingMutations = true;
return;
}
isProcessing = true;
mutationsList.forEach((mutation) => {
if (mutation.type === "childList") {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.TEXT_NODE) {
addEmojisToTextNode(node, brandData);
} else if (node.nodeType === Node.ELEMENT_NODE) {
traverseAndAddEmojis(node, brandData);
}
});
}
});
isProcessing = false;
if (pendingMutations) {
pendingMutations = false;
observer.takeRecords().forEach(mutation => {
if (mutation.type === "childList") {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.TEXT_NODE) {
addEmojisToTextNode(node, brandData);
} else if (node.nodeType === Node.ELEMENT_NODE) {
traverseAndAddEmojis(node, brandData);
}
});
}
});
}
});
observer.observe(document, { childList: true, subtree: true });
}
// Call processPage when the page loads
window.addEventListener("load", processPage);
// Also call processPage when the URL changes
window.addEventListener("hashchange", processPage);
window.addEventListener("popstate", processPage);
});
let hideTooltipTimeout;
function addTooltipEventListeners(tooltip, brandSpan) {
let isTooltipHovered = false;
let isBrandSpanHovered = false;
// Mouseover event listener for the tooltip
tooltip.addEventListener('mouseover', () => {
isTooltipHovered = true;
clearTimeout(hideTooltipTimeout); // Cancel the tooltip hide timeout
});
// Mouseout event listener for the tooltip
tooltip.addEventListener('mouseout', () => {
isTooltipHovered = false;
checkAndHideTooltip();
});
// Mouseover event listener for the brand span
brandSpan.addEventListener('mouseover', () => {
isBrandSpanHovered = true;
clearTimeout(hideTooltipTimeout); // Cancel the tooltip hide timeout
});
// Mouseout event listener for the brand span
brandSpan.addEventListener('mouseout', () => {
isBrandSpanHovered = false;
checkAndHideTooltip();
});
// Prevent click events from propagating to underlying elements
tooltip.addEventListener('click', (event) => {
event.stopPropagation();
});
// Add click event listener to open the link in a new tab/window
const link = tooltip.querySelector('a');
if (link) {
link.addEventListener('click', (event) => {
event.stopPropagation(); // Prevent click event from reaching underlying elements
window.open(link.href, '_blank'); // Open the link in a new tab/window
});
}
function checkAndHideTooltip() {
if (!isTooltipHovered && !isBrandSpanHovered) {
hideTooltipTimeout = setTimeout(() => {
tooltip.style.display = 'none';
}, 500); // 500ms delay before hiding the tooltip
}
}
}
document.body.addEventListener('mouseover', (event) => {
if (event.target.classList.contains('brand-span')) {
// Check if a tooltip is already displayed
const existingTooltip = event.target.querySelector('div');
if (existingTooltip && existingTooltip.style.display === 'block') {
return;
}
// If not, create a new tooltip
const brand = JSON.parse(event.target.dataset.brand);
const tooltip = createTooltip(brand);
if (tooltip) {
// Calculate the position of the tooltip
const rect = event.target.getBoundingClientRect();
tooltip.style.left = `${rect.left}px`;
tooltip.style.top = `${rect.bottom}px`;
event.target.appendChild(tooltip);
addTooltipEventListeners(tooltip, event.target); // Add event listeners to the tooltip and brand span
clearTimeout(hideTooltipTimeout);
tooltip.style.display = 'block';
}
}

const defaultBrandData = [
// {
// name: "Ukrainian Brands",
// enabled: true,
// names: ["Чумак", "Prestigio"], // Add more Ukrainian brands here
// emoji: "🇺🇦",
// },
{
name: "Russian Brands",
enabled: true,
names: [], // Add more Russian brands here
emoji: "❌ ",
},
// {
// name: "Бренди досі активно працюють в росії",
// enabled: false,
// names: [], // Add more Russian brands here
// emoji: "🟨",
// },
// Add more brand categories and their corresponding emojis here
];
function saveDefaultBrandDataToStorage() {
chrome.storage.local.get({ brandData: null }, ({ brandData }) => {
if (!brandData) {
// If brandData is not in local storage, use the defaultBrandData
brandData = defaultBrandData;
// Save the defaultBrandData to local storage
chrome.storage.local.set({ brandData });
}
});
}
// Call the function to save defaultBrandData to local storage
fetchBrandDataFromGithub();
saveDefaultBrandDataToStorage();
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'fetchBrandData') {
fetchBrandDataFromGithub().then(brandData => {
console.log(brandData);
sendResponse({ brandCount: brandData[0].names.length });
});
return true; // Indicate that the response will be sent asynchronously
}
// Check if the message is from the popup
if (sender.tab === undefined) {
// Save the updated brandData to local storage
chrome.storage.local.get({ brandData: null }, ({ brandData }) => {
if (!brandData) {
brandData = defaultBrandData; // Use default brand data if not found in local storage
}
const { name, enabled } = message;
const brandCategory = brandData.find((category) => category.name === name);
if (brandCategory) {
brandCategory.enabled = enabled;
// Save the updated brandData to local storage
chrome.storage.local.set({ brandData }, () => {
sendResponse({ success: true });
});
} else {
sendResponse({ success: false });
}
});
// Return true to indicate that the response will be sent asynchronously
return true;
}
});
function fetchBrandDataFromGithub() {
return fetch('https://raw.githubusercontent.com/vadykoo/russianBrandsInUkraine/master/russianInternationalBrandsNew.json')
.then(response => response.json())
.then(fetchedBrandData => {
return new Promise((resolve, reject) => {
chrome.storage.local.get({ brandData: null, fetchTime: null }, ({ brandData, fetchTime }) => {
const currentTime = Date.now();
if (!brandData || !fetchTime || currentTime - fetchTime > 24 * 60 * 60 * 1000) {
// If brandData is not in local storage or it's older than one day, fetch it again
if (!brandData) {
brandData = defaultBrandData; // Use default brand data if not found in local storage
}
// Update the names in the brandData with the fetched data
for (let brandCategory of brandData) {
if (fetchedBrandData[brandCategory.name]) {
// Assuming brandCategory.names is the array you want to filter
const uniqueNames = new Set(brandCategory.names);
brandCategory.names = [...uniqueNames];
brandCategory.names = fetchedBrandData[brandCategory.name].map(brand => ({
names: brand.name, // brand.name is now an array of brand names
description: brand.description,
linkSource: brand.linkSource
}));
}
}
// Save the updated brandData to local storage
chrome.storage.local.set({ brandData }, () => {
resolve(brandData);
});
} else {
resolve(brandData);
}
});
});
})
.catch(error => console.error('Error:', error));
}

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Russian Brands Marker</title>
<style>
body {
background-color: lightblue;
}
button {
background-color: white;
border: 2px solid black;
color: black;
padding: 10px 24px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
}
#totalBrands {
display: inline-block;
border: 2px solid black;
border-radius: 50%;
padding: 10px;
background-color: white;
}
label {
display: block;
margin-bottom: 8px;
}
</style>
</head>
<body>
<h1>Налаштування/Settings</h1>
<button id="toggleExtension">Toggle Extension</button>
<form id="brandForm">
<!-- Checkbox elements will be dynamically generated here -->
</form>
<p id="brandText"><span>Number of brands: </span><span id="totalBrands" class="circle"></span></p>
<!-- <p id="lastUpdated">Last updated: </p> -->
<button id="fetchBrandDataButton">Оновити список брендів/Update list</button>
<p id="brandCount"></p>
<a href="https://forms.gle/oBgNEt5z7QcMbhjd8" target="_blank">Запропонувати бренд чи залишити відгук</a>
<!-- <script src="constants.js"></script> -->
<script src="popup.js"></script>


Step 2: ⌨️ Coding

  • content_script.js ✅ Commit 304c915
• Add a counter variable at the beginning of the `addEmojisToTextNode` function. • Increment this counter each time a brand is found. • After the page has been processed, save this counter to the Chrome storage with a key specific to the current page URL. • Also, update a global counter in the storage to keep track of the total number of times each brand has been found across all pages.
Sandbox Execution Logs
trunk init 1/3 ✓
⡿ Downloading Trunk 1.16.1...
⡿ Downloading Trunk 1.16.1...
⢿ Downloading Trunk 1.16.1...
⣻ Downloading Trunk 1.16.1...
⣽ Downloading Trunk 1.16.1...
⣾ Downloading Trunk 1.16.1...
⣷ Downloading Trunk 1.16.1...
⣯ Downloading Trunk 1.16.1...
⣟ Downloading Trunk 1.16.1...
⡿ Downloading Trunk 1.16.1...
⢿ Downloading Trunk 1.16.1...
⣻ Downloading Trunk 1.16.1...
⣽ Downloading Trunk 1.16.1...
⣾ Downloading Trunk 1.16.1...
✔ Downloading Trunk 1.16.1... done
⡿ Verifying Trunk sha256...
✔ Verifying Trunk sha256... done
⡿ Unpacking Trunk...
✔ Unpacking Trunk... done






















✔ 7 linters were enabled (.trunk/trunk.yaml)
  checkov 2.4.9 (1 json, 1 yaml file)
  git-diff-check (11 files)
  oxipng 8.0.0 (3 png files)
  prettier 3.0.3 (1 html, 3 javascript, 1 json, 1 yaml file)
  trivy 0.45.1 (1 yaml file)
  trufflehog 3.57.0 (11 files)
  yamllint 1.32.0 (1 yaml file) (created .yamllint.yaml)
Next Steps
 1. Read documentation
    Our documentation can be found at https://docs.trunk.io
 2. Get help and give feedback
    Join the Trunk community at https://slack.trunk.io
trunk fmt content_script.js 2/3 ✓
 ✔ Formatted content_script.js
Re-checking autofixed files...




Checked 1 file
✔ No issues
trunk check --fix content_script.js 3/3 ✓


Checked 1 file
✔ No issues
• Retrieve the counter for the current page from the Chrome storage and display it in the popup. • Also, retrieve the global counter from the storage and display it in the popup.
Sandbox Execution Logs
trunk init 1/3 ✓
⡿ Downloading Trunk 1.16.1...
⡿ Downloading Trunk 1.16.1...
⢿ Downloading Trunk 1.16.1...
⣻ Downloading Trunk 1.16.1...
⣽ Downloading Trunk 1.16.1...
⣾ Downloading Trunk 1.16.1...
⣷ Downloading Trunk 1.16.1...
⣯ Downloading Trunk 1.16.1...
⣟ Downloading Trunk 1.16.1...
⡿ Downloading Trunk 1.16.1...
⢿ Downloading Trunk 1.16.1...
⣻ Downloading Trunk 1.16.1...
⣽ Downloading Trunk 1.16.1...
⣾ Downloading Trunk 1.16.1...
✔ Downloading Trunk 1.16.1... done
⡿ Verifying Trunk sha256...
✔ Verifying Trunk sha256... done
⡿ Unpacking Trunk...
✔ Unpacking Trunk... done
























✔ 7 linters were enabled (.trunk/trunk.yaml)
  checkov 2.4.9 (1 json, 1 yaml file)
  git-diff-check (11 files)
  oxipng 8.0.0 (3 png files)
  prettier 3.0.3 (1 html, 3 javascript, 1 json, 1 yaml file)
  trivy 0.45.1 (1 yaml file)
  trufflehog 3.57.0 (11 files)
  yamllint 1.32.0 (1 yaml file) (created .yamllint.yaml)
Next Steps
 1. Read documentation
    Our documentation can be found at https://docs.trunk.io
 2. Get help and give feedback
    Join the Trunk community at https://slack.trunk.io
trunk fmt popup.js 2/3 ✓
 ✔ Formatted popup.js
Re-checking autofixed files...




Checked 1 file
✔ No issues
trunk check --fix popup.js 3/3 ✓



Checked 1 file
✔ No issues

Step 3: 🔁 Code Review

I have finished reviewing the code for completeness. I did not find errors for sweep/brand-counter.

.


🎉 Latest improvements to Sweep:


💡 To recreate the pull request edit the issue title or description. To tweak the pull request, leave a comment on the pull request.
Join Our Discord