From 4bb21f70edcef8e0845796130aef83d47b67fd01 Mon Sep 17 00:00:00 2001 From: kfj001 Date: Thu, 25 Jun 2026 08:24:45 +0000 Subject: [PATCH] new app store links feature --- backend/app/__pycache__/main.cpython-312.pyc | Bin 8373 -> 8801 bytes .../app/__pycache__/models.cpython-312.pyc | Bin 7560 -> 7763 bytes .../app/__pycache__/schemas.cpython-312.pyc | Bin 10253 -> 10494 bytes backend/app/main.py | 13 ++- backend/app/models.py | 5 + backend/app/schemas.py | 6 ++ backend/seed.py | 3 + .../admin/admin-station-form.component.html | 30 ++++++ src/app/admin/admin-station-form.component.ts | 9 ++ src/app/admin/admin.component.html | 8 ++ src/app/hero/hero.component.html | 19 ++++ src/app/hero/hero.component.scss | 86 +++++++++++++++++- src/app/interfaces/station-config.ts | 3 + src/app/services/station-config.service.ts | 3 + 14 files changed, 183 insertions(+), 2 deletions(-) diff --git a/backend/app/__pycache__/main.cpython-312.pyc b/backend/app/__pycache__/main.cpython-312.pyc index 905da32ff009704a44cdc3e0a3a7ba6cac4f1831..0bcc23a03ac1def6dd6dad4ef63be64f72a97879 100644 GIT binary patch delta 1256 zcmah{-EZ4e6!%U1?bykhv@Wb&+i2ZtZFFUH=~Mzn$Iq|-oXMj=7v~fYn($D z2+}uKS(1&FX;n`csD5IdL?yhRu za8IMr7Sbq;IvmE>EzWP)zX4@Do7ZImr<7A00+^aFdn7H zPro&OIrPRzW;8vL={q(sprLC*iV4KKFGrCl^n%bQVrzsR*Du)~2Mf89p0~~Ys7pMM z5DQkpuvQx-Or$o2r`){*EAe~Us}>8)iQ|^SA{al;GhlmI}4qoVp#j z%iZBNq`pnHb1wahtS<1k<9D??+J=01Q|+BgKU6!fqZ`shV!dTOy*})opK=QocdF`E zO}E|pIbv@G1G2KkQ-X3cv@o%7b_p!?Ej{fjgZKGke?C`_g?_yc5 z*5#4_vB{@B#?`7l_X*ac5b7l7tKtCQwun2}DPhi$aoE6Ma7CpTJ2@|xaoW%jafGY) zJ_F!713-qoCuX<|Z!?CBXF30xi|SIlBhEri*brzs<_UUs__A#IhQMt8 zf7w4&)qkqCfZyh-`1@R&Sq7{+!j-uiApr4BaUV9|G3g4h*Vup4i9 z9>R89qqeGTL>~21h*G$Kr_^v&$tB!k2^6~`9c`&K>?q-#xtHo95BqSFZ(F7}KwfTqSNT&8f5K*1T;G&D%OFptJ)fT6oVL$qL+!X$+envvq#V$z&R zTRO6M5;)or3eu#oket=6<+zEa35}6sg~%`R5qw(*ha6C=@7uZT$zBFd=RTe8g3!PL ze+;%@-Tz!I_D!cLK8wb1Q=rdpP+mu&mqO_!J&Q|PdO<&rR`9JrH`@TazKrMI1Rg<# G7X1P=`pTIA diff --git a/backend/app/__pycache__/models.cpython-312.pyc b/backend/app/__pycache__/models.cpython-312.pyc index cc520cf4812bb3ee56a8364358faaabaf4efd680..f1f7ced8bb8a0fdae72bedb7d864523eecfeed8b 100644 GIT binary patch delta 392 zcmeCMzHGyHnwOW00SK;Nx5?5F-^f?P#U#W$d7`N0=A&HoOp|yO8D)WlxEvEhDod(p z3Tuko8m`sMARP=0QSzHzcxN**Dr|nhr@&}+OSm8>u`<56B)=#%J~KH#FTS)W=N2E5 zAc!xLSWo~}lbV~9ni8K;k~{f7zdYm7&GG{MjFuNz#6PkzNGmQBTfw|odPC`fq$^UM zK*5`$(lgx_7|-_Jpn5>)il|G2-%V-71u7dv4=`PkcD~3Wv3a9lAR|{6&|O6!B6~8A za57`o<^tgsMy`C2D2OPV{7b?4Eo_CWcRg(S>n>%NGU^{UvO&o2(N64hDD3 delta 280 zcmca?(_zhbnwOW00SLV2*kqZAZRD%rV%otpIgmqZa)Su}=A&HoOq=KN&Sqqkom?QS zJUKy*eqWB8OAy%;CBd|?35UwkGD$~gf5U=m2c diff --git a/backend/app/__pycache__/schemas.cpython-312.pyc b/backend/app/__pycache__/schemas.cpython-312.pyc index 9d19e86e0b196b06e8074860cc188b336d6dd349..4394e61e1acb60e5439247a11edeaf163721e052 100644 GIT binary patch delta 1686 zcmZ{kQA}H96vz4St{WXKrL?`bx9tjp0+*#Qb(s^;K$hr$Q4psvT-H>2<3>tXFC!Bc z1UHwtC~@ZK0$H3aJ{l7y56;Cs_~1<312Z+T&S+*#_F(qdxCavT`yUvqIPSymo_o&u z{@?k|_kDM!_wN^+HysXpk$mp|)H-}Mc*Xhk=3<+mdA~*rl?FMse9~ZUGL@vSRpzjz zu2~waq(7-26@#|%fDv50x}{7s->%4{665jF*k`qH6q%QVo)~~ zbCVyH*^*waMhpfDd|b863l{je{&8Vww6}Du+Z~?jJtqr}=4$GvW9RqHzW70RR;kZ5G~QCQ+4}h> z=b}Fn(68W00|=(?(lZrbSz z*;XCPYRKM)7;u6nTrZ10`oT5x|Doyf4!Ywm5mWR{`O{Qep_A{i05!SOqK7WI!_k9K zQGh|95m?uQe)xd*=uda6=;wOdD&}_%Ai#C93JdhWS1(~Wtq|;OKo}Sxmq*)LkV=24 z+wF%C0zRa;Cn*lG`mdgMypL3;32%&+d?mJz<%rQ{>hXK&p0`mPUhQsxdVF1SX%~IF z>4%U6d?CA7M-vez^=nmPNOp4V62rO2ARMTHW6!?zb*4t?) zs8d_T45b2|mA11Eu7mYZXQQ?+1VXlzEMD?3MgdFoPar9Vx!B3zxxL)+jf5X8KaSDs zPDlzqAV(dc4v|`<&{INW=;pR|agQ9~xOjsWyqZ0YJm4<9AMO)ro_RU^jFCa$b%~hO z3v-?RN%(*~B`O2rB#*mL`HS2`tEx-tKztHOz%t#bii=53I=Fo<@+JaP5;1EU=3789 zz}-J3Eu$2h(>e_&Zw}`?+?>-0a24w+JA(jVr;+M@afT=Tt@>_wZPJ1{Sr40JouWT# z8hba);RC^m24@2smT(qPz)Ok9N8&8qkF-yX!Knguzz*PXfPZNG2jc(70r>cW^F2I_ zPrf%pu=rTx5b|;19p>#@X$3c&8^9$Lc80#Jb%=NA+uDSti|A}ezDU0E%|6jIYvhaM LE5Dnbty}sR)V+S+ delta 1402 zcmZ{kZD>|m?3hEn_{xSB6H>-chMQ)vs72G9z$@mV8GEnMl16u+N% zX+*U3&;j3Z*1L(;OY9l%?*F^@mZkJ|SOcSKLfaS=FOJlDfrrOgDK|uvM+< z=imyh2e!Z;d^MP$9@X+`r8;YTsfhf2FaZ8i4Xx#D!o%xAmDDG(X1pf<78)v>U6oz0 zEIZS7hjgbOzQ6@;3O7=}_?!sa)u=sfH$fdzcv!M$pg%Xbd!TpN{?;zSbQ{M_t7Je# zpwR9NMBpDDHk&CU0dJX;g;vkTvp@RH*Xqe7k>Y|CR>@g&Ff1HzVTzW95w6sqIy*Djt&Tg)8uu zZ&&rwUS3{(zGojM_ba6B0}#i6LZ2axD_E}op2sSvDgp74qc4uK+es)60wdCdLl6Nc z_r$yCkQ8?_{xmw9drs<)=VhGl)g(Wk*G95n4{ZxTp>vZ?;2Y4nBXNo*_<5pv+{R=O zs0Ee*%K`Zfr69>)LN32tV7X)6P{?)YgVYb;sM+5Dvd6N4vNzIYS+bN+4U;%fg^!YFth__L@(ui1-OYaiii + +
+

Mobile App

+ +

Google Play Store

+
+ +
+ + +
+
+
+ + +
+ +

Apple App Store

+
+ + +
+
+
+
+ Play Store URL + {{ config.play_store_url || '(not set)' }} +
+
+ App Store Embed + {{ config.app_store_embed_html ? 'Configured' : '(not set)' }} +
} diff --git a/src/app/hero/hero.component.html b/src/app/hero/hero.component.html index 329a888..8969442 100644 --- a/src/app/hero/hero.component.html +++ b/src/app/hero/hero.component.html @@ -30,6 +30,25 @@ } View Schedule + @if (config().play_store_url || config().app_store_embed_html) { +
+ Download our app +
+ @if (config().play_store_url) { + + @if (config().play_store_icon_url) { + Get it on Google Play + } @else { + Google Play + } + + } + @if (config().app_store_embed_html) { +
+ } +
+
+ }
Live on-air now — Stream free worldwide diff --git a/src/app/hero/hero.component.scss b/src/app/hero/hero.component.scss index 90479b7..3348068 100644 --- a/src/app/hero/hero.component.scss +++ b/src/app/hero/hero.component.scss @@ -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 / — constrain it +.appstore-embed { + display: inline-flex; + align-items: center; + + // Apple's embeds typically wrap an around an + > 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}) { diff --git a/src/app/interfaces/station-config.ts b/src/app/interfaces/station-config.ts index 2dd09a2..19413ee 100644 --- a/src/app/interfaces/station-config.ts +++ b/src/app/interfaces/station-config.ts @@ -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; } diff --git a/src/app/services/station-config.service.ts b/src/app/services/station-config.service.ts index 1fd612b..11b19da 100644 --- a/src/app/services/station-config.service.ts +++ b/src/app/services/station-config.service.ts @@ -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({