feat: restore and integrate AppDownloadBanner under Top-Bar

This commit is contained in:
2026-06-27 20:50:00 +07:00
parent 7ab1342690
commit 175668da35
6 changed files with 185 additions and 74 deletions
+2 -2
View File
@@ -21,7 +21,7 @@
<meta name="twitter:title" content="Travel Planner - Lên kế hoạch chuyến đi của bạn" />
<meta name="twitter:description" content="Khám phá những hành trình tuyệt vời và chia sẻ khoảnh khắc đẹp với cộng đồng du lịch." />
<meta name="twitter:image" content="https://yotrip.labz.io.vn/background.avif" />
<script type="module" crossorigin src="/assets/index-BYlJbgKh.js"></script>
<script type="module" crossorigin src="/assets/index-CnmnZivz.js"></script>
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-Cyuzqnbw.js">
<link rel="modulepreload" crossorigin href="/assets/vendor-others-VIU5qAPG.js">
<link rel="modulepreload" crossorigin href="/assets/vendor-editor-DOXkNaRS.js">
@@ -30,7 +30,7 @@
<link rel="modulepreload" crossorigin href="/assets/vendor-pdf-IjT8x9OX.js">
<link rel="stylesheet" crossorigin href="/assets/vendor-maps-CcXJxNtP.css">
<link rel="stylesheet" crossorigin href="/assets/vendor-react-CuSj0z1j.css">
<link rel="stylesheet" crossorigin href="/assets/index-BO3HsKs5.css">
<link rel="stylesheet" crossorigin href="/assets/index-w726aohe.css">
</head>
<body>
<div id="root"></div>
@@ -0,0 +1,60 @@
import React, { useState, useEffect } from 'react';
import { X, Download } from 'lucide-react';
import { BACKEND_URL } from '@/utils/backendEndpoint';
export const AppDownloadBanner: React.FC = () => {
const [isVisible, setIsVisible] = useState(false);
// Force APK download relative to backend endpoint
const APK_DOWNLOAD_URL = `${BACKEND_URL}/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 (
<div className="w-full bg-gradient-to-r from-blue-900 to-indigo-950 border-b border-blue-800 px-4 py-2.5 flex items-center justify-between text-white text-[11px] font-medium z-40 relative shrink-0">
<div className="flex items-center gap-2.5 flex-1 min-w-0">
<div className="p-1 bg-blue-500/20 rounded-lg text-blue-400 shrink-0">
<Download className="w-3.5 h-3.5" />
</div>
<p className="truncate text-slate-200">
Trải nghiệm mượt hơn với ng dụng <span className="text-white font-bold">YoTrip cho Android</span>!
</p>
</div>
<div className="flex items-center gap-3 ml-2 shrink-0">
<a
href={APK_DOWNLOAD_URL}
download="yotrip.apk"
className="bg-blue-600 hover:bg-blue-500 px-3 py-1 rounded-xl font-bold text-white transition-all shadow-sm active:scale-95 text-[10px]"
>
Tải APK
</a>
<button
onClick={handleDismiss}
className="p-1 hover:bg-slate-800 rounded text-slate-400 hover:text-slate-200 transition-colors"
title="Đóng thông báo"
>
<X className="w-3.5 h-3.5" />
</button>
</div>
</div>
);
};
+7 -2
View File
@@ -20,6 +20,7 @@ import { MyToursModal } from '../components/modals/MyToursModal';
import { PhotoGalleryModal as MyPhotosModal } from '../components/modals/PhotoGalleryModal';
import { LiveChatModal } from '../components/modals/LiveChatModal';
import { FriendsManagerModal } from '../components/modals/FriendsManagerModal';
import { AppDownloadBanner } from '../components/layout/AppDownloadBanner';
// Fix lỗi icon mặc định của Leaflet
const DefaultIcon = L.icon({
@@ -654,8 +655,11 @@ export const ExploreMap = ({ onBack, onLogout, user, onViewTour, onLoginSuccess,
}, [publicPhotos]);
return (
<div className="h-dvh w-full relative overflow-hidden">
{/* Top Bar Container - Chứa tất cả các nút điều hướng phía trên */}
<div className="h-dvh w-full flex flex-col overflow-hidden bg-slate-950">
<AppDownloadBanner />
<div className="flex-1 w-full relative min-h-0">
{/* Top Bar Container - Chứa tất cả các nút điều hướng phía trên */}
<div className="absolute top-[calc(1rem+env(safe-area-inset-top,0px))] left-4 right-4 z-[1002] flex items-center justify-between pointer-events-none">
{/* Nhóm bên trái: Quay lại và Thông tin vị trí */}
<div className="flex items-center gap-3 pointer-events-auto">
@@ -1125,6 +1129,7 @@ export const ExploreMap = ({ onBack, onLogout, user, onViewTour, onLoginSuccess,
)}
</div>
)}
</div>
{/* Admin Modal */}
<UserManagementModal isOpen={isAdminModalOpen} onClose={() => setIsAdminModalOpen(false)} />
+6 -1
View File
@@ -9,6 +9,7 @@ import { MyToursModal } from '../components/modals/MyToursModal';
import { PhotoGalleryModal as MyPhotosModal } from '../components/modals/PhotoGalleryModal';
import { LiveChatModal } from '../components/modals/LiveChatModal';
import { FriendsManagerModal } from '../components/modals/FriendsManagerModal';
import { AppDownloadBanner } from '../components/layout/AppDownloadBanner';
import { useNotification } from '@/hooks/useNotification';
import { processImageModeration } from '../hooks/useImageModeration';
import { useTranslation } from '../hooks/useTranslation';
@@ -343,7 +344,10 @@ export const LandingPage: React.FC<LandingPageProps> = ({
return (
<div className="h-dvh w-full overflow-hidden font-sans bg-[var(--background)] relative">
<div className="h-dvh w-full flex flex-col overflow-hidden font-sans bg-[var(--background)]">
<AppDownloadBanner />
<div className="flex-1 w-full relative min-h-0">
{/* Background Image with Horizontal Panning */}
<div className="absolute inset-0 z-0">
{/* Active image for panning */}
@@ -655,6 +659,7 @@ export const LandingPage: React.FC<LandingPageProps> = ({
}
`}</style>
</div>
</div>
{/* Login Modal Component */}
<LoginModal