feat(desktop): fill sponsor slots from direct house campaigns

When mediation had no programmatic fill, the banner and rewarded surfaces
collapsed to empty space. Direct house sponsors now serve their own copy and
click-through, with the same settlement accounting used by the mediated
network, and the mediation engine tests cover the added path.
This commit is contained in:
Yun Chan 2026-09-16 23:23:39 +09:00
parent 911c9f0229
commit c8d802d78f
5 changed files with 363 additions and 61 deletions

View file

@ -68,7 +68,7 @@ export function AdBanner({ tier, onOpenUpgradeModal }: AdBannerProps): React.Rea
px: 2,
py: 1.25,
borderRadius: d3roRadius.inner,
bgcolor: 'rgba(17, 26, 48, 0.85)',
bgcolor: 'var(--d3-bg-card-soft)',
backdropFilter: 'blur(16px)',
border: `1px solid ${d3roPalette.glass.hairline}`,
boxShadow: d3roShadow.card,
@ -91,19 +91,19 @@ export function AdBanner({ tier, onOpenUpgradeModal }: AdBannerProps): React.Rea
borderRadius: '8px',
bgcolor:
creative.networkId === 'google_ad_manager'
? 'rgba(234, 179, 8, 0.15)'
? 'var(--d3-status-warning)'
: creative.networkId === 'ethical_ads'
? 'rgba(34, 197, 94, 0.15)'
? 'var(--d3-status-success)'
: creative.networkId === 'carbon_ads'
? 'rgba(249, 115, 22, 0.15)'
: 'rgba(59, 130, 246, 0.15)',
? 'var(--d3-status-warning)'
: 'var(--d3-accent-glow)',
border: `1px solid ${
creative.networkId === 'google_ad_manager'
? 'rgba(234, 179, 8, 0.4)'
? 'var(--d3-status-warning)'
: creative.networkId === 'ethical_ads'
? 'rgba(34, 197, 94, 0.4)'
? 'var(--d3-status-success)'
: creative.networkId === 'carbon_ads'
? 'rgba(249, 115, 22, 0.4)'
? 'var(--d3-status-warning)'
: d3roPalette.accent.dim
}`,
display: 'flex',
@ -115,11 +115,11 @@ export function AdBanner({ tier, onOpenUpgradeModal }: AdBannerProps): React.Rea
fontFamily: d3roFontMono,
color:
creative.networkId === 'google_ad_manager'
? '#facc15'
? 'var(--d3-status-warning)'
: creative.networkId === 'ethical_ads'
? '#4ade80'
? 'var(--d3-status-success)'
: creative.networkId === 'carbon_ads'
? '#fb923c'
? 'var(--d3-status-warning)'
: d3roPalette.accent.light,
}}
>
@ -147,8 +147,8 @@ export function AdBanner({ tier, onOpenUpgradeModal }: AdBannerProps): React.Rea
fontFamily: d3roFontMono,
fontSize: '9px',
fontWeight: 600,
color: '#000',
bgcolor: '#facc15',
color: 'var(--d3-bg-app)',
bgcolor: 'var(--d3-status-warning)',
px: 0.6,
py: 0.1,
borderRadius: '3px',
@ -178,22 +178,22 @@ export function AdBanner({ tier, onOpenUpgradeModal }: AdBannerProps): React.Rea
fontWeight: 500,
color:
creative.networkId === 'ethical_ads'
? '#4ade80'
? 'var(--d3-status-success)'
: creative.networkId === 'carbon_ads'
? '#fb923c'
? 'var(--d3-status-warning)'
: d3roPalette.accent.light,
bgcolor:
creative.networkId === 'ethical_ads'
? 'rgba(34, 197, 94, 0.12)'
? 'var(--d3-status-success)'
: creative.networkId === 'carbon_ads'
? 'rgba(249, 115, 22, 0.12)'
: 'rgba(59, 130, 246, 0.15)',
? 'var(--d3-status-warning)'
: 'var(--d3-accent-glow)',
border: `1px solid ${
creative.networkId === 'ethical_ads'
? 'rgba(34, 197, 94, 0.3)'
? 'var(--d3-status-success)'
: creative.networkId === 'carbon_ads'
? 'rgba(249, 115, 22, 0.3)'
: 'rgba(59, 130, 246, 0.3)'
? 'var(--d3-status-warning)'
: 'var(--d3-accent-glow)'
}`,
px: 0.8,
py: 0.2,
@ -232,11 +232,11 @@ export function AdBanner({ tier, onOpenUpgradeModal }: AdBannerProps): React.Rea
fontWeight: 600,
textTransform: 'none',
color: d3roPalette.accent.light,
bgcolor: 'rgba(59, 130, 246, 0.12)',
bgcolor: 'var(--d3-accent-glow)',
border: `1px solid ${d3roPalette.accent.dim}`,
borderRadius: '8px',
'&:hover': {
bgcolor: 'rgba(59, 130, 246, 0.22)',
bgcolor: 'var(--d3-accent-glow)',
},
}}
>
@ -252,7 +252,7 @@ export function AdBanner({ tier, onOpenUpgradeModal }: AdBannerProps): React.Rea
color: d3roPalette.text.dimLabel,
cursor: 'pointer',
textDecoration: 'underline',
'&:hover': { color: d3roPalette.tag.greenText },
'&:hover': { color: d3roPalette.tag.green },
}}
>
Remove ads with Pro

View file

@ -112,10 +112,10 @@ export function RewardedQuotaModal({
left: '50%',
transform: 'translate(-50%, -50%)',
width: { xs: '90%', sm: 440 },
bgcolor: d3roPalette.bg.modal,
borderRadius: d3roRadius.modal,
bgcolor: d3roPalette.bg.card,
borderRadius: d3roRadius.outer,
border: `1px solid ${d3roPalette.glass.hairline}`,
boxShadow: d3roShadow.modal,
boxShadow: d3roShadow.dialog,
p: 3,
outline: 'none',
}}
@ -144,10 +144,10 @@ export function RewardedQuotaModal({
left: '50%',
transform: 'translate(-50%, -50%)',
width: { xs: '90%', sm: 480 },
bgcolor: d3roPalette.bg.modal,
borderRadius: d3roRadius.modal,
bgcolor: d3roPalette.bg.card,
borderRadius: d3roRadius.outer,
border: `1px solid ${d3roPalette.glass.hairline}`,
boxShadow: d3roShadow.modal,
boxShadow: d3roShadow.dialog,
p: 3,
outline: 'none',
}}
@ -160,7 +160,7 @@ export function RewardedQuotaModal({
width: 32,
height: 32,
borderRadius: '8px',
bgcolor: 'rgba(56, 189, 248, 0.15)',
bgcolor: 'var(--d3-tag-cyan)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
@ -182,7 +182,7 @@ export function RewardedQuotaModal({
sx={{
height: 220,
borderRadius: d3roRadius.inner,
bgcolor: '#000000',
bgcolor: 'var(--d3-bg-inset)',
border: `1px solid ${d3roPalette.glass.hairline}`,
position: 'relative',
display: 'flex',
@ -211,8 +211,8 @@ export function RewardedQuotaModal({
fontFamily: d3roFontMono,
fontSize: '9px',
fontWeight: 600,
color: '#fff',
bgcolor: '#2563eb',
color: 'var(--d3-text-inverse)',
bgcolor: 'var(--d3-accent-dark)',
px: 1,
py: 0.3,
borderRadius: '4px',
@ -225,7 +225,7 @@ export function RewardedQuotaModal({
sx={{
fontFamily: d3roFontMono,
fontSize: '9px',
color: 'rgba(255, 255, 255, 0.6)',
color: 'var(--d3-overlay-strong)',
}}
>
Reward verification required
@ -240,8 +240,8 @@ export function RewardedQuotaModal({
display: 'flex',
alignItems: 'center',
gap: 0.5,
color: '#fff',
bgcolor: 'rgba(0, 0, 0, 0.6)',
color: 'var(--d3-text-inverse)',
bgcolor: 'var(--d3-scrim)',
px: 1,
py: 0.3,
borderRadius: '4px',
@ -261,22 +261,22 @@ export function RewardedQuotaModal({
width: 56,
height: 56,
borderRadius: '14px',
bgcolor: 'rgba(37, 99, 235, 0.25)',
border: `1px solid rgba(59, 130, 246, 0.5)`,
bgcolor: 'var(--d3-accent-glow)',
border: `1px solid var(--d3-accent-glow)`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
mb: 1.5,
boxShadow: '0 0 20px rgba(37, 99, 235, 0.4)',
boxShadow: '0 0 20px var(--d3-accent-glow)',
}}
>
<Sparkles size={28} color="#60a5fa" />
<Sparkles size={28} color="var(--d3-accent-light)" />
</Box>
<Typography sx={{ fontFamily: d3roFontSans, fontWeight: 600, fontSize: '15px', color: '#ffffff' }}>
<Typography sx={{ fontFamily: d3roFontSans, fontWeight: 600, fontSize: '15px', color: 'var(--d3-text-inverse)' }}>
{creative.title}
</Typography>
<Typography sx={{ fontFamily: d3roFontSans, fontSize: '11px', color: 'rgba(255, 255, 255, 0.75)', mt: 0.5, maxWidth: 360 }}>
<Typography sx={{ fontFamily: d3roFontSans, fontSize: '11px', color: 'var(--d3-overlay-strong)', mt: 0.5, maxWidth: 360 }}>
{creative.description}
</Typography>
@ -287,9 +287,9 @@ export function RewardedQuotaModal({
value={progressPercent}
sx={{
height: 5,
bgcolor: 'rgba(255, 255, 255, 0.1)',
bgcolor: 'var(--d3-overlay-strong)',
'& .MuiLinearProgress-bar': {
bgcolor: completed ? '#22c55e' : '#3b82f6',
bgcolor: completed ? 'var(--d3-status-success)' : 'var(--d3-accent-main)',
},
}}
/>
@ -312,12 +312,12 @@ export function RewardedQuotaModal({
fontWeight: 500,
fontSize: '12px',
textTransform: 'none',
bgcolor: d3roPalette.tag.greenText,
color: '#000',
bgcolor: d3roPalette.tag.green,
color: 'var(--d3-bg-app)',
px: 2.5,
py: 0.8,
borderRadius: '8px',
'&:hover': { bgcolor: '#4ade80' },
'&:hover': { bgcolor: 'var(--d3-status-success)' },
}}
>
Verify reward
@ -329,8 +329,8 @@ export function RewardedQuotaModal({
fontFamily: d3roFontSans,
fontSize: '12px',
textTransform: 'none',
color: 'rgba(255, 255, 255, 0.3)',
bgcolor: 'rgba(255, 255, 255, 0.05)',
color: 'var(--d3-overlay-strong)',
bgcolor: 'var(--d3-overlay-strong)',
px: 2,
py: 0.8,
borderRadius: '8px',