From e728762b4b32a67f08c99fc11dccbdb41698b478 Mon Sep 17 00:00:00 2001 From: Matthias Luger Date: Mon, 23 Feb 2026 21:36:58 +0100 Subject: [PATCH 1/5] Add ads into generic lists --- .../GenericFlipList/GenericFlipList.tsx | 40 ++++++++++++++++--- .../ListItemAdElement/ListItemAdElement.tsx | 29 ++++++++++++++ hooks/useNitroAds.ts | 6 +-- 3 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 components/ListItemAdElement/ListItemAdElement.tsx diff --git a/components/GenericFlipList/GenericFlipList.tsx b/components/GenericFlipList/GenericFlipList.tsx index 23cc7c8f7..7bd7df2e9 100644 --- a/components/GenericFlipList/GenericFlipList.tsx +++ b/components/GenericFlipList/GenericFlipList.tsx @@ -8,6 +8,7 @@ import GoogleSignIn from '../GoogleSignIn/GoogleSignIn' import api from '../../api/ApiHelper' import styles from './GenericFlipList.module.css' import { useSortedAndFilteredItems } from '../../hooks/useSortedAndFilteredItems' +import ListItemAdElement from '../ListItemAdElement/ListItemAdElement' export interface FlipListProps { items: T[] @@ -68,6 +69,8 @@ export function GenericFlipList({ const [showTechSavvyMessage, setShowTechSavvyMessage] = useState(false) const [columns, setColumns] = useState() const [showPremiumModal, setShowPremiumModal] = useState(false) + const [listElementSizes, setListElementSizes] = useState<{ width: number; height: number }>() + const listRef = React.useRef(null) const { processedItems, isProcessing } = useSortedAndFilteredItems(items, orderBy, nameFilter, minimumProfit, filterFunction, sortFunctionArgs) @@ -101,6 +104,14 @@ export function GenericFlipList({ return () => observer.disconnect() }, [sentinelRef.current, processedItems.length, batchSize]) + useEffect(() => { + if (listRef.current && listRef.current.children) { + let height = listRef.current.children[0]?.clientHeight - 15 || 0 + let width = listRef.current.children[0]?.clientWidth - 15 || 0 + setListElementSizes({ width: width, height: height }) + } + }, [listRef.current]) + function setBlurObserver() { if (observer) { observer.disconnect() @@ -302,7 +313,20 @@ export function GenericFlipList({ let shown = 0 // Only render up to renderedCount to reduce DOM size const toRender = processedItems.slice(0, renderedCount) - const list = toRender.map(item => { + const list: React.ReactNode[] = [] + toRender.forEach((item, i) => { + + let currColumns = columns || getDefaultColumns() + if ((list.length + 1) % currColumns === 0) { + let ad: React.ReactNode = null; + if (listElementSizes) { + ad = + } else { + ad = getListElement(item, true) + } + list.push(ad) + } + const defaultContent = getListElement(item, false) if (!hasPremium && ++shown <= 3) { @@ -310,24 +334,28 @@ export function GenericFlipList({ const censoredContent = getListElement(censoredItem, true) if (customItemWrapper) { - return customItemWrapper(censoredItem, true, getItemKeyAction(item), censoredContent, styles.flipCard) + list.push(customItemWrapper(censoredItem, true, getItemKeyAction(item), censoredContent, styles.flipCard)); + return; } - return ( + list.push(
{censoredContent}
) + return; } else { if (customItemWrapper) { - return customItemWrapper(item, false, getItemKeyAction(item), defaultContent, styles.flipCard) + list.push(customItemWrapper(item, false, getItemKeyAction(item), defaultContent, styles.flipCard)); + return; } - return ( + list.push(
{defaultContent}
) + return; } }) @@ -391,7 +419,7 @@ export function GenericFlipList({ const insertIndex = Math.max(0, visibleList.length - 6) visibleList.splice(insertIndex, 0,
) } - return {visibleList} + return {visibleList} })()} )} diff --git a/components/ListItemAdElement/ListItemAdElement.tsx b/components/ListItemAdElement/ListItemAdElement.tsx new file mode 100644 index 000000000..176b31368 --- /dev/null +++ b/components/ListItemAdElement/ListItemAdElement.tsx @@ -0,0 +1,29 @@ +'use client' + +import NitroAdSlot from '../Ads/NitroAdSlot' + + +interface ListItemAdElementProps { + slotId: string, + sizes: [number, number][] +} + +export default function ListItemAdElement(props: ListItemAdElementProps) { + + return ( + + ) +} diff --git a/hooks/useNitroAds.ts b/hooks/useNitroAds.ts index eded80d3f..3aef77ad8 100644 --- a/hooks/useNitroAds.ts +++ b/hooks/useNitroAds.ts @@ -37,7 +37,7 @@ export function useNitroAds(slotId: string, config: Record, enabled console.warn('Failed to remove NitroAd via API:', e) } } - + // This prevents React from trying to manipulate nodes that may have been modified by ad scripts try { const adContainer = document.getElementById(slotId) @@ -64,7 +64,7 @@ export function useNitroAds(slotId: string, config: Record, enabled } catch (e) { console.debug('Ad container cleanup skipped (already removed):', slotId) } - + hasRequestedRef.current = false }, [slotId]) @@ -105,7 +105,7 @@ export function useNitroAds(slotId: string, config: Record, enabled if (cancelled) return const created = createAd() - + if (!created && attempt < maxAttempts) { attempt++ const delay = Math.min(500, 100 + attempt * 50) From 3c883082983bad8d181a828bbaeae4ffdc7b01d5 Mon Sep 17 00:00:00 2001 From: Matthias Luger Date: Wed, 25 Feb 2026 18:58:25 +0100 Subject: [PATCH 2/5] Add support for to store column settings in GenericFlipList and add ad element styling --- .../GenericFlipList.module.css | 1 + .../GenericFlipList/GenericFlipList.tsx | 37 +++++++++++++++---- .../ListItemAdElement.module.css | 3 ++ .../ListItemAdElement/ListItemAdElement.tsx | 1 - utils/SettingsUtils.tsx | 1 + 5 files changed, 34 insertions(+), 9 deletions(-) create mode 100644 components/ListItemAdElement/ListItemAdElement.module.css diff --git a/components/GenericFlipList/GenericFlipList.module.css b/components/GenericFlipList/GenericFlipList.module.css index 859a71f36..5b77202cf 100644 --- a/components/GenericFlipList/GenericFlipList.module.css +++ b/components/GenericFlipList/GenericFlipList.module.css @@ -18,6 +18,7 @@ width: calc(100% - 10px); height: auto; padding-bottom: 15px; + line-height: 1.8; } @media all and (min-width: 768px) { diff --git a/components/GenericFlipList/GenericFlipList.tsx b/components/GenericFlipList/GenericFlipList.tsx index 7bd7df2e9..775ed6449 100644 --- a/components/GenericFlipList/GenericFlipList.tsx +++ b/components/GenericFlipList/GenericFlipList.tsx @@ -9,6 +9,7 @@ import api from '../../api/ApiHelper' import styles from './GenericFlipList.module.css' import { useSortedAndFilteredItems } from '../../hooks/useSortedAndFilteredItems' import ListItemAdElement from '../ListItemAdElement/ListItemAdElement' +import { GENRIC_FLIP_LIST_COLUMNS, getSetting, setSetting } from '../../utils/SettingsUtils' export interface FlipListProps { items: T[] @@ -67,7 +68,7 @@ export function GenericFlipList({ const [hasPremium, setHasPremium] = useState(false) const [isLoggedIn, setIsLoggedIn] = useState(false) const [showTechSavvyMessage, setShowTechSavvyMessage] = useState(false) - const [columns, setColumns] = useState() + const [columns, _setColumns] = useState() const [showPremiumModal, setShowPremiumModal] = useState(false) const [listElementSizes, setListElementSizes] = useState<{ width: number; height: number }>() const listRef = React.useRef(null) @@ -83,11 +84,17 @@ export function GenericFlipList({ useEffect(() => { setTimeout(setBlurObserver, 100) if (showColumns) { - setColumns(getDefaultColumns()) + let columns = parseInt(getSetting(GENRIC_FLIP_LIST_COLUMNS, "0")) + setColumns(isNaN(columns) ? getDefaultColumns() : columns.valueOf()) } setRenderedCount(Math.max(3, safeInitial)) }, []) + function setColumns(value: number) { + _setColumns(value) + setSetting(GENRIC_FLIP_LIST_COLUMNS, value) + } + // Observe the sentinel to incrementally render more items when the user // scrolls near the end of the currently rendered batch. useEffect(() => { @@ -110,7 +117,7 @@ export function GenericFlipList({ let width = listRef.current.children[0]?.clientWidth - 15 || 0 setListElementSizes({ width: width, height: height }) } - }, [listRef.current]) + }, [listRef.current, columns, showColumns]) function setBlurObserver() { if (observer) { @@ -155,6 +162,19 @@ export function GenericFlipList({ } } + function getAdSizes() { + let sizes: [number, number][] = [[300, 250], [336, 280], [320, 100], [970, 90], [728, 90], [970, 250]] + if (listElementSizes) { + // Filter ad sizes to not exceed list element width + // Height can be up to 20% higher than list elements + sizes = sizes.filter(size => + size[0] <= listElementSizes.width && + size[1] <= listElementSizes.height * 1.5 + ) + } + return sizes; + } + const onNameFilterChange = useCallback((e: any) => { setNameFilter(e.target.value) }, []) @@ -316,11 +336,12 @@ export function GenericFlipList({ const list: React.ReactNode[] = [] toRender.forEach((item, i) => { - let currColumns = columns || getDefaultColumns() - if ((list.length + 1) % currColumns === 0) { + if ((list.length + 1) % 12 === 0 || (!hasPremium && i === 1)) { let ad: React.ReactNode = null; if (listElementSizes) { - ad = + ad =
+ +
} else { ad = getListElement(item, true) } @@ -329,7 +350,7 @@ export function GenericFlipList({ const defaultContent = getListElement(item, false) - if (!hasPremium && ++shown <= 3) { + if (!hasPremium && ++shown <= 2) { const censoredItem = censoredItemGenerator ? censoredItemGenerator(item) : item const censoredContent = getListElement(censoredItem, true) @@ -360,7 +381,7 @@ export function GenericFlipList({ }) return list - }, [processedItems, hasPremium, isProcessing, censoredItemGenerator, customItemWrapper, renderedCount]) + }, [processedItems, hasPremium, isProcessing, censoredItemGenerator, customItemWrapper, renderedCount, listElementSizes]) const flipListClass = showColumns && columns ? `${styles.flipList} ${styles[`columns-${columns}`]}` : styles.flipList return ( diff --git a/components/ListItemAdElement/ListItemAdElement.module.css b/components/ListItemAdElement/ListItemAdElement.module.css new file mode 100644 index 000000000..d4dc3dbc1 --- /dev/null +++ b/components/ListItemAdElement/ListItemAdElement.module.css @@ -0,0 +1,3 @@ +.adStyle { + min-height: auto !important; +} \ No newline at end of file diff --git a/components/ListItemAdElement/ListItemAdElement.tsx b/components/ListItemAdElement/ListItemAdElement.tsx index 176b31368..ec0d962c8 100644 --- a/components/ListItemAdElement/ListItemAdElement.tsx +++ b/components/ListItemAdElement/ListItemAdElement.tsx @@ -2,7 +2,6 @@ import NitroAdSlot from '../Ads/NitroAdSlot' - interface ListItemAdElementProps { slotId: string, sizes: [number, number][] diff --git a/utils/SettingsUtils.tsx b/utils/SettingsUtils.tsx index d842d0dec..352d41069 100644 --- a/utils/SettingsUtils.tsx +++ b/utils/SettingsUtils.tsx @@ -525,3 +525,4 @@ export const ITEM_FILER_SHOW_ADVANCED = 'itemFilterShowAdvanced' export const AUTO_REDIRECT_FROM_LINKVERTISE_EXPLANATION = 'autoRedirectFromLinkvertiseExplanation' export const ITEM_ICON_TYPE = 'itemIconType' export const ITEM_FAVORITES_KEY = 'favoriteItems' +export const GENRIC_FLIP_LIST_COLUMNS = 'genericFlipListColumns' From fcc3c9814aada4755243710675e4e7544d4123e2 Mon Sep 17 00:00:00 2001 From: Matthias Luger Date: Wed, 25 Feb 2026 19:44:21 +0100 Subject: [PATCH 3/5] prevent duplicate keys --- components/GenericFlipList/GenericFlipList.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/GenericFlipList/GenericFlipList.tsx b/components/GenericFlipList/GenericFlipList.tsx index 775ed6449..b121b38ea 100644 --- a/components/GenericFlipList/GenericFlipList.tsx +++ b/components/GenericFlipList/GenericFlipList.tsx @@ -343,7 +343,9 @@ export function GenericFlipList({
} else { - ad = getListElement(item, true) + ad =
+ {getListElement(item, true)} +
} list.push(ad) } From 7486ac7fee6484bbc1a620c218afe3077f6d8dd3 Mon Sep 17 00:00:00 2001 From: Matthias Luger Date: Wed, 25 Feb 2026 19:49:10 +0100 Subject: [PATCH 4/5] Fix default column setting retrieval in GenericFlipList --- components/GenericFlipList/GenericFlipList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/GenericFlipList/GenericFlipList.tsx b/components/GenericFlipList/GenericFlipList.tsx index b121b38ea..5574330f4 100644 --- a/components/GenericFlipList/GenericFlipList.tsx +++ b/components/GenericFlipList/GenericFlipList.tsx @@ -84,7 +84,7 @@ export function GenericFlipList({ useEffect(() => { setTimeout(setBlurObserver, 100) if (showColumns) { - let columns = parseInt(getSetting(GENRIC_FLIP_LIST_COLUMNS, "0")) + let columns = parseInt(getSetting(GENRIC_FLIP_LIST_COLUMNS, getDefaultColumns().toString())) setColumns(isNaN(columns) ? getDefaultColumns() : columns.valueOf()) } setRenderedCount(Math.max(3, safeInitial)) From 9c5650d445ca7551c100199d4a951ce8bbccdf0e Mon Sep 17 00:00:00 2001 From: matthias-luger <58751503+matthias-luger@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:29:39 +0100 Subject: [PATCH 5/5] Update components/GenericFlipList/GenericFlipList.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- components/GenericFlipList/GenericFlipList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/GenericFlipList/GenericFlipList.tsx b/components/GenericFlipList/GenericFlipList.tsx index 5574330f4..2f2c47af6 100644 --- a/components/GenericFlipList/GenericFlipList.tsx +++ b/components/GenericFlipList/GenericFlipList.tsx @@ -339,7 +339,7 @@ export function GenericFlipList({ if ((list.length + 1) % 12 === 0 || (!hasPremium && i === 1)) { let ad: React.ReactNode = null; if (listElementSizes) { - ad =
+ ad =
} else {