mirror of
https://github.com/neosubhamoy/neodlp.git
synced 2026-03-22 16:05:50 +05:30
feat: added clear, copy log buttons and pagination in completed downloads
This commit is contained in:
81
src/components/custom/paginationBar.tsx
Normal file
81
src/components/custom/paginationBar.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import { Paginated } from "@/types/download";
|
||||
import { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious } from "@/components/ui/pagination";
|
||||
|
||||
export default function PaginationBar({
|
||||
paginatedData,
|
||||
setPage,
|
||||
}: {
|
||||
paginatedData: Paginated;
|
||||
setPage: (page: number) => void;
|
||||
}) {
|
||||
return (
|
||||
<Pagination className="mt-4">
|
||||
<PaginationContent>
|
||||
<PaginationItem>
|
||||
<PaginationPrevious
|
||||
onClick={() => setPage(paginatedData.prev_page ?? paginatedData.first_page)}
|
||||
aria-disabled={!paginatedData.prev_page}
|
||||
className={!paginatedData.prev_page ? 'pointer-events-none opacity-50' : 'cursor-pointer'}
|
||||
/>
|
||||
</PaginationItem>
|
||||
|
||||
{paginatedData.pages.map((link, index, array) => {
|
||||
const currentPage = paginatedData.current_page;
|
||||
const pageNumber = link.page!;
|
||||
|
||||
// Show first page, last page, current page, and 2 pages around current
|
||||
const showPage =
|
||||
pageNumber === 1 ||
|
||||
pageNumber === paginatedData.last_page ||
|
||||
Math.abs(pageNumber - currentPage) <= 1;
|
||||
|
||||
// Show ellipsis if there's a gap
|
||||
const prevVisiblePage = array
|
||||
.slice(0, index)
|
||||
.reverse()
|
||||
.find((prevLink) => {
|
||||
const prevPageNum = prevLink.page!;
|
||||
return (
|
||||
prevPageNum === 1 ||
|
||||
prevPageNum === paginatedData.last_page ||
|
||||
Math.abs(prevPageNum - currentPage) <= 1
|
||||
);
|
||||
})?.page;
|
||||
|
||||
const showEllipsis = showPage && prevVisiblePage && pageNumber - prevVisiblePage > 1;
|
||||
|
||||
if (!showPage) return null;
|
||||
|
||||
return (
|
||||
<div key={link.page} className="contents">
|
||||
{showEllipsis && (
|
||||
<PaginationItem>
|
||||
<PaginationEllipsis />
|
||||
</PaginationItem>
|
||||
)}
|
||||
{showPage && (
|
||||
<PaginationItem>
|
||||
<PaginationLink
|
||||
className="cursor-pointer"
|
||||
onClick={() => setPage(link.page)}
|
||||
isActive={link.active}
|
||||
>
|
||||
{link.label}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
<PaginationItem>
|
||||
<PaginationNext
|
||||
onClick={() => setPage(paginatedData.next_page ?? paginatedData.last_page)}
|
||||
aria-disabled={!paginatedData.next_page}
|
||||
className={!paginatedData.next_page ? 'pointer-events-none opacity-50' : 'cursor-pointer'}
|
||||
/>
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
)
|
||||
}
|
||||
@@ -1,15 +1,26 @@
|
||||
import { useState } from "react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { SidebarTrigger } from "@/components/ui/sidebar";
|
||||
import { getRouteName } from "@/utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Terminal } from "lucide-react";
|
||||
import { BrushCleaning, Check, Copy, Terminal } from "lucide-react";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
||||
import { useLogger } from "@/helpers/use-logger";
|
||||
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
|
||||
|
||||
export default function Navbar() {
|
||||
const [copied, setCopied] = useState(false);
|
||||
const location = useLocation();
|
||||
const logs = useLogger().getLogs();
|
||||
const logger = useLogger();
|
||||
const logs = logger.getLogs();
|
||||
const logText = logs.map(log => `${new Date(log.timestamp).toLocaleTimeString()} [${log.level.toUpperCase()}] ${log.context}: ${log.message}`).join('\n');
|
||||
|
||||
const handleCopyLogs = async () => {
|
||||
await writeText(logText);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 1500);
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className="flex justify-between items-center py-3 px-4 sticky top-0 backdrop-blur supports-backdrop-filter:bg-background/60 border-b z-50">
|
||||
@@ -48,6 +59,28 @@ export default function Navbar() {
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
disabled={logs.length === 0}
|
||||
onClick={() => logger.clearLogs()}
|
||||
>
|
||||
<BrushCleaning className="size-4" />
|
||||
Clear Logs
|
||||
</Button>
|
||||
<Button
|
||||
className="transition-all duration-300"
|
||||
disabled={logs.length === 0}
|
||||
onClick={() => handleCopyLogs()}
|
||||
>
|
||||
{copied ? (
|
||||
<Check className="size-4" />
|
||||
) : (
|
||||
<Copy className="size-4" />
|
||||
)}
|
||||
Copy Logs
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { useEffect } from "react";
|
||||
import { ProxyImage } from "@/components/custom/proxyImage";
|
||||
import { AspectRatio } from "@/components/ui/aspect-ratio";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { toast } from "sonner";
|
||||
import { useCurrentVideoMetadataStore, useDownloadActionStatesStore } from "@/services/store";
|
||||
import { formatBitrate, formatCodec, formatDurationString, formatFileSize } from "@/utils";
|
||||
import { useCurrentVideoMetadataStore, useDownloadActionStatesStore, useLibraryPageStatesStore } from "@/services/store";
|
||||
import { formatBitrate, formatCodec, formatDurationString, formatFileSize, paginate } from "@/utils";
|
||||
import { ArrowUpRightIcon, AudioLines, CircleArrowDown, Clock, File, FileAudio2, FileQuestion, FileVideo2, FolderInput, ListVideo, Music, Play, Search, Trash2, Video } from "lucide-react";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import * as fs from "@tauri-apps/plugin-fs";
|
||||
@@ -17,6 +18,7 @@ import { Label } from "@/components/ui/label";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useLogger } from "@/helpers/use-logger";
|
||||
import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "@/components/ui/empty";
|
||||
import PaginationBar from "@/components/custom/paginationBar";
|
||||
|
||||
interface CompletedDownloadProps {
|
||||
state: DownloadState;
|
||||
@@ -250,16 +252,35 @@ export function CompletedDownload({ state }: CompletedDownloadProps) {
|
||||
}
|
||||
|
||||
export function CompletedDownloads({ downloads }: CompletedDownloadsProps) {
|
||||
const activeCompletedDownloadsPage = useLibraryPageStatesStore(state => state.activeCompletedDownloadsPage);
|
||||
const setActiveCompletedDownloadsPage = useLibraryPageStatesStore(state => state.setActiveCompletedDownloadsPage);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const paginatedCompletedDownloads = paginate(downloads, activeCompletedDownloadsPage, 5);
|
||||
|
||||
// Ensure current page is valid when downloads change
|
||||
useEffect(() => {
|
||||
if (downloads.length > 0 && activeCompletedDownloadsPage > paginatedCompletedDownloads.last_page) {
|
||||
setActiveCompletedDownloadsPage(paginatedCompletedDownloads.last_page);
|
||||
}
|
||||
}, [downloads.length, activeCompletedDownloadsPage, paginatedCompletedDownloads.last_page, setActiveCompletedDownloadsPage]);
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-col gap-2">
|
||||
{downloads.length > 0 ? (
|
||||
downloads.map((state) => {
|
||||
{paginatedCompletedDownloads.data.length > 0 ? (
|
||||
<>
|
||||
{paginatedCompletedDownloads.data.map((state) => {
|
||||
return (
|
||||
<CompletedDownload key={state.download_id} state={state} />
|
||||
);
|
||||
})
|
||||
})}
|
||||
{paginatedCompletedDownloads.pages.length > 1 && (
|
||||
<PaginationBar
|
||||
paginatedData={paginatedCompletedDownloads}
|
||||
setPage={setActiveCompletedDownloadsPage}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Empty className="mt-10">
|
||||
<EmptyHeader>
|
||||
|
||||
Reference in New Issue
Block a user