new text ad provider (#3303)

* new text ad provider

* fix test

* fixes for production

* Add loading skeleton to SevioTextAd with script-driven timeout fallback

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* effect fix

---------

Co-authored-by: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
Igor Stuev
2026-03-04 16:26:16 +01:00
committed by GitHub
co-authored by Claude Sonnet 4.6
parent 8b06d6f369
commit 74ef64a117
96 changed files with 202 additions and 126 deletions
+19 -1
View File
@@ -5,6 +5,18 @@ import type { AdTextProviders } from 'types/client/adProviders';
import app from '../app';
import { getEnvValue } from '../utils';
const SEVIO_ZONE = '179b0a0e-19b0-4b2a-ad15-7446870bf189';
const SEVIO_INVENTORY_ID = '65597ae2-67b8-404b-ac28-43d5029389da';
const SEVIO_ACCOUNT_ID = 'e08c2e1e-2213-49d8-a397-c2e32094fba4';
const SEVIO_AD_TYPE = 'native';
interface SevioConfig {
readonly zone: string;
readonly inventoryId: string;
readonly accountId: string;
readonly adType: string;
}
const provider: AdTextProviders = (() => {
const envValue = getEnvValue('NEXT_PUBLIC_AD_TEXT_PROVIDER') as AdTextProviders;
return envValue && SUPPORTED_AD_TEXT_PROVIDERS.includes(envValue) ? envValue : 'coinzilla';
@@ -12,12 +24,18 @@ const provider: AdTextProviders = (() => {
const title = 'Text ads';
const config: Feature<{ provider: AdTextProviders }> = (() => {
const config: Feature<{ provider: AdTextProviders; sevio: SevioConfig }> = (() => {
if (!app.isPrivateMode && provider !== 'none') {
return Object.freeze({
title,
isEnabled: true,
provider,
sevio: {
zone: SEVIO_ZONE,
inventoryId: SEVIO_INVENTORY_ID,
accountId: SEVIO_ACCOUNT_ID,
adType: SEVIO_AD_TYPE,
},
});
}
+2
View File
@@ -213,6 +213,8 @@
"SEMRESATTRS",
"Semrush",
"servedbyadbutler",
"sevio",
"sevioads",
"Shavuha",
"Shavukha",
"shibarium",
Vendored
+1
View File
@@ -13,6 +13,7 @@ declare global {
export interface Window {
ethereum?: WalletProvider | undefined;
coinzilla_display: Array<CPreferences>;
sevioads: Array<Array<Record<string, string>>> | undefined;
ga?: {
getAll: () => Array<{ get: (prop: string) => string }>;
};
+5 -7
View File
@@ -1,9 +1,7 @@
export const duck = {
ad: {
name: 'Hello utia!',
description_short: 'Utia is the best! Go with utia! Utia is the best! Go with utia!',
thumbnail: 'http://localhost:3100/utia.jpg',
url: 'https://test.url',
cta_button: 'Click me!',
},
thumbnail: 'http://localhost:3100/utia.jpg',
sponsored: 'Hello utia!',
title: 'Utia is the best! Go with utia! Utia is the best! Go with utia!',
clickURL: 'https://test.url',
ctatext: 'Click me!',
};
+9
View File
@@ -12,6 +12,9 @@ export function ad(): CspDev.DirectiveDescriptor {
'*.coinzilla.com',
'https://request-global.czilladx.com',
// sevio (coinzilla text ad)
'*.adx.ws',
// adbutler
'servedbyadbutler.com',
@@ -37,6 +40,9 @@ export function ad(): CspDev.DirectiveDescriptor {
// slise
'*.slise.xyz',
// sevio
'cdn.adx.ws',
],
'img-src': [
// coinzilla
@@ -44,6 +50,9 @@ export function ad(): CspDev.DirectiveDescriptor {
// adbutler
'servedbyadbutler.com',
// sevio
'*.adx.ws',
],
'font-src': [
// coinzilla
+38 -5
View File
@@ -4,14 +4,47 @@ import * as textAdMock from 'mocks/ad/textAd';
export type MockTextAdFixture = () => Promise<void>;
const SEVIO_LOADER_URL = 'https://cdn.adx.ws/scripts/loader.js';
const fixture: TestFixture<MockTextAdFixture, { page: Page }> = async({ page }, use) => {
await use(async() => {
const { thumbnail, sponsored, title, clickURL, ctatext } = textAdMock.duck;
await page.route('https://request-global.czilladx.com/serve/native.php?z=19260bf627546ab7242', (route) => route.fulfill({
status: 200,
json: textAdMock.duck,
}));
await page.route(textAdMock.duck.ad.thumbnail, (route) => {
const adHtml = [
`<strong>Ads:</strong> `,
// eslint-disable-next-line max-len
`<img src="${ thumbnail }" width="20" height="20" style="display: inline-block; vertical-align: text-bottom; margin-right: 4px; margin-left: 7px;" alt="" />`,
`${ sponsored } \u2013 ${ title } `,
`<a href="${ clickURL }" target="_blank">${ ctatext }</a>`,
].join('');
// Route the Sevio loader script so that onLoad fires (not onError), which starts
// the ad-served timeout in the component.
await page.route(SEVIO_LOADER_URL, (route) => {
return route.fulfill({ status: 200, contentType: 'application/javascript', body: '' });
});
await page.evaluate((html) => {
const queue: Array<Array<Record<string, string>>> = [];
queue.push = () => {
// Defer injection so the MutationObserver (set up in the next useEffect) is
// already observing the div when the HTML arrives.
setTimeout(() => {
const container = document.querySelector('.sevioads');
if (container) {
container.innerHTML = html;
}
}, 0);
return queue.length;
};
Object.defineProperty(window, 'sevioads', {
value: queue,
writable: true,
configurable: true,
});
}, adHtml);
await page.route(thumbnail, (route) => {
return route.fulfill({
status: 200,
path: './playwright/mocks/image_s.jpg',
Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 10 KiB

-111
View File
@@ -1,111 +0,0 @@
import { Box, Text, chakra } from '@chakra-ui/react';
import React, { useEffect } from 'react';
import { Image } from 'toolkit/chakra/image';
import { Link } from 'toolkit/chakra/link';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { ndash } from 'toolkit/utils/htmlEntities';
import { isBrowser } from 'toolkit/utils/isBrowser';
type AdData = {
ad: {
name: string;
description_short: string;
thumbnail: string;
url: string;
cta_button: string;
impressionUrl?: string;
};
};
// const MOCK: AdData = {
// ad: {
// url: 'https://unsplash.com/s/photos/cute-kitten',
// thumbnail: 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/gnosis.svg',
// name: 'All about kitties',
// description_short: 'To see millions picture of cute kitties',
// cta_button: 'click here',
// },
// };
const CoinzillaTextAd = ({ className }: { className?: string }) => {
const [ adData, setAdData ] = React.useState<AdData | null>(null);
const [ isLoading, setIsLoading ] = React.useState(true);
useEffect(() => {
if (isBrowser()) {
fetch('https://request-global.czilladx.com/serve/native.php?z=19260bf627546ab7242')
.then(res => res.status === 200 ? res.json() : null)
.then((_data) => {
const data = _data as AdData;
setAdData(data);
if (data?.ad?.impressionUrl) {
fetch(data.ad.impressionUrl);
}
})
.finally(() => {
// setAdData(MOCK);
setIsLoading(false);
});
}
}, [ ]);
if (isLoading) {
return (
<Skeleton
loading
className={ className }
h={{ base: 12, lg: 6 }}
w="100%"
flexGrow={ 1 }
maxW="800px"
display="block"
/>
);
}
if (!adData) {
return null;
}
const urlObject = new URL(adData.ad.url);
return (
<Box
className={ className }
display={{ base: 'flex', lg: 'block' }}
textStyle={{ base: 'xs', lg: 'md' }}
color={{ base: 'text.secondary', lg: 'text.primary' }}
>
<Text
as="span"
whiteSpace="pre-wrap"
fontWeight={ 700 }
mr={ 3 }
display={{ base: 'none', lg: 'inline' }}
>
Ads:
</Text>
{ urlObject.hostname === 'nifty.ink' ?
<Text as="span" mr={ 1 }>🎨</Text> : (
<Image
src={ adData.ad.thumbnail }
width="20px"
height="20px"
display="inline-block"
verticalAlign="text-bottom"
flexShrink={ 0 }
alignSelf={{ base: 'flex-start', lg: 'center' }}
mr={ 1 }
alt=""
/>
) }
<Box display={{ base: 'block', lg: 'inline' }}>
<Text as="span" whiteSpace="pre-wrap">{ `${ adData.ad.name } ${ ndash } ${ adData.ad.description_short } ` }</Text>
<Link href={ adData.ad.url } external noIcon>{ adData.ad.cta_button }</Link>
</Box>
</Box>
);
};
export default chakra(CoinzillaTextAd);
+126
View File
@@ -0,0 +1,126 @@
import { Box, chakra } from '@chakra-ui/react';
import Script from 'next/script';
import React, { useCallback, useEffect, useRef } from 'react';
import config from 'configs/app';
import { Skeleton } from 'toolkit/chakra/skeleton';
const adTextFeature = config.features.adsText;
const AD_LOAD_TIMEOUT = 1_000;
// Sevio native ad template (configured on the Sevio platform):
// <img src="[%thumbnail%]" width="20" height="20" style="display: inline-block; vertical-align: text-bottom; margin-right: 4px;" alt="" />
// <strong>Sponsored:</strong> [%sponsored%] &#8211; [%title%] <a href="[%clickURL%]" target="_blank">[%ctatext%]</a>
type Status = 'loading' | 'loaded' | 'empty';
const SevioTextAd = ({ className }: { className?: string }) => {
const [ status, setStatus ] = React.useState<Status>('loading');
const adRef = useRef<HTMLDivElement>(null);
const timeoutRef = useRef<ReturnType<typeof setTimeout>>(undefined);
useEffect(() => {
if (!adTextFeature.isEnabled) {
return;
}
const { zone, adType, inventoryId, accountId } = adTextFeature.sevio;
window.sevioads = window.sevioads || [];
window.sevioads.push([ { zone, adType, inventoryId, accountId } ]);
}, []);
// Watch for Sevio injecting content into the ad div.
// When it does, cancel the timeout and mark the ad as loaded.
useEffect(() => {
const node = adRef.current;
if (!node) {
return;
}
const observer = new MutationObserver(() => {
if (node.childNodes.length > 0) {
clearTimeout(timeoutRef.current);
setStatus('loaded');
observer.disconnect();
}
});
observer.observe(node, { childList: true });
return () => {
observer.disconnect();
};
}, []);
useEffect(() => {
return () => {
clearTimeout(timeoutRef.current);
};
}, []);
// Once the Sevio script is loaded, give it a short window to inject the ad.
// If nothing appears within that window, assume no ad was served and hide the skeleton.
const handleScriptLoad = useCallback(() => {
timeoutRef.current = setTimeout(() => {
setStatus((prev) => prev === 'loading' ? 'empty' : prev);
}, AD_LOAD_TIMEOUT);
}, []);
// Script failed to load — no ad will ever appear, skip straight to empty.
const handleScriptError = useCallback(() => {
setStatus('empty');
}, []);
if (!adTextFeature.isEnabled || status === 'empty') {
return null;
}
const { zone } = adTextFeature.sevio;
return (
<>
{ status === 'loading' && (
<Skeleton
loading
className={ className }
h={{ base: 12, lg: 6 }}
w="100%"
flexGrow={ 1 }
maxW="800px"
display="block"
/>
) }
<Box
className={ status === 'loaded' ? className : undefined }
display={ status === 'loaded' ? undefined : 'none' }
textStyle={{ base: 'xs', lg: 'md' }}
color={{ base: 'text.secondary', lg: 'text.primary' }}
css={{
'& .sevioads *': {
fontFamily: 'inherit',
fontSize: 'inherit',
},
'& .sevioads strong': {
color: 'inherit',
},
'& .sevioads a': {
color: 'link.primary',
_hover: {
color: 'link.primary.hover',
},
},
}}
>
<Script
strategy="lazyOnload"
src="https://cdn.adx.ws/scripts/loader.js"
onLoad={ handleScriptLoad }
onError={ handleScriptError }
/>
<div ref={ adRef } className="sevioads" data-zone={ zone }/>
</Box>
</>
);
};
export default chakra(SevioTextAd);
+2 -2
View File
@@ -5,7 +5,7 @@ import config from 'configs/app';
import { useAppContext } from 'lib/contexts/app';
import * as cookies from 'lib/cookies';
import CoinzillaTextAd from './CoinzillaTextAd';
import SevioTextAd from './SevioTextAd';
const TextAd = ({ className }: { className?: string }) => {
const hasAdblockCookie = cookies.get(cookies.NAMES.ADBLOCK_DETECTED, useAppContext().cookies);
@@ -14,7 +14,7 @@ const TextAd = ({ className }: { className?: string }) => {
return null;
}
return <CoinzillaTextAd className={ className }/>;
return <SevioTextAd className={ className }/>;
};
export default chakra(TextAd);
Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB