Add Zapstore app download nudge
Prompt mobile-web visitors to install the native Android app from Zapstore. Shows a card at the bottom of the home feed and a link in the account switcher menu. Both are hidden inside the native app (Capacitor.isNativePlatform) and on desktop (sm:hidden for the banner). Adds nav.getApp and feed.getApp.* strings across all locales.
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
import { Capacitor } from '@capacitor/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ArrowRight } from 'lucide-react';
|
||||
import { useAppContext } from '@/hooks/useAppContext';
|
||||
import { ZAPSTORE_URL } from '@/lib/zapstore';
|
||||
|
||||
/**
|
||||
* Zapstore download nudge — prompts mobile-web visitors to install the native
|
||||
* Android app. Hidden inside the native app (you're already in it) and on
|
||||
* desktop (`sm:hidden`), where downloading works differently.
|
||||
*/
|
||||
export function AppDownloadNudge() {
|
||||
const { t } = useTranslation();
|
||||
const { config } = useAppContext();
|
||||
|
||||
if (Capacitor.isNativePlatform()) return null;
|
||||
|
||||
return (
|
||||
<div className="sm:hidden px-4 pt-8 pb-4">
|
||||
<p className="text-xs font-semibold uppercase tracking-widest text-muted-foreground mb-3">
|
||||
{t('feed.getApp.eyebrow')}
|
||||
</p>
|
||||
<div className="flex items-center gap-3">
|
||||
<img
|
||||
src="/logo.png"
|
||||
alt={config.appName}
|
||||
className="h-10 w-10 shrink-0 rounded-xl"
|
||||
/>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-semibold text-foreground">
|
||||
{t('feed.getApp.title', { appName: config.appName })}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t('feed.getApp.subtitle', { appName: config.appName })}
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href={ZAPSTORE_URL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="shrink-0 inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg bg-primary/10 hover:bg-primary/20 text-primary text-xs font-medium transition-colors"
|
||||
>
|
||||
{t('feed.getApp.download')}
|
||||
<ArrowRight className="h-3 w-3" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -7,8 +7,10 @@
|
||||
import { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Activity, Bell, ChevronDown, CircleHelp, LayoutDashboard, LogOut, Search, Settings, User, UserIcon, UserPlus, Wallet } from 'lucide-react';
|
||||
import { Capacitor } from '@capacitor/core';
|
||||
import { Activity, Bell, ChevronDown, CircleHelp, Download, LayoutDashboard, LogOut, Search, Settings, User, UserIcon, UserPlus, Wallet } from 'lucide-react';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import { ZAPSTORE_URL } from '@/lib/zapstore';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -144,6 +146,14 @@ export function AccountSwitcher({ onAddAccountClick }: AccountSwitcherProps) {
|
||||
<span>{t('nav.about')}</span>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
{!Capacitor.isNativePlatform() && (
|
||||
<DropdownMenuItem asChild className='flex items-center gap-2 cursor-pointer p-2 rounded-md'>
|
||||
<a href={ZAPSTORE_URL} target="_blank" rel="noopener noreferrer">
|
||||
<Download className='w-4 h-4' />
|
||||
<span>{t('nav.getApp')}</span>
|
||||
</a>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onClick={onAddAccountClick}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
/** Zapstore app id for the Android build. */
|
||||
export const ZAPSTORE_APP_ID = 'spot.agora.app';
|
||||
|
||||
/** Public Zapstore page for the Android app. */
|
||||
export const ZAPSTORE_URL = `https://zapstore.dev/apps/${encodeURIComponent(ZAPSTORE_APP_ID)}`;
|
||||
+8
-1
@@ -60,7 +60,8 @@
|
||||
"privacy": "الخصوصية",
|
||||
"safety": "السلامة",
|
||||
"changelog": "سجل التغييرات",
|
||||
"sourceCode": "الكود المصدري"
|
||||
"sourceCode": "الكود المصدري",
|
||||
"getApp": "احصل على التطبيق"
|
||||
},
|
||||
"auth": {
|
||||
"join": "انضمام",
|
||||
@@ -128,6 +129,12 @@
|
||||
},
|
||||
"feed": {
|
||||
"indexTagline": "محتواك. أسلوبك. قواعدك.",
|
||||
"getApp": {
|
||||
"eyebrow": "احصل على التطبيق",
|
||||
"title": "{{appName}} لنظام أندرويد",
|
||||
"subtitle": "تجربة {{appName}} الكاملة",
|
||||
"download": "تنزيل"
|
||||
},
|
||||
"compose": {
|
||||
"placeholder": "ماذا يحدث؟"
|
||||
},
|
||||
|
||||
+8
-1
@@ -64,7 +64,8 @@
|
||||
"privacy": "Privacy",
|
||||
"safety": "Safety",
|
||||
"changelog": "Changelog",
|
||||
"sourceCode": "Source code"
|
||||
"sourceCode": "Source code",
|
||||
"getApp": "Get the app"
|
||||
},
|
||||
"auth": {
|
||||
"join": "Join",
|
||||
@@ -132,6 +133,12 @@
|
||||
},
|
||||
"feed": {
|
||||
"indexTagline": "Your content. Your vibe. Your rules.",
|
||||
"getApp": {
|
||||
"eyebrow": "Get the app",
|
||||
"title": "{{appName}} for Android",
|
||||
"subtitle": "The full {{appName}} experience",
|
||||
"download": "Download"
|
||||
},
|
||||
"compose": {
|
||||
"placeholder": "What's happening?"
|
||||
},
|
||||
|
||||
+8
-1
@@ -64,7 +64,8 @@
|
||||
"privacy": "Privacidad",
|
||||
"safety": "Seguridad",
|
||||
"changelog": "Novedades",
|
||||
"sourceCode": "Código fuente"
|
||||
"sourceCode": "Código fuente",
|
||||
"getApp": "Descargar la app"
|
||||
},
|
||||
"auth": {
|
||||
"join": "Unirse",
|
||||
@@ -132,6 +133,12 @@
|
||||
},
|
||||
"feed": {
|
||||
"indexTagline": "Tu contenido. Tu estilo. Tus reglas.",
|
||||
"getApp": {
|
||||
"eyebrow": "Descargar la app",
|
||||
"title": "{{appName}} para Android",
|
||||
"subtitle": "La experiencia completa de {{appName}}",
|
||||
"download": "Descargar"
|
||||
},
|
||||
"compose": {
|
||||
"placeholder": "¿Qué está pasando?"
|
||||
},
|
||||
|
||||
+8
-1
@@ -64,7 +64,8 @@
|
||||
"privacy": "حریم خصوصی",
|
||||
"safety": "ایمنی",
|
||||
"changelog": "تغییرات",
|
||||
"sourceCode": "کد منبع"
|
||||
"sourceCode": "کد منبع",
|
||||
"getApp": "دریافت برنامه"
|
||||
},
|
||||
"auth": {
|
||||
"join": "پیوستن",
|
||||
@@ -132,6 +133,12 @@
|
||||
},
|
||||
"feed": {
|
||||
"indexTagline": "محتوای شما. سبک شما. قوانین شما.",
|
||||
"getApp": {
|
||||
"eyebrow": "دریافت برنامه",
|
||||
"title": "{{appName}} برای اندروید",
|
||||
"subtitle": "تجربه کامل {{appName}}",
|
||||
"download": "دانلود"
|
||||
},
|
||||
"compose": {
|
||||
"placeholder": "چه خبر؟"
|
||||
},
|
||||
|
||||
+8
-1
@@ -63,7 +63,8 @@
|
||||
"privacy": "Confidentialité",
|
||||
"safety": "Sécurité",
|
||||
"changelog": "Journal des modifications",
|
||||
"sourceCode": "Code source"
|
||||
"sourceCode": "Code source",
|
||||
"getApp": "Télécharger l'application"
|
||||
},
|
||||
"auth": {
|
||||
"join": "Rejoindre",
|
||||
@@ -131,6 +132,12 @@
|
||||
},
|
||||
"feed": {
|
||||
"indexTagline": "Votre contenu. Votre ambiance. Vos règles.",
|
||||
"getApp": {
|
||||
"eyebrow": "Télécharger l'application",
|
||||
"title": "{{appName}} pour Android",
|
||||
"subtitle": "L'expérience {{appName}} complète",
|
||||
"download": "Télécharger"
|
||||
},
|
||||
"compose": {
|
||||
"placeholder": "Que se passe-t-il ?"
|
||||
},
|
||||
|
||||
+8
-1
@@ -64,7 +64,8 @@
|
||||
"privacy": "प्राइवेसी",
|
||||
"safety": "सुरक्षा",
|
||||
"changelog": "बदलाव",
|
||||
"sourceCode": "सोर्स कोड"
|
||||
"sourceCode": "सोर्स कोड",
|
||||
"getApp": "ऐप पाएं"
|
||||
},
|
||||
"auth": {
|
||||
"join": "जुड़ें",
|
||||
@@ -132,6 +133,12 @@
|
||||
},
|
||||
"feed": {
|
||||
"indexTagline": "आपका कंटेंट। आपका अंदाज़। आपके नियम।",
|
||||
"getApp": {
|
||||
"eyebrow": "ऐप पाएं",
|
||||
"title": "Android के लिए {{appName}}",
|
||||
"subtitle": "संपूर्ण {{appName}} अनुभव",
|
||||
"download": "डाउनलोड करें"
|
||||
},
|
||||
"compose": {
|
||||
"placeholder": "क्या चल रहा है?"
|
||||
},
|
||||
|
||||
+8
-1
@@ -64,7 +64,8 @@
|
||||
"privacy": "Privasi",
|
||||
"safety": "Keamanan",
|
||||
"changelog": "Catatan Versi",
|
||||
"sourceCode": "Kode sumber"
|
||||
"sourceCode": "Kode sumber",
|
||||
"getApp": "Dapatkan aplikasi"
|
||||
},
|
||||
"auth": {
|
||||
"join": "Gabung",
|
||||
@@ -132,6 +133,12 @@
|
||||
},
|
||||
"feed": {
|
||||
"indexTagline": "Konten Anda. Gaya Anda. Aturan Anda.",
|
||||
"getApp": {
|
||||
"eyebrow": "Dapatkan aplikasi",
|
||||
"title": "{{appName}} untuk Android",
|
||||
"subtitle": "Pengalaman {{appName}} sepenuhnya",
|
||||
"download": "Unduh"
|
||||
},
|
||||
"compose": {
|
||||
"placeholder": "Apa yang sedang terjadi?"
|
||||
},
|
||||
|
||||
+8
-1
@@ -64,7 +64,8 @@
|
||||
"privacy": "ភាពឯកជន",
|
||||
"safety": "សុវត្ថិភាព",
|
||||
"changelog": "កំណត់ហេតុការផ្លាស់ប្តូរ",
|
||||
"sourceCode": "កូដប្រភព"
|
||||
"sourceCode": "កូដប្រភព",
|
||||
"getApp": "ទាញយកកម្មវិធី"
|
||||
},
|
||||
"auth": {
|
||||
"join": "ចូលរួម",
|
||||
@@ -132,6 +133,12 @@
|
||||
},
|
||||
"feed": {
|
||||
"indexTagline": "ខ្លឹមសាររបស់អ្នក។ ស្ទីលរបស់អ្នក។ ច្បាប់របស់អ្នក។",
|
||||
"getApp": {
|
||||
"eyebrow": "ទាញយកកម្មវិធី",
|
||||
"title": "{{appName}} សម្រាប់ Android",
|
||||
"subtitle": "បទពិសោធន៍ {{appName}} ពេញលេញ",
|
||||
"download": "ទាញយក"
|
||||
},
|
||||
"compose": {
|
||||
"placeholder": "មានរឿងអ្វី?"
|
||||
},
|
||||
|
||||
+8
-1
@@ -64,7 +64,8 @@
|
||||
"privacy": "محرمیت",
|
||||
"safety": "خوندیتوب",
|
||||
"changelog": "د بدلونونو لاګ",
|
||||
"sourceCode": "د سرچینې کوډ"
|
||||
"sourceCode": "د سرچینې کوډ",
|
||||
"getApp": "اپ ترلاسه کړئ"
|
||||
},
|
||||
"auth": {
|
||||
"join": "ګډون",
|
||||
@@ -132,6 +133,12 @@
|
||||
},
|
||||
"feed": {
|
||||
"indexTagline": "ستاسو مینځپانګه. ستاسو سټایل. ستاسو قواعد.",
|
||||
"getApp": {
|
||||
"eyebrow": "اپ ترلاسه کړئ",
|
||||
"title": "{{appName}} د اندروید لپاره",
|
||||
"subtitle": "بشپړ {{appName}} تجربه",
|
||||
"download": "ډاونلوډ"
|
||||
},
|
||||
"compose": {
|
||||
"placeholder": "څه روان دي؟"
|
||||
},
|
||||
|
||||
+8
-1
@@ -64,7 +64,8 @@
|
||||
"privacy": "Privacidade",
|
||||
"safety": "Segurança",
|
||||
"changelog": "Notas de versão",
|
||||
"sourceCode": "Código-fonte"
|
||||
"sourceCode": "Código-fonte",
|
||||
"getApp": "Baixar o app"
|
||||
},
|
||||
"auth": {
|
||||
"join": "Entrar",
|
||||
@@ -132,6 +133,12 @@
|
||||
},
|
||||
"feed": {
|
||||
"indexTagline": "Seu conteúdo. Seu estilo. Suas regras.",
|
||||
"getApp": {
|
||||
"eyebrow": "Baixar o app",
|
||||
"title": "{{appName}} para Android",
|
||||
"subtitle": "A experiência completa do {{appName}}",
|
||||
"download": "Baixar"
|
||||
},
|
||||
"compose": {
|
||||
"placeholder": "O que está acontecendo?"
|
||||
},
|
||||
|
||||
+8
-1
@@ -64,7 +64,8 @@
|
||||
"privacy": "Конфиденциальность",
|
||||
"safety": "Безопасность",
|
||||
"changelog": "История изменений",
|
||||
"sourceCode": "Исходный код"
|
||||
"sourceCode": "Исходный код",
|
||||
"getApp": "Скачать приложение"
|
||||
},
|
||||
"auth": {
|
||||
"join": "Присоединиться",
|
||||
@@ -132,6 +133,12 @@
|
||||
},
|
||||
"feed": {
|
||||
"indexTagline": "Ваш контент. Ваша атмосфера. Ваши правила.",
|
||||
"getApp": {
|
||||
"eyebrow": "Скачать приложение",
|
||||
"title": "{{appName}} для Android",
|
||||
"subtitle": "Полный опыт {{appName}}",
|
||||
"download": "Скачать"
|
||||
},
|
||||
"compose": {
|
||||
"placeholder": "Что происходит?"
|
||||
},
|
||||
|
||||
+8
-1
@@ -64,7 +64,8 @@
|
||||
"privacy": "Akavanzika",
|
||||
"safety": "Kuchengetedzeka",
|
||||
"changelog": "Rondedzero yeshanduko",
|
||||
"sourceCode": "Kodhi yetsime"
|
||||
"sourceCode": "Kodhi yetsime",
|
||||
"getApp": "Tora app"
|
||||
},
|
||||
"auth": {
|
||||
"join": "Joinha",
|
||||
@@ -132,6 +133,12 @@
|
||||
},
|
||||
"feed": {
|
||||
"indexTagline": "Zviri zvako. Mafambisirwo ako. Mitemo yako.",
|
||||
"getApp": {
|
||||
"eyebrow": "Tora app",
|
||||
"title": "{{appName}} yeAndroid",
|
||||
"subtitle": "Ruzivo rwakazara rwe{{appName}}",
|
||||
"download": "Dhaunirodha"
|
||||
},
|
||||
"compose": {
|
||||
"placeholder": "Chii chiri kuitika?"
|
||||
},
|
||||
|
||||
+8
-1
@@ -63,7 +63,8 @@
|
||||
"privacy": "Faragha",
|
||||
"safety": "Usalama",
|
||||
"changelog": "Kumbukumbu ya mabadiliko",
|
||||
"sourceCode": "Msimbo wa chanzo"
|
||||
"sourceCode": "Msimbo wa chanzo",
|
||||
"getApp": "Pata programu"
|
||||
},
|
||||
"auth": {
|
||||
"join": "Jiunge",
|
||||
@@ -131,6 +132,12 @@
|
||||
},
|
||||
"feed": {
|
||||
"indexTagline": "Maudhui yako. Mtindo wako. Sheria zako.",
|
||||
"getApp": {
|
||||
"eyebrow": "Pata programu",
|
||||
"title": "{{appName}} kwa Android",
|
||||
"subtitle": "Uzoefu kamili wa {{appName}}",
|
||||
"download": "Pakua"
|
||||
},
|
||||
"compose": {
|
||||
"placeholder": "Kuna nini?"
|
||||
},
|
||||
|
||||
+8
-1
@@ -63,7 +63,8 @@
|
||||
"privacy": "Gizlilik",
|
||||
"safety": "Güvenlik",
|
||||
"changelog": "Sürüm notları",
|
||||
"sourceCode": "Kaynak kod"
|
||||
"sourceCode": "Kaynak kod",
|
||||
"getApp": "Uygulamayı edinin"
|
||||
},
|
||||
"auth": {
|
||||
"join": "Katıl",
|
||||
@@ -131,6 +132,12 @@
|
||||
},
|
||||
"feed": {
|
||||
"indexTagline": "İçeriğin senin. Tarzın senin. Kuralların senin.",
|
||||
"getApp": {
|
||||
"eyebrow": "Uygulamayı edinin",
|
||||
"title": "Android için {{appName}}",
|
||||
"subtitle": "Tüm {{appName}} deneyimi",
|
||||
"download": "İndir"
|
||||
},
|
||||
"compose": {
|
||||
"placeholder": "Neler oluyor?"
|
||||
},
|
||||
|
||||
@@ -64,7 +64,8 @@
|
||||
"privacy": "隱私",
|
||||
"safety": "安全",
|
||||
"changelog": "更新日誌",
|
||||
"sourceCode": "原始碼"
|
||||
"sourceCode": "原始碼",
|
||||
"getApp": "取得應用程式"
|
||||
},
|
||||
"auth": {
|
||||
"join": "加入",
|
||||
@@ -132,6 +133,12 @@
|
||||
},
|
||||
"feed": {
|
||||
"indexTagline": "你的內容。你的風格。你的規則。",
|
||||
"getApp": {
|
||||
"eyebrow": "取得應用程式",
|
||||
"title": "適用於 Android 的 {{appName}}",
|
||||
"subtitle": "完整的 {{appName}} 體驗",
|
||||
"download": "下載"
|
||||
},
|
||||
"compose": {
|
||||
"placeholder": "有什麼新鮮事?"
|
||||
},
|
||||
|
||||
+8
-1
@@ -64,7 +64,8 @@
|
||||
"privacy": "隐私",
|
||||
"safety": "安全",
|
||||
"changelog": "更新日志",
|
||||
"sourceCode": "源代码"
|
||||
"sourceCode": "源代码",
|
||||
"getApp": "获取应用"
|
||||
},
|
||||
"auth": {
|
||||
"join": "加入",
|
||||
@@ -132,6 +133,12 @@
|
||||
},
|
||||
"feed": {
|
||||
"indexTagline": "你的内容。你的风格。你的规则。",
|
||||
"getApp": {
|
||||
"eyebrow": "获取应用",
|
||||
"title": "适用于 Android 的 {{appName}}",
|
||||
"subtitle": "完整的 {{appName}} 体验",
|
||||
"download": "下载"
|
||||
},
|
||||
"compose": {
|
||||
"placeholder": "有什么新鲜事?"
|
||||
},
|
||||
|
||||
+7
-1
@@ -1,6 +1,7 @@
|
||||
import { useSeoMeta } from '@unhead/react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Feed } from '@/components/Feed';
|
||||
import { AppDownloadNudge } from '@/components/AppDownloadNudge';
|
||||
import { useAppContext } from '@/hooks/useAppContext';
|
||||
|
||||
const Index = () => {
|
||||
@@ -12,7 +13,12 @@ const Index = () => {
|
||||
description: t('feed.indexTagline'),
|
||||
});
|
||||
|
||||
return <Feed />;
|
||||
return (
|
||||
<>
|
||||
<Feed />
|
||||
<AppDownloadNudge />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Index;
|
||||
|
||||
Reference in New Issue
Block a user