mirror of
https://github.com/tlsnotary/tlsn-extension.git
synced 2026-01-09 21:18:02 -05:00
Added Errors to Notarization Progress (#147)
* feat: added Error for RequestProgress * feat: added logic to handle errors depending on progress * chore: linting errors * fix: linting build error * Avoid magic numbers * lint * feat: alpha.8 * fix: package.json path * chore: update lockfiles * fix: notary url * feat: notarization timeouts * fix: notarization timeouts * feat: fixed comments + errors are properly thrown and status changes * fix: notarization timeouts work properly now * fix: timeout error message fix * fix: lint * fix: lint * fix: errors should keep state after being thrown now * chore: lint * fix: fixing metadata with new progress status --------- Co-authored-by: Hendrik Eeckhaut <hendrik@eeckhaut.org> Co-authored-by: tsukino <0xtsukino@gmail.com>
This commit is contained in:
24
package-lock.json
generated
24
package-lock.json
generated
@@ -3603,9 +3603,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@pmmmwh/react-refresh-webpack-plugin": {
|
||||
"version": "0.5.15",
|
||||
"resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.15.tgz",
|
||||
"integrity": "sha512-LFWllMA55pzB9D34w/wXUCf8+c+IYKuJDgxiZ3qMhl64KRMBHYM1I3VdGaD2BV5FNPV2/S2596bppxHbv2ZydQ==",
|
||||
"version": "0.5.16",
|
||||
"resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.16.tgz",
|
||||
"integrity": "sha512-kLQc9xz6QIqd2oIYyXRUiAp79kGpFBm3fEM9ahfG1HI0WI5gdZ2OVHWdmZYnwODt7ISck+QuQ6sBPrtvUBML7Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -8710,9 +8710,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/html-entities": {
|
||||
"version": "2.5.3",
|
||||
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.3.tgz",
|
||||
"integrity": "sha512-D3AfvN7SjhTgBSA8L1BN4FpPzuEd06uy4lHwSoRWr0lndi9BKaNzPLKGOWZ2ocSGguozr08TTb2jhCLHaemruw==",
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz",
|
||||
"integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -11735,9 +11735,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-load-config/node_modules/yaml": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz",
|
||||
"integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==",
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz",
|
||||
"integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"yaml": "bin.mjs"
|
||||
@@ -14744,9 +14744,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz",
|
||||
"integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==",
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
|
||||
"integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
|
||||
@@ -115,14 +115,15 @@ export async function setNotaryRequestError(
|
||||
export async function setNotaryRequestProgress(
|
||||
id: string,
|
||||
progress: RequestProgress,
|
||||
errorMessage?: string,
|
||||
): Promise<RequestHistory | null> {
|
||||
const existing = await historyDb.get(id);
|
||||
|
||||
if (!existing) return null;
|
||||
|
||||
const newReq: RequestHistory = {
|
||||
...existing,
|
||||
progress,
|
||||
errorMessage,
|
||||
};
|
||||
|
||||
await historyDb.put(id, newReq);
|
||||
|
||||
@@ -167,9 +167,13 @@ export enum RequestProgress {
|
||||
SendingRequest,
|
||||
ReadingTranscript,
|
||||
FinalizingOutputs,
|
||||
Error,
|
||||
}
|
||||
|
||||
export function progressText(progress: RequestProgress): string {
|
||||
export function progressText(
|
||||
progress: RequestProgress,
|
||||
errorMessage?: string,
|
||||
): string {
|
||||
switch (progress) {
|
||||
case RequestProgress.CreatingProver:
|
||||
return 'Creating prover...';
|
||||
@@ -183,6 +187,8 @@ export function progressText(progress: RequestProgress): string {
|
||||
return 'Reading request transcript...';
|
||||
case RequestProgress.FinalizingOutputs:
|
||||
return 'Finalizing notarization outputs...';
|
||||
case RequestProgress.Error:
|
||||
return errorMessage ? errorMessage : 'Error: Notarization Failed';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,6 +216,7 @@ export type RequestHistory = {
|
||||
secretHeaders?: string[];
|
||||
secretResps?: string[];
|
||||
cid?: string;
|
||||
errorMessage?: string;
|
||||
metadata?: {
|
||||
[k: string]: string;
|
||||
};
|
||||
@@ -430,9 +437,9 @@ async function handleUpdateRequestProgress(
|
||||
request: BackgroundAction,
|
||||
sendResponse: (data?: any) => void,
|
||||
) {
|
||||
const { id, progress } = request.data;
|
||||
const { id, progress, errorMessage } = request.data;
|
||||
|
||||
const newReq = await setNotaryRequestProgress(id, progress);
|
||||
const newReq = await setNotaryRequestProgress(id, progress, errorMessage);
|
||||
if (!newReq) return;
|
||||
await pushToRedux(addRequestHistory(await getNotaryRequest(id)));
|
||||
|
||||
@@ -553,69 +560,82 @@ async function runPluginProver(request: BackgroundAction, now = Date.now()) {
|
||||
});
|
||||
|
||||
await setNotaryRequestStatus(id, 'pending');
|
||||
|
||||
await pushToRedux(addRequestHistory(await getNotaryRequest(id)));
|
||||
|
||||
const onProverResponse = async (request: any) => {
|
||||
const { data, type } = request;
|
||||
let listenerActive = true;
|
||||
let responseListener: (request: any) => void;
|
||||
|
||||
if (type !== OffscreenActionTypes.create_prover_response) {
|
||||
return;
|
||||
}
|
||||
const proverPromise = new Promise<void>((resolve, reject) => {
|
||||
responseListener = async (request: any) => {
|
||||
if (!listenerActive) return;
|
||||
|
||||
if (data.error) {
|
||||
console.error(data.error);
|
||||
return;
|
||||
}
|
||||
const { data, type } = request;
|
||||
|
||||
if (data.id !== id) {
|
||||
return;
|
||||
}
|
||||
if (type !== OffscreenActionTypes.create_prover_response) {
|
||||
return;
|
||||
}
|
||||
|
||||
const transcript: { recv: number[]; sent: number[] } = data.transcript;
|
||||
if (data.id !== id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { body: recvBody } = parseHttpMessage(
|
||||
Buffer.from(transcript.recv),
|
||||
'response',
|
||||
);
|
||||
try {
|
||||
if (data.error) {
|
||||
throw new Error(data.error);
|
||||
}
|
||||
|
||||
if (getSecretResponse) {
|
||||
secretResps = await getSecretResponseFn(
|
||||
...recvBody.map((body) => body.toString('utf-8')),
|
||||
);
|
||||
}
|
||||
const transcript: { recv: number[]; sent: number[] } = data.transcript;
|
||||
|
||||
const commit = {
|
||||
sent: subtractRanges(
|
||||
{ start: 0, end: transcript.sent.length },
|
||||
mapStringToRange(
|
||||
secretHeaders,
|
||||
Buffer.from(transcript.sent).toString('utf-8'),
|
||||
),
|
||||
),
|
||||
recv: subtractRanges(
|
||||
{ start: 0, end: transcript.recv.length },
|
||||
mapStringToRange(
|
||||
secretResps,
|
||||
Buffer.from(transcript.recv).toString('utf-8'),
|
||||
),
|
||||
),
|
||||
const { body: recvBody } = parseHttpMessage(
|
||||
Buffer.from(transcript.recv),
|
||||
'response',
|
||||
);
|
||||
|
||||
if (getSecretResponse) {
|
||||
secretResps = await getSecretResponseFn(
|
||||
...recvBody.map((body) => body.toString('utf-8')),
|
||||
);
|
||||
}
|
||||
|
||||
const commit = {
|
||||
sent: subtractRanges(
|
||||
{ start: 0, end: transcript.sent.length },
|
||||
mapStringToRange(
|
||||
secretHeaders,
|
||||
Buffer.from(transcript.sent).toString('utf-8'),
|
||||
),
|
||||
),
|
||||
recv: subtractRanges(
|
||||
{ start: 0, end: transcript.recv.length },
|
||||
mapStringToRange(
|
||||
secretResps,
|
||||
Buffer.from(transcript.recv).toString('utf-8'),
|
||||
),
|
||||
),
|
||||
};
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
type: OffscreenActionTypes.create_presentation_request,
|
||||
data: {
|
||||
id,
|
||||
commit,
|
||||
notaryUrl,
|
||||
websocketProxyUrl,
|
||||
},
|
||||
});
|
||||
|
||||
resolve();
|
||||
} catch (error) {
|
||||
console.error('Prover response error:', error);
|
||||
reject(error);
|
||||
} finally {
|
||||
listenerActive = false;
|
||||
browser.runtime.onMessage.removeListener(responseListener);
|
||||
}
|
||||
};
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
type: OffscreenActionTypes.create_presentation_request,
|
||||
data: {
|
||||
id,
|
||||
commit,
|
||||
notaryUrl,
|
||||
websocketProxyUrl,
|
||||
},
|
||||
});
|
||||
|
||||
browser.runtime.onMessage.removeListener(onProverResponse);
|
||||
};
|
||||
|
||||
browser.runtime.onMessage.addListener(onProverResponse);
|
||||
browser.runtime.onMessage.addListener(responseListener);
|
||||
});
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
type: OffscreenActionTypes.create_prover_request,
|
||||
@@ -631,6 +651,34 @@ async function runPluginProver(request: BackgroundAction, now = Date.now()) {
|
||||
maxSentData,
|
||||
},
|
||||
});
|
||||
|
||||
const timeoutPromise = new Promise((_, reject) => {
|
||||
setTimeout(() => {
|
||||
if (listenerActive) {
|
||||
listenerActive = false;
|
||||
browser.runtime.onMessage.removeListener(responseListener);
|
||||
reject(new Error('Notarization Timed Out'));
|
||||
}
|
||||
// 3 minute timeout
|
||||
}, 180000);
|
||||
});
|
||||
|
||||
try {
|
||||
await Promise.race([proverPromise, timeoutPromise]);
|
||||
} catch (error: any) {
|
||||
await setNotaryRequestStatus(id, 'error');
|
||||
await setNotaryRequestError(id, error.message);
|
||||
browser.runtime.sendMessage({
|
||||
type: BackgroundActiontype.update_request_progress,
|
||||
data: {
|
||||
id,
|
||||
progress: RequestProgress.Error,
|
||||
error: error.message,
|
||||
},
|
||||
});
|
||||
await pushToRedux(addRequestHistory(await getNotaryRequest(id)));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleGetSecretsFromTranscript(
|
||||
|
||||
@@ -21,6 +21,10 @@ import { OffscreenActionTypes } from './types';
|
||||
import { PresentationJSON } from '../../utils/types';
|
||||
import { verify } from 'tlsn-js-v5';
|
||||
import { waitForEvent } from '../utils';
|
||||
import {
|
||||
setNotaryRequestError,
|
||||
setNotaryRequestStatus,
|
||||
} from '../Background/db';
|
||||
|
||||
const { init, Prover, Presentation, Verifier }: any = Comlink.wrap(
|
||||
new Worker(new URL('./worker.ts', import.meta.url)),
|
||||
@@ -122,7 +126,6 @@ export const onCreatePresentationRequest = async (request: any) => {
|
||||
reveal: commit,
|
||||
})) as TPresentation;
|
||||
const json = await presentation.json();
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
type: BackgroundActiontype.finish_prove_request,
|
||||
data: {
|
||||
@@ -435,7 +438,6 @@ async function createProof(options: {
|
||||
})) as TPresentation;
|
||||
|
||||
const json = await presentation.json();
|
||||
|
||||
return {
|
||||
...json,
|
||||
meta: {
|
||||
@@ -473,30 +475,51 @@ async function createProver(options: {
|
||||
|
||||
const hostname = urlify(url)?.hostname || '';
|
||||
const notary = NotaryServer.from(notaryUrl);
|
||||
try {
|
||||
const prover: TProver = await handleProgress(
|
||||
id,
|
||||
RequestProgress.CreatingProver,
|
||||
() =>
|
||||
new Prover({
|
||||
id,
|
||||
serverDns: hostname,
|
||||
maxSentData,
|
||||
maxRecvData,
|
||||
}),
|
||||
'Error creating prover',
|
||||
);
|
||||
|
||||
updateRequestProgress(id, RequestProgress.CreatingProver);
|
||||
const prover: TProver = await new Prover({
|
||||
id,
|
||||
serverDns: hostname,
|
||||
maxSentData,
|
||||
maxRecvData,
|
||||
});
|
||||
const sessionUrl = await handleProgress(
|
||||
id,
|
||||
RequestProgress.GettingSession,
|
||||
() => notary.sessionUrl(maxSentData, maxRecvData),
|
||||
'Error getting session from Notary',
|
||||
);
|
||||
|
||||
updateRequestProgress(id, RequestProgress.GettingSession);
|
||||
const sessionUrl = await notary.sessionUrl(maxSentData, maxRecvData);
|
||||
await handleProgress(
|
||||
id,
|
||||
RequestProgress.SettingUpProver,
|
||||
() => prover.setup(sessionUrl),
|
||||
'Error setting up prover',
|
||||
);
|
||||
|
||||
updateRequestProgress(id, RequestProgress.SettingUpProver);
|
||||
await prover.setup(sessionUrl);
|
||||
await handleProgress(
|
||||
id,
|
||||
RequestProgress.SendingRequest,
|
||||
() =>
|
||||
prover.sendRequest(websocketProxyUrl + `?token=${hostname}`, {
|
||||
url,
|
||||
method,
|
||||
headers,
|
||||
body,
|
||||
}),
|
||||
'Error sending request',
|
||||
);
|
||||
|
||||
updateRequestProgress(id, RequestProgress.SendingRequest);
|
||||
await prover.sendRequest(websocketProxyUrl + `?token=${hostname}`, {
|
||||
url,
|
||||
method,
|
||||
headers,
|
||||
body,
|
||||
});
|
||||
|
||||
return prover;
|
||||
return prover;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function verifyProof(proof: PresentationJSON): Promise<{
|
||||
@@ -545,13 +568,43 @@ async function verifyProof(proof: PresentationJSON): Promise<{
|
||||
return result;
|
||||
}
|
||||
|
||||
function updateRequestProgress(id: string, progress: RequestProgress) {
|
||||
devlog(`Request ${id}: ${progressText(progress)}`);
|
||||
function updateRequestProgress(
|
||||
id: string,
|
||||
progress: RequestProgress,
|
||||
errorMessage?: string,
|
||||
) {
|
||||
const progressMessage =
|
||||
progress === RequestProgress.Error
|
||||
? `${errorMessage || 'Notarization Failed'}`
|
||||
: progressText(progress);
|
||||
devlog(`Request ${id}: ${progressMessage}`);
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
type: BackgroundActiontype.update_request_progress,
|
||||
data: {
|
||||
id,
|
||||
progress: progress,
|
||||
progress,
|
||||
errorMessage,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function handleProgress<T>(
|
||||
id: string,
|
||||
progress: RequestProgress,
|
||||
action: () => Promise<T>,
|
||||
errorMessage: string,
|
||||
): Promise<T> {
|
||||
try {
|
||||
updateRequestProgress(id, progress);
|
||||
return await action();
|
||||
} catch (error: any) {
|
||||
updateRequestProgress(id, RequestProgress.Error, errorMessage);
|
||||
await setNotaryRequestStatus(id, 'error');
|
||||
await setNotaryRequestError(
|
||||
id,
|
||||
errorMessage || error.message || 'Unknown error',
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,11 @@ import logo from '../../assets/img/icon-128.png';
|
||||
import classNames from 'classnames';
|
||||
import Icon from '../../components/Icon';
|
||||
import { useRequestHistory } from '../../reducers/history';
|
||||
import { BackgroundActiontype, progressText } from '../Background/rpc';
|
||||
import {
|
||||
BackgroundActiontype,
|
||||
progressText,
|
||||
RequestProgress,
|
||||
} from '../Background/rpc';
|
||||
import { getPluginByHash, getPluginConfigByHash } from '../Background/db';
|
||||
import { SidePanelActionTypes } from './types';
|
||||
import { fetchP2PState, useClientId } from '../../reducers/p2p';
|
||||
@@ -338,17 +342,27 @@ function StepContent(
|
||||
);
|
||||
} else if (notaryRequest?.status === 'pending' || pending || notarizationId) {
|
||||
btnContent = (
|
||||
<button className="button mt-2 w-fit flex flex-row flex-nowrap items-center gap-2 cursor-default">
|
||||
<Icon className="animate-spin" fa="fa-solid fa-spinner" size={1} />
|
||||
<span className="text-sm">
|
||||
{notaryRequest?.progress
|
||||
? `(${(
|
||||
((notaryRequest.progress + 1) / 6.06) *
|
||||
100
|
||||
).toFixed()}%) ${progressText(notaryRequest.progress)}`
|
||||
: 'Pending...'}
|
||||
</span>
|
||||
</button>
|
||||
<div className="flex flex-col gap-2">
|
||||
{notaryRequest?.progress === RequestProgress.Error && (
|
||||
<div className="flex flex-row items-center gap-2 text-red-600">
|
||||
<Icon fa="fa-solid fa-triangle-exclamation" size={1} />
|
||||
<span className="text-sm">
|
||||
{notaryRequest?.errorMessage ||
|
||||
progressText(notaryRequest.progress)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{notaryRequest?.progress !== RequestProgress.Error && (
|
||||
<button className="button mt-2 w-fit flex flex-row flex-nowrap items-center gap-2 cursor-default">
|
||||
<Icon className="animate-spin" fa="fa-solid fa-spinner" size={1} />
|
||||
<span className="text-sm">
|
||||
{notaryRequest?.progress !== undefined
|
||||
? `(${(((notaryRequest.progress + 1) / 6.06) * 100).toFixed()}%) ${progressText(notaryRequest.progress)}`
|
||||
: 'Pending...'}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
btnContent = (
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
import React, { ReactElement, useState, useCallback } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useNavigate } from 'react-router';
|
||||
import {
|
||||
useHistoryOrder,
|
||||
useRequestHistory,
|
||||
deleteRequestHistory,
|
||||
} from '../../reducers/history';
|
||||
import { useHistoryOrder, useRequestHistory } from '../../reducers/history';
|
||||
import Icon from '../../components/Icon';
|
||||
import NotarizeIcon from '../../assets/img/notarize.png';
|
||||
import { getNotaryApi, getProxyApi } from '../../utils/storage';
|
||||
import { urlify } from '../../utils/misc';
|
||||
import {
|
||||
BackgroundActiontype,
|
||||
progressText,
|
||||
RequestProgress,
|
||||
} from '../../entries/Background/rpc';
|
||||
import Modal, { ModalContent } from '../../components/Modal/Modal';
|
||||
import classNames from 'classnames';
|
||||
import dayjs from 'dayjs';
|
||||
import RequestMenu from './request-menu';
|
||||
|
||||
const charwise = require('charwise');
|
||||
|
||||
export default function History(): ReactElement {
|
||||
@@ -110,12 +107,14 @@ export function OneRequestHistory(props: {
|
||||
size={1}
|
||||
/>
|
||||
<span className="">
|
||||
{request?.progress
|
||||
? `(${(
|
||||
((request.progress + 1) / 6.06) *
|
||||
100
|
||||
).toFixed()}%) ${progressText(request.progress)}`
|
||||
: 'Pending...'}
|
||||
{request?.progress === RequestProgress.Error
|
||||
? `${progressText(request.progress, request.errorMessage)}`
|
||||
: request?.progress
|
||||
? `(${(
|
||||
((request.progress + 1) / 6.06) *
|
||||
100
|
||||
).toFixed()}%) ${progressText(request.progress)}`
|
||||
: 'Pending...'}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -155,7 +154,7 @@ export function OneRequestHistory(props: {
|
||||
onClose={closeAllModal}
|
||||
>
|
||||
<ModalContent className="flex justify-center items-center text-slate-500">
|
||||
{msg || 'Something went wrong :('}
|
||||
{msg || request?.errorMessage}
|
||||
</ModalContent>
|
||||
<button
|
||||
className="m-0 w-24 bg-red-100 text-red-300 hover:bg-red-200 hover:text-red-500"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
BackgroundActiontype,
|
||||
RequestHistory,
|
||||
RequestProgress,
|
||||
} from '../entries/Background/rpc';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppRootState } from './index';
|
||||
@@ -76,6 +77,9 @@ export default function history(
|
||||
if (!payload) return state;
|
||||
|
||||
const existing = state.map[payload.id];
|
||||
if (existing?.progress === RequestProgress.Error) {
|
||||
return state;
|
||||
}
|
||||
const newMap = {
|
||||
...state.map,
|
||||
[payload.id]: payload,
|
||||
@@ -90,13 +94,20 @@ export default function history(
|
||||
}
|
||||
case ActionType['/history/setRequests']: {
|
||||
const payload: RequestHistory[] = action.payload;
|
||||
|
||||
const newMap = payload.reduce(
|
||||
(map: { [id: string]: RequestHistory }, req) => {
|
||||
if (state.map[req.id]?.progress === RequestProgress.Error) {
|
||||
map[req.id] = state.map[req.id];
|
||||
} else {
|
||||
map[req.id] = req;
|
||||
}
|
||||
return map;
|
||||
},
|
||||
{},
|
||||
);
|
||||
return {
|
||||
...state,
|
||||
map: payload.reduce((map: { [id: string]: RequestHistory }, req) => {
|
||||
map[req.id] = req;
|
||||
return map;
|
||||
}, {}),
|
||||
map: newMap,
|
||||
order: payload.map(({ id }) => id),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user