@adtogether/web-sdk 0.1.0 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,7 +20,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/react/index.ts
21
21
  var react_exports = {};
22
22
  __export(react_exports, {
23
- AdTogetherBanner: () => AdTogetherBanner
23
+ AdTogetherBanner: () => AdTogetherBanner,
24
+ AdTogetherInterstitial: () => AdTogetherInterstitial
24
25
  });
25
26
  module.exports = __toCommonJS(react_exports);
26
27
 
@@ -30,7 +31,8 @@ var import_react = require("react");
30
31
  // src/core/AdTogether.ts
31
32
  var AdTogether = class _AdTogether {
32
33
  constructor() {
33
- this.baseUrl = "https://adtogether.com";
34
+ this.allowSelfAds = true;
35
+ this.baseUrl = "https://adtogether.relaxsoftwareapps.com";
34
36
  }
35
37
  static get shared() {
36
38
  if (!_AdTogether.instance) {
@@ -40,11 +42,16 @@ var AdTogether = class _AdTogether {
40
42
  }
41
43
  static initialize(options) {
42
44
  const sdk = _AdTogether.shared;
43
- sdk.appId = options.appId;
45
+ sdk.appId = options.apiKey || options.appId;
46
+ if (options.allowSelfAds !== void 0) {
47
+ sdk.allowSelfAds = options.allowSelfAds;
48
+ }
44
49
  if (options.baseUrl) {
45
50
  sdk.baseUrl = options.baseUrl;
51
+ } else if (typeof window !== "undefined") {
52
+ sdk.baseUrl = "";
46
53
  }
47
- console.log(`AdTogether SDK Initialized with App ID: ${options.appId}`);
54
+ console.log(`AdTogether SDK Initialized with App ID: ${sdk.appId}`);
48
55
  }
49
56
  assertInitialized() {
50
57
  if (!this.appId) {
@@ -53,30 +60,52 @@ var AdTogether = class _AdTogether {
53
60
  }
54
61
  return true;
55
62
  }
56
- static async fetchAd(adUnitId) {
63
+ static async fetchAd(adUnitId, adType) {
57
64
  if (!_AdTogether.shared.assertInitialized()) {
58
65
  throw new Error("AdTogether not initialized");
59
66
  }
60
- const response = await fetch(`${_AdTogether.shared.baseUrl}/api/ads/serve?country=global&adUnitId=${adUnitId}`);
61
- if (!response.ok) {
67
+ try {
68
+ const sdk = _AdTogether.shared;
69
+ let url = `${sdk.baseUrl}/api/ads/serve?country=global&adUnitId=${adUnitId}&apiKey=${sdk.appId}`;
70
+ if (adType) {
71
+ url += `&adType=${adType}`;
72
+ }
73
+ if (sdk.lastAdId) {
74
+ url += `&exclude=${sdk.lastAdId}`;
75
+ }
76
+ url += `&allowSelfAds=${sdk.allowSelfAds}`;
77
+ if (typeof window !== "undefined") {
78
+ url += `&sourceUrl=${encodeURIComponent(window.location.href)}`;
79
+ }
80
+ const response = await fetch(url);
81
+ if (response.ok) {
82
+ const ad = await response.json();
83
+ sdk.lastAdId = ad.id;
84
+ return ad;
85
+ }
62
86
  throw new Error(`Failed to fetch ad. Status: ${response.status}`);
87
+ } catch (err) {
88
+ throw err;
63
89
  }
64
- return response.json();
65
90
  }
66
- static trackImpression(adId) {
67
- this.trackEvent("/api/ads/impression", adId);
91
+ static trackImpression(adId, token) {
92
+ this.trackEvent("/api/ads/impression", adId, token);
68
93
  }
69
- static trackClick(adId) {
70
- this.trackEvent("/api/ads/click", adId);
94
+ static trackClick(adId, token) {
95
+ this.trackEvent("/api/ads/click", adId, token);
71
96
  }
72
- static trackEvent(endpoint, adId) {
97
+ static trackEvent(endpoint, adId, token) {
73
98
  if (!_AdTogether.shared.assertInitialized()) return;
74
99
  fetch(`${_AdTogether.shared.baseUrl}${endpoint}`, {
75
100
  method: "POST",
76
101
  headers: {
77
102
  "Content-Type": "application/json"
78
103
  },
79
- body: JSON.stringify({ adId })
104
+ body: JSON.stringify({
105
+ adId,
106
+ token,
107
+ apiKey: _AdTogether.shared.appId
108
+ })
80
109
  }).catch(console.error);
81
110
  }
82
111
  };
@@ -136,7 +165,7 @@ var AdTogetherBanner = ({
136
165
  (entries) => {
137
166
  if (entries[0].isIntersecting && entries[0].intersectionRatio >= 0.5 && !impressionTrackedRef.current) {
138
167
  impressionTrackedRef.current = true;
139
- AdTogether.trackImpression(adData.id);
168
+ AdTogether.trackImpression(adData.id, adData.token);
140
169
  observer.disconnect();
141
170
  }
142
171
  },
@@ -149,7 +178,7 @@ var AdTogetherBanner = ({
149
178
  }, [adData, isLoading, hasError]);
150
179
  const handleContainerClick = () => {
151
180
  if (!adData) return;
152
- AdTogether.trackClick(adData.id);
181
+ AdTogether.trackClick(adData.id, adData.token);
153
182
  if (adData.clickUrl) {
154
183
  window.open(adData.clickUrl, "_blank", "noopener,noreferrer");
155
184
  }
@@ -225,7 +254,291 @@ var AdTogetherBanner = ({
225
254
  }
226
255
  );
227
256
  };
257
+
258
+ // src/react/AdTogetherInterstitial.tsx
259
+ var import_react2 = require("react");
260
+ var import_react_dom = require("react-dom");
261
+ var import_jsx_runtime2 = require("react/jsx-runtime");
262
+ var AdTogetherInterstitial = ({
263
+ adUnitId,
264
+ isOpen,
265
+ onClose,
266
+ onAdLoaded,
267
+ onAdFailedToLoad,
268
+ theme = "auto",
269
+ closeDelay = 3
270
+ }) => {
271
+ const [adData, setAdData] = (0, import_react2.useState)(null);
272
+ const [isLoading, setIsLoading] = (0, import_react2.useState)(false);
273
+ const [hasError, setHasError] = (0, import_react2.useState)(false);
274
+ const [isDarkMode, setIsDarkMode] = (0, import_react2.useState)(theme === "dark");
275
+ const [canClose, setCanClose] = (0, import_react2.useState)(false);
276
+ const [countdown, setCountdown] = (0, import_react2.useState)(closeDelay);
277
+ const impressionTrackedRef = (0, import_react2.useRef)(false);
278
+ const closeTimerRef = (0, import_react2.useRef)(null);
279
+ const countdownRef = (0, import_react2.useRef)(null);
280
+ (0, import_react2.useEffect)(() => {
281
+ if (theme === "auto") {
282
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
283
+ setIsDarkMode(mediaQuery.matches);
284
+ const handler = (e) => setIsDarkMode(e.matches);
285
+ mediaQuery.addEventListener("change", handler);
286
+ return () => mediaQuery.removeEventListener("change", handler);
287
+ } else {
288
+ setIsDarkMode(theme === "dark");
289
+ }
290
+ }, [theme]);
291
+ (0, import_react2.useEffect)(() => {
292
+ if (!isOpen) {
293
+ setAdData(null);
294
+ setIsLoading(false);
295
+ setHasError(false);
296
+ setCanClose(false);
297
+ setCountdown(closeDelay);
298
+ impressionTrackedRef.current = false;
299
+ if (closeTimerRef.current) clearTimeout(closeTimerRef.current);
300
+ if (countdownRef.current) clearInterval(countdownRef.current);
301
+ return;
302
+ }
303
+ let isMounted = true;
304
+ setIsLoading(true);
305
+ AdTogether.fetchAd(adUnitId, "interstitial").then((ad) => {
306
+ if (isMounted) {
307
+ setAdData(ad);
308
+ setIsLoading(false);
309
+ onAdLoaded?.();
310
+ }
311
+ }).catch((err) => {
312
+ if (isMounted) {
313
+ console.error("AdTogether Failed to load interstitial:", err);
314
+ setHasError(true);
315
+ setIsLoading(false);
316
+ onAdFailedToLoad?.(err);
317
+ onClose();
318
+ }
319
+ });
320
+ return () => {
321
+ isMounted = false;
322
+ };
323
+ }, [isOpen, adUnitId]);
324
+ (0, import_react2.useEffect)(() => {
325
+ if (!isOpen || !adData) return;
326
+ setCountdown(closeDelay);
327
+ countdownRef.current = setInterval(() => {
328
+ setCountdown((prev) => {
329
+ if (prev <= 1) {
330
+ if (countdownRef.current) clearInterval(countdownRef.current);
331
+ setCanClose(true);
332
+ return 0;
333
+ }
334
+ return prev - 1;
335
+ });
336
+ }, 1e3);
337
+ return () => {
338
+ if (countdownRef.current) clearInterval(countdownRef.current);
339
+ };
340
+ }, [isOpen, adData, closeDelay]);
341
+ (0, import_react2.useEffect)(() => {
342
+ if (!adData || !isOpen || impressionTrackedRef.current) return;
343
+ impressionTrackedRef.current = true;
344
+ AdTogether.trackImpression(adData.id, adData.token);
345
+ }, [adData, isOpen]);
346
+ const handleAdClick = (0, import_react2.useCallback)(() => {
347
+ if (!adData) return;
348
+ AdTogether.trackClick(adData.id, adData.token);
349
+ if (adData.clickUrl) {
350
+ window.open(adData.clickUrl, "_blank", "noopener,noreferrer");
351
+ }
352
+ }, [adData]);
353
+ const handleClose = (0, import_react2.useCallback)(() => {
354
+ if (canClose) {
355
+ onClose();
356
+ }
357
+ }, [canClose, onClose]);
358
+ (0, import_react2.useEffect)(() => {
359
+ if (isOpen) {
360
+ document.body.style.overflow = "hidden";
361
+ } else {
362
+ document.body.style.overflow = "";
363
+ }
364
+ return () => {
365
+ document.body.style.overflow = "";
366
+ };
367
+ }, [isOpen]);
368
+ if (!isOpen) return null;
369
+ const bgOverlay = "rgba(0, 0, 0, 0.7)";
370
+ const cardBg = isDarkMode ? "#1F2937" : "#ffffff";
371
+ const borderColor = isDarkMode ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.1)";
372
+ const textColor = isDarkMode ? "#F9FAFB" : "#111827";
373
+ const descColor = isDarkMode ? "#9CA3AF" : "#6B7280";
374
+ const content = /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
375
+ "div",
376
+ {
377
+ className: "adtogether-interstitial-overlay",
378
+ style: {
379
+ position: "fixed",
380
+ top: 0,
381
+ left: 0,
382
+ width: "100vw",
383
+ height: "100vh",
384
+ backgroundColor: bgOverlay,
385
+ display: "flex",
386
+ alignItems: "center",
387
+ justifyContent: "center",
388
+ zIndex: 1e5,
389
+ backdropFilter: "blur(8px)",
390
+ animation: "adtogether-fade-in 0.3s ease-out"
391
+ },
392
+ onClick: (e) => {
393
+ if (e.target === e.currentTarget && canClose) handleClose();
394
+ },
395
+ children: [
396
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: `
397
+ @keyframes adtogether-fade-in {
398
+ from { opacity: 0; }
399
+ to { opacity: 1; }
400
+ }
401
+ @keyframes adtogether-scale-in {
402
+ from { opacity: 0; transform: scale(0.9); }
403
+ to { opacity: 1; transform: scale(1); }
404
+ }
405
+ ` }),
406
+ isLoading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { color: "#fff", fontSize: "18px" }, children: "Loading Ad..." }) : adData ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
407
+ "div",
408
+ {
409
+ style: {
410
+ position: "relative",
411
+ maxWidth: "800px",
412
+ width: "95%",
413
+ backgroundColor: cardBg,
414
+ borderRadius: "24px",
415
+ border: `1px solid ${borderColor}`,
416
+ overflow: "hidden",
417
+ boxShadow: "0 25px 50px rgba(0, 0, 0, 0.5)",
418
+ animation: "adtogether-scale-in 0.3s ease-out"
419
+ },
420
+ children: [
421
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { position: "absolute", top: "12px", right: "12px", zIndex: 10 }, children: canClose ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
422
+ "button",
423
+ {
424
+ onClick: handleClose,
425
+ style: {
426
+ width: "36px",
427
+ height: "36px",
428
+ borderRadius: "50%",
429
+ backgroundColor: "rgba(0,0,0,0.6)",
430
+ border: "none",
431
+ color: "#fff",
432
+ fontSize: "18px",
433
+ cursor: "pointer",
434
+ display: "flex",
435
+ alignItems: "center",
436
+ justifyContent: "center",
437
+ backdropFilter: "blur(4px)"
438
+ },
439
+ "aria-label": "Close ad",
440
+ children: "\u2715"
441
+ }
442
+ ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
443
+ "div",
444
+ {
445
+ style: {
446
+ width: "36px",
447
+ height: "36px",
448
+ borderRadius: "50%",
449
+ backgroundColor: "rgba(0,0,0,0.6)",
450
+ color: "#fff",
451
+ fontSize: "14px",
452
+ fontWeight: "bold",
453
+ display: "flex",
454
+ alignItems: "center",
455
+ justifyContent: "center",
456
+ backdropFilter: "blur(4px)"
457
+ },
458
+ children: countdown
459
+ }
460
+ ) }),
461
+ adData.imageUrl && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
462
+ "div",
463
+ {
464
+ style: { cursor: "pointer", position: "relative" },
465
+ onClick: handleAdClick,
466
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
467
+ "img",
468
+ {
469
+ src: adData.imageUrl,
470
+ alt: adData.title,
471
+ style: {
472
+ width: "100%",
473
+ aspectRatio: "16/9",
474
+ objectFit: "cover",
475
+ display: "block"
476
+ }
477
+ }
478
+ )
479
+ }
480
+ ),
481
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
482
+ "div",
483
+ {
484
+ style: { padding: "20px", cursor: "pointer" },
485
+ onClick: handleAdClick,
486
+ children: [
487
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: "8px" }, children: [
488
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontWeight: "bold", fontSize: "18px", color: textColor }, children: adData.title }),
489
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
490
+ "span",
491
+ {
492
+ style: {
493
+ backgroundColor: "#FBBF24",
494
+ color: "#000",
495
+ fontSize: "10px",
496
+ fontWeight: "bold",
497
+ padding: "3px 6px",
498
+ borderRadius: "4px",
499
+ marginLeft: "8px",
500
+ flexShrink: 0
501
+ },
502
+ children: "AD"
503
+ }
504
+ )
505
+ ] }),
506
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { fontSize: "14px", color: descColor, margin: 0, lineHeight: 1.5 }, children: adData.description }),
507
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
508
+ "button",
509
+ {
510
+ style: {
511
+ marginTop: "16px",
512
+ width: "100%",
513
+ padding: "12px",
514
+ backgroundColor: "#F59E0B",
515
+ color: "#000",
516
+ fontWeight: "bold",
517
+ fontSize: "14px",
518
+ border: "none",
519
+ borderRadius: "12px",
520
+ cursor: "pointer"
521
+ },
522
+ onClick: (e) => {
523
+ e.stopPropagation();
524
+ handleAdClick();
525
+ },
526
+ children: "Learn More \u2192"
527
+ }
528
+ )
529
+ ]
530
+ }
531
+ )
532
+ ]
533
+ }
534
+ ) : null
535
+ ]
536
+ }
537
+ );
538
+ return (0, import_react_dom.createPortal)(content, document.body);
539
+ };
228
540
  // Annotate the CommonJS export names for ESM import in node:
229
541
  0 && (module.exports = {
230
- AdTogetherBanner
542
+ AdTogetherBanner,
543
+ AdTogetherInterstitial
231
544
  });