react-ads-sdk
Version:
Complete digital ads service for React and Next.js with Prebid integration
373 lines (364 loc) • 11.9 kB
JavaScript
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
class AdService {
constructor() {
this.isInitialized = false;
this.config = null;
this.adSlots = new Map();
this.observers = new Map();
}
static getInstance() {
if (!AdService.instance) {
AdService.instance = new AdService();
}
return AdService.instance;
}
async initialize(config) {
if (this.isInitialized) {
console.warn('AdService já foi inicializado');
return;
}
this.config = config;
try {
await Promise.all([
this.loadGoogleTag(),
this.loadPrebid()
]);
this.setupGoogleTag();
this.setupPrebid();
this.isInitialized = true;
console.log('AdService inicializado com sucesso');
}
catch (error) {
console.error('Erro ao inicializar AdService:', error);
throw error;
}
}
loadGoogleTag() {
return new Promise((resolve, reject) => {
if (window.googletag) {
resolve();
return;
}
const script = document.createElement('script');
script.async = true;
script.src = 'https://securepubads.g.doubleclick.net/tag/js/gpt.js';
script.onload = () => resolve();
script.onerror = () => reject(new Error('Falha ao carregar Google Tag'));
document.head.appendChild(script);
});
}
loadPrebid() {
return new Promise((resolve, reject) => {
if (window.pbjs) {
resolve();
return;
}
const script = document.createElement('script');
script.async = true;
script.src = 'https://cdn.jsdelivr.net/npm/prebid.js@latest/dist/not-for-prod/prebid.js';
script.onload = () => {
window.pbjs = window.pbjs || {};
window.pbjs.que = window.pbjs.que || [];
resolve();
};
script.onerror = () => reject(new Error('Falha ao carregar Prebid'));
document.head.appendChild(script);
});
}
setupGoogleTag() {
window.googletag = window.googletag || { cmd: [] };
window.googletag.cmd.push(() => {
if (this.config?.enableLazyLoad) {
window.googletag.pubads().enableLazyLoad({
fetchMarginPercent: 500,
renderMarginPercent: 200,
mobileScaling: 2.0
});
}
window.googletag.pubads().enableSingleRequest();
window.googletag.pubads().collapseEmptyDivs();
window.googletag.enableServices();
});
}
setupPrebid() {
window.pbjs.que.push(() => {
window.pbjs.setConfig({
debug: this.config?.testMode || false,
bidderTimeout: this.config?.prebidTimeout || 2000,
enableSendAllBids: true,
useBidCache: true,
cache: {
url: 'https://prebid.adnxs.com/pbc/v1/cache'
}
});
});
}
defineAdSlot(slot, bidders = []) {
if (!this.isInitialized) {
throw new Error('AdService não foi inicializado');
}
window.googletag.cmd.push(() => {
const adSlot = window.googletag
.defineSlot(slot.path, slot.sizes, slot.id)
?.addService(window.googletag.pubads());
if (slot.targeting) {
Object.entries(slot.targeting).forEach(([key, value]) => {
adSlot?.setTargeting(key, value);
});
}
this.adSlots.set(slot.id, adSlot);
});
const hasBidders = bidders.length > 0;
if (hasBidders) {
const adUnit = {
code: slot.id,
mediaTypes: {
banner: {
sizes: slot.sizes
}
},
bids: bidders
};
window.pbjs.que.push(() => {
window.pbjs.addAdUnits([adUnit]);
});
}
}
displayAd(slotId, lazyLoad = false) {
if (!this.isInitialized) {
throw new Error('AdService não foi inicializado');
}
if (lazyLoad && this.config?.enableLazyLoad) {
this.setupLazyLoading(slotId);
}
else {
this.requestAndDisplayAd(slotId);
}
}
setupLazyLoading(slotId) {
const element = document.getElementById(slotId);
if (!element)
return;
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.requestAndDisplayAd(slotId);
observer.disconnect();
this.observers.delete(slotId);
}
});
}, { threshold: 0.1 });
observer.observe(element);
this.observers.set(slotId, observer);
}
requestAndDisplayAd(slotId) {
window.pbjs.que.push(() => {
window.pbjs.requestBids({
adUnitCodes: [slotId],
timeout: this.config?.prebidTimeout || 2000,
bidsBackHandler: () => {
window.googletag.cmd.push(() => {
window.pbjs.setTargetingForGPTAsync([slotId]);
window.googletag.display(slotId);
});
}
});
});
}
refreshAd(slotId) {
const slot = this.adSlots.get(slotId);
if (!slot)
return;
window.pbjs.que.push(() => {
window.pbjs.requestBids({
adUnitCodes: [slotId],
timeout: this.config?.prebidTimeout || 2000,
bidsBackHandler: () => {
window.googletag.cmd.push(() => {
window.pbjs.setTargetingForGPTAsync([slotId]);
window.googletag.pubads().refresh([slot]);
});
}
});
});
}
destroyAd(slotId) {
const observer = this.observers.get(slotId);
if (observer) {
observer.disconnect();
this.observers.delete(slotId);
}
const slot = this.adSlots.get(slotId);
if (slot) {
window.googletag.cmd.push(() => {
window.googletag.destroySlots([slot]);
});
this.adSlots.delete(slotId);
}
window.pbjs.que.push(() => {
window.pbjs.removeAdUnit(slotId);
});
}
setTargeting(key, value) {
window.googletag.cmd.push(() => {
window.googletag.pubads().setTargeting(key, value);
});
}
clearTargeting(key) {
window.googletag.cmd.push(() => {
if (key) {
window.googletag.pubads().clearTargeting(key);
}
else {
window.googletag.pubads().clearTargeting();
}
});
}
getSlotInfo(slotId) {
return {
slot: this.adSlots.get(slotId),
hasObserver: this.observers.has(slotId),
isInitialized: this.isInitialized
};
}
}
const AdContext = React.createContext({
adService: null,
isReady: false
});
const AdProvider = ({ config, children }) => {
const [isReady, setIsReady] = React.useState(false);
const [adService] = React.useState(() => AdService.getInstance());
React.useEffect(() => {
adService.initialize(config)
.then(() => {
setIsReady(true);
window.adService = adService;
})
.catch(error => {
console.error('Erro ao inicializar AdService:', error);
});
}, [adService, config]);
return (jsxRuntime.jsx(AdContext.Provider, { value: { adService, isReady }, children: children }));
};
const useAd = () => {
const context = React.useContext(AdContext);
if (!context) {
throw new Error('useAd deve ser usado dentro de um AdProvider');
}
return context;
};
const AdSlot = ({ slot, bidders = [], lazyLoad = false, refreshInterval, className, style, onLoad, onError }) => {
const adServiceRef = React.useRef();
const refreshIntervalRef = React.useRef();
React.useEffect(() => {
const checkAdService = () => {
try {
const adService = window.adService;
if (adService) {
adServiceRef.current = adService;
initializeAd();
}
else {
setTimeout(checkAdService, 100);
}
}
catch (error) {
onError?.(error);
}
};
const initializeAd = () => {
try {
adServiceRef.current.defineAdSlot(slot, bidders);
adServiceRef.current.displayAd(slot.id, lazyLoad);
if (refreshInterval && refreshInterval > 0) {
setupAutoRefresh();
}
onLoad?.();
}
catch (error) {
onError?.(error);
}
};
const setupAutoRefresh = () => {
if (refreshInterval && refreshInterval >= 30000) { // Mínimo 30 segundos
refreshIntervalRef.current = setInterval(() => {
if (adServiceRef.current) {
adServiceRef.current.refreshAd(slot.id);
}
}, refreshInterval);
}
};
checkAdService();
return () => {
if (refreshIntervalRef.current) {
clearInterval(refreshIntervalRef.current);
}
if (adServiceRef.current) {
adServiceRef.current.destroyAd(slot.id);
}
};
}, [slot, bidders, lazyLoad, refreshInterval, onLoad, onError]);
return (jsxRuntime.jsx("div", { id: slot.id, className: className, style: {
minHeight: slot.sizes[0] ? `${slot.sizes[0][1]}px` : '250px',
minWidth: slot.sizes[0] ? `${slot.sizes[0][0]}px` : '300px',
...style
} }));
};
const useAdService = (config) => {
const adServiceRef = React.useRef();
const isInitializedRef = React.useRef(false);
React.useEffect(() => {
if (!isInitializedRef.current) {
adServiceRef.current = AdService.getInstance();
adServiceRef.current.initialize(config).catch(error => {
console.error('Erro ao inicializar AdService:', error);
});
isInitializedRef.current = true;
}
}, [config]);
return adServiceRef.current;
};
const BidderPresets = {
// Amazon A9/TAM
amazon: (params) => ({
bidder: 'amazon',
params
}),
// Google Ad Exchange
rubicon: (params) => ({
bidder: 'rubicon',
params
}),
// AppNexus
appnexus: (params) => ({
bidder: 'appnexus',
params
}),
// Index Exchange
ix: (params) => ({
bidder: 'ix',
params
}),
// OpenX
openx: (params) => ({
bidder: 'openx',
params
}),
// PubMatic
pubmatic: (params) => ({
bidder: 'pubmatic',
params
})
};
const VERSION = '1.0.0';
exports.AdProvider = AdProvider;
exports.AdService = AdService;
exports.AdSlot = AdSlot;
exports.BidderPresets = BidderPresets;
exports.VERSION = VERSION;
exports.useAd = useAd;
exports.useAdService = useAdService;
//# sourceMappingURL=index.js.map
;