Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions apps/web/src/app/c/[orderId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { fetchQuery } from "convex/nextjs";
import { api } from "@packages/backend/convex/_generated/api";
import type { Id } from "@packages/backend/convex/_generated/dataModel";
import { TrackingView } from "@/components/tracking/tracking-view";
import { IOSInstallBottomSheet } from "@/components/a2hs-ios";

const TENANT_COOKIE = "__Host-kb_tenant";

Expand Down Expand Up @@ -83,6 +84,13 @@ export default async function TrackingPage(props: {
restoName={restoName}
/>

{/* PWA-S11 (#463) — iOS A2HS instructional bottom-sheet, rendered
on the tracking page T+0 (« Cmd reçue ») state per decisions-log
Q4 + US 58. Self-gates on iOS Safari + not standalone + not
enrolled + not dismissed — renders nothing on Android (#462
handles that platform) / desktop / non-Safari iOS browsers. */}
<IOSInstallBottomSheet tenantId={tenantId} />

<footer>
<Link
href="/"
Expand Down
20 changes: 20 additions & 0 deletions apps/web/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { cookies } from "next/headers";
import { ConvexAuthNextjsServerProvider } from "@convex-dev/auth/nextjs/server";
import type { Id } from "@packages/backend/convex/_generated/dataModel";
import { ConvexClientProvider } from "@/providers/convex-client-provider";
import { ServiceWorkerRegistration } from "@/components/service-worker-registration";
import { WalletBridgeRunner } from "@/components/wallet-bridge";
import { PWAInstallProvider } from "@/components/a2hs-install";
import { IOSStandaloneHeuristicRunner } from "@/components/a2hs-ios";
import { WALLET_BRIDGE_PENDING_COOKIE } from "@/lib/wallet-bridge";

const TENANT_COOKIE = "__Host-kb_tenant";

const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
Expand Down Expand Up @@ -77,6 +81,13 @@ export default async function RootLayout({
const cookieStore = await cookies();
const pendingBridgeSerial =
cookieStore.get(WALLET_BRIDGE_PENDING_COOKIE)?.value ?? null;
// PWA-S11 (#463) — the iOS A2HS standalone heuristic runner needs the
// tenantId for the `customer.pushEnrollment.recordA2hsAccepted` mutation
// (self-scoped, wrapper-arg). Skipped in the degraded shell (cookie
// missing) — the runner mounts but the Convex query is `"skip"`d.
const tenantId = cookieStore.get(TENANT_COOKIE)?.value as
| Id<"tenants">
| undefined;

return (
<ConvexAuthNextjsServerProvider>
Expand All @@ -101,6 +112,15 @@ export default async function RootLayout({
Wrapped INSIDE the Convex provider so the button (which
consumes the captured prompt) has access to Convex hooks
in its subtree (mutation + getCurrentCustomer query). */}
{/* PWA-S11 (#463) — root-mounted runner that flips
`customer.pushEnrollment.a2hsStatus = "enrolled"` the first
time it detects `display-mode: standalone` on a fiche that
hasn't been flipped yet. Mounted at root so EVERY standalone
visit gets a chance to attribute the install — also covers
the rare Android cross-session race where the #462
`appinstalled` event fired after tab close. Renders nothing
(pure side-effect). */}
<IOSStandaloneHeuristicRunner tenantId={tenantId} />
<PWAInstallProvider>{children}</PWAInstallProvider>
</ConvexClientProvider>
</body>
Expand Down
26 changes: 26 additions & 0 deletions apps/web/src/components/a2hs-ios/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* PWA-S11 (#463) — `a2hs-ios` component re-exports (iOS A2HS bottom-sheet
* + standalone heuristique runner — decisions-log Q4, US 58 / 59).
*
* - `<IOSInstallBottomSheet>` — Vaul Drawer rendered on `/c/[orderId]`
* state T+0 with the 3-step Safari Share → « Sur l'écran d'accueil » →
* « Ajouter » instructions. Visible only on iOS Safari, not standalone,
* not enrolled, not dismissed-this-session.
* - `<IOSStandaloneHeuristicRunner>` — mounted at the root layout, fires
* `customer.pushEnrollment.recordA2hsAccepted` the first time it detects
* standalone display-mode on a fiche whose `a2hsStatus` is not yet
* enrolled. Cross-platform safety net (iOS install signal proxy +
* Android `appinstalled`-event-missed race).
*
* Android Chrome install path (`beforeinstallprompt` event capture +
* post-cart floating button) is the SIBLING surface PWA-S10 #462 — see
* `apps/web/src/components/a2hs-install`.
*/
export {
type IOSInstallBottomSheetProps,
IOSInstallBottomSheet,
} from "./ios-install-bottom-sheet";
export {
type IOSStandaloneHeuristicRunnerProps,
IOSStandaloneHeuristicRunner,
} from "./ios-standalone-heuristic-runner";
Loading
Loading