|
1 | 1 | <script lang="ts"> |
2 | 2 | import type { AppFullDetail } from '../types'; |
3 | | - import { formatNumber } from '$lib/utils/formatNumber'; |
| 3 | + import { formatNumber, getRevenueBucket } from '$lib/utils/formatNumber'; |
4 | 4 |
|
5 | 5 | interface Props { |
6 | 6 | app: AppFullDetail; |
|
83 | 83 | const weeklyInstalls = $derived(app.installs_sum_1w ? formatNumber(app.installs_sum_1w) : null); |
84 | 84 | const weeklyRatings = $derived(app.ratings_sum_1w ? formatNumber(app.ratings_sum_1w) : null); |
85 | 85 | const monthlyInstalls = $derived(app.installs_sum_4w ? formatNumber(app.installs_sum_4w) : null); |
| 86 | + const monthlyActiveUsers = $derived( |
| 87 | + app.monthly_active_users && app.monthly_active_users > 0 |
| 88 | + ? formatNumber(app.monthly_active_users) |
| 89 | + : null |
| 90 | + ); |
| 91 | + const monthlyAdRevenue = $derived(Number(app.monthly_ad_revenue) || 0); |
| 92 | + const monthlyIapRevenue = $derived(Number(app.monthly_iap_revenue) || 0); |
| 93 | + const monthlyRevenueTotal = $derived(monthlyAdRevenue + monthlyIapRevenue); |
| 94 | + const monthlyRevenueBucket = $derived( |
| 95 | + monthlyRevenueTotal > 0 ? getRevenueBucket(monthlyRevenueTotal) : null |
| 96 | + ); |
| 97 | + const monthlyAdRevenueShare = $derived( |
| 98 | + monthlyRevenueTotal > 0 ? Math.round((monthlyAdRevenue / monthlyRevenueTotal) * 100) : 0 |
| 99 | + ); |
| 100 | + const monthlyIapRevenueShare = $derived( |
| 101 | + monthlyRevenueTotal > 0 ? 100 - monthlyAdRevenueShare : 0 |
| 102 | + ); |
| 103 | + const releaseMonthYear = $derived( |
| 104 | + app.release_date |
| 105 | + ? new Date(app.release_date).toLocaleDateString('en-US', { |
| 106 | + month: 'short', |
| 107 | + year: 'numeric' |
| 108 | + }) |
| 109 | + : null |
| 110 | + ); |
86 | 111 |
|
87 | 112 | // Format date helper |
88 | 113 | const formatDate = (dateStr: string | undefined | null): string | null => { |
|
100 | 125 |
|
101 | 126 | // Ad and API tracking info |
102 | 127 | const adCreativeCount = $derived(app.ad_creative_count ?? 0); |
103 | | - const hasAdMonetization = $derived((app?.ad_monetized_creative_count ?? 0) > 0); |
| 128 | + const adMonetizedCreativeCount = $derived(app.ad_monetized_creative_count ?? 0); |
| 129 | + const hasAdMonetization = $derived(adMonetizedCreativeCount > 0); |
104 | 130 | const apiLastCrawled = $derived(formatDate(app.api_last_crawled)); |
105 | 131 | const sdkLastCrawled = $derived(formatDate(app.sdk_last_crawled)); |
106 | 132 |
|
|
113 | 139 | <h2 class="text-lg font-bold">{app.name} Summary</h2> |
114 | 140 | <p> |
115 | 141 | <strong>{app.name}</strong> is a |
116 | | - {#if monetization}{monetization}{/if} |
| 142 | + {#if monetization}{monetization}{:else}mobile{/if} |
117 | 143 | {platformName} app |
118 | | - {#if categoryName}in the <span class="font-medium">{categoryName}</span> category{/if}{#if app.developer_name}, |
119 | | - developed by <span class="font-medium">{app.developer_name}</span>{/if}. |
120 | | - {#if appAge} |
121 | | - First released {appAge} ago{#if app.release_date} |
122 | | - ({new Date(app.release_date).toLocaleDateString('en-US', { |
123 | | - month: 'short', |
124 | | - year: 'numeric' |
125 | | - })}){/if},{/if} |
| 144 | + {#if categoryName}in <span class="font-medium">{categoryName}</span>{/if} |
| 145 | + {#if app.developer_name} |
| 146 | + by <span class="font-medium">{app.developer_name}</span>{/if}. |
| 147 | + {#if releaseMonthYear} |
| 148 | + Released in <span class="font-semibold">{releaseMonthYear}</span> |
| 149 | + {#if appAge}({appAge} ago){/if}. |
| 150 | + {/if} |
126 | 151 | {#if installsFormatted && isAndroid} |
127 | | - the app has accumulated <span class="font-semibold">{installsFormatted}+</span> total installs |
| 152 | + It has about <span class="font-semibold">{installsFormatted}+</span> installs |
128 | 153 | {/if} |
129 | 154 | {#if ratingsFormatted} |
130 | | - {#if installsFormatted && isAndroid}and{:else}the app has{/if} |
| 155 | + {#if installsFormatted && isAndroid}and{:else}It has{/if} |
131 | 156 | <span class="font-semibold">{ratingsFormatted}</span> ratings |
132 | 157 | {#if app.rating} |
133 | 158 | with a <span class="font-semibold">{Number(app.rating).toFixed(2)}★</span> ({ratingQuality}) |
134 | | - average rating{/if}. |
| 159 | + average |
| 160 | + {/if}. |
| 161 | + {/if} |
| 162 | + {#if monthlyActiveUsers || monthlyRevenueBucket} |
| 163 | + Based on AppGoblin estimates, |
| 164 | + {#if monthlyActiveUsers} |
| 165 | + it reaches roughly <span class="font-semibold">{monthlyActiveUsers}</span> monthly active users |
| 166 | + {/if} |
| 167 | + {#if monthlyActiveUsers && monthlyRevenueBucket}and{/if} |
| 168 | + {#if monthlyRevenueBucket} |
| 169 | + generates around <span class="font-semibold">{monthlyRevenueBucket}</span> monthly revenue ({monthlyIapRevenueShare}% |
| 170 | + IAP / {monthlyAdRevenueShare}% ads) |
| 171 | + {/if}. |
| 172 | + {/if} |
| 173 | + {#if app.store_last_updated || app.version_code || app.content_rating} |
| 174 | + Store metadata{#if app.store_last_updated}: updated <span class="font-semibold" |
| 175 | + >{formatDate(app.store_last_updated)}</span |
| 176 | + >{/if}{#if app.version_code}, version <span class="font-semibold">{app.version_code}</span |
| 177 | + >{/if}{#if app.content_rating}, rated <span class="font-semibold">{app.content_rating}</span |
| 178 | + >{/if}. |
135 | 179 | {/if} |
136 | 180 | </p> |
137 | 181 |
|
|
157 | 201 | <p> |
158 | 202 | <span class="font-medium text-primary-800-200">Advertising:</span> |
159 | 203 | {#if adCreativeCount > 0} |
160 | | - We've tracked <a href="{appBasePath}/ad-creatives" class="font-semibold" |
| 204 | + AppGoblin has tracked <a href="{appBasePath}/ad-creatives" class="font-semibold" |
161 | 205 | >{formatNumber(adCreativeCount)} ad creatives</a |
162 | | - > where this app is running paid user acquisition campaigns. |
| 206 | + > this app uses for paid user acquisition across ad networks. |
163 | 207 | {/if} |
164 | | - {#if hasAdMonetization} |
165 | | - {#if adCreativeCount > 0}Additionally, the{:else}The{/if} app has been identified as showing |
166 | | - <a href="{appBasePath}/monetized-ads">monetized advertisements</a> based on detected ad network |
167 | | - creatives. |
| 208 | + {#if adMonetizedCreativeCount > 0} |
| 209 | + {#if adCreativeCount > 0}AppGoblin also{/if} |
| 210 | + {#if adCreativeCount === 0}AppGoblin{/if} detected |
| 211 | + <a href="{appBasePath}/monetized-ads" class="font-semibold" |
| 212 | + >{formatNumber(adMonetizedCreativeCount)} monetized ad creatives</a |
| 213 | + > |
| 214 | + shown inside the app. |
168 | 215 | {/if} |
169 | 216 | </p> |
170 | 217 | {/if} |
|
0 commit comments