Merge branch 'dev' into test
This commit is contained in:
@@ -4,7 +4,6 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"dev:proxy": "HTTPS_PROXY=http://127.0.0.1:7891 next dev",
|
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start -p 9135",
|
"start": "next start -p 9135",
|
||||||
"build:start": "npm run build && npm run start",
|
"build:start": "npm run build && npm run start",
|
||||||
|
|||||||
@@ -4,5 +4,3 @@
|
|||||||
|
|
||||||
export * from "./use-breakpoint";
|
export * from "./use-breakpoint";
|
||||||
export * from "./use-pull-to-refresh";
|
export * from "./use-pull-to-refresh";
|
||||||
export * from "./use-pwa-install";
|
|
||||||
export * from "./use-visibility";
|
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
"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;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
"use client";
|
|
||||||
/**
|
|
||||||
* 元素全可见性 hook
|
|
||||||
*
|
|
||||||
* 原始 Dart: `visibility_detector` 包的 `VisibilityDetector`(chat_screen 用法:
|
|
||||||
* onVisibilityChanged: (info) { if (info.visibleFraction == 1.0) ... })。
|
|
||||||
*
|
|
||||||
* 用 IntersectionObserver,threshold=1.0 触发 `isIntersecting` 即视为「完全可见」。
|
|
||||||
* 返回 `[isVisible, ref]`,与 Flutter `VisibilityDetector` 的语义对齐。
|
|
||||||
*
|
|
||||||
* 用法:
|
|
||||||
* const [isVisible, ref] = useFullVisibility();
|
|
||||||
* useEffect(() => { dispatch(isVisible ? Visible() : Invisible()); }, [isVisible]);
|
|
||||||
*/
|
|
||||||
import { useEffect, useRef, useState } from "react";
|
|
||||||
|
|
||||||
export function useFullVisibility(
|
|
||||||
threshold = 1.0,
|
|
||||||
): readonly [boolean, React.RefObject<HTMLDivElement | null>] {
|
|
||||||
const ref = useRef<HTMLDivElement | null>(null);
|
|
||||||
const [visible, setVisible] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const el = ref.current;
|
|
||||||
if (!el || typeof IntersectionObserver === "undefined") return;
|
|
||||||
|
|
||||||
const observer = new IntersectionObserver(
|
|
||||||
(entries) => {
|
|
||||||
const entry = entries[0];
|
|
||||||
if (!entry) return;
|
|
||||||
setVisible(
|
|
||||||
entry.isIntersecting && entry.intersectionRatio >= threshold,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
{ threshold: [threshold] },
|
|
||||||
);
|
|
||||||
observer.observe(el);
|
|
||||||
return () => observer.disconnect();
|
|
||||||
}, [threshold]);
|
|
||||||
|
|
||||||
return [visible, ref] as const;
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
* @file Automatically generated by barrelsby.
|
* @file Automatically generated by barrelsby.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export * from "./user-auth-sync";
|
||||||
export * from "./user-context";
|
export * from "./user-context";
|
||||||
export * from "./user-events";
|
export * from "./user-events";
|
||||||
export * from "./user-machine.actors";
|
export * from "./user-machine.actors";
|
||||||
|
|||||||
Reference in New Issue
Block a user