diff --git a/packages/shared/src/components/FeedItemComponent.tsx b/packages/shared/src/components/FeedItemComponent.tsx index 243de76c3df..3744e8733c1 100644 --- a/packages/shared/src/components/FeedItemComponent.tsx +++ b/packages/shared/src/components/FeedItemComponent.tsx @@ -268,7 +268,6 @@ function FeedItemComponent({ onCommentClick, onReadArticleClick, virtualizedNumCards, - disableAdRefresh, }: FeedItemComponentProps): ReactElement | null { const { logEvent } = useLogContext(); const queryClient = useQueryClient(); @@ -516,11 +515,6 @@ function FeedItemComponent({ index={item.index} feedIndex={index} onLinkClick={(ad: Ad) => onAdAction(AdActions.Click, ad)} - onRefresh={ - disableAdRefresh - ? undefined - : (ad: Ad) => onAdAction(AdActions.Refresh, ad) - } /> ); } diff --git a/packages/shared/src/components/cards/ad/AdCard.spec.tsx b/packages/shared/src/components/cards/ad/AdCard.spec.tsx index 5e1df160a2a..89dc6368ff4 100644 --- a/packages/shared/src/components/cards/ad/AdCard.spec.tsx +++ b/packages/shared/src/components/cards/ad/AdCard.spec.tsx @@ -2,6 +2,7 @@ import React from 'react'; import type { RenderResult } from '@testing-library/react'; import { render, screen, waitFor, within } from '@testing-library/react'; import { QueryClient } from '@tanstack/react-query'; +import { GrowthBook } from '@growthbook/growthbook-react'; import ad from '../../../../__tests__/fixture/ad'; import { AdGrid } from './AdGrid'; import { AdList } from './AdList'; @@ -9,9 +10,12 @@ import { SignalAdList } from './SignalAdList'; import type { AdCardProps } from './common/common'; import { TestBootProvider } from '../../../../__tests__/helpers/boot'; import { ActiveFeedContext } from '../../../contexts'; +import { businessWebsiteUrl } from '../../../lib/constants'; const defaultProps: AdCardProps = { ad, + index: 0, + feedIndex: 0, onLinkClick: jest.fn(), }; @@ -19,24 +23,14 @@ beforeEach(() => { jest.clearAllMocks(); }); -const renderComponent = (props: Partial = {}): RenderResult => { - const client = new QueryClient(); - return render( - - - - - , - ); -}; - const renderListComponent = ( props: Partial = {}, + gb = new GrowthBook(), ): RenderResult => { const client = new QueryClient(); return render( - - + + , @@ -45,22 +39,50 @@ const renderListComponent = ( const renderSignalListComponent = ( props: Partial = {}, + gb = new GrowthBook(), ): RenderResult => { const client = new QueryClient(); return render( - - + + , ); }; +const getGrowthBook = (isAdReferralCtaEnabled = false): GrowthBook => { + const gb = new GrowthBook(); + + gb.setFeatures({ + ad_referral_cta: { + defaultValue: isAdReferralCtaEnabled, + }, + }); + + return gb; +}; + const getNormalizedText = (element?: Element | null): string => element?.textContent?.replace(/\u200B/g, '').trim() ?? ''; +const renderGridComponent = ( + props: Partial = {}, + gb = new GrowthBook(), +): RenderResult => { + const client = new QueryClient(); + + return render( + + + + + , + ); +}; + it('should call on click on component left click', async () => { - renderComponent(); + renderGridComponent(); const el = await screen.findByTestId('adItem'); const links = await within(el).findAllByRole('link'); links[0].click(); @@ -68,7 +90,7 @@ it('should call on click on component left click', async () => { }); it('should call on click on component middle mouse up', async () => { - renderComponent(); + renderGridComponent(); const el = await screen.findByTestId('adItem'); const links = await within(el).findAllByRole('link'); links[0].dispatchEvent( @@ -78,7 +100,7 @@ it('should call on click on component middle mouse up', async () => { }); it('should show a single image by default', async () => { - renderComponent(); + renderGridComponent(); const img = await screen.findByAltText('Ad image'); const background = screen.queryByAltText('Ad image background'); expect(img).toBeInTheDocument(); @@ -86,7 +108,7 @@ it('should show a single image by default', async () => { }); it('should show blurred image for carbon', async () => { - renderComponent({ ad: { ...ad, source: 'Carbon' } }); + renderGridComponent({ ad: { ...ad, source: 'Carbon' } }); const img = await screen.findByAltText('Ad image'); const background = screen.queryByAltText('Ad image background'); expect(img).toHaveClass('absolute'); @@ -94,13 +116,30 @@ it('should show blurred image for carbon', async () => { }); it('should show pixel images', async () => { - renderComponent({ + renderGridComponent({ ad: { ...ad, pixel: ['https://daily.dev/pixel'] }, }); const el = await screen.findByTestId('pixel'); expect(el).toHaveAttribute('src', 'https://daily.dev/pixel'); }); +it('should not render advertise link by default', () => { + renderGridComponent(); + + expect( + screen.queryByRole('link', { name: 'Advertise here' }), + ).not.toBeInTheDocument(); +}); + +it('should render advertise link on grid ad when feature is enabled', () => { + renderGridComponent({}, getGrowthBook(true)); + + expect(screen.getByRole('link', { name: 'Advertise here' })).toHaveAttribute( + 'href', + businessWebsiteUrl, + ); +}); + it('should render promoted attribution outside of list title clamp', async () => { const promotedMatcher = (_: string, element?: Element | null): boolean => { const text = getNormalizedText(element); @@ -114,6 +153,15 @@ it('should render promoted attribution outside of list title clamp', async () => expect(await screen.findByText(promotedMatcher)).toBeInTheDocument(); }); +it('should render advertise link on list ad when feature is enabled', () => { + renderListComponent({}, getGrowthBook(true)); + + expect(screen.getByRole('link', { name: 'Advertise here' })).toHaveAttribute( + 'href', + businessWebsiteUrl, + ); +}); + it('should render company logo and company name in signal ad header', async () => { const companyLogo = 'https://daily.dev/company-logo.png'; const companyName = 'daily.dev'; @@ -126,3 +174,12 @@ it('should render company logo and company name in signal ad header', async () = expect(logo).toHaveAttribute('src', companyLogo); expect(screen.getByText(companyName)).toBeInTheDocument(); }); + +it('should render advertise link on signal list ad when feature is enabled', () => { + renderSignalListComponent({}, getGrowthBook(true)); + + expect(screen.getByRole('link', { name: 'Advertise here' })).toHaveAttribute( + 'href', + businessWebsiteUrl, + ); +}); diff --git a/packages/shared/src/components/cards/ad/AdGrid.tsx b/packages/shared/src/components/cards/ad/AdGrid.tsx index 9f6680b99c1..20ecab93450 100644 --- a/packages/shared/src/components/cards/ad/AdGrid.tsx +++ b/packages/shared/src/components/cards/ad/AdGrid.tsx @@ -1,4 +1,4 @@ -import type { ReactElement } from 'react'; +import type { ForwardedRef, ReactElement } from 'react'; import React, { forwardRef, useCallback } from 'react'; import { @@ -18,31 +18,39 @@ import { RemoveAd } from './common/RemoveAd'; import { usePlusSubscription } from '../../../hooks/usePlusSubscription'; import type { InViewRef } from '../../../hooks/feed/useAutoRotatingAds'; import { useAutoRotatingAds } from '../../../hooks/feed/useAutoRotatingAds'; -import { AdRefresh } from './common/AdRefresh'; import { Button } from '../../buttons/Button'; import { ButtonSize, ButtonVariant } from '../../buttons/common'; import { AdFavicon } from './common/AdFavicon'; import PostTags from '../common/PostTags'; import { useFeature } from '../../GrowthBookProvider'; import { adImprovementsV3Feature } from '../../../lib/featureManagement'; +import { TargetId } from '../../../lib/log'; +import { AdvertiseLink } from './common/AdvertiseLink'; -export const AdGrid = forwardRef(function AdGrid( - { ad, onLinkClick, onRefresh, domProps, index, feedIndex }: AdCardProps, - inViewRef: InViewRef, +export const AdGrid = forwardRef(function AdGrid( + { ad, onLinkClick, domProps, index, feedIndex }: AdCardProps, + forwardedRef: ForwardedRef, ): ReactElement { const { isPlus } = usePlusSubscription(); const adImprovementsV3 = useFeature(adImprovementsV3Feature); - const { ref, refetch, isRefetching } = useAutoRotatingAds( - ad, - index, - feedIndex, - inViewRef, - ); + const matchingTags = ad.matchingTags ?? []; + const inViewRef = useCallback( + (node) => { + const nextNode = node as HTMLElement | null; + + if (typeof forwardedRef === 'function') { + forwardedRef(nextNode); + return; + } - const onRefreshClick = useCallback(async () => { - onRefresh?.(ad); - await refetch(); - }, [ad, onRefresh, refetch]); + if (forwardedRef) { + const forwardedRefObject = forwardedRef; + forwardedRefObject.current = nextNode; + } + }, + [forwardedRef], + ); + const { ref } = useAutoRotatingAds(ad, index, feedIndex, inViewRef); return ( @@ -51,9 +59,9 @@ export const AdGrid = forwardRef(function AdGrid( {ad.description} - {adImprovementsV3 && ad?.matchingTags?.length > 0 ? ( + {adImprovementsV3 && matchingTags.length > 0 ? ( ) : null} @@ -62,33 +70,33 @@ export const AdGrid = forwardRef(function AdGrid(
- {!!ad.callToAction && ( - - )} -
- {!!onRefresh && ( - + {!!ad.callToAction && ( + )} + +
+
{!isPlus && ( )}
diff --git a/packages/shared/src/components/cards/ad/AdList.tsx b/packages/shared/src/components/cards/ad/AdList.tsx index 88cb46fd672..7e0bc05add7 100644 --- a/packages/shared/src/components/cards/ad/AdList.tsx +++ b/packages/shared/src/components/cards/ad/AdList.tsx @@ -1,4 +1,4 @@ -import type { AnchorHTMLAttributes, ReactElement } from 'react'; +import type { AnchorHTMLAttributes, ForwardedRef, ReactElement } from 'react'; import React, { forwardRef, useCallback } from 'react'; import { CardContent, @@ -17,7 +17,6 @@ import { RemoveAd } from './common/RemoveAd'; import { usePlusSubscription } from '../../../hooks/usePlusSubscription'; import type { InViewRef } from '../../../hooks/feed/useAutoRotatingAds'; import { useAutoRotatingAds } from '../../../hooks/feed/useAutoRotatingAds'; -import { AdRefresh } from './common/AdRefresh'; import { Button } from '../../buttons/Button'; import { ButtonSize, ButtonVariant } from '../../buttons/common'; import AdAttribution from './common/AdAttribution'; @@ -25,13 +24,15 @@ import { AdFavicon } from './common/AdFavicon'; import PostTags from '../common/PostTags'; import { useFeature } from '../../GrowthBookProvider'; import { adImprovementsV3Feature } from '../../../lib/featureManagement'; +import { TargetId } from '../../../lib/log'; +import { AdvertiseLink } from './common/AdvertiseLink'; const getLinkProps = ({ ad, onLinkClick, }: { ad: Ad; - onLinkClick: (ad: Ad) => unknown; + onLinkClick?: (ad: Ad) => unknown; }): AnchorHTMLAttributes => { return { href: ad.link, @@ -42,27 +43,34 @@ const getLinkProps = ({ }; }; -export const AdList = forwardRef(function AdCard( - { ad, onLinkClick, onRefresh, domProps, index, feedIndex }: AdCardProps, - inViewRef: InViewRef, +export const AdList = forwardRef(function AdCard( + { ad, onLinkClick, domProps, index, feedIndex }: AdCardProps, + forwardedRef: ForwardedRef, ): ReactElement { const { isPlus } = usePlusSubscription(); const adImprovementsV3 = useFeature(adImprovementsV3Feature); - const { ref, refetch, isRefetching } = useAutoRotatingAds( - ad, - index, - feedIndex, - inViewRef, - ); + const matchingTags = ad.matchingTags ?? []; + const inViewRef = useCallback( + (node) => { + const nextNode = node as HTMLElement | null; + + if (typeof forwardedRef === 'function') { + forwardedRef(nextNode); + return; + } - const onRefreshClick = useCallback(async () => { - onRefresh?.(ad); - await refetch(); - }, [ad, onRefresh, refetch]); + if (forwardedRef) { + const forwardedRefObject = forwardedRef; + forwardedRefObject.current = nextNode; + } + }, + [forwardedRef], + ); + const { ref } = useAutoRotatingAds(ad, index, feedIndex, inViewRef); return ( {ad.description} - {adImprovementsV3 && ad?.matchingTags?.length > 0 ? ( - + {adImprovementsV3 && matchingTags.length > 0 ? ( + ) : null}
- {!!ad.callToAction && ( - + )} + onLinkClick?.(ad))} - > - {ad.callToAction} - - )} -
- {!!onRefresh && ( - + /> +
+
+ {!isPlus && ( + )} - {!isPlus && }
diff --git a/packages/shared/src/components/cards/ad/SignalAdList.tsx b/packages/shared/src/components/cards/ad/SignalAdList.tsx index 5aa81d4f517..a4d927fce67 100644 --- a/packages/shared/src/components/cards/ad/SignalAdList.tsx +++ b/packages/shared/src/components/cards/ad/SignalAdList.tsx @@ -1,5 +1,5 @@ -import type { AnchorHTMLAttributes, ReactElement } from 'react'; -import React, { forwardRef } from 'react'; +import type { AnchorHTMLAttributes, ForwardedRef, ReactElement } from 'react'; +import React, { forwardRef, useCallback } from 'react'; import classNames from 'classnames'; import type { Ad } from '../../../graphql/posts'; import { combinedClicks } from '../../../lib/click'; @@ -11,7 +11,10 @@ import FeedItemContainer from '../common/list/FeedItemContainer'; import { ProfileImageSize } from '../../ProfilePicture'; import AdAttribution from './common/AdAttribution'; import { useFeature } from '../../GrowthBookProvider'; -import { adImprovementsV3Feature } from '../../../lib/featureManagement'; +import { + adImprovementsV3Feature, + featureAdReferralCta, +} from '../../../lib/featureManagement'; import PostTags from '../common/PostTags'; import { Button } from '../../buttons/Button'; import { ButtonSize, ButtonVariant } from '../../buttons/common'; @@ -20,13 +23,15 @@ import { AdPixel } from './common/AdPixel'; import { SourceAvatar } from '../../profile/source/SourceAvatar'; import { MiniCloseIcon } from '../../icons'; import { getAdFaviconImageLink } from './common/getAdFaviconImageLink'; +import { AdvertiseLink } from './common/AdvertiseLink'; +import { TargetId } from '../../../lib/log'; const getLinkProps = ({ ad, onLinkClick, }: { ad: Ad; - onLinkClick: (ad: Ad) => unknown; + onLinkClick?: (ad: Ad) => unknown; }): AnchorHTMLAttributes => { return { href: ad.link, @@ -37,86 +42,113 @@ const getLinkProps = ({ }; }; -export const SignalAdList = forwardRef(function SignalAdList( - { ad, onLinkClick, domProps, index, feedIndex }: AdCardProps, - inViewRef: InViewRef, -): ReactElement { - const { isPlus } = usePlusSubscription(); - const adImprovementsV3 = useFeature(adImprovementsV3Feature); - const { ref } = useAutoRotatingAds(ad, index, feedIndex, inViewRef); +export const SignalAdList = forwardRef( + function SignalAdList( + { ad, onLinkClick, domProps, index, feedIndex }: AdCardProps, + forwardedRef: ForwardedRef, + ): ReactElement { + const { isPlus } = usePlusSubscription(); + const adImprovementsV3 = Boolean(useFeature(adImprovementsV3Feature)); + const isAdReferralCtaEnabled = useFeature(featureAdReferralCta); + const matchingTags = ad.matchingTags ?? []; + const inViewRef = useCallback( + (node) => { + const nextNode = node as HTMLElement | null; - const sourceName = ad.company?.trim() || ad.source?.trim() || 'Promoted'; - const sourceImage = getAdFaviconImageLink({ - ad, - adImprovementsV3, - size: 20, - }); - const sourceHandle = ad.company?.trim() || ad.source?.trim() || 'promoted'; + if (typeof forwardedRef === 'function') { + forwardedRef(nextNode); + return; + } - return ( - -
-
- - - {sourceName} - - · - - {!isPlus && ( - } - className="relative z-1" + if (forwardedRef) { + const forwardedRefObject = forwardedRef; + forwardedRefObject.current = nextNode; + } + }, + [forwardedRef], + ); + const { ref } = useAutoRotatingAds(ad, index, feedIndex, inViewRef); + + const sourceName = ad.company?.trim() || ad.source?.trim() || 'Promoted'; + const sourceImage = getAdFaviconImageLink({ + ad, + adImprovementsV3, + size: 20, + }); + const sourceHandle = ad.company?.trim() || ad.source?.trim() || 'promoted'; + + return ( + +
+
+ + + {sourceName} + + · + + {!isPlus && ( + } + className="relative z-1" + /> + )} +
+

+ {ad.description} +

+ {adImprovementsV3 && matchingTags.length > 0 ? ( + + ) : null} + {(!!ad.callToAction || isAdReferralCtaEnabled) && ( +
+ {!!ad.callToAction && ( + + )} + +
)}
-

- {ad.description} -

- {adImprovementsV3 && ad?.matchingTags?.length > 0 ? ( - - ) : null} - {!!ad.callToAction && ( -
- -
- )} -
- - - ); -}); + + + ); + }, +); diff --git a/packages/shared/src/components/cards/ad/common/AdvertiseLink.tsx b/packages/shared/src/components/cards/ad/common/AdvertiseLink.tsx new file mode 100644 index 00000000000..2fc9862aac0 --- /dev/null +++ b/packages/shared/src/components/cards/ad/common/AdvertiseLink.tsx @@ -0,0 +1,87 @@ +import type { ReactElement } from 'react'; +import React, { useEffect } from 'react'; +import classNames from 'classnames'; +import { Button, ButtonSize, ButtonVariant } from '../../../buttons/Button'; +import { useFeature } from '../../../GrowthBookProvider'; +import { useLogContext } from '../../../../contexts/LogContext'; +import { combinedClicks } from '../../../../lib/click'; +import { featureAdReferralCta } from '../../../../lib/featureManagement'; +import { businessWebsiteUrl } from '../../../../lib/constants'; +import type { TargetId } from '../../../../lib/log'; +import { LogEvent, TargetType } from '../../../../lib/log'; +import { anchorDefaultRel } from '../../../../lib/strings'; + +type AdvertiseLinkProps = { + targetId: TargetId; + className?: string; + buttonStyle?: boolean; + size?: ButtonSize; +}; + +export const AdvertiseLink = ({ + targetId, + className, + buttonStyle = false, + size = ButtonSize.Medium, +}: AdvertiseLinkProps): ReactElement | null => { + const isEnabled = useFeature(featureAdReferralCta); + const { logEvent } = useLogContext(); + + useEffect(() => { + if (!isEnabled) { + return; + } + + logEvent({ + event_name: LogEvent.Impression, + target_type: TargetType.AdvertiseHereCta, + target_id: targetId, + }); + }, [isEnabled, logEvent, targetId]); + + const onClick = () => + logEvent({ + event_name: LogEvent.Click, + target_type: TargetType.AdvertiseHereCta, + target_id: targetId, + }); + + if (!isEnabled) { + return null; + } + + if (buttonStyle) { + return ( + + ); + } + + return ( + + Advertise here + + ); +}; diff --git a/packages/shared/src/components/cards/ad/common/RemoveAd.tsx b/packages/shared/src/components/cards/ad/common/RemoveAd.tsx index a6b8aad01a4..ff7eb68af9e 100644 --- a/packages/shared/src/components/cards/ad/common/RemoveAd.tsx +++ b/packages/shared/src/components/cards/ad/common/RemoveAd.tsx @@ -38,7 +38,7 @@ export const RemoveAd = ({ }} {...props} > - {!iconOnly ? 'Remove' : undefined} + {!iconOnly ? 'Go ad-free' : undefined} ); diff --git a/packages/shared/src/components/cards/ad/common/common.tsx b/packages/shared/src/components/cards/ad/common/common.tsx index 59b350e986d..2b56f91c8e6 100644 --- a/packages/shared/src/components/cards/ad/common/common.tsx +++ b/packages/shared/src/components/cards/ad/common/common.tsx @@ -7,6 +7,5 @@ export interface AdCardProps { index: number; feedIndex: number; onLinkClick?: Callback; - onRefresh?: Callback; domProps?: HTMLAttributes; } diff --git a/packages/shared/src/components/comments/AdAsComment.tsx b/packages/shared/src/components/comments/AdAsComment.tsx index 4ecae5b0c91..3afefc231b5 100644 --- a/packages/shared/src/components/comments/AdAsComment.tsx +++ b/packages/shared/src/components/comments/AdAsComment.tsx @@ -19,11 +19,15 @@ import { MiniCloseIcon } from '../icons'; import { useAdQuery } from '../../features/monetization/useAdQuery'; import { ImpressionStatus } from '../../hooks/feed/useLogImpression'; import { useScrambler } from '../../hooks/useScrambler'; +import { TargetId } from '../../lib/log'; +import { AdvertiseLink } from '../cards/ad/common/AdvertiseLink'; interface AdAsCommentProps { postId: string; } -export const AdAsComment = ({ postId }: AdAsCommentProps): ReactElement => { +export const AdAsComment = ({ + postId, +}: AdAsCommentProps): ReactElement | null => { const { logEvent } = useLogContext(); const { user } = useAuthContext(); const { isPlus } = usePlusSubscription(); @@ -41,6 +45,10 @@ export const AdAsComment = ({ postId }: AdAsCommentProps): ReactElement => { const onAdAction = useCallback( (action: AdActions) => { + if (!ad) { + return; + } + logEvent( adLogEvent(action, ad, { extra: { @@ -52,7 +60,9 @@ export const AdAsComment = ({ postId }: AdAsCommentProps): ReactElement => { [logEvent, ad], ); - const promotedText = useScrambler(!ad ? null : `Promoted by ${ad.source}`); + const promotedText = useScrambler( + ad ? `Promoted by ${ad.source}` : undefined, + ); const onRefreshClick = useCallback(async () => { onAdAction(AdActions.Refresh); @@ -117,6 +127,10 @@ export const AdAsComment = ({ postId }: AdAsCommentProps): ReactElement => {
{description}

+
); diff --git a/packages/shared/src/components/post/PostSidebarAdWidget.tsx b/packages/shared/src/components/post/PostSidebarAdWidget.tsx index 5585c0a6e51..5e442100b06 100644 --- a/packages/shared/src/components/post/PostSidebarAdWidget.tsx +++ b/packages/shared/src/components/post/PostSidebarAdWidget.tsx @@ -1,5 +1,6 @@ import type { ReactElement } from 'react'; import React, { useCallback, useEffect } from 'react'; +import classNames from 'classnames'; import { Button, ButtonSize, ButtonVariant } from '../buttons/Button'; import EntityCard from '../cards/entity/EntityCard'; import EntityCardSkeleton from '../cards/entity/EntityCardSkeleton'; @@ -16,12 +17,15 @@ import { AdActions, AdPlacement } from '../../lib/ads'; import { adLogEvent } from '../../lib/feed'; import { adImprovementsV3Feature } from '../../lib/featureManagement'; import { generateQueryKey, RequestKey, StaleTime } from '../../lib/query'; +import { TargetId } from '../../lib/log'; +import { combinedClicks } from '../../lib/click'; import { Typography, TypographyColor, TypographyTag, TypographyType, } from '../typography/Typography'; +import { AdvertiseLink } from '../cards/ad/common/AdvertiseLink'; interface PostSidebarAdWidgetProps { postId: string; @@ -37,7 +41,7 @@ export function PostSidebarAdWidget({ const { user } = useAuthContext(); const { isPlus } = usePlusSubscription(); const { logEvent } = useLogContext(); - const adImprovementsV3 = useFeature(adImprovementsV3Feature); + const adImprovementsV3 = Boolean(useFeature(adImprovementsV3Feature)); const { data: ad, isPending } = useAdQuery({ placement: AdPlacement.PostSidebar, @@ -96,7 +100,7 @@ export function PostSidebarAdWidget({ permalink={ad.link} entityName={ad.source} className={{ - container: className?.container, + container: classNames('relative cursor-pointer', className?.container), image: 'size-10 rounded-full', }} actionButtons={ @@ -107,12 +111,21 @@ export function PostSidebarAdWidget({ rel="noopener" variant={ButtonVariant.Primary} size={ButtonSize.Small} + className="relative z-1" onClick={() => onAdAction(AdActions.Click)} > Visit } > + onAdAction(AdActions.Click))} + />
{company && ( )} - + {hasDescription && ( )} +
diff --git a/packages/shared/src/lib/featureManagement.ts b/packages/shared/src/lib/featureManagement.ts index e3a53c8e85f..92a4dc83f5a 100644 --- a/packages/shared/src/lib/featureManagement.ts +++ b/packages/shared/src/lib/featureManagement.ts @@ -51,6 +51,7 @@ export const featurePlusCtaCopy = new Feature('plus_cta_copy', { }); export const featureAutorotateAds = new Feature('autorotate_ads', 0); +export const featureAdReferralCta = new Feature('ad_referral_cta', false); export const featureFeedAdTemplate = new Feature('feed_ad_template', { default: { diff --git a/packages/shared/src/lib/log.ts b/packages/shared/src/lib/log.ts index d5be9bc4700..c6c285cd98b 100644 --- a/packages/shared/src/lib/log.ts +++ b/packages/shared/src/lib/log.ts @@ -466,6 +466,7 @@ export enum TargetType { AchievementCard = 'achievement card', BriefCard = 'brief card', HighlightsCard = 'highlights card', + AdvertiseHereCta = 'advertise here cta', } export enum TargetId { @@ -491,6 +492,9 @@ export enum TargetId { Header = 'header', ProfileDropdown = 'profile dropdown', Ads = 'ads', + AdCard = 'ad card', + AdSidebar = 'ad sidebar', + AdComment = 'ad comment', MyProfile = 'my profile', PlusBadge = 'plus badge', PlusPage = 'plus page',