From cf567d0eb4fe7c46f2bbf093e24e8550b9341cf5 Mon Sep 17 00:00:00 2001 From: TerribleDev <1020010-TerribleDev@users.noreply.replit.com> Date: Sun, 2 Mar 2025 07:43:07 +0000 Subject: [PATCH] Checkpoint before revert - Enhance the newsletter archive into a fully installable PWA with offline capabilities Replit-Commit-Author: Agent Replit-Commit-Session-Id: 0aa507c2-4fa6-42bc-9ce7-ee6f559c10a5 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/9dda30b6-4149-4bce-89dc-76333005952c/c6cce535-1f81-43c1-8b69-181a5f1f9aa1.jpg --- client/src/pages/home.tsx | 138 ++++++++++++++++++++------------------ 1 file changed, 74 insertions(+), 64 deletions(-) diff --git a/client/src/pages/home.tsx b/client/src/pages/home.tsx index ad3e53c..c0845f9 100644 --- a/client/src/pages/home.tsx +++ b/client/src/pages/home.tsx @@ -22,9 +22,12 @@ import { Rss, Bell, BellOff, - BellRing, } from "lucide-react"; -import { useNewsletters, useNewsletterSearch, type NewslettersResponse } from "@/lib/newsletter-data"; +import { + useNewsletters, + useNewsletterSearch, + type NewslettersResponse, +} from "@/lib/newsletter-data"; import { useToast } from "@/hooks/use-toast"; import { apiRequest } from "@/lib/queryClient"; import { queryClient } from "@/lib/queryClient"; @@ -36,7 +39,6 @@ const ITEMS_PER_PAGE = 20; export default function Home() { const [searchQuery, setSearchQuery] = useState(""); const [isImporting, setIsImporting] = useState(false); - const [isSendingTestNotification, setIsSendingTestNotification] = useState(false); const [page, setPage] = useState(1); const [isSubscribed, setIsSubscribed] = useState(false); const [allItems, setAllItems] = useState([]); @@ -63,8 +65,9 @@ export default function Home() { const isCurrentFetching = searchQuery ? isSearchFetching : isFetching; // Check if there are more pages to load - const hasMorePages = currentData ? - (currentData.page * currentData.limit < currentData.total) : false; + const hasMorePages = currentData + ? currentData.page * currentData.limit < currentData.total + : false; // Merge newsletter items when data changes useEffect(() => { @@ -75,12 +78,16 @@ export default function Home() { setAllItems(currentData.newsletters); } else { // Merge items, ensuring we don't have duplicates - setAllItems(prevItems => { + setAllItems((prevItems) => { // Create a set of IDs from new items for faster lookups - const newItemIds = new Set(currentData.newsletters.map(item => item.id)); + const newItemIds = new Set( + currentData.newsletters.map((item) => item.id), + ); // Filter out any previous items that would be duplicated - const filteredPrevItems = prevItems.filter(item => !newItemIds.has(item.id)); + const filteredPrevItems = prevItems.filter( + (item) => !newItemIds.has(item.id), + ); // Combine previous (non-duplicate) items with new items return [...filteredPrevItems, ...currentData.newsletters]; @@ -97,16 +104,15 @@ export default function Home() { const handleImport = async () => { try { setIsImporting(true); - await apiRequest("POST", "/api/newsletters/import"); - await queryClient.invalidateQueries({ queryKey: ["/api/newsletters"] }); + const response = await apiRequest("POST", "/api/newsletters/import"); toast({ title: "Success", - description: "Newsletters imported successfully", + description: response.message, }); - } catch (error) { + } catch (error: any) { toast({ title: "Error", - description: "Failed to import newsletters", + description: error.message || "Failed to import newsletters", variant: "destructive", }); } finally { @@ -114,6 +120,22 @@ export default function Home() { } }; + const handleTestNotification = async () => { + try { + const response = await apiRequest("POST", "/api/notifications/test"); + toast({ + title: "Notifications Sent", + description: `${response.message} (${response.totalSubscribers} subscribers)`, + }); + } catch (error: any) { + toast({ + title: "Error", + description: error.message || "Failed to send test notifications", + variant: "destructive", + }); + } + }; + const handleShare = async (newsletter: Newsletter) => { if (navigator.share) { try { @@ -136,25 +158,6 @@ export default function Home() { } }; - const handleSendTestNotification = async () => { - try { - setIsSendingTestNotification(true); - const response = await apiRequest("POST", "/api/notifications/test"); - toast({ - title: "Test Notifications Sent", - description: `Results: ${response.succeeded} succeeded, ${response.failed} failed out of ${response.total} subscriptions`, - }); - } catch (error: any) { - toast({ - title: "Error", - description: error.message || "Failed to send test notifications", - variant: "destructive", - }); - } finally { - setIsSendingTestNotification(false); - } - }; - const handleSubscribe = async () => { try { if (!("serviceWorker" in navigator) || !("Notification" in window)) { @@ -210,13 +213,21 @@ export default function Home() { } }; - const handleObserver = useCallback((entries: IntersectionObserverEntry[]) => { - const target = entries[0]; - if (target.isIntersecting && !isCurrentLoading && !isCurrentFetching && hasMorePages) { - console.log("Loading more newsletters. Current page:", page); - setPage((prev) => prev + 1); - } - }, [isCurrentLoading, isCurrentFetching, hasMorePages, page]); + const handleObserver = useCallback( + (entries: IntersectionObserverEntry[]) => { + const target = entries[0]; + if ( + target.isIntersecting && + !isCurrentLoading && + !isCurrentFetching && + hasMorePages + ) { + console.log("Loading more newsletters. Current page:", page); + setPage((prev) => prev + 1); + } + }, + [isCurrentLoading, isCurrentFetching, hasMorePages, page], + ); useEffect(() => { const currentLoader = loader.current; @@ -264,29 +275,25 @@ export default function Home() { /> {isDevelopment && ( - <> - - - + + )} + {isDevelopment && ( + )}