Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Imroved Settings page, Updated content.js coupons auto-apply. #106

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 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
6 changes: 5 additions & 1 deletion Extension-React/public/_locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,9 @@
"Language": "Language",
"Browser_domain": "This domain is not supported by the extension",
"no_coupons_found": "No coupons available",
"keep_looking": "I will keep you updated if I find any!"
"keep_looking": "I will keep you updated if I find any!",
"custom_database": "Custom database",
"leave_empty_default": "Leave empty for default",
"Updated": "Updated",
"url_invalide": "Invalid URL"
}
6 changes: 5 additions & 1 deletion Extension-React/public/_locales/fr/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,9 @@
"Settings": "Paramètres",
"Language": "Langue",
"no_coupons_found": "Aucun coupon trouvé",
"keep_looking": "Je vous tiendrai informé si j'en trouve un!"
"keep_looking": "Je vous tiendrai informé si j'en trouve un!",
"custom_database": "Base de données personnalisée",
"leave_empty_default": "Laissez vide pour le paramètre par défaut",
"Updated": "Mis à jour",
"url_invalide": "URL invalide"
}
24 changes: 19 additions & 5 deletions Extension-React/public/background.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === "getCoupons") {
fetch(
"https://api.discountdb.ch/api/v1/syrup/coupons?domain=" +
request.domain
)
let database = "";
try {
if (localStorage.getItem("database") === null) {
database = "https://api.discountdb.ch/api/v1/syrup/coupons";
} else {
database = localStorage.getItem("database");
}
} catch (e) {
database = "https://api.discountdb.ch/api/v1/syrup/coupons";
}
fetch(database + "?domain=" + request.domain)
.then((response) => response.json())
.then((data) => {
sendResponse({ coupons: data });
chrome.storage.local.set({ coupons: data }, () => {
if (chrome.runtime.lastError) {
console.error("Error setting coupons:", chrome.runtime.lastError);
sendResponse({ coupons: [] });
} else {
sendResponse({ coupons: data });
}
});
Comment on lines -3 to +23
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use the types/classes/functions from the sas client. I know you said thats not possible but as of a quick search it should be. Though i didnt try yet.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot import anything in content.js

})
.catch((err) => {
sendResponse({ coupons: [] });
Expand Down
75 changes: 46 additions & 29 deletions Extension-React/public/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@
removeCouponButtonSelector:
"button[data-testid='coupon-delete-applied-button']",
},
"fossil.com": {
inputSelector: ".coupon-code-field",
preApplyButtonSelector: ".optional-promo",
applyButtonSelector: ".promo-code-btn",
successSelector: ".coupon-code-applied",
failureSelector: ".coupon-error-message",
priceSelector: ".grand-total",
removeCouponButtonSelector: ".remove-coupon",
removeCouponButtonSelectorConfirm: ".delete-coupon-confirmation-btn",
},
};

const platformConfigs = {
Expand Down Expand Up @@ -75,29 +85,30 @@
try {
chrome.runtime.sendMessage(
{ action: "getCoupons", domain },
(response) => {
if (chrome.runtime.lastError) {
logger.error(
"Runtime error during message passing:",
chrome.runtime.lastError
);
reject(chrome.runtime.lastError.message);
return;
}

if (
response &&
response.coupons &&
response.coupons.length > 0
) {
coupons = response.coupons;
resolve();
chrome.storage.local.set({ coupons });
} else {
logger.warn("No coupons returned from background");
resolve();
(response) => {
const fetchedCoupons = JSON.stringify(response.coupons.coupons);
if (chrome.runtime.lastError) {
logger.error(
"Runtime error during message passing:",
chrome.runtime.lastError
);
reject(chrome.runtime.lastError.message);
return;
}

if (
fetchedCoupons &&
fetchedCoupons.length > 0
) {
localStorage.setItem("database", fetchedCoupons);
coupons = fetchedCoupons;
logger.warn("Coupons fetched:", coupons);
resolve();
} else {
logger.warn("No coupons returned from background");
resolve();
}
}
}
);
} catch (error) {
logger.error("Error fetching coupons:", error);
Expand Down Expand Up @@ -214,7 +225,7 @@
}
}

async function revertCoupon(inputSelector, removeCouponButtonSelector) {
async function revertCoupon(inputSelector, removeCouponButtonSelector, removeCouponButtonSelectorConfirm) {
try {
if (inputSelector) {
const input = document.querySelector(inputSelector);
Expand All @@ -225,6 +236,9 @@
if (removeCouponButtonSelector) {
document.querySelector(removeCouponButtonSelector)?.click();
}
if (removeCouponButtonSelectorConfirm) {
document.querySelector(removeCouponButtonSelectorConfirm)?.click();
}
await new Promise((resolve) => setTimeout(resolve, 1000));
} catch (error) {
logger.error("Error reverting coupon:", error);
Expand Down Expand Up @@ -408,15 +422,16 @@

showTestingPopover(coupons.length);

for (let i = 0; i < coupons.length; i++) {
const ParseCoupons = JSON.parse(coupons);
for (let i = 0; i < ParseCoupons.length; i++) {
if (stopTesting || useBestNow) {
break;
}

const coupon = coupons[i];
const couponCode = coupon.couponCode || coupon; // handle either {couponCode: "..."} or raw string
const coupon = ParseCoupons[i];
const couponCode = coupon.code || coupon; // handle either {couponCode: "..."} or raw string

updateTestingPopover(i + 1, coupons.length, couponCode, bestPrice);
updateTestingPopover(i + 1, coupons.length, couponCode, bestPrice);

try {
const result = await applySingleCoupon(
Expand All @@ -440,7 +455,8 @@

await revertCoupon(
config.inputSelector,
config.removeCouponButtonSelector
config.removeCouponButtonSelector,
config.removeCouponButtonSelectorConfirm
);
} catch (error) {
logger.error(`Error testing coupon ${couponCode}:`, error);
Expand Down Expand Up @@ -844,7 +860,8 @@
*******************************************************/
async function main() {
await logger.init();
let domain = window.location.hostname.replace("www.", "");
let domain = window.location.hostname.replace("www.", "").replace("https://", "").replace("http://", "");
console.error(domain);
Abstra208 marked this conversation as resolved.
Show resolved Hide resolved
if (domainReplacements[domain]) domain = domainReplacements[domain];
const path = window.location.pathname;

Expand Down
2 changes: 1 addition & 1 deletion Extension-React/public/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "A Honey alternative, find and apply the best coupons automatically.",
"version": "0.4",
"default_locale": "en",
"permissions": ["activeTab", "storage"],
"permissions": ["storage", "activeTab"],
"background": {
"service_worker": "background.js",
"scripts": ["background.js"]
Expand Down
44 changes: 8 additions & 36 deletions Extension-React/src/components/CouponsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,8 @@ const CouponsPage: React.FC<CouponData> = ({
const subDomainName: string = getSubDomainName(parsedSubDomain);

const handleDomain = () => {
const domain = document.querySelector(
'input[name="couponType"]:checked'
)?.id;

const domain = document.querySelector('input[name="couponType"]:checked')?.id;
if (domain === "domain") {
document.querySelector(".Domain")?.classList.remove("hidden");
document.querySelector(".SubDomain")?.classList.add("hidden");
Expand Down Expand Up @@ -80,46 +79,19 @@ const CouponsPage: React.FC<CouponData> = ({
<>
{isSubDomain ? (
<>
<label
htmlFor="domain"
className="DomainLabel w-[50%] m-1.5 flex items-center justify-center rounded-lg text-foreground text-sm hover:cursor-pointer bg-card"
>
<label htmlFor="domain" className="DomainLabel w-[50%] m-1.5 flex items-center justify-center rounded-lg text-foreground text-sm hover:cursor-pointer bg-card">
{domainName}
<input
className="hidden"
type="radio"
name="couponType"
id="domain"
onChange={handleDomain}
defaultChecked
/>
<input className="hidden" type="radio" name="couponType" id="domain" onChange={handleDomain} defaultChecked/>
</label>
<label
htmlFor="subDomain"
className="SubDomainLabel w-[50%] m-1.5 flex items-center justify-center rounded-lg text-foreground text-sm hover:cursor-pointer"
>
<label htmlFor="subDomain" className="SubDomainLabel w-[50%] m-1.5 flex items-center justify-center rounded-lg text-foreground text-sm hover:cursor-pointer">
{subDomainName}
<input
className="hidden"
type="radio"
name="couponType"
id="subDomain"
onChange={handleDomain}
/>
<input className="hidden" type="radio" name="couponType" id="subDomain" onChange={handleDomain}/>
</label>
</>
) : (
<label
htmlFor="domain"
className="w-[100%] m-1.5 flex items-center justify-center bg-card rounded-lg text-foreground text-sm cursor-pointer"
>
<label htmlFor="domain" className="w-[100%] m-1.5 flex items-center justify-center bg-card rounded-lg text-foreground text-sm cursor-pointer">
{domainName}
<input
className="hidden"
type="radio"
name="couponType"
id="domain"
/>
<input className="hidden" type="radio" name="couponType" id="domain"/>
</label>
)}
</>
Expand Down
48 changes: 48 additions & 0 deletions Extension-React/src/components/CustomDatabase.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useTranslation } from "react-i18next";

export default function customDatabase() {
const { t } = useTranslation();

const handleDatabase = (event: React.ChangeEvent<HTMLInputElement>) => {
const inputValue = event.target.value;

if (inputValue === '') {
localStorage.removeItem('database');
return;
}
try {
const url = new URL(inputValue);
fetch(url.toString())
.then(() => {
localStorage.setItem('database', inputValue);
const updated = document.querySelector('#customDatabase .update') as HTMLParagraphElement;
updated.classList.remove('hidden');
setTimeout(() => {
updated.classList.add('hidden');
}, 2000);
})
.catch(() => {
const error = document.querySelector('#customDatabase .invalid') as HTMLParagraphElement;
error.classList.remove('hidden');
setTimeout(() => {
error.classList.add('hidden');
}, 2000);
});
} catch (e) {
const error = document.querySelector('#customDatabase .invalid') as HTMLParagraphElement;
error.classList.remove('hidden');
setTimeout(() => {
error.classList.add('hidden');
}, 2000);
}
}

return (
<div id="customDatabase" className="flex flex-col">
<input onChange={handleDatabase} className="bg-card border-white border-1 rounded-sm" type="url" name="" id="" defaultValue={localStorage.getItem("database") || ''} />
<p className="text-xs">{t("leave_empty_default")}</p>
<p className="update hidden">{t("Updated")}</p>
<p className="invalid text-red hidden">{t("url_invalide")}</p>
</div>
);
}
38 changes: 5 additions & 33 deletions Extension-React/src/components/LanguageSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { Button } from "@/components/ui/button";
import { getLanguage, languageNames, languages, switchLanguage } from "@/i18n";
import { useEffect, useState } from "react";
import CenteredScrollZone from "@/components/ui/CenteredScrollZone";

export function LanguageSwitcher() {
// defaults to english, will be updated on mount
Expand All @@ -13,11 +11,6 @@ export function LanguageSwitcher() {
setLanguagePath(`/icons/${lang}.svg`);
}

const toggleDropdown = () => {
const wrapper = document.querySelector('.language') as HTMLDivElement;
wrapper.classList.toggle('hidden');
}

useEffect(() => {
getLanguage().then((lang: string) => {
setLanguage(lang);
Expand All @@ -26,32 +19,11 @@ export function LanguageSwitcher() {
}, [])

return (
<div className="flex relative">
<Button
variant="ghost"
size="icon"
onClick={() => {
toggleDropdown();
}}>
<img src={languagePath} alt={language} className="h-[1.2rem] aspect-auto" />
</Button>
<CenteredScrollZone className="language">
<div className="w-[60%] h-[100%] flex flex-col border-1 border-white border-opacity-70 bg-card rounded-lg dropdown-content">
{
languages.map((lang) => (
<div className="w-[100%] flex flex-row justify-start items-start" onClick={() => {
switchLanguage(lang);
toggleDropdown();
}}>
<Button onClick={() => {updateLanguage(lang);}} className="w-[100%] bg-card text-card-foreground flex flex-row items-start hover:text-primary hover:cursor-pointer hover:bg-primary/10">
<img src={`/icons/${lang}.svg`} alt={lang} className="h-[1.2rem] w-[1.2rem] aspect-auto" />
<p className="text-card-foreground text-sm ml-2 hover:cursor-pointer">{languageNames[lang]}</p>
</Button>
</div>
))}
</div>
</CenteredScrollZone>
</div>
<select name="" id="" onChange={(e) => { switchLanguage(e.target.value); updateLanguage(e.target.value); }} value={language} className="bg-card text-card-foreground rounded-lg">
{languages.map((lang) => (
<option value={lang}><img src={languagePath} alt={lang} /><span className="text-sm">{languageNames[lang]}</span></option>
))}
</select>
);
}

Expand Down
Loading