Restored to '544b55132d2633b08a78bd0d486ec3d654d1cab8'
Replit-Restored-To: 544b55132d
This commit is contained in:
@@ -22,12 +22,9 @@ 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";
|
||||
@@ -39,6 +36,7 @@ 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<Newsletter[]>([]);
|
||||
@@ -65,9 +63,8 @@ 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(() => {
|
||||
@@ -78,16 +75,12 @@ 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];
|
||||
@@ -104,15 +97,16 @@ export default function Home() {
|
||||
const handleImport = async () => {
|
||||
try {
|
||||
setIsImporting(true);
|
||||
const response = await apiRequest("POST", "/api/newsletters/import");
|
||||
await apiRequest("POST", "/api/newsletters/import");
|
||||
await queryClient.invalidateQueries({ queryKey: ["/api/newsletters"] });
|
||||
toast({
|
||||
title: "Success",
|
||||
description: response.message,
|
||||
description: "Newsletters imported successfully",
|
||||
});
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: "Error",
|
||||
description: error.message || "Failed to import newsletters",
|
||||
description: "Failed to import newsletters",
|
||||
variant: "destructive",
|
||||
});
|
||||
} finally {
|
||||
@@ -120,22 +114,6 @@ 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 {
|
||||
@@ -158,6 +136,25 @@ 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)) {
|
||||
@@ -213,21 +210,13 @@ 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;
|
||||
@@ -275,25 +264,29 @@ export default function Home() {
|
||||
/>
|
||||
</div>
|
||||
{isDevelopment && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={handleImport}
|
||||
disabled={isImporting}
|
||||
>
|
||||
<RefreshCw
|
||||
className={`h-4 w-4 ${isImporting ? "animate-spin" : ""}`}
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
{isDevelopment && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={handleTestNotification}
|
||||
>
|
||||
Test Notification
|
||||
</Button>
|
||||
<>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={handleImport}
|
||||
disabled={isImporting}
|
||||
>
|
||||
<RefreshCw
|
||||
className={`h-4 w-4 ${isImporting ? "animate-spin" : ""}`}
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={handleSendTestNotification}
|
||||
disabled={isSendingTestNotification}
|
||||
title="Send test notification to all subscribers"
|
||||
>
|
||||
<BellRing
|
||||
className={`h-4 w-4 ${isSendingTestNotification ? "animate-pulse" : ""}`}
|
||||
/>
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -421,10 +414,7 @@ export default function Home() {
|
||||
|
||||
{/* Loading indicator at bottom */}
|
||||
{hasMorePages && (
|
||||
<div
|
||||
ref={loader}
|
||||
className="h-20 my-4 flex justify-center items-center"
|
||||
>
|
||||
<div ref={loader} className="h-20 my-4 flex justify-center items-center">
|
||||
{isCurrentFetching && page > 1 && (
|
||||
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-primary"></div>
|
||||
)}
|
||||
@@ -433,4 +423,4 @@ export default function Home() {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user