Show download progress and cancel request on unmount (#46)

* Improve loading feedback

* Add ms metric

* Add download progress and ability to cancel fetch
This commit is contained in:
Piotr Rogowski 2021-04-06 23:09:17 +02:00 committed by GitHub
parent 9652b33e4d
commit 14e21702aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 68 additions and 21 deletions

14
package-lock.json generated
View File

@ -12,7 +12,7 @@
"antd": "^4.15.0",
"electron-squirrel-startup": "^1.0.0",
"js-yaml": "^4.0.0 ",
"mlg-converter": "^0.4.0",
"mlg-converter": "^0.5.0",
"parsimmon": "^1.16.0",
"react": "^17.0.1",
"react-dom": "^17.0.2",
@ -15534,9 +15534,9 @@
}
},
"node_modules/mlg-converter": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/mlg-converter/-/mlg-converter-0.4.0.tgz",
"integrity": "sha512-lWO5KYOpGogrW8bqAfFTQU+22g4LnCehGf7ZiFfMOfu/S0Rt5t9ZIlHYAOi3Qh79qoDnD1hk/oMXhKRouagI0w=="
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/mlg-converter/-/mlg-converter-0.5.0.tgz",
"integrity": "sha512-FKqJ0vbbxFZuziwMbih8fRF8JFF2xFSWmYNOylji1IkRPu83dCWCGV4AkG9fkKs/z9ThOqfuSkaUEnWbL33c8g=="
},
"node_modules/moment": {
"version": "2.29.1",
@ -38513,9 +38513,9 @@
}
},
"mlg-converter": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/mlg-converter/-/mlg-converter-0.4.0.tgz",
"integrity": "sha512-lWO5KYOpGogrW8bqAfFTQU+22g4LnCehGf7ZiFfMOfu/S0Rt5t9ZIlHYAOi3Qh79qoDnD1hk/oMXhKRouagI0w=="
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/mlg-converter/-/mlg-converter-0.5.0.tgz",
"integrity": "sha512-FKqJ0vbbxFZuziwMbih8fRF8JFF2xFSWmYNOylji1IkRPu83dCWCGV4AkG9fkKs/z9ThOqfuSkaUEnWbL33c8g=="
},
"moment": {
"version": "2.29.1",

View File

@ -39,7 +39,7 @@
"antd": "^4.15.0",
"electron-squirrel-startup": "^1.0.0",
"js-yaml": "^4.0.0 ",
"mlg-converter": "^0.4.0",
"mlg-converter": "^0.5.0",
"parsimmon": "^1.16.0",
"react": "^17.0.1",
"react-dom": "^17.0.2",

BIN
public/logs/long.mlg Normal file

Binary file not shown.

View File

@ -26,7 +26,7 @@ import useBreakpoint from 'antd/lib/grid/hooks/useBreakpoint';
import { connect } from 'react-redux';
import {
Field,
Result as ParserResult,
Result as ParserResult,
} from 'mlg-converter/dist/types';
import PerfectScrollbar from 'react-perfect-scrollbar';
// eslint-disable-next-line import/no-unresolved
@ -35,13 +35,13 @@ import { loadLogs } from '../utils/api';
import Canvas, { LogEntry } from './Log/Canvas';
import {
AppState,
UIState,
UIState,
} from '../types/state';
import { Config } from '../types/config';
import store from '../store';
import {
formatBytes,
msToTime,
msToTime,
} from '../utils/number';
const { TabPane } = Tabs;
@ -89,9 +89,13 @@ const Log = ({ ui, config }: { ui: UIState, config: Config }) => {
useEffect(() => {
const worker = new MlgParserWorker();
const controller = new AbortController();
const { signal } = controller;
const loadData = async () => {
const raw = await loadLogs();
setFileSize(formatBytes(raw.byteLength));
const raw = await loadLogs((percent, total) => {
setProgress(percent);
setFileSize(formatBytes(total));
}, signal);
worker.postMessage(raw);
worker.onmessage = ({ data }) => {
@ -99,15 +103,17 @@ const Log = ({ ui, config }: { ui: UIState, config: Config }) => {
case 'progress':
setStep(1);
setProgress(data.progress);
setParseElapsed(msToTime(data.elapsed));
break;
case 'result':
setSamplesCount(data.result.records.length);
setStep(2);
setLogs(data.result);
setFields(data.result.fields);
break;
case 'metrics':
setParseElapsed(msToTime(data.metrics.elapsedMs));
console.log(`Log parsed in ${data.elapsed}ms`);
setParseElapsed(msToTime(data.elapsed));
setSamplesCount(data.records);
setStep(2);
break;
default:
break;
@ -121,8 +127,9 @@ const Log = ({ ui, config }: { ui: UIState, config: Config }) => {
window.addEventListener('resize', calculateCanvasWidth);
return () => {
window.removeEventListener('resize', calculateCanvasWidth);
controller.abort();
worker.terminate();
window.removeEventListener('resize', calculateCanvasWidth);
};
}, [calculateCanvasWidth]);

View File

@ -3,6 +3,10 @@ import { Config as ConfigType } from '../types/config';
import stdDialogs from '../data/standardDialogs';
import help from '../data/help';
import { divider } from '../data/constants';
import {
fetchWithProgress,
onProgress as onProgressType,
} from './http';
export const loadAll = async () => {
const started = new Date();
@ -74,4 +78,5 @@ export const loadAll = async () => {
});
};
export const loadLogs = () => fetch('./logs/middle.mlg').then((response) => response.arrayBuffer());
export const loadLogs = (onProgress?: onProgressType, signal?: AbortSignal) => fetchWithProgress('./logs/long.mlg', onProgress, signal)
.then((response) => response.buffer);

34
src/utils/http.ts Normal file
View File

@ -0,0 +1,34 @@
export type onProgress = (percent: number, total: number) => void;
export const fetchWithProgress = async (url: string, onProgress?: onProgress, signal?: AbortSignal): Promise<Uint8Array> => {
const response = await fetch(url, { signal });
const contentLength = response.headers.get('Content-Length');
if (!contentLength) {
throw new Error('Missing Content-Length while fetching');
}
const reader = response.body!.getReader();
const length = Number(contentLength);
const array = new Uint8Array(length);
let at = 0;
for (; ;) {
// eslint-disable-next-line no-await-in-loop
const { done, value } = await reader.read();
if (done) {
break;
}
array.set(value as Uint8Array, at);
at += (value as Uint8Array).length;
if (onProgress) {
// eslint-disable-next-line no-bitwise
onProgress(~~(at / length * 100), length);
}
}
return array;
};

View File

@ -1,3 +1,4 @@
/* eslint-disable no-bitwise */
import { Parser } from 'mlg-converter';
// eslint-disable-next-line no-restricted-globals
@ -10,13 +11,13 @@ ctx.addEventListener('message', ({ data }: { data: ArrayBuffer }) => {
ctx.postMessage({
type: 'progress',
progress,
elapsed: ~~(performance.now() - t0),
});
});
ctx.postMessage({
type: 'metrics',
metrics: {
elapsedMs: Math.round(performance.now() - t0),
},
elapsed: ~~(performance.now() - t0),
records: result.records.length,
});
ctx.postMessage({ type: 'result', result });
});