# To AI Agent: Restore Smart Android App Download Banner Below Top-Bar Header ## 1. Context & Feature Objective Previously, the codebase had a promotional banner encouraging mobile users to download the native Android `.apk` file. However, during the recent overhaul of the Top-Bar header and Member Dropdown Menu configurations, this banner component was accidentally unmounted or hidden. **Objective:** Restore and refactor this promotional frame (`AppDownloadBanner.tsx`). It must **ONLY** appear when a user accesses the web application via a **Mobile Android Browser** (Chrome, Samsung Internet, Opera Mobile, etc.). It must be positioned dynamically as a small, clean horizontal frame pinned directly underneath the main Top-Bar header layout, without conflicting with the new absolute Profile Dropdown Menu. --- ## 2. Visual & Behavioral Layout Specifications - **Placement Context:** Directly underneath the Top-Bar layer, shifting the core page content (Map Canvas or Landing Hero) down proportionally (`relative` or `sticky` stack). It must not overlay or block map navigation tools. - **Conditional Trigger Logic:** The banner must evaluate `navigator.userAgent`. If the user agent includes `'android'` **AND** the user is running inside a standard web browser (not inside the compiled wrapper app itself), display the banner. - **Dismissible Interaction:** Include a small close button (`X`). Clicking it should temporarily store a flag in `sessionStorage` or `localStorage` to prevent the banner from bugging the user repeatedly during their session. --- ## 3. Technical Implementation Blueprint ### Step 1: Create the Responsive Banner Component (`AppDownloadBanner.tsx`) Create or re-engineer the banner layout within `frontend/src/components/layout/AppDownloadBanner.tsx`: ```typescript import React, { useState, useEffect } from 'react'; import { X, Download } from 'lucide-react'; export const AppDownloadBanner: React.FC = () => { const [isVisible, setIsVisible] = useState(false); const APK_DOWNLOAD_URL = `${import.meta.env.VITE_BACKEND_URL || window.location.origin}/downloads/yotrip-latest.apk`; useEffect(() => { const userAgent = navigator.userAgent.toLowerCase(); const isAndroidBrowser = userAgent.includes('android') && !window.location.origin.includes('capacitor://') && !window.location.origin.includes('localhost:80'); const isBannerDismissed = localStorage.getItem('yotrip_apk_banner_dismissed') === 'true'; // ✅ Target condition match: User is on Android mobile browser and hasn't closed it yet if (isAndroidBrowser && !isBannerDismissed) { setIsVisible(true); } }, []); const handleDismiss = () => { localStorage.setItem('yotrip_apk_banner_dismissed', 'true'); setIsVisible(false); }; if (!isVisible) return null; return (

Trải nghiệm mượt mà hơn với ứng dụng YoTrip cho Android!

{/* Direct Download Trigger Target Link */} Tải APK {/* Close Button Trigger */}
); }; ### Step 2: Integrate into Layout Hierarchies (LandingPage.tsx & ExplorerMap.tsx) Mount the verified download banner right below your primary header or top-bar row item array context: {/* ❌ BEFORE STRUCTURAL INTEGRATION */}
{/* ✅ AFTER STRUCTURAL INTEGRATION: Stacked relative context */}
{/* 1. Main Application Header */} {/* 2. THE RESTORED ANDROID APP PROMOTIONAL BANNER */} {/* 3. Primary Workspace Area - Shifts down cleanly when banner initializes */}
## 4. Verification & Quality Acceptance Criteria for AI Agent [ ] Targeted UserAgent Isolation: Emulate a desktop display width (iPhone or Desktop layouts). Verify the banner remains completely hidden. Toggle the network responsive preview device model to Android (e.g., Pixel 7) and refresh. Confirm the download bar pops up seamlessly. [ ] Absolute Dropdown Overlay Preservation: Expand the Member Avatar menu row dropdown list while the banner is visible. Confirm that the dropdown box displays layered ON TOP of the banner context, without layout shifting or text clipping. [ ] State Dismissal Memory: Click the X button on the banner, then refresh the browser session. Confirm the banner remains hidden and respects the localStorage condition toggle.