computer-scienceangular-roadmapbackend-roadmapblockchain-roadmapdba-roadmapdeveloper-roadmapdevops-roadmapfrontend-roadmapgo-roadmaphactoberfestjava-roadmapjavascript-roadmapnodejs-roadmappython-roadmapqa-roadmapreact-roadmaproadmapstudy-planvue-roadmapweb3-roadmap
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
194 lines
5.1 KiB
194 lines
5.1 KiB
import { useEffect, useState } from 'react'; |
|
import { httpGet, httpPatch, httpPost } from '../lib/http'; |
|
import { sponsorHidden } from '../stores/page'; |
|
import { useStore } from '@nanostores/react'; |
|
import { X } from 'lucide-react'; |
|
import { setViewSponsorCookie } from '../lib/jwt'; |
|
import { isMobile } from '../lib/is-mobile'; |
|
import Cookies from 'js-cookie'; |
|
import { getUrlUtmParams } from '../lib/browser.ts'; |
|
|
|
export type PageSponsorType = { |
|
company: string; |
|
description: string; |
|
gaLabel: string; |
|
imageUrl: string; |
|
pageUrl: string; |
|
title: string; |
|
url: string; |
|
}; |
|
|
|
type V1GetSponsorResponse = { |
|
id?: string; |
|
href?: string; |
|
sponsor?: PageSponsorType; |
|
}; |
|
|
|
type PageSponsorProps = { |
|
gaPageIdentifier?: string; |
|
}; |
|
|
|
const CLOSE_SPONSOR_KEY = 'sponsorClosed'; |
|
|
|
function markSponsorHidden(sponsorId: string) { |
|
Cookies.set(`${CLOSE_SPONSOR_KEY}-${sponsorId}`, '1', { |
|
path: '/', |
|
expires: 1, |
|
sameSite: 'lax', |
|
secure: true, |
|
domain: import.meta.env.DEV ? 'localhost' : '.roadmap.sh', |
|
}); |
|
} |
|
|
|
function isSponsorMarkedHidden(sponsorId: string) { |
|
return Cookies.get(`${CLOSE_SPONSOR_KEY}-${sponsorId}`) === '1'; |
|
} |
|
|
|
export function PageSponsor(props: PageSponsorProps) { |
|
const { gaPageIdentifier } = props; |
|
const $isSponsorHidden = useStore(sponsorHidden); |
|
|
|
const [sponsorId, setSponsorId] = useState<string | null>(null); |
|
const [sponsor, setSponsor] = useState<PageSponsorType>(); |
|
|
|
useEffect(() => { |
|
const foundUtmParams = getUrlUtmParams(); |
|
|
|
if (!foundUtmParams.utmSource) { |
|
return; |
|
} |
|
|
|
localStorage.setItem('utm_params', JSON.stringify(foundUtmParams)); |
|
}, []); |
|
|
|
const loadSponsor = async () => { |
|
const currentPath = window.location.pathname; |
|
if ( |
|
currentPath === '/' || |
|
currentPath === '/best-practices' || |
|
currentPath === '/roadmaps' || |
|
currentPath.startsWith('/guides') || |
|
currentPath.startsWith('/videos') || |
|
currentPath.startsWith('/account') || |
|
currentPath.startsWith('/team/') |
|
) { |
|
return; |
|
} |
|
|
|
const { response, error } = await httpGet<V1GetSponsorResponse>( |
|
`${import.meta.env.PUBLIC_API_URL}/v1-get-sponsor`, |
|
{ |
|
href: window.location.pathname, |
|
mobile: isMobile() ? 'true' : 'false', |
|
}, |
|
); |
|
|
|
if (error) { |
|
console.error(error); |
|
return; |
|
} |
|
|
|
if ( |
|
!response?.sponsor || |
|
!response.id || |
|
isSponsorMarkedHidden(response.id) |
|
) { |
|
return; |
|
} |
|
|
|
setSponsor(response.sponsor); |
|
setSponsorId(response.id); |
|
|
|
window.fireEvent({ |
|
category: 'SponsorImpression', |
|
action: `${response.sponsor?.company} Impression`, |
|
label: |
|
response.sponsor.gaLabel || |
|
`${gaPageIdentifier} / ${response.sponsor?.company} Link`, |
|
}); |
|
}; |
|
|
|
const clickSponsor = async (sponsorId: string) => { |
|
const clickUrl = new URL( |
|
`${import.meta.env.PUBLIC_API_URL}/v1-view-sponsor/${sponsorId}`, |
|
); |
|
|
|
const { response, error } = await httpPatch<{ status: 'ok' }>( |
|
clickUrl.toString(), |
|
{ |
|
mobile: isMobile(), |
|
}, |
|
); |
|
|
|
if (error || !response) { |
|
console.error(error); |
|
return; |
|
} |
|
|
|
setViewSponsorCookie(sponsorId); |
|
}; |
|
|
|
useEffect(() => { |
|
window.setTimeout(loadSponsor); |
|
}, []); |
|
|
|
if ($isSponsorHidden || !sponsor) { |
|
return null; |
|
} |
|
|
|
const { url, title, imageUrl, description, company, gaLabel } = sponsor; |
|
|
|
const isRoadmapAd = title.toLowerCase() === 'advertise with us!'; |
|
|
|
return ( |
|
<a |
|
href={url} |
|
target="_blank" |
|
rel="noopener sponsored nofollow" |
|
className="fixed bottom-0 left-0 right-0 z-50 flex bg-white shadow-lg outline-0 outline-transparent sm:bottom-[15px] sm:left-auto sm:right-[15px] sm:max-w-[350px]" |
|
onClick={async () => { |
|
window.fireEvent({ |
|
category: 'SponsorClick', |
|
action: `${company} Redirect`, |
|
label: gaLabel || `${gaPageIdentifier} / ${company} Link`, |
|
}); |
|
await clickSponsor(sponsorId || ''); |
|
}} |
|
> |
|
<span |
|
className="absolute right-1 top-1 text-gray-400 hover:text-gray-800 sm:right-1.5 sm:top-1.5 sm:text-gray-300" |
|
aria-label="Close" |
|
onClick={(e) => { |
|
e.preventDefault(); |
|
markSponsorHidden(sponsorId || ''); |
|
sponsorHidden.set(true); |
|
}} |
|
> |
|
<X className="h-5 w-5 sm:h-4 sm:w-4" /> |
|
</span> |
|
<span> |
|
<img |
|
src={imageUrl} |
|
className="block h-[106px] object-cover sm:h-[153px] sm:w-[118.18px]" |
|
alt="Sponsor Banner" |
|
/> |
|
</span> |
|
<span className="flex flex-1 flex-col justify-between text-xs sm:text-sm"> |
|
<span className="p-[10px]"> |
|
<span className="mb-0.5 block font-semibold">{title}</span> |
|
<span className="block text-gray-500">{description}</span> |
|
</span> |
|
{!isRoadmapAd && ( |
|
<> |
|
<span className="sponsor-footer hidden sm:block"> |
|
Partner Content |
|
</span> |
|
<span className="block pb-1 text-center text-[10px] uppercase text-gray-400 sm:hidden"> |
|
Partner Content |
|
</span> |
|
</> |
|
)} |
|
</span> |
|
</a> |
|
); |
|
}
|
|
|