/* ======================================================================== screen-webchat.jsx, emirates.com on LAPTOP Animated flow: home -> typing-name -> typing-pnr -> submit -> loading -> manage (Manage Booking page, with chat FAB peek + 1 unread) -> opening -> open (chat widget runs) ======================================================================== */ function ScreenWebChat({ active }) { const turns = window.DEMO_SCRIPT.web_chat; const lukas = window.DEMO_SCRIPT.passenger; const hala = window.DEMO_SCRIPT.hala; const opts = window.DEMO_SCRIPT.options; const dates = window.DEMO_DATES; const BOOKING_REF = lukas.booking_ref; // "K7RJ4X" const LAST_NAME = "Weber"; const PHASES = ["home", "typing-name", "typing-pnr", "submit", "loading", "manage", "opening", "open"]; const [phase, setPhase] = useState("home"); const [typedName, setTypedName] = useState(""); const [typedPnr, setTypedPnr] = useState(""); const timersRef = useRef([]); const clearTimers = () => { timersRef.current.forEach(clearTimeout); timersRef.current = []; }; const after = (ms, fn) => { const id = setTimeout(fn, ms); timersRef.current.push(id); return id; }; // Choreograph useEffect(() => { if (!active) { clearTimers(); setPhase("home"); setTypedName(""); setTypedPnr(""); return; } clearTimers(); setPhase("home"); setTypedName(""); setTypedPnr(""); // Faster intro pacing, get to the chat quickly. after(900, () => setPhase("typing-name")); const FULLNAME = LAST_NAME; // "Weber", 5 chars FULLNAME.split("").forEach((ch, i) => { after(900 + 200 + i * 90, () => setTypedName(FULLNAME.slice(0, i + 1))); }); // 900 + 200 + 5*90 = 1550 -> switch to PNR field after(1700, () => setPhase("typing-pnr")); const FULLPNR = BOOKING_REF; // "K7RJ4X", 6 chars FULLPNR.split("").forEach((ch, i) => { after(1700 + 200 + i * 90, () => setTypedPnr(FULLPNR.slice(0, i + 1))); }); // 1700 + 200 + 7*90 = 2530 -> submit after(2700, () => setPhase("submit")); after(3000, () => setPhase("loading")); after(3700, () => setPhase("manage")); // FAB peek for ~1.8s, then open after(5500, () => setPhase("opening")); after(6100, () => setPhase("open")); const skip = () => { clearTimers(); setTypedName(LAST_NAME); setTypedPnr(BOOKING_REF); setPhase("open"); }; window.addEventListener("demo:skip", skip); return () => { clearTimers(); window.removeEventListener("demo:skip", skip); }; }, [active]); const openWidget = () => { if (phase !== "manage") return; clearTimers(); setPhase("opening"); after(700, () => setPhase("open")); }; // Readable playback pace (1.0x = script time exactly). const { shown, typing } = window.useChatPlayback(turns, { sceneActive: active && phase === "open", paused: false, speed: 1.0, }); // Smart auto-scroll: only follow the latest message if the user is already pinned // to the bottom. If they have scrolled up to re-read something, leave them alone. const feedRef = useRef(null); const pinnedRef = useRef(true); // assume pinned at start const handleFeedScroll = () => { const el = feedRef.current; if (!el) return; const dist = el.scrollHeight - el.scrollTop - el.clientHeight; pinnedRef.current = dist < 48; // within 48px of bottom counts as "pinned" }; useEffect(() => { const el = feedRef.current; if (!el) return; if (pinnedRef.current) { el.scrollTop = el.scrollHeight; } }, [shown, typing]); // Cursor target by phase const cursorPos = (() => { switch (phase) { case "home": return { x: "47%", y: "62%" }; // moving toward last-name case "typing-name": return { x: "47%", y: "70%" }; // hovering near last-name case "typing-pnr": return { x: "60%", y: "70%" }; // hovering near pnr case "submit": return { x: "75%", y: "70%" }; // on Manage booking button case "loading": return { x: "75%", y: "70%" }; case "manage": return { x: "92%", y: "92%" }; // moving to FAB case "opening": case "open": return { x: "92%", y: "92%" }; default: return { x: "50%", y: "50%" }; } })(); const showHomepage = ["home","typing-name","typing-pnr","submit","loading"].includes(phase); const showManage = ["manage","opening","open"].includes(phase); // URL bar text changes by phase const urlText = showHomepage ? "emirates.com" : `emirates.com/manage-booking · ${BOOKING_REF}`; const tabText = showHomepage ? "Emirates · Fly Better" : "Emirates · Manage Booking"; return (
{tabText}