feat(android): pass-the-app card (QR + share sheet + NFC tag)
About page renders ShareAppCard on Android only: a QR of the Zapstore listing, a share-sheet button for the install link, and NfcWriteButton to write it to a tag. Single-sourced APP_INSTALL_URL in lib/platform.ts (also used by the About download button). No peer-to-peer APK transfer (needs native Wi-Fi Direct/BLE + PackageInstaller + device QA); Zapstore handles install/update.
This commit is contained in:
parent
77b3c23633
commit
87f58cd84e
4 changed files with 95 additions and 1 deletions
|
|
@ -1537,6 +1537,14 @@ session with **no other memory of this project**. So:
|
|||
auto-opened welcome modal. ⚠️ `SignupDialog` is a **default-only** export, so
|
||||
lazy-load it as
|
||||
`lazy(() => import('…/SignupDialog'))`.
|
||||
- **Pass the app on — Android only (2026-10-26):** the About page renders
|
||||
`ShareAppCard` when `getPlatform() === 'android'` (APK/Zapstore; hidden on
|
||||
web/iOS): a **QR of the Zapstore listing** (`APP_INSTALL_URL` in
|
||||
`lib/platform.ts`, single-sourced), a **share-sheet** button (Nearby
|
||||
Share/SMS/etc.), and `NfcWriteButton` to write the install URL to an NFC tag.
|
||||
⚠️ Deliberately **no peer-to-peer APK transfer** — that needs native Wi‑Fi
|
||||
Direct/BLE + a `PackageInstaller` flow and real-device QA; Zapstore handles
|
||||
installs/updates. (Not visually verified on-device yet.)
|
||||
- **First-run checklist + backup-reminder gating (fixed 2026-10-12).** Two
|
||||
onboarding prompts nagged returning users:
|
||||
1. `FirstRunChecklist` (map) showed for **any logged-in user** until they
|
||||
|
|
|
|||
78
src/components/ShareAppCard.tsx
Normal file
78
src/components/ShareAppCard.tsx
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import { useState } from 'react';
|
||||
import { Check, Share2 } from 'lucide-react';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { QRCodeCanvas } from '@/components/ui/qrcode';
|
||||
import { NfcWriteButton } from '@/components/NfcWriteButton';
|
||||
import { useTranslation } from '@/contexts/language';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { APP_INSTALL_URL, getPlatform } from '@/lib/platform';
|
||||
|
||||
/**
|
||||
* "Pass the app on" — Android only (APK / Zapstore).
|
||||
*
|
||||
* The QR carries the **Zapstore listing** for this app; scanning it on another
|
||||
* Android phone opens the store page and installs/updates via Zapstore. The
|
||||
* share button hands the same link to Android's share sheet (Nearby Share,
|
||||
* Bluetooth, messages…), and — where the device supports NFC writing — the link
|
||||
* can be written to a tag for a genuine tap-to-open handoff.
|
||||
*
|
||||
* We deliberately do NOT move APK bytes peer-to-peer: that needs native
|
||||
* Wi‑Fi Direct/BLE + a PackageInstaller flow and real-device QA, and Zapstore
|
||||
* already handles installs/updates. Renders nothing off Android (iOS can't
|
||||
* sideload; desktop has no use for it).
|
||||
*/
|
||||
export function ShareAppCard() {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
if (getPlatform() !== 'android') return null;
|
||||
|
||||
const share = async () => {
|
||||
try {
|
||||
if (navigator.share) {
|
||||
await navigator.share({
|
||||
title: 'SkateSpots',
|
||||
text: t('settingsUi.about.downloadAppDesc'),
|
||||
url: APP_INSTALL_URL,
|
||||
});
|
||||
return;
|
||||
}
|
||||
await navigator.clipboard.writeText(APP_INSTALL_URL);
|
||||
setCopied(true);
|
||||
toast({ title: t('common.copied'), description: APP_INSTALL_URL });
|
||||
window.setTimeout(() => setCopied(false), 1600);
|
||||
} catch {
|
||||
// Share sheet cancelled — nothing to report.
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="bg-black/90 border border-white/10 mt-4">
|
||||
<CardContent className="space-y-3 p-5">
|
||||
<div className="space-y-1 text-center">
|
||||
<p className="text-xs font-black uppercase tracking-widest text-gray-300">
|
||||
{t('common.share')} SkateSpots
|
||||
</p>
|
||||
<p className="text-xs text-gray-500">{t('settingsUi.about.downloadAppDesc')}</p>
|
||||
</div>
|
||||
|
||||
<div className="mx-auto w-fit rounded-xl bg-white p-3">
|
||||
<QRCodeCanvas value={APP_INSTALL_URL} size={180} level="M" />
|
||||
</div>
|
||||
|
||||
<Button
|
||||
className="w-full gap-2 bg-[#ccff00] font-bold text-black hover:bg-[#ccff00]/90"
|
||||
onClick={() => void share()}
|
||||
>
|
||||
{copied ? <Check className="h-4 w-4" /> : <Share2 className="h-4 w-4" />}
|
||||
{copied ? t('common.copied') : t('common.share')}
|
||||
</Button>
|
||||
|
||||
{/* Tap-to-open: write the install link to an NFC tag (no-op where unsupported). */}
|
||||
<NfcWriteButton url={APP_INSTALL_URL} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
|
@ -27,6 +27,9 @@ export function getPlatform(): string {
|
|||
}
|
||||
}
|
||||
|
||||
/** Canonical Zapstore listing for this app (install + updates). Android only. */
|
||||
export const APP_INSTALL_URL = 'https://zapstore.dev/apps/org.skatespots.app';
|
||||
|
||||
/**
|
||||
* SkateSpots platform Lightning address. Receives Sasquatch Pro passes AND
|
||||
* marketplace listing fees — the same address, deliberately. Single source of
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import { useSeoMeta } from '@unhead/react';
|
|||
import { Link } from 'react-router-dom';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ShareAppCard } from '@/components/ShareAppCard';
|
||||
import { APP_INSTALL_URL } from '@/lib/platform';
|
||||
import { useCurrentUser } from '@/hooks/useCurrentUser';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import {
|
||||
|
|
@ -233,7 +235,7 @@ Version: v0.1.0`;
|
|||
</p>
|
||||
</div>
|
||||
<a
|
||||
href="https://zapstore.dev/apps/org.skatespots.app"
|
||||
href={APP_INSTALL_URL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex-shrink-0"
|
||||
|
|
@ -247,6 +249,9 @@ Version: v0.1.0`;
|
|||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Pass the app on (Android only) — QR / share sheet / NFC tag. */}
|
||||
<ShareAppCard />
|
||||
|
||||
{/* Credits */}
|
||||
<Card className="bg-black/90 border-2 border-gray-700 mt-8">
|
||||
<CardContent className="p-6">
|
||||
|
|
|
|||
Loading…
Reference in a new issue