From 94a35b1ea690051b4f4bc7e3201233b3ebcfaf9d Mon Sep 17 00:00:00 2001 From: Ahmed Al-Taiar Date: Mon, 28 Jul 2025 18:29:17 -0400 Subject: [PATCH] Prompt regular browser --- src/components/hooks/useInAppBrowser.ts | 34 +++++++++++++++++++++++++ src/components/scene/Scene.tsx | 19 +++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/components/hooks/useInAppBrowser.ts diff --git a/src/components/hooks/useInAppBrowser.ts b/src/components/hooks/useInAppBrowser.ts new file mode 100644 index 0000000..111ab0b --- /dev/null +++ b/src/components/hooks/useInAppBrowser.ts @@ -0,0 +1,34 @@ +// https://github.com/f2etw/detect-inapp/blob/master/src/inapp.js + +import { findKey } from "lodash"; + +const BROWSER = { + messenger: /\bFB[\w_]+\/(Messenger|MESSENGER)/, + facebook: /\bFB[\w_]+\//, + twitter: /\bTwitter/i, + line: /\bLine\//i, + wechat: /\bMicroMessenger\//i, + puffin: /\bPuffin/i, + miui: /\bMiuiBrowser\//i, + instagram: /\bInstagram/i, + chrome: /\bCrMo\b|CriOS|Android.*Chrome\/[.0-9]* (Mobile)?/, + safari: /Version.*Mobile.*Safari|Safari.*Mobile|MobileSafari/, + ie: /IEMobile|MSIEMobile/, + firefox: + /fennec|firefox.*maemo|(Mobile|Tablet).*Firefox|Firefox.*Mobile|FxiOS/, +}; + +export const useInAppBrowser = (): boolean => { + const userAgent = + navigator.userAgent || + navigator.vendor || + (window as unknown as { opera: string }).opera; + + const rules = ["WebView", "(iPhone|iPod|iPad)(?!.*Safari/)", "Android.*(wv)"]; + const regex = new RegExp(`(${rules.join("|")})`, "ig"); + + return ( + Boolean(userAgent.match(regex)) && + findKey(BROWSER, (regex) => regex.test(userAgent)) !== undefined + ); +}; diff --git a/src/components/scene/Scene.tsx b/src/components/scene/Scene.tsx index e69fc68..380c35d 100644 --- a/src/components/scene/Scene.tsx +++ b/src/components/scene/Scene.tsx @@ -32,7 +32,7 @@ import round from "lodash/round"; import Stack from "@/util/stack"; import isMobile from "@/util/isMobile"; import { Canvas } from "@react-three/fiber"; -import { mdiArrowLeftBold, mdiOpenInNew } from "@mdi/js"; +import { mdiArrowLeftBold, mdiClose, mdiOpenInNew } from "@mdi/js"; import { Icon } from "@mdi/react"; import { PDF } from "../util/PDF"; import { CellphoneUI } from "../ui/CellphoneUI"; @@ -48,6 +48,7 @@ import { useWindowSize } from "../hooks/useWindowSize"; import { Info } from "../util/Info"; import { Loader } from "../ui/Loader"; import { MobileMenu } from "../ui/MobileMenu"; +import { useInAppBrowser } from "../hooks/useInAppBrowser"; const AnimatedText = threeAnimated(Text); const AnimatedCam = threeAnimated(PerspectiveCamera); @@ -60,10 +61,12 @@ export function Scene(props: JSX.IntrinsicElements["group"]) { const { width, height } = useWindowSize(); const config = useConfig(); const mobile = isMobile(); + const isInAppBrowser = useInAppBrowser(); // States const [fps, setFps] = useState(0); const [dpr, setDpr] = useState(2); + const [warningDismissed, setWarningDismissed] = useState(false); const [pendingView, setPendingView] = useState(null); const [backHovered, setBackHovered] = useState(false); const [backClicked, setBackClicked] = useState(false); @@ -174,6 +177,20 @@ export function Scene(props: JSX.IntrinsicElements["group"]) { /> )} + {!warningDismissed && isInAppBrowser ? ( +
+

+ For the best experience, please open this page in a regular + browser +

+ +
+ ) : null}
{fps > 0 && (