diff --git a/app/components/cart/AddProductToast.tsx b/app/components/cart/AddProductToast.tsx index 65a88cd..9dd678c 100644 --- a/app/components/cart/AddProductToast.tsx +++ b/app/components/cart/AddProductToast.tsx @@ -15,7 +15,7 @@ interface ToastProduct { isOnSale?: boolean; quantity: number; url: string; - options: Array<{ name: string; value: string; hideValue?: boolean }>; + options: { name: string; value: string; hideValue?: boolean }[]; } interface ProductAddedEntry { @@ -25,7 +25,7 @@ interface ProductAddedEntry { interface AddItemResponse { data?: { - cart?: { items?: Array<{ id: string | number; product_id?: string | number }> }; + cart?: { items?: { id: string | number; product_id?: string | number }[] }; events?: { 'twilight::api'?: { events?: { 'Product Added'?: ProductAddedEntry[] } } }; }; } @@ -194,10 +194,10 @@ export function AddProductToast() { // `Product Added` lists every cart line; `cart::item.added` names the product that was added. useEffect(() => { - type EventBus = { + interface EventBus { on?: (name: string, cb: (...args: unknown[]) => void) => void; off?: (name: string, cb: (...args: unknown[]) => void) => void; - }; + } // The emitter the listeners were added to. Cleanup removes them from this // one, not from whatever `window.salla.event` is by then — after an SDK // re-init a fresh lookup would `off` a different emitter and leave these bound. diff --git a/app/components/cart/CartPage.tsx b/app/components/cart/CartPage.tsx index 788648b..0a07285 100644 --- a/app/components/cart/CartPage.tsx +++ b/app/components/cart/CartPage.tsx @@ -34,7 +34,7 @@ function CartPageContent({ page }: CartPageProps) { const cartId = cartIdQuery.data; const cartQuery = useQuery({ - ...cart.queries.detail(cartId!), + ...cart.queries.detail(cartId ?? 0), enabled: !!cartId, }); const cartData = cartQuery.data; diff --git a/app/components/common/BottomSheet.tsx b/app/components/common/BottomSheet.tsx index 9255696..a395310 100644 --- a/app/components/common/BottomSheet.tsx +++ b/app/components/common/BottomSheet.tsx @@ -49,12 +49,10 @@ export function BottomSheet({ useScrollLock(open); + if (open && !mounted) setMounted(true); + useEffect(() => { - if (open) { - setMounted(true); - return; - } - setEntered(false); + if (open) return; const unmount = setTimeout(() => setMounted(false), TRANSITION_MS); return () => clearTimeout(unmount); }, [open]); @@ -62,7 +60,11 @@ export function BottomSheet({ useEffect(() => { if (!mounted || !open) return; void sheetRef.current?.offsetWidth; - return afterNextPaint(() => setEntered(true)); + const cancelEnter = afterNextPaint(() => setEntered(true)); + return () => { + cancelEnter(); + setEntered(false); + }; }, [mounted, open]); // Move focus into the sheet on open, trap Tab, restore it to the trigger on close. diff --git a/app/components/home/EnhancedSlider.tsx b/app/components/home/EnhancedSlider.tsx index 6ba3650..2213082 100644 --- a/app/components/home/EnhancedSlider.tsx +++ b/app/components/home/EnhancedSlider.tsx @@ -1,6 +1,7 @@ import { memo, useCallback, useEffect, useRef, useState, type RefObject } from 'react'; import { SallaSlider } from '@salla.sa/twilight-components-react/slider'; import { Link } from '@salla.sa/twilight-theme-engine/common'; +import { useTranslation } from '@salla.sa/twilight-theme-engine/i18n'; import { parseSlideVideo, youtubeEmbedUrl } from '../../lib/slideVideo'; import { useMotionAllowed } from '../../hooks/useMotionAllowed'; @@ -99,6 +100,7 @@ const YOUTUBE_PLAYING = 1; * the page says it is listening, and the frame answers with its state. */ function YouTubeBackground({ id }: { id: string }) { + const { t } = useTranslation(); const ref = useRef(null); const [state, setState] = useState<'loading' | 'playing' | 'failed'>('loading'); @@ -151,13 +153,12 @@ function YouTubeBackground({ id }: { id: string }) { if (state === 'failed') return null; return ( - // eslint-disable-next-line jsx-a11y/iframe-has-title -- decorative background video; the empty title is intentional