Files
cozsweet-frontend-nextjs/src/hooks/use-pwa-install.ts
T
admin 8cab34864e feat(ui): migrate Flutter UI library to Next.js 16 (App Router)
Migrate all 87 Dart files from /Users/chase/Documents/cozsweet/lib/ui/
to TypeScript React components, replacing Flutter-native features with
Web equivalents and reimplementing the BLoC pattern with React
Context + useReducer.

Highlights
- 1:1 BLoC -> Context+useReducer+side-effects mapping for auth, chat,
  user, and sidebar features (4 providers wired in root-providers.tsx)
- 500px MobileShell primitive eliminates per-screen layout boilerplate
- Headless Dialog primitive (createPortal + ESC + scroll lock) powers
  quota / pwa-install / username / pronouns / subscription / other-options
  dialogs
- New integrations/: facebook-sdk, google-identity, pwa-install,
  media-recorder (Web Speech API + MediaRecorder), browser-detect,
  chat-websocket (singleton), message-queue (throttled send)
- CSS Modules + existing design tokens (no new styling system);
  extended with breakpoints.css and dimensions.css
- Custom hooks: useBreakpoint (useSyncExternalStore),
  useFullVisibility (IntersectionObserver), usePwaInstall,
  usePullToRefresh
- Vitest + jsdom; 15 unit tests (chat-reducer, auth-validators)
- React 19.2.4, Next.js 16.2.7, Zod 4, lottie-react, classnames

Workarounds
- next.config.ts: experimental.staticGenerationRetryCount = 0 to
  bypass Next.js 16.2.7 /_global-error workStore prerender bug
- src/app/global-error.tsx: required Client Component
- src/types/globals.d.ts: centralized window.google / FB /
  SpeechRecognition declarations

Routes (/, /splash, /auth, /chat, /chat/deviceid/[deviceId],
/sidebar) all build and lint clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-09 13:39:49 +08:00

48 lines
1.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
/**
* PWA Install 提示 hook
*
* 原始 Dart: `pwa_install` 包的 BeforeInstallPrompt 事件封装
* `lib/ui/chat/widgets/pwa_install_overlay.dart` + `pwa_install_dialog.dart`)。
*
* 浏览器 API`beforeinstallprompt` 事件 → 用户触发 → 浏览器展示原生安装提示。
*
* 用法:
* const { canInstall, promptInstall } = usePwaInstall();
* if (canInstall) <button onClick={promptInstall}>Install</button>;
*/
import { useEffect, useState } from "react";
interface BeforeInstallPromptEvent extends Event {
prompt(): Promise<void>;
userChoice: Promise<{ outcome: "accepted" | "dismissed" }>;
}
export function usePwaInstall(): {
canInstall: boolean;
promptInstall: () => Promise<"accepted" | "dismissed" | "unavailable">;
} {
const [deferred, setDeferred] = useState<BeforeInstallPromptEvent | null>(null);
useEffect(() => {
if (typeof window === "undefined") return;
const handler = (e: Event) => {
e.preventDefault();
setDeferred(e as BeforeInstallPromptEvent);
};
window.addEventListener("beforeinstallprompt", handler);
return () => window.removeEventListener("beforeinstallprompt", handler);
}, []);
return {
canInstall: deferred !== null,
promptInstall: async () => {
if (!deferred) return "unavailable";
await deferred.prompt();
const choice = await deferred.userChoice;
setDeferred(null);
return choice.outcome;
},
};
}