diff --git a/src/App.tsx b/src/App.tsx index 1b12a8b..1759925 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,6 @@ import React, { useState } from 'react'; import { Link, Route, Routes } from "react-router-dom"; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { library } from '@fortawesome/fontawesome-svg-core'; import { Navbar, @@ -60,6 +61,8 @@ export const App = () => { maxBlock: 0, }]); const [currentDownloadedBlock, setCurrentDownloadedBlock] = useState(0); + const [abortController, setAbortController] = useState<{ controllerStop: AbortController, controllerPause: AbortController } | null>(null); + const [pausedStatus, setPausedStatus] = useState(''); const [menuActive, setMenuActive] = useState(false); const [isLoading, setLoading] = useState(false); const [modal, setModal] = useState({ @@ -83,12 +86,18 @@ export const App = () => {
onModal()} + onClick={() => { + onModal(); + setCurrentDownloadedBlock(0); + }} />
onModal()} + onClick={() => { + onModal(); + setCurrentDownloadedBlock(0); + }} > {
{} : () => { - onModal(); + abortController?.controllerStop.abort(); setCurrentDownloadedBlock(0); }} />
{`Snapshot`} {`${modal.params.spanStart} - ${modal.params.spanEnd} (${nets[modal.params.network].title})`} - {currentDownloadedBlock / (modal.params.spanEnd - modal.params.spanStart + 1) === 1 ? 'Success!' : 'Writing'} + {currentDownloadedBlock / (modal.params.spanEnd - modal.params.spanStart + 1) === 1 ? 'Success!' : 'Downloading'} {`${currentDownloadedBlock} / ${modal.params.spanEnd - modal.params.spanStart + 1} (${roundNumber((currentDownloadedBlock / (modal.params.spanEnd - modal.params.spanStart + 1)) * 100)}%)`} + +
)} @@ -185,6 +235,7 @@ export const App = () => { nets={nets} setNets={setNets} setCurrentDownloadedBlock={setCurrentDownloadedBlock} + setAbortController={setAbortController} isLoading={isLoading} setLoading={setLoading} />} diff --git a/src/Home.tsx b/src/Home.tsx index bd60c02..39cb12c 100644 --- a/src/Home.tsx +++ b/src/Home.tsx @@ -31,6 +31,7 @@ const Home = ({ nets, setNets, setCurrentDownloadedBlock, + setAbortController, isLoading, setLoading, }) => { @@ -80,7 +81,14 @@ const Home = ({ types: [{ accept: { 'application/octet-stream': ['.acc'] } }], }); } - onModal('loading', formData); + onModal('loading', formData, (retryIndexTemp: number) => fetchBlocksInRange(+formData.spanStart + retryIndexTemp)); + + const controllerStop = new AbortController(); + const controllerPause = new AbortController(); + setAbortController({ + controllerStop, + controllerPause, + }); const writableStream = await fileHandle?.createWritable(retryIndex === null ? {} : { keepExistingData: true }); @@ -95,6 +103,8 @@ const Home = ({ const indexFileStart = Math.floor(formData.spanStart / 128000); const indexFileCount = Math.ceil((formData.spanEnd - formData.spanStart) / 128000) + indexFileStart; for (let indexFile = indexFileStart; indexFile <= indexFileCount; indexFile += 1) { + if (controllerStop.signal.aborted) throw new Error('Fetching aborted'); + if (controllerPause.signal.aborted) throw new Error('Paused'); const indexData: Uint8Array | string = await fetchIndexFile(currentNet, indexFile); if (typeof indexData === 'string') { @@ -113,12 +123,15 @@ const Home = ({ const startBlock = retryIndex !== null ? retryIndex : formData.spanStart; for (let i = startBlock - (128000 * indexFile); i <= objectsData.length; i += 1) { + if (controllerStop.signal.aborted) throw new Error('Fetching aborted'); + if (controllerPause.signal.aborted) throw new Error('Paused'); + if (blockCount <= (indexFile * 128000 + i - formData.spanStart)) { await writableStream?.close(); return } - const objectData: Uint8Array | string = await fetchBlock(currentNet, objectsData[i]); + const objectData: Uint8Array | string = await fetchBlock(currentNet, i, objectsData[i]); if (typeof objectData === 'string') { await writableStream?.close(); onModal('failed', objectData, (currentDownloadedBlockTemp: number) => fetchBlocksInRange(+formData.spanStart + currentDownloadedBlockTemp)); @@ -132,11 +145,14 @@ const Home = ({ } } } catch (error: any) { - if (error.message.indexOf('showSaveFilePicker is not a function') !== -1) { + if (error.message.indexOf('Fetching aborted') !== -1) { + onModal('failed', 'Fetching was cancelled'); + setAbortController(null); + } else if (error.message.indexOf('showSaveFilePicker is not a function') !== -1) { onModal('failed', 'Your current browser does not support this site\'s functionality. For the best experience, please use Chrome 86+ (recommended).', 'about'); } else if (error.message.indexOf('The user aborted a request.') !== -1) { onModal('failed', 'Aborted by user.'); - } else { + } else if (error.message.indexOf('Paused') === -1) { onModal('failed', error.message || 'Error occurred during block fetching.', (retryIndexTemp: number) => fetchBlocksInRange(+formData.spanStart + retryIndexTemp)); } } finally { @@ -166,12 +182,12 @@ const Home = ({ } }; - const fetchBlock = async (currentNet: NetItem, objectId: string): Promise => { + const fetchBlock = async (currentNet: NetItem, objectNumber: number, objectId: string): Promise => { try { const blockResponse = await api('GET', `/objects/${currentNet.containerId}/by_id/${objectId}?walletConnect=false`); return blockResponse as Uint8Array; } catch (err: any) { - return `Error occurred during object fetching ${objectId}: ${err.message}`; + return `Error occurred during object fetching #${objectNumber}: ${err.message}`; } }; diff --git a/src/api.ts b/src/api.ts index 24577ef..d329232 100644 --- a/src/api.ts +++ b/src/api.ts @@ -5,7 +5,13 @@ type Methods = "GET" | "POST"; async function serverRequest(method: Methods, url: string, params: object, headers: any) { const json: any = { method, - headers, + cache: 'no-store', + headers: { + 'Cache-Control': 'no-cache, no-store, must-revalidate', + 'Pragma': 'no-cache', + 'Expires': '0', + ...headers, + }, } if (json['headers']['Content-Type']) {