import {Fragment, useEffect, useState} from "react"; import axios, {AxiosResponse} from 'axios' interface DownloadRequestResponse { id: string; status: string; link: string; } interface DownloadRequest { link: string } const calculateProgressAndAppend = (di: DownloadRequestResponse): { di: DownloadRequestResponse, progress?: number } => { if (!di.status?.match) { return {di} } const matches = di.status.match(/in-progress:\s([\d\.]+)/) if (!matches || matches.length < 2) { return {di} } const progress = matches[1] const progressNum = parseInt(progress) if (typeof (progressNum) !== "number") { console.log(progress) return {di} } return {di, progress: progressNum} } const displayDownloads = (displayItems: DownloadRequestResponse[]) => { if (displayItems.length === 0) { return null } if (!displayItems.map) { console.log(displayItems) } const items = displayItems.map(calculateProgressAndAppend) const renderListItem = (di: DownloadRequestResponse, progress?: number) => { if (typeof (progress) !== 'number') { return <div className="space-y-1"> <div> <a href={di.link} target="_blank">{di.link}</a> : {di.status} </div> </div> } return ( <div className="space-y-1"> <p> <a href={di.link} target="_blank"> {di.link} </a> : {progress}% </p> <div className="w-full bg-gray-200 rounded-full h-2.5"> <div className="bg-orange-500 h-2.5 rounded-full" style={{width: `${progress}%`}}/> </div> </div> ) } return <div className="flex flex-col max-h-80 overflow-scroll overflow-x-hidden"> <ul className="space-y-3"> { items .sort((a, b) => a.progress - b.progress) .map(({di, progress}, i) => ( <Fragment key={di.id}> {i !== 0 && (<hr className="border-gray-400 border-1 rounded-full mx-8 m-auto"/>)} <li> {renderListItem(di, progress)} </li> </Fragment> ))} </ul> </div> } export default function Home() { const [downloadUrl, setDownloadUrl] = useState("") const [downloadingItems, setDownloadingItems] = useState<DownloadRequestResponse[]>([]) const [downloadedItems, setDownloadedItems] = useState<DownloadRequestResponse[]>([]) useEffect(() => { getInitialDownloads() const interval = setInterval(() => { getInitialDownloads() }, 3000) return () => { clearInterval(interval) } }, []) function setMockedDownloadingItems() { setDownloadingItems([ {id: "some-id-1", status: "in-progress: 10.3", link: "https://youtube.com"}, {id: "some-id-2", status: "in-progress: 0", link: "https://youtube.com"}, {id: "some-id-3", status: "in-progress: 100.0", link: "https://youtube.com"}, {id: "some-id-4", status: "in-progress: 0", link: "https://youtube.com"}, {id: "some-id-5", status: "in-progress: 100.0", link: "https://youtube.com"}, {id: "some-id-6", status: "in-progress: 0", link: "https://youtube.com"}, {id: "some-id-7", status: "in-progress: 100.0", link: "https://youtube.com"}, {id: "some-id-8", status: "in-progress: 0", link: "https://youtube.com"}, {id: "some-id-9", status: "in-progress: 100.0", link: "https://youtube.com"}, {id: "some-id-10", status: "in-progress: 10.3", link: "https://youtube.com"}, {id: "some-id-11", status: "in-progress: 10.3", link: "https://youtube.com"}, {id: "some-id-12", status: "in-progress: 10.3", link: "https://youtube.com"}, {id: "some-id-13", status: "in-progress: 10.3", link: "https://youtube.com"}, {id: "some-id-14", status: "in-progress: 10.3", link: "https://youtube.com"}, {id: "some-id-15", status: "in-progress: 10.3", link: "https://youtube.com"}, {id: "some-id-16", status: "in-progress: 10.3", link: "https://youtube.com"}, {id: "some-id-17", status: "in-progress: 10.3", link: "https://youtube.com"}, {id: "some-id-18", status: "in-progress: 10.3", link: "https://youtube.com"}, {id: "some-id-19", status: "in-progress: 10.3", link: "https://youtube.com"}, {id: "some-id-20", status: "in-progress: 10.3", link: "https://youtube.com"}, {id: "some-id-21", status: "in-progress: 10.3", link: "https://youtube.com"}, {id: "some-id-22", status: "in-progress: 10.3", link: "https://youtube.com"}, {id: "some-id-23", status: "in-progress: 10.3", link: "https://youtube.com"}, {id: "some-id-24", status: "scheduled", link: "https://youtube.com"}, {id: "some-id-25", status: "done", link: "https://youtube.com"}, ]) } const getInitialDownloads = () => { axios.get<DownloadRequestResponse[]>("http://localhost:3333/downloads?active=true" ).then(res => { setDownloadingItems(res.data) //setMockedDownloadingItems(); }) axios.get<DownloadRequestResponse[]>("http://localhost:3333/downloads?done=true" ).then(res => { setDownloadedItems(res.data) }) } const download = (url: string) => { axios.post<DownloadRequest, AxiosResponse<DownloadRequestResponse>>("http://localhost:3333/downloads", { link: url }).then(res => { setDownloadingItems([{ id: res.data.id, status: res.data.status, link: res.data.link }, ...downloadingItems.filter(di => di.id !== res.data.id)]) }) } return ( <div className="min-w-full min-h-screen flex justify-center items-center h-screen bg-cover bg-center absolute z-0 bg-[url('https://images.unsplash.com/photo-1539693565400-356293b6df0f?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1470&q=80')]" > <div className="bg-gray-200 p-4 rounded-xl w-full mx-2 sm:mx-10 lg:mx-40 space-y-6"> <div className="flex flex-col md:flex-row gap-2"> <input className="bg-gray-200 border-2 border-gray-400 focus:border-gray-600 px-4 py-2 flex-1 rounded-xl outline-none appearance-none" placeholder="Your download url" value={downloadUrl} onChange={(e) => setDownloadUrl(e.target.value)}> </input> <button type="button" className="py-2 px-4 bg-orange-600 rounded-xl text-white active:bg-orange-800" onClick={() => { if (downloadUrl) { download(downloadUrl) } }}> Download </button> </div> {downloadingItems && downloadingItems.length > 0 && displayDownloads(downloadingItems)} {downloadedItems && downloadedItems.length > 0 && displayDownloads(downloadedItems)} </div> </div> ) }