Skip to content

Commit d18a4ca

Browse files
committed
specify ad provider
1 parent 7309e1c commit d18a4ca

File tree

7 files changed

+99
-8
lines changed

7 files changed

+99
-8
lines changed

configs/app/features/adsBanner.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ const provider: AdBannerProviders = (() => {
1212
})();
1313

1414
const additionalProvider = getEnvValue('NEXT_PUBLIC_AD_BANNER_ADDITIONAL_PROVIDER') as AdBannerAdditionalProviders;
15+
const isSpecifyEnabled = getEnvValue('NEXT_PUBLIC_AD_BANNER_ENABLE_SPECIFY') === 'true';
1516

1617
const title = 'Banner ads';
1718

18-
type AdsBannerFeaturePayload = {
19+
type AdsBannerFeatureProviderPayload = {
1920
provider: Exclude<AdBannerProviders, 'adbutler' | 'none'>;
2021
} | {
2122
provider: 'adbutler';
@@ -36,6 +37,10 @@ type AdsBannerFeaturePayload = {
3637
};
3738
};
3839

40+
type AdsBannerFeaturePayload = AdsBannerFeatureProviderPayload & {
41+
isSpecifyEnabled: boolean;
42+
};
43+
3944
const config: Feature<AdsBannerFeaturePayload> = (() => {
4045
if (provider === 'adbutler') {
4146
const desktopConfig = parseEnvJson<AdButlerConfig>(getEnvValue('NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP'));
@@ -52,6 +57,7 @@ const config: Feature<AdsBannerFeaturePayload> = (() => {
5257
mobile: mobileConfig,
5358
},
5459
},
60+
isSpecifyEnabled,
5561
});
5662
}
5763
} else if (provider !== 'none') {
@@ -71,12 +77,14 @@ const config: Feature<AdsBannerFeaturePayload> = (() => {
7177
mobile: mobileConfig,
7278
},
7379
},
80+
isSpecifyEnabled,
7481
});
7582
}
7683
return Object.freeze({
7784
title,
7885
isEnabled: true,
7986
provider,
87+
isSpecifyEnabled,
8088
});
8189
}
8290

configs/envs/.env.eth_sepolia

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,4 @@ NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED=true
7272
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
7373
NEXT_PUBLIC_XSTAR_SCORE_URL=https://docs.xname.app/the-solution-adaptive-proof-of-humanity-on-blockchain/xhs-scoring-algorithm?utm_source=blockscout&utm_medium=address
7474
NEXT_PUBLIC_VIEWS_CONTRACT_LANGUAGE_FILTERS=['solidity','vyper','yul','geas']
75+
NEXT_PUBLIC_AD_BANNER_ENABLE_SPECIFY=true

nextjs/csp/policies/ad.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ export function ad(): CspDev.DirectiveDescriptor {
2323
'api.hypelab.com',
2424
'*.ixncdn.com',
2525
'*.cloudfront.net',
26+
27+
// specify
28+
'app.specify.sh',
2629
],
2730
'frame-src': [
2831
// coinzilla

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
"@rollbar/react": "0.12.1",
7474
"@scure/base": "1.1.9",
7575
"@slise/embed-react": "^2.2.0",
76+
"@specify-sh/sdk": "^0.2.2",
7677
"@tanstack/react-query": "5.55.4",
7778
"@tanstack/react-query-devtools": "5.55.4",
7879
"@types/papaparse": "^5.3.5",

ui/shared/ad/AdBannerContent.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ import type { BannerPlatform } from './types';
55
import type { AdBannerProviders } from 'types/client/adProviders';
66

77
import config from 'configs/app';
8+
import useAccount from 'lib/web3/useAccount';
89
import { Skeleton } from 'toolkit/chakra/skeleton';
910

1011
import AdbutlerBanner from './AdbutlerBanner';
1112
import CoinzillaBanner from './CoinzillaBanner';
1213
import HypeBanner from './HypeBanner';
1314
import SliseBanner from './SliseBanner';
15+
import SpecifyBanner from './SpecifyBanner';
1416

1517
const feature = config.features.adsBanner;
1618

@@ -22,7 +24,19 @@ interface Props {
2224
}
2325

2426
const AdBannerContent = ({ className, isLoading, provider, platform }: Props) => {
27+
const { address } = useAccount();
28+
const [ showSpecify, setShowSpecify ] = React.useState(feature.isEnabled && feature.isSpecifyEnabled && Boolean(address));
29+
// const [ showSpecify, setShowSpecify ] = React.useState(feature.isEnabled && feature.isSpecifyEnabled);
30+
31+
const handleEmptySpecify = React.useCallback(() => {
32+
setShowSpecify(false);
33+
}, []);
34+
2535
const content = (() => {
36+
if (showSpecify) {
37+
return <SpecifyBanner platform={ platform } address={ address as `0x${ string }` } onEmpty={ handleEmptySpecify }/>;
38+
// return <SpecifyBanner platform={ platform } address={ '0x5c4C6c6d0358BAF2adE28FfB1723e70139cd53' } onEmpty={ handleEmptySpecify }/>;
39+
}
2640
switch (provider) {
2741
case 'adbutler':
2842
return <AdbutlerBanner platform={ platform }/>;

ui/shared/ad/SpecifyBanner.tsx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { chakra } from '@chakra-ui/react';
2+
import type { SpecifyAd } from '@specify-sh/sdk';
3+
import Specify, { ImageFormat } from '@specify-sh/sdk';
4+
import React from 'react';
5+
6+
import type { BannerProps } from './types';
7+
8+
import useIsMobile from 'lib/hooks/useIsMobile';
9+
import { Image } from 'toolkit/chakra/image';
10+
11+
const PUBLISHER_KEY = 'spk_1dqfv5mkgwpwl58zcaziklpurezud8';
12+
13+
const SpecifyBanner = ({ className, platform, address, onEmpty }: BannerProps & { address: string; onEmpty: () => void }) => {
14+
const isMobileViewport = useIsMobile();
15+
const isMobile = platform === 'mobile' || isMobileViewport;
16+
const [ ad, setAd ] = React.useState<SpecifyAd | null>(null);
17+
React.useEffect(() => {
18+
const fetchContent = async() => {
19+
try {
20+
const specify = new Specify({
21+
publisherKey: PUBLISHER_KEY,
22+
});
23+
const content = await specify.serve(
24+
[ address as `0x${ string }` ],
25+
{ imageFormat: isMobile ? ImageFormat.SHORT_BANNER : ImageFormat.LONG_BANNER },
26+
);
27+
if (content?.imageUrl) {
28+
setAd(content);
29+
} else {
30+
onEmpty();
31+
}
32+
} catch (error) {
33+
onEmpty();
34+
}
35+
};
36+
37+
fetchContent();
38+
}, [ address, isMobile, onEmpty ]);
39+
40+
const handleClick = React.useCallback(() => {
41+
window.open(ad?.ctaUrl, '_blank');
42+
}, [ ad?.ctaUrl ]);
43+
44+
if (!ad) return null;
45+
46+
return (
47+
<Image
48+
src={ ad.imageUrl }
49+
alt={ ad.headline }
50+
cursor="pointer"
51+
onClick={ handleClick }
52+
className={ className }
53+
/>
54+
);
55+
};
56+
57+
export default chakra(SpecifyBanner);

yarn.lock

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6423,6 +6423,13 @@
64236423
resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553"
64246424
integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==
64256425

6426+
"@specify-sh/sdk@^0.2.2":
6427+
version "0.2.2"
6428+
resolved "https://registry.yarnpkg.com/@specify-sh/sdk/-/sdk-0.2.2.tgz#f81ea379cbeb70378de7cf13e093001bff3488ef"
6429+
integrity sha512-Mezgmcj/auVW3PvyTa+glC4P1ynVCsz6jJfAHIWMwyOxO91EULH8SYnoTapUswVM2jyxGinANjstcLl4yEUzug==
6430+
dependencies:
6431+
cross-fetch "4.0.0"
6432+
64266433
"@stablelib/aead@^1.0.1":
64276434
version "1.0.1"
64286435
resolved "https://registry.yarnpkg.com/@stablelib/aead/-/aead-1.0.1.tgz#c4b1106df9c23d1b867eb9b276d8f42d5fc4c0c3"
@@ -10892,6 +10899,13 @@ create-require@^1.1.0:
1089210899
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
1089310900
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
1089410901

10902+
[email protected], cross-fetch@^4.0.0:
10903+
version "4.0.0"
10904+
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983"
10905+
integrity sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==
10906+
dependencies:
10907+
node-fetch "^2.6.12"
10908+
1089510909
cross-fetch@^3.0.4:
1089610910
version "3.1.6"
1089710911
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.6.tgz#bae05aa31a4da760969756318feeee6e70f15d6c"
@@ -10906,13 +10920,6 @@ cross-fetch@^3.1.4, cross-fetch@^3.1.5:
1090610920
dependencies:
1090710921
node-fetch "2.6.7"
1090810922

10909-
cross-fetch@^4.0.0:
10910-
version "4.0.0"
10911-
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983"
10912-
integrity sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==
10913-
dependencies:
10914-
node-fetch "^2.6.12"
10915-
1091610923
cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
1091710924
version "7.0.6"
1091810925
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"

0 commit comments

Comments
 (0)