new app store links feature

This commit is contained in:
2026-06-25 08:24:45 +00:00
parent d9b0964f07
commit 4bb21f70ed
14 changed files with 183 additions and 2 deletions
@@ -154,6 +154,36 @@
</div>
</div>
<!-- Mobile App -->
<div class="form-section">
<h3>Mobile App</h3>
<h4>Google Play Store</h4>
<div class="form-group">
<label for="play_store_icon_url">Play Store Badge (PNG)</label>
<div class="url-with-upload">
<input id="play_store_icon_url" type="text" [(ngModel)]="play_store_icon_url" name="play_store_icon_url"
placeholder="uploads/…">
<label class="btn btn-upload">
Upload
<input type="file" accept="image/png" (change)="onImageUpload($event, 'play_store_icon_url')" [disabled]="loading">
</label>
</div>
</div>
<div class="form-group">
<label for="play_store_url">Play Store URL</label>
<input id="play_store_url" type="url" [(ngModel)]="play_store_url" name="play_store_url"
placeholder="https://play.google.com/store/apps/details?id=…">
</div>
<h4>Apple App Store</h4>
<div class="form-group">
<label for="app_store_embed_html">App Store Embed HTML</label>
<textarea id="app_store_embed_html" [(ngModel)]="app_store_embed_html" name="app_store_embed_html"
rows="6" placeholder="Paste the embed HTML from Apple Marketing Resources"></textarea>
</div>
</div>
<div class="form-actions">
<button type="button" class="btn btn-cancel" (click)="onClose()">Cancel</button>
<button type="submit" class="btn btn-save" [disabled]="saving">
@@ -40,6 +40,9 @@ export class AdminStationFormComponent implements OnChanges {
donation_url = '';
stream_url = '';
stream_metadata_url = '';
play_store_icon_url = '';
play_store_url = '';
app_store_embed_html = '';
loading = false;
saving = false;
@@ -73,6 +76,9 @@ export class AdminStationFormComponent implements OnChanges {
this.donation_url = config.donation_url;
this.stream_url = config.stream_url;
this.stream_metadata_url = config.stream_metadata_url;
this.play_store_icon_url = config.play_store_icon_url;
this.play_store_url = config.play_store_url;
this.app_store_embed_html = config.app_store_embed_html;
}
/** Reset form with current service values. */
@@ -161,6 +167,9 @@ export class AdminStationFormComponent implements OnChanges {
donation_url: this.donation_url,
stream_url: this.stream_url,
stream_metadata_url: this.stream_metadata_url,
play_store_icon_url: this.play_store_icon_url,
play_store_url: this.play_store_url,
app_store_embed_html: this.app_store_embed_html,
};
try {
+8
View File
@@ -300,6 +300,14 @@
<span class="config-label">Donation URL</span>
<span class="config-value">{{ config.donation_url || '(not set)' }}</span>
</div>
<div class="config-row">
<span class="config-label">Play Store URL</span>
<span class="config-value">{{ config.play_store_url || '(not set)' }}</span>
</div>
<div class="config-row">
<span class="config-label">App Store Embed</span>
<span class="config-value">{{ config.app_store_embed_html ? 'Configured' : '(not set)' }}</span>
</div>
</div>
</div>
}
+19
View File
@@ -30,6 +30,25 @@
}
<a routerLink="/schedule" class="btn btn-hero-secondary">View Schedule</a>
</div>
@if (config().play_store_url || config().app_store_embed_html) {
<div class="hero-download">
<span class="download-label">Download our app</span>
<div class="download-buttons">
@if (config().play_store_url) {
<a [href]="config().play_store_url" target="_blank" rel="noopener noreferrer" class="store-link">
@if (config().play_store_icon_url) {
<img [src]="resolveImageUrl(config().play_store_icon_url)" alt="Get it on Google Play" class="store-badge">
} @else {
<span class="store-text">Google Play</span>
}
</a>
}
@if (config().app_store_embed_html) {
<div class="appstore-embed" [innerHTML]="config().app_store_embed_html"></div>
}
</div>
</div>
}
<div class="hero-listeners">
<span class="listeners-dot"></span>
<span class="listeners-text">Live on-air now — Stream free worldwide</span>
+85 -1
View File
@@ -38,7 +38,7 @@
.hero-content {
position: relative;
z-index: 2;
z-index: 4;
text-align: center;
padding: $spacing-3xl $spacing-lg;
@@ -368,6 +368,90 @@
letter-spacing: 0.03em;
}
// ── Download CTAs ───────────────────────────────────────────
.hero-download {
display: flex;
flex-direction: column;
align-items: center;
gap: $spacing-sm;
margin: $spacing-lg 0 $spacing-md;
}
.download-label {
font-size: 0.9rem;
color: rgba(255, 255, 255, 0.65);
letter-spacing: 0.03em;
}
.download-buttons {
display: flex;
gap: $spacing-md;
justify-content: center;
flex-wrap: wrap;
}
// Both store badges are constrained to the same size (160x52)
.store-link {
display: inline-block;
text-decoration: none;
}
.store-badge {
width: 160px;
height: 52px;
object-fit: contain;
background: rgba(255, 255, 255, 0.1);
border-radius: $radius-sm;
padding: 4px;
transition: opacity $transition-fast;
&:hover {
opacity: 0.85;
}
}
.store-text {
color: var(--color-white);
text-decoration: underline;
font-size: 0.95rem;
padding: $spacing-xs $spacing-sm;
border-radius: $radius-sm;
background: rgba(255, 255, 255, 0.1);
&:hover {
background: rgba(255, 255, 255, 0.2);
}
}
// Apple's embed HTML contains its own <a>/<img> — constrain it
.appstore-embed {
display: inline-flex;
align-items: center;
// Apple's embeds typically wrap an <a> around an <img>
> a,
a {
display: inline-block;
text-decoration: none;
}
img {
width: 160px;
height: 52px;
object-fit: contain;
display: block;
}
// If the embed is an iframe (Apple's new style), constrain that too
iframe {
width: 160px;
height: 52px;
border: none;
border-radius: $radius-sm;
}
}
// ── Responsive ──────────────────────────────────────────────
@media (max-width: #{$bp-md - 1px}) {
+3
View File
@@ -20,4 +20,7 @@ export interface StationConfig {
donation_url: string;
stream_url: string;
stream_metadata_url: string;
play_store_icon_url: string;
play_store_url: string;
app_store_embed_html: string;
}
@@ -28,6 +28,9 @@ const DEFAULT_CONFIG: StationConfig = {
donation_url: '',
stream_url: '',
stream_metadata_url: '',
play_store_icon_url: '',
play_store_url: '',
app_store_embed_html: '',
};
@Injectable({