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:
parent
9652b33e4d
commit
14e21702aa
|
@ -12,7 +12,7 @@
|
||||||
"antd": "^4.15.0",
|
"antd": "^4.15.0",
|
||||||
"electron-squirrel-startup": "^1.0.0",
|
"electron-squirrel-startup": "^1.0.0",
|
||||||
"js-yaml": "^4.0.0 ",
|
"js-yaml": "^4.0.0 ",
|
||||||
"mlg-converter": "^0.4.0",
|
"mlg-converter": "^0.5.0",
|
||||||
"parsimmon": "^1.16.0",
|
"parsimmon": "^1.16.0",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
|
@ -15534,9 +15534,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mlg-converter": {
|
"node_modules/mlg-converter": {
|
||||||
"version": "0.4.0",
|
"version": "0.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/mlg-converter/-/mlg-converter-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/mlg-converter/-/mlg-converter-0.5.0.tgz",
|
||||||
"integrity": "sha512-lWO5KYOpGogrW8bqAfFTQU+22g4LnCehGf7ZiFfMOfu/S0Rt5t9ZIlHYAOi3Qh79qoDnD1hk/oMXhKRouagI0w=="
|
"integrity": "sha512-FKqJ0vbbxFZuziwMbih8fRF8JFF2xFSWmYNOylji1IkRPu83dCWCGV4AkG9fkKs/z9ThOqfuSkaUEnWbL33c8g=="
|
||||||
},
|
},
|
||||||
"node_modules/moment": {
|
"node_modules/moment": {
|
||||||
"version": "2.29.1",
|
"version": "2.29.1",
|
||||||
|
@ -38513,9 +38513,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mlg-converter": {
|
"mlg-converter": {
|
||||||
"version": "0.4.0",
|
"version": "0.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/mlg-converter/-/mlg-converter-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/mlg-converter/-/mlg-converter-0.5.0.tgz",
|
||||||
"integrity": "sha512-lWO5KYOpGogrW8bqAfFTQU+22g4LnCehGf7ZiFfMOfu/S0Rt5t9ZIlHYAOi3Qh79qoDnD1hk/oMXhKRouagI0w=="
|
"integrity": "sha512-FKqJ0vbbxFZuziwMbih8fRF8JFF2xFSWmYNOylji1IkRPu83dCWCGV4AkG9fkKs/z9ThOqfuSkaUEnWbL33c8g=="
|
||||||
},
|
},
|
||||||
"moment": {
|
"moment": {
|
||||||
"version": "2.29.1",
|
"version": "2.29.1",
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
"antd": "^4.15.0",
|
"antd": "^4.15.0",
|
||||||
"electron-squirrel-startup": "^1.0.0",
|
"electron-squirrel-startup": "^1.0.0",
|
||||||
"js-yaml": "^4.0.0 ",
|
"js-yaml": "^4.0.0 ",
|
||||||
"mlg-converter": "^0.4.0",
|
"mlg-converter": "^0.5.0",
|
||||||
"parsimmon": "^1.16.0",
|
"parsimmon": "^1.16.0",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
|
|
Binary file not shown.
|
@ -26,7 +26,7 @@ import useBreakpoint from 'antd/lib/grid/hooks/useBreakpoint';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
Field,
|
Field,
|
||||||
Result as ParserResult,
|
Result as ParserResult,
|
||||||
} from 'mlg-converter/dist/types';
|
} from 'mlg-converter/dist/types';
|
||||||
import PerfectScrollbar from 'react-perfect-scrollbar';
|
import PerfectScrollbar from 'react-perfect-scrollbar';
|
||||||
// eslint-disable-next-line import/no-unresolved
|
// eslint-disable-next-line import/no-unresolved
|
||||||
|
@ -35,13 +35,13 @@ import { loadLogs } from '../utils/api';
|
||||||
import Canvas, { LogEntry } from './Log/Canvas';
|
import Canvas, { LogEntry } from './Log/Canvas';
|
||||||
import {
|
import {
|
||||||
AppState,
|
AppState,
|
||||||
UIState,
|
UIState,
|
||||||
} from '../types/state';
|
} from '../types/state';
|
||||||
import { Config } from '../types/config';
|
import { Config } from '../types/config';
|
||||||
import store from '../store';
|
import store from '../store';
|
||||||
import {
|
import {
|
||||||
formatBytes,
|
formatBytes,
|
||||||
msToTime,
|
msToTime,
|
||||||
} from '../utils/number';
|
} from '../utils/number';
|
||||||
|
|
||||||
const { TabPane } = Tabs;
|
const { TabPane } = Tabs;
|
||||||
|
@ -89,9 +89,13 @@ const Log = ({ ui, config }: { ui: UIState, config: Config }) => {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const worker = new MlgParserWorker();
|
const worker = new MlgParserWorker();
|
||||||
|
const controller = new AbortController();
|
||||||
|
const { signal } = controller;
|
||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
const raw = await loadLogs();
|
const raw = await loadLogs((percent, total) => {
|
||||||
setFileSize(formatBytes(raw.byteLength));
|
setProgress(percent);
|
||||||
|
setFileSize(formatBytes(total));
|
||||||
|
}, signal);
|
||||||
|
|
||||||
worker.postMessage(raw);
|
worker.postMessage(raw);
|
||||||
worker.onmessage = ({ data }) => {
|
worker.onmessage = ({ data }) => {
|
||||||
|
@ -99,15 +103,17 @@ const Log = ({ ui, config }: { ui: UIState, config: Config }) => {
|
||||||
case 'progress':
|
case 'progress':
|
||||||
setStep(1);
|
setStep(1);
|
||||||
setProgress(data.progress);
|
setProgress(data.progress);
|
||||||
|
setParseElapsed(msToTime(data.elapsed));
|
||||||
break;
|
break;
|
||||||
case 'result':
|
case 'result':
|
||||||
setSamplesCount(data.result.records.length);
|
|
||||||
setStep(2);
|
|
||||||
setLogs(data.result);
|
setLogs(data.result);
|
||||||
setFields(data.result.fields);
|
setFields(data.result.fields);
|
||||||
break;
|
break;
|
||||||
case 'metrics':
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -121,8 +127,9 @@ const Log = ({ ui, config }: { ui: UIState, config: Config }) => {
|
||||||
window.addEventListener('resize', calculateCanvasWidth);
|
window.addEventListener('resize', calculateCanvasWidth);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('resize', calculateCanvasWidth);
|
controller.abort();
|
||||||
worker.terminate();
|
worker.terminate();
|
||||||
|
window.removeEventListener('resize', calculateCanvasWidth);
|
||||||
};
|
};
|
||||||
}, [calculateCanvasWidth]);
|
}, [calculateCanvasWidth]);
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,10 @@ import { Config as ConfigType } from '../types/config';
|
||||||
import stdDialogs from '../data/standardDialogs';
|
import stdDialogs from '../data/standardDialogs';
|
||||||
import help from '../data/help';
|
import help from '../data/help';
|
||||||
import { divider } from '../data/constants';
|
import { divider } from '../data/constants';
|
||||||
|
import {
|
||||||
|
fetchWithProgress,
|
||||||
|
onProgress as onProgressType,
|
||||||
|
} from './http';
|
||||||
|
|
||||||
export const loadAll = async () => {
|
export const loadAll = async () => {
|
||||||
const started = new Date();
|
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);
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable no-bitwise */
|
||||||
import { Parser } from 'mlg-converter';
|
import { Parser } from 'mlg-converter';
|
||||||
|
|
||||||
// eslint-disable-next-line no-restricted-globals
|
// eslint-disable-next-line no-restricted-globals
|
||||||
|
@ -10,13 +11,13 @@ ctx.addEventListener('message', ({ data }: { data: ArrayBuffer }) => {
|
||||||
ctx.postMessage({
|
ctx.postMessage({
|
||||||
type: 'progress',
|
type: 'progress',
|
||||||
progress,
|
progress,
|
||||||
|
elapsed: ~~(performance.now() - t0),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
ctx.postMessage({
|
ctx.postMessage({
|
||||||
type: 'metrics',
|
type: 'metrics',
|
||||||
metrics: {
|
elapsed: ~~(performance.now() - t0),
|
||||||
elapsedMs: Math.round(performance.now() - t0),
|
records: result.records.length,
|
||||||
},
|
|
||||||
});
|
});
|
||||||
ctx.postMessage({ type: 'result', result });
|
ctx.postMessage({ type: 'result', result });
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue