From 8b694e8a3a6ea6851524cb7c3870063a66143abb Mon Sep 17 00:00:00 2001 From: sebastjanartic <45803536-sebastjanartic@users.noreply.replit.com> Date: Thu, 28 Aug 2025 10:03:01 +0000 Subject: [PATCH] Add explanation for ad indicators and improve video loading Introduce a new modal to explain the meaning of ad indicators and enhance video loading performance by implementing lazy loading and async decoding. Replit-Commit-Author: Agent Replit-Commit-Session-Id: d7424866-83d1-4486-a212-ac12b4c7becf Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8cc42625-c1f5-4e43-99bd-77f2c4dedee2/d7424866-83d1-4486-a212-ac12b4c7becf/BWSPHB9 --- client/src/components/ad-explanation.tsx | 80 ++++++++++++++++++++++++ client/src/components/search-header.tsx | 6 +- client/src/components/video-card.tsx | 4 +- client/src/lib/queryClient.ts | 5 +- client/src/pages/home.tsx | 9 +++ server/routes.ts | 27 +++++--- 6 files changed, 116 insertions(+), 15 deletions(-) create mode 100644 client/src/components/ad-explanation.tsx diff --git a/client/src/components/ad-explanation.tsx b/client/src/components/ad-explanation.tsx new file mode 100644 index 0000000..6d46dd1 --- /dev/null +++ b/client/src/components/ad-explanation.tsx @@ -0,0 +1,80 @@ +import { useState } from "react"; +import { Button } from "@/components/ui/button"; +import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; +import { Info, DollarSign, PlayCircle, Eye } from "lucide-react"; + +interface AdExplanationProps { + isOpen: boolean; + onClose: () => void; +} + +export default function AdExplanation({ isOpen, onClose }: AdExplanationProps) { + return ( + + + + + + Kaj pomenijo oznake "💰 OGLAS"? + + + +
+
+
+
+ 💰 OGLAS +
+ Monetizirani videji +
+

+ Te oznake prikazujejo, da so videji opremljeni z naprednim VAST oglasnim sistemom +

+
+ +
+
+
+ +

Kako deluje?

+
+

+ Pred ali med predvajanjem videa se prikazujejo oglasi, ki omogočajo brezplačno gledanje vsebine +

+
+ +
+
+ +

Brezplačno gledanje

+
+

+ Oglasi omogočajo, da lahko vse videji gledate povsem brezplačno brez naročnine +

+
+
+ +
+

VAST oglasni sistem

+

+ Platforma uporablja profesionalni VAST (Video Ad Serving Template) sistem z 5 oglasnimi mrežami: +

+
    +
  • • Publift - Agregator več oglaševalskih omrežij
  • +
  • • Vdo.ai - AI ciljanje z visokim eCPM
  • +
  • • Primis - Video discovery platforma
  • +
  • • AdPlayer.Pro - Outstream rešitve
  • +
  • • Aniview - Programmatic oglasne tehnologije
  • +
+
+ +
+ +
+
+
+
+ ); +} \ No newline at end of file diff --git a/client/src/components/search-header.tsx b/client/src/components/search-header.tsx index 53c4978..96a661c 100644 --- a/client/src/components/search-header.tsx +++ b/client/src/components/search-header.tsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import { Search, Play, Menu, Grid3X3, List, DollarSign, Settings } from "lucide-react"; +import { Search, Play, Menu, Grid3X3, List, DollarSign, Settings, Info } from "lucide-react"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; @@ -9,13 +9,15 @@ interface SearchHeaderProps { onViewChange: (view: "grid" | "list") => void; currentView: "grid" | "list"; onAdSettingsOpen?: () => void; + onAdExplanationOpen?: () => void; } export default function SearchHeader({ onSearch, onViewChange, currentView, - onAdSettingsOpen + onAdSettingsOpen, + onAdExplanationOpen }: SearchHeaderProps) { const [searchQuery, setSearchQuery] = useState(""); diff --git a/client/src/components/video-card.tsx b/client/src/components/video-card.tsx index 058e7b2..2c7d318 100644 --- a/client/src/components/video-card.tsx +++ b/client/src/components/video-card.tsx @@ -56,6 +56,8 @@ export default function VideoCard({ video, onClick }: VideoCardProps) { alt={video.title} className="w-full h-full object-cover transition-all duration-300 group-hover:scale-105" data-testid={`img-thumbnail-${video.id}`} + loading="lazy" + decoding="async" onError={(e) => { const target = e.target as HTMLImageElement; target.style.display = 'none'; @@ -72,7 +74,7 @@ export default function VideoCard({ video, onClick }: VideoCardProps) { {/* VAST Ad monetization indicator */}
- 💰 SPOT + 💰 OGLAS
{/* Play button overlay */} diff --git a/client/src/lib/queryClient.ts b/client/src/lib/queryClient.ts index f315a14..52dd842 100644 --- a/client/src/lib/queryClient.ts +++ b/client/src/lib/queryClient.ts @@ -47,8 +47,9 @@ export const queryClient = new QueryClient({ queryFn: getQueryFn({ on401: "throw" }), refetchInterval: false, refetchOnWindowFocus: false, - staleTime: Infinity, - retry: false, + staleTime: 5 * 60 * 1000, // 5 minutes cache + gcTime: 10 * 60 * 1000, // 10 minutes retention + retry: 1, }, mutations: { retry: false, diff --git a/client/src/pages/home.tsx b/client/src/pages/home.tsx index 29f6d58..761a25d 100644 --- a/client/src/pages/home.tsx +++ b/client/src/pages/home.tsx @@ -4,6 +4,7 @@ import { type Video } from "@shared/schema"; import SearchHeader from "@/components/search-header"; import VideoGrid from "@/components/video-grid"; import AdSettings from "@/components/ad-settings"; +import AdExplanation from "@/components/ad-explanation"; interface VideosResponse { videos: Video[]; @@ -17,6 +18,7 @@ export default function Home() { const [offset, setOffset] = useState(0); const [allVideos, setAllVideos] = useState([]); const [showAdSettings, setShowAdSettings] = useState(false); + const [showAdExplanation, setShowAdExplanation] = useState(false); // Fetch videos const { data: videosResponse, isLoading, refetch } = useQuery({ @@ -77,6 +79,7 @@ export default function Home() { onViewChange={setViewMode} currentView={viewMode} onAdSettingsOpen={() => setShowAdSettings(true)} + onAdExplanationOpen={() => setShowAdExplanation(true)} />
@@ -94,6 +97,12 @@ export default function Home() { isOpen={showAdSettings} onClose={() => setShowAdSettings(false)} /> + + {/* Ad Explanation Modal */} + setShowAdExplanation(false)} + /> {/* Footer */}