diff --git a/CLAUDE.md b/CLAUDE.md index b1088e6..4d655bc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -92,7 +92,7 @@ Gated by path secret (see `proxy.ts` above) *and* by role at the tRPC layer: `as ### Download redirect `/app` -`src/app/app/route.ts` (public in `proxy.ts`) is the target for flyers/QR codes: Android → Play Store with sanitized `utm_*` passed as Play `referrer`, iOS → `APP_STORE_URL` in `src/lib/app-links.ts` once set (still `null` until the iOS release; until then the web start page), everything else → web start page; `?ref=` → `/r/`. Logic is pure in `src/lib/app-redirect.ts`; clicks are counted per day/source/platform in Redis (`appredirect:` hashes, `src/lib/app-redirect-stats.ts`) and shown in the admin referral overview. Redis on the NAS is the `pawfeed-redis` container (not `glitchtip-redis`), password in `docker/.env` `REDIS_PASSWORD`. +`src/app/app/route.ts` (public in `proxy.ts`) is the target for flyers/QR codes: Android → Play Store with sanitized `utm_*` passed as Play `referrer`, iOS → `APP_STORE_URL` in `src/lib/app-links.ts` (App Store, live since 2026-10-08), everything else → web start page; `?ref=` → `/r/`. Logic is pure in `src/lib/app-redirect.ts`; clicks are counted per day/source/platform in Redis (`appredirect:` hashes, `src/lib/app-redirect-stats.ts`) and shown in the admin referral overview. Redis on the NAS is the `pawfeed-redis` container (not `glitchtip-redis`), password in `docker/.env` `REDIS_PASSWORD`. ### New-pet welcome & founding members diff --git a/messages/de.json b/messages/de.json index 6de5b44..8765dc3 100644 --- a/messages/de.json +++ b/messages/de.json @@ -775,10 +775,11 @@ "footerTagline": "Wo Haustiere die Stars sind.", "footerDesignedBy": "Designed & Powered by PawFeed", "footerSecurityBadge": "Sicherheits-Check: Note A+", - "heroAndroidHint": "Auch als App für Android", + "heroAndroidHint": "Auch als App für iPhone und Android", "playStoreAlt": "Jetzt bei Google Play", + "appStoreAlt": "Laden im App Store", "appHeading": "PawFeed für die Hosentasche", - "appBody": "Die Android-App bringt Feed, Storys und Nachrichten direkt aufs Handy — mit Push-Benachrichtigungen, wenn dein Tier ein neues Like oder einen neuen Follower bekommt." + "appBody": "Die App für iPhone und Android bringt Feed, Storys und Nachrichten direkt aufs Handy — mit Push-Benachrichtigungen, wenn dein Tier ein neues Like oder einen neuen Follower bekommt." }, "AboutPage": { "metaTitle": "Über uns — PawFeed", diff --git a/messages/en.json b/messages/en.json index 9dcea03..5c93bae 100644 --- a/messages/en.json +++ b/messages/en.json @@ -775,10 +775,11 @@ "footerTagline": "Where pets are the stars.", "footerDesignedBy": "Designed & Powered by PawFeed", "footerSecurityBadge": "Security check: Grade A+", - "heroAndroidHint": "Also available as an Android app", + "heroAndroidHint": "Also available as an app for iPhone and Android", "playStoreAlt": "Get it on Google Play", + "appStoreAlt": "Download on the App Store", "appHeading": "PawFeed in your pocket", - "appBody": "The Android app puts the feed, stories, and messages right on your phone — with push notifications when your pet gets a new like or follower." + "appBody": "The iPhone and Android app puts the feed, stories, and messages right on your phone — with push notifications when your pet gets a new like or follower." }, "AboutPage": { "metaTitle": "About us — PawFeed", diff --git a/public/badges/app-store-de.svg b/public/badges/app-store-de.svg new file mode 100644 index 0000000..536f370 --- /dev/null +++ b/public/badges/app-store-de.svg @@ -0,0 +1,40 @@ + + Download_on_the_App_Store_Badge_DE_RGB_blk_092917 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/badges/app-store-en.svg b/public/badges/app-store-en.svg new file mode 100644 index 0000000..072b425 --- /dev/null +++ b/public/badges/app-store-en.svg @@ -0,0 +1,46 @@ + + Download_on_the_App_Store_Badge_US-UK_RGB_blk_4SVG_092917 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/app/page.tsx b/src/app/page.tsx index 2f52ff2..dbca8c2 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -7,7 +7,7 @@ import { PawPrint, HeartPulse, MessageCircle, Images, Heart, Sparkles, CheckCirc import { ScrollReveal } from "@/components/landing/ScrollReveal"; import { LandingHeader } from "@/components/landing/LandingHeader"; import { LandingFooter } from "@/components/landing/LandingFooter"; -import { PlayStoreBadge } from "@/components/landing/PlayStoreBadge"; +import { StoreBadges } from "@/components/landing/StoreBadges"; /** * Root route (/) — public landing page for logged-out visitors, redirects @@ -78,7 +78,7 @@ export default async function RootPage() {
{t("heroAndroidHint")} - +
@@ -228,7 +228,7 @@ export default async function RootPage() {

{t("appBody")}

- + diff --git a/src/app/r/[code]/page.tsx b/src/app/r/[code]/page.tsx index 1ec1217..ff511f0 100644 --- a/src/app/r/[code]/page.tsx +++ b/src/app/r/[code]/page.tsx @@ -9,7 +9,7 @@ import { buttonVariants } from "@/components/ui/button"; import { PetAvatar } from "@/components/ui/pet-avatar"; import { LandingHeader } from "@/components/landing/LandingHeader"; import { LandingFooter } from "@/components/landing/LandingFooter"; -import { PlayStoreBadge } from "@/components/landing/PlayStoreBadge"; +import { StoreBadges } from "@/components/landing/StoreBadges"; import { RememberReferralCode } from "@/components/referrals/RememberReferralCode"; export const metadata: Metadata = { @@ -78,7 +78,7 @@ export default async function ReferralLandingPage({ params }: { params: Promise< )}
- + {t("webSignUp")} diff --git a/src/components/landing/LandingFooter.tsx b/src/components/landing/LandingFooter.tsx index d80597b..d5abc1e 100644 --- a/src/components/landing/LandingFooter.tsx +++ b/src/components/landing/LandingFooter.tsx @@ -1,7 +1,7 @@ import { getTranslations } from "next-intl/server"; import Link from "next/link"; import { PawPrint, ShieldCheck } from "lucide-react"; -import { PlayStoreBadge } from "./PlayStoreBadge"; +import { StoreBadges } from "./StoreBadges"; /** * Shared footer for the public landing page and "Über uns" (Gitea #23) — @@ -21,7 +21,7 @@ export async function LandingFooter() {

{t("footerTagline")}

- +
diff --git a/src/components/landing/PlayStoreBadge.tsx b/src/components/landing/PlayStoreBadge.tsx deleted file mode 100644 index 37b2c7b..0000000 --- a/src/components/landing/PlayStoreBadge.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import Image from "next/image"; -import { getLocale, getTranslations } from "next-intl/server"; -import { PLAY_STORE_URL } from "@/lib/app-links"; - -/** - * Official "Get it on Google Play" badge — Google's brand guidelines require - * their own artwork (no recolouring, no custom text), vendored locally in - * /public/badges so the page makes no request to Google. - */ -export async function PlayStoreBadge({ className = "h-12" }: { className?: string }) { - const locale = await getLocale(); - const t = await getTranslations("Landing"); - const src = locale.startsWith("de") ? "/badges/google-play-de.png" : "/badges/google-play-en.png"; - - return ( - - {t("playStoreAlt")} - - ); -} diff --git a/src/components/landing/StoreBadges.tsx b/src/components/landing/StoreBadges.tsx new file mode 100644 index 0000000..5fbe822 --- /dev/null +++ b/src/components/landing/StoreBadges.tsx @@ -0,0 +1,44 @@ +import Image from "next/image"; +import { getLocale, getTranslations } from "next-intl/server"; +import { APP_STORE_URL, PLAY_STORE_URL } from "@/lib/app-links"; + +const badgeLink = + "inline-flex items-center rounded-lg transition-opacity hover:opacity-85 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-orange-500"; + +/** + * Official App Store + Google Play badges — both brands require their own + * artwork (no recolouring, no custom text), vendored locally in + * /public/badges so the page makes no request to Apple or Google. + * The Google PNGs carry transparent padding, the Apple SVGs none, so the + * Apple badge is scaled to the Google badge's visible height + * (DE 192/250 px, EN 168/250 px). + */ +export async function StoreBadges({ className = "h-12" }: { className?: string }) { + const locale = await getLocale(); + const t = await getTranslations("Landing"); + const isDe = locale.startsWith("de"); + + return ( + + ); +} diff --git a/src/lib/app-links.ts b/src/lib/app-links.ts index 0cb716c..fcc7d4c 100644 --- a/src/lib/app-links.ts +++ b/src/lib/app-links.ts @@ -7,9 +7,5 @@ export const ANDROID_PACKAGE_ID = "org.pawfeed.app"; export const PLAY_STORE_URL = `https://play.google.com/store/apps/details?id=${ANDROID_PACKAGE_ID}`; -/** - * App-Store-Eintrag der iOS-App. Bleibt null, bis die App freigegeben ist - * (Phase-0-Aufgabe D5) — bis dahin leitet /app iPhones auf die Web-Startseite. - * Eintragen genügt, /app nutzt den Wert automatisch. - */ -export const APP_STORE_URL: string | null = null; +/** App-Store-Eintrag der iOS-App (Apple-ID = `ascAppId` in mobile/eas.json), live seit 08.10.2026. */ +export const APP_STORE_URL = "https://apps.apple.com/app/id6816366478";