mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 06:56:20 +00:00
🆙 Added google ADS
This commit is contained in:
@@ -0,0 +1 @@
|
||||
google.com, ## YOUR pub-XXXXXXXXX, DIRECT, XXXXXXX
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"slot": "### SLOT ID FROM GOOGLE - data-ad-slot ###",
|
||||
"format": "auto",
|
||||
"fullWidthResponsive": true
|
||||
}
|
||||
@@ -27,6 +27,7 @@
|
||||
"guides.enabled": true,
|
||||
"toolbar.hide.quests": true,
|
||||
"catalog.style.new": true,
|
||||
"show.google.ads": false,
|
||||
"loginview": {
|
||||
"images": {
|
||||
"background": "${asset.url}/c_images/reception/stretch_blue.png",
|
||||
|
||||
@@ -24,6 +24,7 @@ import { NavigatorView } from './navigator/NavigatorView';
|
||||
import { NitrobubbleHiddenView } from './nitrobubblehidden/NitrobubbleHiddenView';
|
||||
import { NitropediaView } from './nitropedia/NitropediaView';
|
||||
import { ExternalPluginLoader } from './plugins/ExternalPluginLoader';
|
||||
import { GoogleAdsView } from './ads/GoogleAdsView';
|
||||
import { RightSideView } from './right-side/RightSideView';
|
||||
import { RoomView } from './room/RoomView';
|
||||
import { ToolbarView } from './toolbar/ToolbarView';
|
||||
@@ -97,6 +98,7 @@ export const MainView: FC<{}> = props =>
|
||||
</motion.div> }
|
||||
</AnimatePresence>
|
||||
<ToolbarView isInRoom={ !landingViewVisible } />
|
||||
<GoogleAdsView />
|
||||
<ModToolsView />
|
||||
<WiredCreatorToolsView />
|
||||
<RoomView />
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
import { FC, useEffect, useRef, useState } from 'react';
|
||||
import { GetConfigurationValue } from '../../api';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../common';
|
||||
|
||||
interface AdsenseConfig {
|
||||
slot: string;
|
||||
format?: string;
|
||||
fullWidthResponsive?: boolean;
|
||||
}
|
||||
|
||||
const ADSENSE_SCRIPT_ID = 'google-adsense-script';
|
||||
|
||||
const parsePublisherIdFromAdsTxt = (text: string): string | null => {
|
||||
for (const rawLine of text.split(/\r?\n/)) {
|
||||
const line = rawLine.split('#')[0].trim();
|
||||
if (!line) continue;
|
||||
const parts = line.split(',').map(part => part.trim());
|
||||
if (parts.length < 2) continue;
|
||||
if (parts[0].toLowerCase() !== 'google.com') continue;
|
||||
const pub = parts[1];
|
||||
if (/^pub-\d+$/.test(pub)) return pub;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const ensureAdsenseScript = (publisherId: string): void => {
|
||||
if (typeof document === 'undefined') return;
|
||||
if (document.getElementById(ADSENSE_SCRIPT_ID)) return;
|
||||
|
||||
const script = document.createElement('script');
|
||||
script.id = ADSENSE_SCRIPT_ID;
|
||||
script.async = true;
|
||||
script.crossOrigin = 'anonymous';
|
||||
script.src = `https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-${ publisherId }`;
|
||||
document.head.appendChild(script);
|
||||
};
|
||||
|
||||
export const GoogleAdsView: FC<{}> = () => {
|
||||
const adsEnabled = GetConfigurationValue<boolean>('show.google.ads', false);
|
||||
const [ isOpen, setIsOpen ] = useState(false);
|
||||
const [ publisherId, setPublisherId ] = useState<string | null>(null);
|
||||
const [ config, setConfig ] = useState<AdsenseConfig | null>(null);
|
||||
const [ loadError, setLoadError ] = useState<string | null>(null);
|
||||
const insRef = useRef<HTMLModElement>(null);
|
||||
const pushedRef = useRef(false);
|
||||
const autoOpenedRef = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!adsEnabled) return;
|
||||
const handler = () => setIsOpen(prev => !prev);
|
||||
window.addEventListener('ads:toggle', handler);
|
||||
return () => window.removeEventListener('ads:toggle', handler);
|
||||
}, [ adsEnabled ]);
|
||||
|
||||
// Auto-open once on initial mount (the login / landing stage).
|
||||
// Subsequent toggles are driven by the "ads:toggle" window event
|
||||
// (e.g. the Show Ad button in NitroSystemAlertView).
|
||||
useEffect(() => {
|
||||
if (!adsEnabled) return;
|
||||
if (autoOpenedRef.current) return;
|
||||
autoOpenedRef.current = true;
|
||||
const t = setTimeout(() => setIsOpen(true), 500);
|
||||
return () => clearTimeout(t);
|
||||
}, [ adsEnabled ]);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const [ adsTxtRes, configRes ] = await Promise.all([
|
||||
fetch('/ads.txt', { cache: 'no-cache' }),
|
||||
fetch('/adsense.json', { cache: 'no-cache' })
|
||||
]);
|
||||
|
||||
if (!adsTxtRes.ok) throw new Error(`ads.txt ${ adsTxtRes.status }`);
|
||||
|
||||
const adsTxt = await adsTxtRes.text();
|
||||
const pubId = parsePublisherIdFromAdsTxt(adsTxt);
|
||||
|
||||
if (!pubId) throw new Error('No google.com publisher id in ads.txt');
|
||||
|
||||
let cfg: AdsenseConfig = { slot: '', format: 'auto', fullWidthResponsive: true };
|
||||
if (configRes.ok) cfg = { ...cfg, ...(await configRes.json()) };
|
||||
|
||||
if (cancelled) return;
|
||||
setPublisherId(pubId);
|
||||
setConfig(cfg);
|
||||
} catch (err) {
|
||||
if (!cancelled) setLoadError((err as Error).message);
|
||||
}
|
||||
})();
|
||||
|
||||
return () => { cancelled = true; };
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen || !publisherId || !config) return;
|
||||
ensureAdsenseScript(publisherId);
|
||||
}, [ isOpen, publisherId, config ]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
pushedRef.current = false;
|
||||
return;
|
||||
}
|
||||
if (!insRef.current || pushedRef.current) return;
|
||||
if (!publisherId || !config?.slot) return;
|
||||
|
||||
const tryPush = () => {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const w = window as any;
|
||||
w.adsbygoogle = w.adsbygoogle || [];
|
||||
w.adsbygoogle.push({});
|
||||
pushedRef.current = true;
|
||||
} catch {
|
||||
// AdSense script may not be ready yet; retry once
|
||||
setTimeout(() => {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const w = window as any;
|
||||
w.adsbygoogle = w.adsbygoogle || [];
|
||||
w.adsbygoogle.push({});
|
||||
pushedRef.current = true;
|
||||
} catch { /* give up */ }
|
||||
}, 500);
|
||||
}
|
||||
};
|
||||
|
||||
const t = setTimeout(tryPush, 50);
|
||||
return () => clearTimeout(t);
|
||||
}, [ isOpen, publisherId, config ]);
|
||||
|
||||
if (!adsEnabled) return null;
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-google-ads" uniqueKey="google-ads" theme="primary">
|
||||
<NitroCardHeaderView headerText="Sponsored" onCloseClick={ () => setIsOpen(false) } />
|
||||
<NitroCardContentView>
|
||||
<div className="flex items-center justify-center w-[300px] h-[250px] bg-white">
|
||||
{ loadError &&
|
||||
<div className="text-xs text-red-600 text-center px-2">Ads unavailable: { loadError }</div> }
|
||||
{ !loadError && (!publisherId || !config) &&
|
||||
<div className="text-xs text-gray-500">Loading…</div> }
|
||||
{ !loadError && publisherId && config?.slot &&
|
||||
<ins
|
||||
ref={ insRef }
|
||||
key={ `${ publisherId }-${ config.slot }` }
|
||||
className="adsbygoogle"
|
||||
style={ { display: 'block', width: '100%', height: '100%' } }
|
||||
data-ad-client={ `ca-${ publisherId }` }
|
||||
data-ad-slot={ config.slot }
|
||||
data-ad-format={ config.format ?? 'auto' }
|
||||
data-full-width-responsive={ (config.fullWidthResponsive ?? true) ? 'true' : 'false' }
|
||||
/> }
|
||||
{ !loadError && publisherId && config && !config.slot &&
|
||||
<div className="text-xs text-gray-500 text-center px-2">Ad slot not configured in adsense.json</div> }
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import { FC } from 'react';
|
||||
import { GetRendererVersion, GetUIVersion, NotificationAlertItem } from '../../../../api';
|
||||
import { GetConfigurationValue, GetRendererVersion, GetUIVersion, NotificationAlertItem } from '../../../../api';
|
||||
import { Button, Column, Grid, LayoutNotificationAlertView, LayoutNotificationAlertViewProps, Text } from '../../../../common';
|
||||
|
||||
interface NotificationDefaultAlertViewProps extends LayoutNotificationAlertViewProps
|
||||
@@ -9,10 +9,11 @@ interface NotificationDefaultAlertViewProps extends LayoutNotificationAlertViewP
|
||||
|
||||
export const NitroSystemAlertView: FC<NotificationDefaultAlertViewProps> = props =>
|
||||
{
|
||||
const { title = 'Nitro', onClose = null, ...rest } = props;
|
||||
const { title = 'Nitro', onClose = null, classNames = [], ...rest } = props;
|
||||
const adsEnabled = GetConfigurationValue<boolean>('show.google.ads', false);
|
||||
|
||||
return (
|
||||
<LayoutNotificationAlertView title={ title } onClose={ onClose } { ...rest }>
|
||||
<LayoutNotificationAlertView title={ title } onClose={ onClose } classNames={ [ 'nitro-alert-system', ...classNames ] } { ...rest }>
|
||||
<Grid>
|
||||
<Column size={ 12 }>
|
||||
<Column alignItems="center" gap={ 0 }>
|
||||
@@ -23,6 +24,8 @@ export const NitroSystemAlertView: FC<NotificationDefaultAlertViewProps> = props
|
||||
<Text><b>Renderer:</b> v{ GetRendererVersion() }</Text>
|
||||
<Column fullWidth gap={ 1 }>
|
||||
<Button fullWidth variant="success" onClick={ event => window.open('https://discord.nitrodev.co') }>Discord</Button>
|
||||
{ adsEnabled &&
|
||||
<Button fullWidth onClick={ () => window.dispatchEvent(new CustomEvent('ads:toggle')) }>Show Ad</Button> }
|
||||
</Column>
|
||||
</Column>
|
||||
<div className="alertView_nitro-coolui-logo"></div>
|
||||
@@ -35,7 +38,7 @@ export const NitroSystemAlertView: FC<NotificationDefaultAlertViewProps> = props
|
||||
</Column>
|
||||
</Column>
|
||||
</Column>
|
||||
|
||||
|
||||
</Grid>
|
||||
</LayoutNotificationAlertView>
|
||||
);
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&.nitro-alert-credits {
|
||||
width: 370px;
|
||||
.notification-text {
|
||||
@@ -34,6 +34,19 @@
|
||||
min-width: 225px;
|
||||
}
|
||||
}
|
||||
|
||||
&.nitro-alert-system {
|
||||
width: auto;
|
||||
min-width: 260px;
|
||||
max-width: 90vw;
|
||||
min-height: auto;
|
||||
max-height: none;
|
||||
height: auto;
|
||||
|
||||
.notification-text {
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nitro-notification-bubble {
|
||||
|
||||
Reference in New Issue
Block a user