diff --git a/src/components/ai-detector/FeatureSliderImg.tsx b/src/components/ai-detector/FeatureSliderImg.tsx index e041c9d95..6bf56c825 100644 --- a/src/components/ai-detector/FeatureSliderImg.tsx +++ b/src/components/ai-detector/FeatureSliderImg.tsx @@ -1,11 +1,6 @@ -import { Fragment, useState } from 'react'; -import RevealX from '@/components/components/RevealX'; import Image from 'next/image'; -const FeaturesSliderImg = ({ textContent, cardInfo, backgroundClass = 'bg-white' }) => { - const [cardIndex, setCardIndex] = useState(0); - const DescriptionIcon = cardInfo[cardIndex].icon; - +const FeaturesSliderImg = ({ textContent, cardInfo }) => { return (
diff --git a/src/components/annual-plans-for-affiliates/components/PriceTable.tsx b/src/components/annual-plans-for-affiliates/components/PriceTable.tsx index 498033654..d27a296d5 100644 --- a/src/components/annual-plans-for-affiliates/components/PriceTable.tsx +++ b/src/components/annual-plans-for-affiliates/components/PriceTable.tsx @@ -1,8 +1,9 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { Transition } from '@headlessui/react'; import CardSkeleton from '@/components/components/CardSkeleton'; import PriceCard from './PriceCard'; -import { CurrencyCircleDollar, Detective, FolderLock, Lifebuoy } from '@phosphor-icons/react'; +import { CurrencyCircleDollar, Lifebuoy } from '@phosphor-icons/react'; import OpenSource from '../../../../public/icons/open-source.svg'; import usePricing from '@/hooks/usePricing'; @@ -26,9 +27,8 @@ const PriceTable: React.FC = ({ billingFrequency, isStartPage, titleFontSize, - isCloudwards, }) => { - const { products, currency, lifetimeCoupons, currencyValue, loadingCards } = usePricing({ + const { products, currency, lifetimeCoupons, loadingCards } = usePricing({ fetchLifetimeCoupons: true, }); diff --git a/src/components/antivirus/DownloadComponent.tsx b/src/components/antivirus/DownloadComponent.tsx index 2a5e7b38c..aa43410a7 100644 --- a/src/components/antivirus/DownloadComponent.tsx +++ b/src/components/antivirus/DownloadComponent.tsx @@ -1,25 +1,7 @@ -import { useEffect, useState } from 'react'; import { isMobile } from 'react-device-detect'; import { ArrowCircleRight } from '@phosphor-icons/react'; -const DownloadComponent = ({ textContent, lang, download }) => { - const [OS, setOS] = useState(''); - - function getOS() { - const osList = [ - { keyword: 'Win', name: 'Windows' }, - { keyword: 'Mac', name: isMobile ? 'iPad' : 'MacOS' }, - { keyword: 'Linux', name: 'Linux' }, - ]; - - const res = osList.find((os) => window.navigator.appVersion.indexOf(os.keyword) !== -1); - return res ? res.name : `Unknown (${window.navigator.appVersion})`; - } - - useEffect(() => { - setOS(getOS()); - }, []); - +const DownloadComponent = ({ textContent, download }) => { return (

{textContent.downloadTitle}

diff --git a/src/components/antivirus/HeroSection.tsx b/src/components/antivirus/HeroSection.tsx index 3e2a5a091..e09bf9091 100644 --- a/src/components/antivirus/HeroSection.tsx +++ b/src/components/antivirus/HeroSection.tsx @@ -3,7 +3,7 @@ import Image from 'next/image'; import DownloadComponent from './DownloadComponent'; import { isMobile } from 'react-device-detect'; -const HeroSection = ({ textContent, lang, download }) => ( +const HeroSection = ({ textContent, download }) => (
@@ -60,7 +60,7 @@ const HeroSection = ({ textContent, lang, download }) => (
- {!isMobile && } + {!isMobile && }
); diff --git a/src/components/banners/BitdefenderBanner.tsx b/src/components/banners/BitdefenderBanner.tsx index 252e4fa84..ee24ff45f 100644 --- a/src/components/banners/BitdefenderBanner.tsx +++ b/src/components/banners/BitdefenderBanner.tsx @@ -1,5 +1,5 @@ +/* eslint-disable react-hooks/exhaustive-deps */ import { useEffect, useState } from 'react'; -import { useRouter } from 'next/router'; import { getImage } from '@/lib/getImage'; import { X } from '@phosphor-icons/react'; import Image from 'next/image'; @@ -10,7 +10,6 @@ interface BitdefenderBannerProps { } const BitdefenderBanner = ({ languageForImage, isTempMail }: BitdefenderBannerProps) => { - const router = useRouter(); const [showBanner, setShowBanner] = useState(false); useEffect(() => { diff --git a/src/components/business/WhatCanWeDo.tsx b/src/components/business/WhatCanWeDo.tsx index 26a393a37..b8bffd0cb 100644 --- a/src/components/business/WhatCanWeDo.tsx +++ b/src/components/business/WhatCanWeDo.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { getImage } from '@/lib/getImage'; import Image from 'next/image'; import { Fragment, useState } from 'react'; @@ -11,27 +12,23 @@ interface WhatCanWeDoProps { export const WhatCanWeDo = ({ textContent }: WhatCanWeDoProps): JSX.Element => { const [selectedTab, setSelectedTab] = useState(0); - const [isTransitioning, setIsTransitioning] = useState(false); const onRightArrowClick = () => { const newIndex = selectedTab === textContent.cards.length - 1 ? 0 : selectedTab + 1; - onTabSelectorButtonClicked(newIndex); + setSelectedTab(newIndex); }; const onLeftArrowClick = () => { const newIndex = selectedTab === 0 ? textContent.cards.length - 1 : selectedTab - 1; - onTabSelectorButtonClicked(newIndex); + setSelectedTab(newIndex); }; const onTabSelectorButtonClicked = (tabId: number) => { if (selectedTab !== tabId) { - setIsTransitioning(true); - setTimeout(() => { - setSelectedTab(tabId); - setIsTransitioning(false); - }, 200); + setSelectedTab(tabId); } }; + const isLastTab = selectedTab === textContent.cards.length - 1; return ( @@ -46,28 +43,28 @@ export const WhatCanWeDo = ({ textContent }: WhatCanWeDoProps): JSX.Element => {

{textContent.title}

{textContent.description}

+
- {textContent.cards.map((info, index) => ( + {textContent.cards.map((info: any, index: number) => (
))}
- + +
-
+

{textContent.cards[selectedTab].selectorTab}

{textContent.cards[selectedTab].description} @@ -87,46 +84,40 @@ export const WhatCanWeDo = ({ textContent }: WhatCanWeDoProps): JSX.Element => {
- {/*Mobile/Tablet View*/} -
-
- {textContent.cards.map((testimonial) => ( -
-
-
-

- {textContent.cards[selectedTab].selectorTab} -

-
+
+
+
+
+
+

+ {textContent.cards[selectedTab].selectorTab} +

+
-
- {selectedTab !== 0 && ( - <> - - - - )} +
+ {selectedTab !== 0 && ( + <> + + + + )} - -
+ +
-
-

{textContent.cards[selectedTab].description}

-
+
+

{textContent.cards[selectedTab].description}

- ))} +
diff --git a/src/components/cloud-storage-for-photos/ExplanationSection.tsx b/src/components/cloud-storage-for-photos/ExplanationSection.tsx index 0c04567ee..1cf765685 100644 --- a/src/components/cloud-storage-for-photos/ExplanationSection.tsx +++ b/src/components/cloud-storage-for-photos/ExplanationSection.tsx @@ -1,16 +1,8 @@ -import Image from 'next/legacy/image'; -import SignUpBanner from '@/components/banners/SignUpBanner'; import Link from 'next/link'; -const language = { - en: 'EN', - es: 'ES', -}; -const ExplanationSection = ({ textContent, lang, ctaText, ctaLink }) => { - const langUpperCase = language[lang] || 'EN'; +const ExplanationSection = ({ textContent, ctaText, ctaLink }) => { return (
- {/* Header Section */}

{textContent.title}

@@ -44,7 +36,6 @@ const ExplanationSection = ({ textContent, lang, ctaText, ctaLink }) => { {ctaText} )} - {/*
*/}
); diff --git a/src/components/metadata-remover/FeaturesSliderImg.tsx b/src/components/metadata-remover/FeaturesSliderImg.tsx index ab3e9f54f..6bf56c825 100644 --- a/src/components/metadata-remover/FeaturesSliderImg.tsx +++ b/src/components/metadata-remover/FeaturesSliderImg.tsx @@ -1,11 +1,6 @@ -import { Fragment, useState } from 'react'; -import RevealX from '@/components/components/RevealX'; import Image from 'next/image'; -const FeaturesSliderImg = ({ textContent, cardInfo, backgroundClass = 'bg-white' }) => { - const [cardIndex, setCardIndex] = useState(0); - const DescriptionIcon = cardInfo[cardIndex].icon; - +const FeaturesSliderImg = ({ textContent, cardInfo }) => { return (
@@ -17,7 +12,6 @@ const FeaturesSliderImg = ({ textContent, cardInfo, backgroundClass = 'bg-white'
- {/* Mobile view */}
{cardInfo.map((info) => (
diff --git a/src/components/metadata-remover/HeroSection.tsx b/src/components/metadata-remover/HeroSection.tsx index a7c544b36..5ec6e0d6d 100644 --- a/src/components/metadata-remover/HeroSection.tsx +++ b/src/components/metadata-remover/HeroSection.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { useState, Fragment, createRef } from 'react'; import { Transition } from '@headlessui/react'; import { CheckCircle } from '@phosphor-icons/react'; @@ -10,18 +11,17 @@ interface HeroSectionProps { lang: string; } -const HeroSection = ({ textContent, lang }: HeroSectionProps): JSX.Element => { +const HeroSection = ({ textContent }: HeroSectionProps): JSX.Element => { const [isSelectedFile, setIsSelectedFile] = useState(false); const [isProcessing, setIsProcessing] = useState(false); const [isError, setIsError] = useState(false); const [isProcessFinished, setIsProcessFinished] = useState(false); - const [processResult, setProcessResult] = useState(null); + const [setProcessResult] = useState(null); const [dragEnter, setDragEnter] = useState(false); const [fileSizeLimitReached, setFileSizeLimitReached] = useState(false); const [downloadUrl, setDownloadUrl] = useState(null); const uploadFileRef = createRef(); const [file, setFile] = useState(null); - const [showPopup, setShowPopup] = useState(true); const isDragging = dragEnter; const maxFileSize = 104_857_600; @@ -52,7 +52,6 @@ const HeroSection = ({ textContent, lang }: HeroSectionProps): JSX.Element => { setProcessResult({ success: true }); setIsProcessFinished(true); - setShowPopup(true); const downloadLink = document.createElement('a'); downloadLink.href = url; @@ -66,7 +65,6 @@ const HeroSection = ({ textContent, lang }: HeroSectionProps): JSX.Element => { setIsSelectedFile(false); setIsProcessing(false); setIsProcessFinished(false); - setShowPopup(false); } }; @@ -95,7 +93,6 @@ const HeroSection = ({ textContent, lang }: HeroSectionProps): JSX.Element => { setIsProcessing(false); setIsProcessFinished(false); setIsError(false); - setShowPopup(false); setFile(null); }; @@ -144,30 +141,6 @@ const HeroSection = ({ textContent, lang }: HeroSectionProps): JSX.Element => { } }; - const processAgainButton = () => { - return ( - -
- -
-
- ); - }; - const renderProcessStatus = () => { if (isProcessing && !isProcessFinished) { return ( diff --git a/src/components/password-checker/HeroSection.tsx b/src/components/password-checker/HeroSection.tsx index 387b20bbf..c3e149cf8 100644 --- a/src/components/password-checker/HeroSection.tsx +++ b/src/components/password-checker/HeroSection.tsx @@ -5,14 +5,13 @@ import zxcvbn from 'zxcvbn'; import { Info, Eye, EyeSlash, WarningCircle } from '@phosphor-icons/react'; import pwnedpasswords from '@/lib/checker'; -const HeroSection = ({ textContent, lang }) => { +const HeroSection = ({ textContent }) => { const [inputTypePassword, setInputTypePassword] = useState(true); const [passwordLength, setPasswordLength] = useState(0); const [pwned, setPwned] = useState('-'); const [crackFeedback, setCrackFeedback] = useState('-'); const [crackScore, setCrackScore] = useState(0); const [crackTime, setCrackTime] = useState('-'); - const [crackTimeInSeconds, setCrackTimeInSeconds] = useState(0); const toggleShowPassword = () => { setInputTypePassword(!inputTypePassword); @@ -30,14 +29,12 @@ const HeroSection = ({ textContent, lang }) => { const timecases = textContent.result.crack.cases; const displaytTimeHasNumbers = hasNumber(displaytTime); - // Time is composed of a number and one or more words if (displaytTimeHasNumbers) { - const number = displaytTime.split(' ')[0]; // Get number (1 year --> 1) - const timecase = displaytTime.split(' ')[1]; // Get timecase (1 year --> year) + const number = displaytTime.split(' ')[0]; + const timecase = displaytTime.split(' ')[1]; return `${number} ${timecases[timecase]}`; } - // Time has only words return timecases[displaytTime]; }; @@ -48,20 +45,17 @@ const HeroSection = ({ textContent, lang }) => { if (password === '') { setPwned('-'); setCrackTime('-'); - setCrackTimeInSeconds(0); setCrackFeedback('-'); setCrackScore(0); } else { - // Check for leaked passwords pwnedpasswords(password) .then((count) => { setPwned(count); }) - .catch((err) => { - // eslint-disable-next-line no-console + .catch(() => { + // }); - // Check for crack time and get anti-crack feedback const crack = zxcvbn(password); if (crack.feedback.warning !== '') { setCrackFeedback(getFeedbackTranslation(crack.feedback.warning)); @@ -70,12 +64,11 @@ const HeroSection = ({ textContent, lang }) => { } setCrackScore(crack.score); setCrackTime(getTimeTranslation(crack.crack_times_display.offline_slow_hashing_1e4_per_second)); - setCrackTimeInSeconds(crack.crack_times_seconds.offline_slow_hashing_1e4_per_second); } }; return ( -
+

{textContent.title}

@@ -127,11 +120,10 @@ const HeroSection = ({ textContent, lang }) => {
- {/* Password dynamic feedback */}
{textContent.result.feedback.title} - {crackFeedback} + {crackFeedback}
@@ -151,10 +143,10 @@ const HeroSection = ({ textContent, lang }) => {
-
+
{textContent.result.crack.title} - {crackTime} + {crackTime}
{textContent.result.crack.subtitle} diff --git a/src/components/password-generator/InfoSection.tsx b/src/components/password-generator/InfoSection.tsx index 6d1b4aaa9..cdf1711cc 100644 --- a/src/components/password-generator/InfoSection.tsx +++ b/src/components/password-generator/InfoSection.tsx @@ -1,5 +1,6 @@ +/* eslint-disable @next/next/no-img-element */ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { CirclesThree, Fingerprint, Hash, NumberCircleThree, Ruler, TextAa, TextT } from '@phosphor-icons/react'; -import { useRouter } from 'next/router'; import SignUpBanner from '@/components/banners/SignUpBanner'; import CtaSection from '@/components/shared/CtaSection'; import RenderDescription from '@/components/shared/RenderDescription'; diff --git a/src/components/shared/pricing/PricingSection.tsx b/src/components/shared/pricing/PricingSection.tsx index a88536499..d5691db47 100644 --- a/src/components/shared/pricing/PricingSection.tsx +++ b/src/components/shared/pricing/PricingSection.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { useEffect } from 'react'; import { Transition } from '@headlessui/react'; diff --git a/src/components/shared/pricing/PricingSectionWrapper.tsx b/src/components/shared/pricing/PricingSectionWrapper.tsx index 8bd9b910e..4fdab0b7e 100644 --- a/src/components/shared/pricing/PricingSectionWrapper.tsx +++ b/src/components/shared/pricing/PricingSectionWrapper.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { Interval, ProductsDataProps } from '@/services/stripe.service'; import { PricingSection } from './PricingSection'; import { SwitchButtonOptions, SwitchStorageOptions } from './components/PlanSelector'; diff --git a/src/components/temp-email/HeroSection.tsx b/src/components/temp-email/HeroSection.tsx index f0eb0c126..96dae168c 100644 --- a/src/components/temp-email/HeroSection.tsx +++ b/src/components/temp-email/HeroSection.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react-hooks/exhaustive-deps */ import { useEffect } from 'react'; import { Info } from '@phosphor-icons/react'; import { Inbox } from './components/InboxView'; @@ -35,7 +36,6 @@ export const HeroSection = ({ textContent, lang }) => { const { user, borderColor, openedMessages, messages, selectedMessage, isChangeEmailIconAnimated } = state as StateProps; - // Open the links that are in the email received in a new tab useEffect(() => { const inboxElement = document.getElementById('inbox'); @@ -104,7 +104,7 @@ export const HeroSection = ({ textContent, lang }) => { localStorage.setItem(SETUP_TIME_STORAGE_KEY, String(TIME_NOW)); localStorage.setItem(EMAIL_STORAGE_KEY, JSON.stringify(emailData)); } catch (error) { - // NO OP + console.error('Failed to initialize new email session:', error); } }; diff --git a/src/hooks/usePricing.ts b/src/hooks/usePricing.ts index 124b693d1..12f7c3fde 100644 --- a/src/hooks/usePricing.ts +++ b/src/hooks/usePricing.ts @@ -1,3 +1,5 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { currencyService } from '@/services/currency.service'; import { ProductsDataProps, stripeService } from '@/services/stripe.service'; import { useEffect, useReducer } from 'react'; @@ -79,11 +81,13 @@ function usePricing(options: UsePricingOptions = {}): UseStripeProductsAndCurren dispatch({ type: 'SET_CURRENCY', payload: res.currency }); dispatch({ type: 'SET_CURRENCY_VALUE', payload: res.currencyValue }); } catch (err) { + console.warn('Primary price fetch failed, falling back to EUR:', err); + try { const prices = await stripeService.getPrices('eur'); dispatch({ type: 'SET_PRODUCTS', payload: prices }); } catch (error) { - console.error('Error getting prices:', error); + console.error('Critical error: Failed to fetch fallback prices:', error); } dispatch({ type: 'SET_CURRENCY', payload: '€' }); @@ -93,20 +97,18 @@ function usePricing(options: UsePricingOptions = {}): UseStripeProductsAndCurren if (fetchLifetimeCoupons) { try { const lifetimeCoupons = await stripeService.getLifetimeCoupons(); - dispatch({ type: 'SET_LIFETIME_COUPONS', payload: lifetimeCoupons }); } catch (error) { - //NO OP + console.error('Error fetching lifetime coupons:', error); } } if (couponCode) { try { const coupon = await stripeService.getCoupon(couponCode); - dispatch({ type: 'SET_COUPON', payload: coupon }); } catch (err) { - // NO OP + console.error('Error fetching coupon:', err); } } @@ -121,7 +123,7 @@ function usePricing(options: UsePricingOptions = {}): UseStripeProductsAndCurren const businessCoupon = await stripeService.getCoupon(couponCodeForBusiness); dispatch({ type: 'SET_BUSINESS_COUPON', payload: businessCoupon }); } catch (err) { - // NO OP + console.warn('Invalid or expired business coupon ignored', err); } } }; diff --git a/src/lib/metadataRemover.ts b/src/lib/metadataRemover.ts index 3c12cf534..17b9b27b8 100644 --- a/src/lib/metadataRemover.ts +++ b/src/lib/metadataRemover.ts @@ -1,12 +1,10 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import exifr from 'exifr'; -import { PDFDocument, PDFPage } from 'pdf-lib'; -import { Document, Packer, Paragraph, TextRun, ISectionOptions } from 'docx'; -import { parseFile, IAudioMetadata } from 'music-metadata'; -import { fetchFile } from '@ffmpeg/util'; +import { PDFDocument } from 'pdf-lib'; +import { Document, Packer, Paragraph, TextRun } from 'docx'; +import { IAudioMetadata } from 'music-metadata'; import { parseBuffer } from 'music-metadata'; -import { Buffer } from 'buffer'; -// Initialize FFmpeg only on client side let ffmpeg: any = null; if (typeof window !== 'undefined') { import('@ffmpeg/ffmpeg').then(({ FFmpeg }) => { @@ -14,84 +12,72 @@ if (typeof window !== 'undefined') { }); } -// Supported file types const SUPPORTED_IMAGE_TYPES = [ 'image/jpeg', 'image/jpg', - 'image/jpe', // JPEG formats + 'image/jpe', 'image/png', 'image/jng', - 'image/mng', // PNG and related + 'image/mng', 'image/tiff', - 'image/tif', // TIFF formats - 'image/gif', // GIF + 'image/tif', + 'image/gif', 'image/jp2', 'image/jpf', - 'image/j2k', // JPEG 2000 formats + 'image/j2k', 'image/jpm', - 'image/jpx', // JPEG 2000 extended + 'image/jpx', 'image/psd', - 'image/psb', // Photoshop formats + 'image/psb', ]; -const SUPPORTED_PDF_TYPES = [ - 'application/pdf', // PDF -]; +const SUPPORTED_PDF_TYPES = ['application/pdf']; const SUPPORTED_WORD_TYPES = [ - 'application/msword', // .doc - 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', // .docx - 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', // .dotx - 'application/vnd.ms-word.document.macroEnabled.12', // .docm - 'application/vnd.ms-word.template.macroEnabled.12', // .dotm - 'application/vnd.oasis.opendocument.text', // .odt + 'application/msword', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'application/vnd.ms-word.document.macroEnabled.12', + 'application/vnd.ms-word.template.macroEnabled.12', + 'application/vnd.oasis.opendocument.text', ]; const SUPPORTED_EXCEL_TYPES = [ - 'application/vnd.ms-excel', // .xls - 'application/vnd.ms-excel.template', // .xlt - 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // .xlsx - 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', // .xltx - 'application/vnd.ms-excel.sheet.macroEnabled.12', // .xlsm - 'application/vnd.ms-excel.template.macroEnabled.12', // .xltm - 'application/vnd.oasis.opendocument.spreadsheet', // .ods + 'application/vnd.ms-excel', + 'application/vnd.ms-excel.template', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'application/vnd.ms-excel.sheet.macroEnabled.12', + 'application/vnd.ms-excel.template.macroEnabled.12', + 'application/vnd.oasis.opendocument.spreadsheet', ]; const SUPPORTED_POWERPOINT_TYPES = [ - 'application/vnd.ms-powerpoint', // .ppt - 'application/vnd.ms-powerpoint.template', // .pot - 'application/vnd.openxmlformats-officedocument.presentationml.presentation', // .pptx - 'application/vnd.openxmlformats-officedocument.presentationml.template', // .potx - 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', // .pptm - 'application/vnd.ms-powerpoint.template.macroEnabled.12', // .potm - 'application/vnd.oasis.opendocument.presentation', // .odp + 'application/vnd.ms-powerpoint', + 'application/vnd.ms-powerpoint.template', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', + 'application/vnd.ms-powerpoint.template.macroEnabled.12', + 'application/vnd.oasis.opendocument.presentation', ]; const SUPPORTED_AUDIO_TYPES = [ 'audio/mpeg', 'audio/mp3', - 'audio/mp4', // MP3 and MP4 audio + 'audio/mp4', 'audio/x-m4a', 'audio/x-m4b', - 'audio/x-m4p', // M4A formats + 'audio/x-m4p', 'audio/wav', 'audio/ogg', - 'audio/flac', // Other audio formats + 'audio/flac', ]; -const SUPPORTED_VIDEO_TYPES = [ - 'video/mp4', - 'video/x-m4v', // MP4 formats - 'video/quicktime', // .mov, .qt - 'video/x-msvideo', // .avi - 'video/x-matroska', // .mkv -]; +const SUPPORTED_VIDEO_TYPES = ['video/mp4', 'video/x-m4v', 'video/quicktime', 'video/x-msvideo', 'video/x-matroska']; -const SUPPORTED_VISIO_TYPES = [ - 'application/vnd.visio', // .vsd -]; +const SUPPORTED_VISIO_TYPES = ['application/vnd.visio']; -// Combine all supported types for checking const SUPPORTED_TYPES = [ ...SUPPORTED_IMAGE_TYPES, ...SUPPORTED_PDF_TYPES, @@ -107,49 +93,40 @@ export async function removeMetadata(file: File): Promise { const fileType = file.type; try { - // Check if file type is supported if (!SUPPORTED_TYPES.includes(fileType)) { throw new Error(`Unsupported file type: ${fileType}. Supported types are: ${SUPPORTED_TYPES.join(', ')}`); } - // Handle images if (SUPPORTED_IMAGE_TYPES.includes(fileType)) { return await handleImageMetadata(file); } - // Handle PDFs if (SUPPORTED_PDF_TYPES.includes(fileType)) { return await handlePdfMetadata(file); } - // Handle Word documents if (SUPPORTED_WORD_TYPES.includes(fileType)) { return await handleWordMetadata(file); } - // Handle Excel documents if (SUPPORTED_EXCEL_TYPES.includes(fileType)) { - return await handleWordMetadata(file); // Reuse Word handler as it works for Excel too + return await handleWordMetadata(file); } - // Handle PowerPoint documents if (SUPPORTED_POWERPOINT_TYPES.includes(fileType)) { - return await handleWordMetadata(file); // Reuse Word handler as it works for PowerPoint too + return await handleWordMetadata(file); } - // Handle audio files if (SUPPORTED_AUDIO_TYPES.includes(fileType)) { return await handleAudioMetadata(file); } - // Handle video files if (SUPPORTED_VIDEO_TYPES.includes(fileType)) { return await handleVideoMetadata(file); } - // Handle Visio files if (SUPPORTED_VISIO_TYPES.includes(fileType)) { - return await handlePdfMetadata(file); // Reuse PDF handler as it works for Visio too + return await handlePdfMetadata(file); } throw new Error(`Unsupported file type: ${fileType}`); @@ -160,18 +137,15 @@ export async function removeMetadata(file: File): Promise { } async function handleImageMetadata(file: File): Promise { - // First try to parse and remove EXIF data try { const exifData = await exifr.parse(file, { pick: ['all'] }); if (exifData) { - // If EXIF data exists, use canvas method to strip it return await stripImageMetadata(file); } } catch (error) { console.warn('No EXIF data found or error parsing:', error); } - // If no EXIF data or error, still try to strip any remaining metadata return await stripImageMetadata(file); } @@ -182,17 +156,13 @@ async function stripImageMetadata(file: File): Promise { return new Promise((resolve, reject) => { img.onload = () => { - // Set canvas dimensions to match image canvas.width = img.width; canvas.height = img.height; - // Clear any existing data ctx?.clearRect(0, 0, canvas.width, canvas.height); - // Draw image without metadata ctx?.drawImage(img, 0, 0); - // Convert to blob with original file type canvas.toBlob( (blob) => { if (blob) { @@ -202,7 +172,7 @@ async function stripImageMetadata(file: File): Promise { } }, file.type, - 1.0, // Maximum quality + 1.0, ); }; @@ -215,7 +185,6 @@ async function handlePdfMetadata(file: File): Promise { const arrayBuffer = await file.arrayBuffer(); const pdfDoc = await PDFDocument.load(arrayBuffer); - // Remove all standard PDF metadata pdfDoc.setTitle(''); pdfDoc.setAuthor(''); pdfDoc.setSubject(''); @@ -225,56 +194,23 @@ async function handlePdfMetadata(file: File): Promise { pdfDoc.setCreationDate(new Date(0)); pdfDoc.setModificationDate(new Date(0)); - // Create a new PDF document without metadata const newPdfDoc = await PDFDocument.create(); - // Copy pages without metadata const pages = pdfDoc.getPages(); const pageIndices = Array.from({ length: pages.length }, (_, i) => i); const copiedPages = await newPdfDoc.copyPages(pdfDoc, pageIndices); - // Add all copied pages to the new document copiedPages.forEach((page) => newPdfDoc.addPage(page)); - const pdfBytes = await newPdfDoc.save({ - useObjectStreams: false, // Disable object streams to remove some metadata - addDefaultPage: false, - }); + const pdfBytes = await newPdfDoc.save(); - return new Blob([pdfBytes], { type: 'application/pdf' }); + return new Blob([pdfBytes.buffer as ArrayBuffer], { type: 'application/pdf' }); } async function handleWordMetadata(file: File): Promise { const arrayBuffer = await file.arrayBuffer(); - const uint8Array = new Uint8Array(arrayBuffer); - - // Create a new document with minimal metadata - const doc = new Document({ - sections: [ - { - properties: {}, - children: [ - new Paragraph({ - children: [new TextRun('')], - }), - ], - }, - ], - styles: { - default: { - document: { - run: { - font: 'Calibri', - size: 24, - }, - }, - }, - }, - }); - // Copy content from original document but without metadata try { - // Create a new document from the buffer const originalDoc = new Document({ sections: [ { @@ -284,12 +220,10 @@ async function handleWordMetadata(file: File): Promise { ], }); - // Copy content without metadata const buffer = await Packer.toBuffer(originalDoc); return new Blob([buffer], { type: file.type }); } catch (error) { console.warn('Error copying document content:', error); - // Return original file if we can't process it return new Blob([arrayBuffer], { type: file.type }); } } @@ -299,21 +233,17 @@ async function handleAudioMetadata(file: File): Promise { const uint8Array = new Uint8Array(arrayBuffer); try { - // Parse metadata to see what we need to remove const metadata = await parseBuffer(uint8Array); - // For MP3 files, we can handle ID3 tags directly if (metadata.format.container === 'MPEG') { const cleanBuffer = await removeAudioMetadata(uint8Array, metadata); - return new Blob([cleanBuffer], { type: file.type }); + return new Blob([cleanBuffer.buffer as ArrayBuffer], { type: file.type }); } - // For other formats, we need FFmpeg if (typeof window === 'undefined') { throw new Error('FFmpeg is only available in the browser'); } - // Wait for FFmpeg to be loaded if (!ffmpeg) { await new Promise((resolve) => { const checkFFmpeg = setInterval(() => { @@ -339,32 +269,26 @@ async function handleAudioMetadata(file: File): Promise { return new Blob([data], { type: file.type }); } catch (error) { console.warn('Error removing audio metadata:', error); - // If we can't remove metadata, return original file return new Blob([arrayBuffer], { type: file.type }); } } async function removeAudioMetadata(buffer: Uint8Array, metadata: IAudioMetadata): Promise { - // For MP3 files, we need to handle ID3 tags if (metadata.format.container === 'MPEG') { - // Remove ID3v1 and ID3v2 tags const id3v1Size = 128; const id3v2HeaderSize = 10; - // Check for ID3v2 tag const decoder = new TextDecoder(); if (decoder.decode(buffer.slice(0, 3)) === 'ID3') { const id3v2Size = (buffer[6] << 21) | (buffer[7] << 14) | (buffer[8] << 7) | buffer[9]; buffer = buffer.slice(id3v2Size + id3v2HeaderSize); } - // Check for ID3v1 tag at the end if (decoder.decode(buffer.slice(buffer.length - id3v1Size, buffer.length - id3v1Size + 3)) === 'TAG') { buffer = buffer.slice(0, buffer.length - id3v1Size); } } - // For other formats, we'll use FFmpeg to strip metadata if (!ffmpeg.loaded) { await ffmpeg.load(); } @@ -376,15 +300,12 @@ async function removeAudioMetadata(buffer: Uint8Array, metadata: IAudioMetadata) await ffmpeg.exec(['-i', inputName, '-map_metadata', '-1', '-c:a', 'copy', outputName]); const data = await ffmpeg.readFile(outputName); - // Handle different data types that FFmpeg might return if (data instanceof Uint8Array) { return data; } else if (typeof data === 'string') { - // Convert string to Uint8Array if needed const encoder = new TextEncoder(); return encoder.encode(data); } - // Default case: try to create a Uint8Array from the data return new Uint8Array(data as ArrayBuffer); } @@ -393,7 +314,6 @@ async function handleVideoMetadata(file: File): Promise { throw new Error('FFmpeg is only available in the browser'); } - // Wait for FFmpeg to be loaded if (!ffmpeg) { await new Promise((resolve) => { const checkFFmpeg = setInterval(() => { @@ -417,7 +337,6 @@ async function handleVideoMetadata(file: File): Promise { await ffmpeg.writeFile(inputName, uint8Array); - // Use FFmpeg to strip all possible metadata while preserving video quality await ffmpeg.exec([ '-i', inputName, diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 8ad314f7d..1d87e656b 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -39,7 +39,6 @@ function MyApp({ Component, pageProps }: AppProps) { }; }, [router.events]); - // eslint-disable-next-line react/jsx-props-no-spreading return ( - {/* Google Tag Manager (noscript) */} - {/* End Google Tag Manager (noscript) */} - {hideIntercomButton ? null : }
@@ -110,13 +106,10 @@ function MyApp({ Component, pageProps }: AppProps) { <> - {/* */} ) : undefined} - {/* {shouldShowBeforeYouGoBanner ? : undefined} */}
- {/* Show snackbar in all pages */}
diff --git a/src/pages/antivirus.tsx b/src/pages/antivirus.tsx index 15c019f52..f9b0b4443 100644 --- a/src/pages/antivirus.tsx +++ b/src/pages/antivirus.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { GetServerSidePropsContext } from 'next'; import { FooterText, MetatagsDescription, NavigationBarText } from '@/assets/types/layout/types'; import Footer from '@/components/layout/footers/Footer'; @@ -96,7 +97,7 @@ const AntivirusPage = ({ - + { +const IntegratedCheckout = ({ textContent }: IntegratedCheckoutProps): JSX.Element => { const paymentService = new ObjStoragePaymentsService(process.env.NEXT_PUBLIC_PAYMENTS_API as string); const router = useRouter(); @@ -92,7 +94,8 @@ const IntegratedCheckout = ({ locale, textContent }: IntegratedCheckoutProps): J setPlan(plan); loadStripeElements(textColor, backgroundColor, borderColor, borderInputColor, plan); }) - .catch(() => { + .catch((err) => { + console.error('Plan fetch failed, redirecting to storage page:', err); router.push('/cloud-object-storage'); }); }, []); @@ -267,6 +270,7 @@ const IntegratedCheckout = ({ locale, textContent }: IntegratedCheckoutProps): J setCoupon(couponData); setCouponError(''); } catch (error) { + console.warn('Coupon validation failed:', error); setCouponError(textContent.invalidCoupon); setCoupon(undefined); } diff --git a/src/pages/cloud-storage-for-photos.tsx b/src/pages/cloud-storage-for-photos.tsx index e49c92dc4..6d35bffd5 100644 --- a/src/pages/cloud-storage-for-photos.tsx +++ b/src/pages/cloud-storage-for-photos.tsx @@ -97,7 +97,6 @@ const CloudStorageBackupSolutions = ({ diff --git a/src/pages/password-checker.tsx b/src/pages/password-checker.tsx index 24c63f9bf..291fc4423 100644 --- a/src/pages/password-checker.tsx +++ b/src/pages/password-checker.tsx @@ -51,7 +51,7 @@ const PasswordChecker = ({ url={'https://drive.internxt.com/new?utm_source=website&utm_medium=banner&utm_campaign=internxtpw'} /> - + { +const PCComponentes5tb = ({ metatagsDescriptions, lang }): JSX.Element => { const metatags = metatagsDescriptions.filter((desc) => desc.id === 'pricing'); - const [pageName, setPageName] = useState('Pricing Individuals Annually'); + const [pageName] = useState('Pricing Individuals Annually'); - const { products, currency, currencyValue, loadingCards, coupon } = usePricing({ + const { currency, currencyValue, loadingCards, coupon } = usePricing({ couponCode: PromoCodeName.PcComponentes5TB, }); diff --git a/src/pages/pccomponentes-business.tsx b/src/pages/pccomponentes-business.tsx index 26bc3c6df..94daefa8c 100644 --- a/src/pages/pccomponentes-business.tsx +++ b/src/pages/pccomponentes-business.tsx @@ -1,7 +1,7 @@ import { useState } from 'react'; import Layout from '@/components/layout/Layout'; -import { Interval, stripeService } from '@/services/stripe.service'; +import { stripeService } from '@/services/stripe.service'; import usePricing from '@/hooks/usePricing'; import { PromoCodeName } from '@/lib/types'; import { PricingSectionWrapper } from '@/components/shared/pricing/PricingSectionWrapper'; @@ -10,10 +10,9 @@ const ALLOWED_LANGUAGES = ['es', 'fr', 'pt-br']; const PCComponentesBusiness = ({ metatagsDescriptions, textContent, lang }): JSX.Element => { const metatags = metatagsDescriptions.filter((desc) => desc.id === 'pricing'); - const [pageName, setPageName] = useState('Pricing Individuals Annually'); + const [pageName] = useState('Pricing Individuals Annually'); - const [billingFrequency, setBillingFrequency] = useState(Interval.Year); - const { products, currency, currencyValue, loadingCards, businessCoupon } = usePricing({ + const { products, currencyValue, loadingCards, businessCoupon } = usePricing({ couponCodeForBusiness: PromoCodeName.PcComponentesCoupon, }); diff --git a/src/pages/pccomponentes-products.tsx b/src/pages/pccomponentes-products.tsx index c9cd1af50..0a2e79d17 100644 --- a/src/pages/pccomponentes-products.tsx +++ b/src/pages/pccomponentes-products.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { useState } from 'react'; import { Transition } from '@headlessui/react'; @@ -23,7 +24,6 @@ const PCComponentesProducts = ({ metatagsDescriptions, textContent, lang }): JSX const contentText = textContent; const isIndividual = activeSwitchPlan !== 'Business'; - const isIndividualSwitchEnabled = billingFrequency === Interval.Year; const isSubscription = activeSwitchPlan === 'Individuals'; const isLifetime = activeSwitchPlan === 'Lifetime'; diff --git a/src/services/file-compressor.service.ts b/src/services/file-compressor.service.ts index 81822aeb7..ee12d9f4a 100644 --- a/src/services/file-compressor.service.ts +++ b/src/services/file-compressor.service.ts @@ -66,9 +66,6 @@ const compressExcelFile = async (file: File): Promise => { workbook.SheetNames.forEach((sheetName) => { const worksheet = workbook.Sheets[sheetName]; - // Remove empty rows and columns - const range = XLSX.utils.decode_range(worksheet['!ref'] || 'A1'); - // Clean up the worksheet by removing empty cells Object.keys(worksheet).forEach((key) => { if (key.startsWith('!')) return; // Skip special keys @@ -199,12 +196,6 @@ const compressPowerPointFile = async (file: File, fileExtension: string): Promis let contentEnd = uint8Array.length; // Look for common PPT file endings and remove trailing data - const pptEndings = [ - // Common PPT file endings - [0x00, 0x00, 0x00, 0x00], // Null padding - [0xff, 0xff, 0xff, 0xff], // Filled padding - [0x20, 0x20, 0x20, 0x20], // Space padding - ]; // Remove trailing zeros and padding while (contentEnd > 0) { @@ -298,8 +289,6 @@ const compressPowerPointFile = async (file: File, fileExtension: string): Promis */ const compressOfficeDocument = async (file: File, fileExtension: string): Promise => { try { - const arrayBuffer = await file.arrayBuffer(); - // Handle Excel files with xlsx library if (['xls', 'xlsx'].includes(fileExtension.toLowerCase())) { return await compressExcelFile(file); diff --git a/src/services/impact.service.ts b/src/services/impact.service.ts index e6e580da1..ea1b8b589 100644 --- a/src/services/impact.service.ts +++ b/src/services/impact.service.ts @@ -51,9 +51,9 @@ export const handleImpact = async ({ const { data } = await axios.get(`${process.env.NEXT_PUBLIC_COUNTRY_API_URL}`); ip = data.ip; } catch (error) { + console.warn('IP lookup service unavailable, defaulting to undefined', error); ip = undefined; } - const impactAnonymousId = getCookie('impactAnonymousId'); const randomUUID = impactAnonymousId ?? crypto.randomUUID(); @@ -69,6 +69,6 @@ export const handleImpact = async ({ try { await sendImpactTrack({ randomUUID, ip, userAgent, page }); } catch (error) { - // + console.warn('Analytics tracking failed:', error); } };