+
+ Notarize request
+
+ {requestUrl?.hostname}
+
-
-
Time:
-
- {new Date(charwise.decode(props.requestId, 'hex')).toISOString()}
-
-
-
-
Host:
-
{requestUrl?.host}
-
-
-
Notary API:
-
{request?.notaryUrl}
-
-
-
TLS Proxy API:
-
- {request?.websocketProxyUrl}
-
+
+ {status === 'success' && 'Success'}
+ {status === 'error' && 'Error'}
+ {status === 'pending' && (
+
+
+
+ {request?.progress
+ ? `(${(
+ ((request.progress + 1) / 6.06) *
+ 100
+ ).toFixed()}%) ${progressText(request.progress)}`
+ : 'Pending...'}
+
+
+ )}
-
- {status === 'success' && (
- <>
-
-
- download(`${request?.id}.json`, JSON.stringify(request?.proof))
- }
- fa="fa-solid fa-download"
- ctaText="Download"
- hidden={hideActions.includes('download')}
- />
- setShowingShareConfirmation(true)}
- fa="fa-solid fa-upload"
- ctaText="Share"
- hidden={hideActions.includes('share')}
- />
- >
- )}
- {status === 'error' && !!request?.error && (
-
- )}
- {(!status || status === 'error') && (
-
- )}
- {status === 'pending' && (
-
- )}
-
+
+
+ {!hideActions.length && (
+ {
+ e.stopPropagation();
+ showMenu(true);
+ }}
+ >
+ {showingMenu && (
+
+ )}
+
+ )}
+
+
+ {day.fromNow()}
+
);
- function RetryButton(p: { hidden?: boolean }): ReactElement {
- if (p.hidden) return <>>;
- return (
-
- );
- }
-
- function ErrorButton(p: { hidden?: boolean }): ReactElement {
- if (p.hidden) return <>>;
- return (
-
- );
- }
-
function ErrorModal(): ReactElement {
const msg = typeof request?.error === 'string' && request?.error;
return !showingError ? (
@@ -253,99 +166,4 @@ export function OneRequestHistory(props: {
);
}
-
- function ShareConfirmationModal(): ReactElement {
- return !showingShareConfirmation ? (
- <>>
- ) : (
-
-
- {!cid[props.requestId] ? (
-
- {uploadError ||
- 'This will make your proof publicly accessible by anyone with the CID'}
-
- ) : (
- e.target.select()}
- />
- )}
-
-
- {!cid[props.requestId] ? (
- <>
- {!uploadError && (
-
- )}
-
- >
- ) : (
- <>
-
-
- >
- )}
-
-
- );
- }
-}
-
-function ActionButton(props: {
- onClick: () => void;
- fa: string;
- ctaText: string;
- className?: string;
- hidden?: boolean;
-}): ReactElement {
- if (props.hidden) return <>>;
-
- return (
-
- );
}
diff --git a/src/pages/History/request-menu.tsx b/src/pages/History/request-menu.tsx
new file mode 100644
index 0000000..82645d4
--- /dev/null
+++ b/src/pages/History/request-menu.tsx
@@ -0,0 +1,256 @@
+import React, {
+ MouseEventHandler,
+ ReactElement,
+ ReactNode,
+ useCallback,
+ useEffect,
+ useState,
+} from 'react';
+import classNames from 'classnames';
+import Icon from '../../components/Icon';
+import {
+ addRequestCid,
+ deleteRequestHistory,
+ useRequestHistory,
+} from '../../reducers/history';
+import { download, upload } from '../../utils/misc';
+import Modal, { ModalContent } from '../../components/Modal/Modal';
+import { EXPLORER_API } from '../../utils/constants';
+import copy from 'copy-to-clipboard';
+import { setNotaryRequestCid } from '../../entries/Background/db';
+import { useDispatch } from 'react-redux';
+import { getNotaryApi, getProxyApi } from '../../utils/storage';
+import { BackgroundActiontype } from '../../entries/Background/rpc';
+
+export default function RequestMenu({
+ requestId,
+ showMenu,
+}: {
+ showMenu: (opened: boolean) => void;
+ requestId: string;
+}): ReactElement {
+ const dispatch = useDispatch();
+ const request = useRequestHistory(requestId);
+ const [showingShareConfirmation, setShowingShareConfirmation] =
+ useState(false);
+
+ const onRetry = useCallback(async () => {
+ const notaryUrl = await getNotaryApi();
+ const websocketProxyUrl = await getProxyApi();
+ chrome.runtime.sendMessage
({
+ type: BackgroundActiontype.retry_prove_request,
+ data: {
+ id: requestId,
+ notaryUrl,
+ websocketProxyUrl,
+ },
+ });
+ }, [requestId]);
+
+ const onDelete = useCallback(async () => {
+ dispatch(deleteRequestHistory(requestId));
+ }, [requestId]);
+
+ if (!request) return <>>;
+
+ const { status } = request;
+
+ return (
+ <>
+ {showingShareConfirmation && (
+
+ )}
+ {
+ e.stopPropagation();
+ showMenu(false);
+ }}
+ />
+
+
+ {status === 'success' && (
+ <>
+ {
+ e.stopPropagation();
+ showMenu(false);
+ download(`${request.id}.json`, JSON.stringify(request.proof));
+ }}
+ >
+ Download
+
+ {
+ e.stopPropagation();
+ setShowingShareConfirmation(true);
+ }}
+ >
+ Share
+
+ >
+ )}
+ {status === 'error' && (
+ {
+ e.stopPropagation();
+ onRetry();
+ showMenu(false);
+ }}
+ >
+ Retry
+
+ )}
+ {
+ e.stopPropagation();
+ onDelete();
+ showMenu(false);
+ }}
+ >
+ Delete
+
+
+
+ >
+ );
+}
+
+function RequestMenuRow(props: {
+ fa: string;
+ children?: ReactNode;
+ onClick?: MouseEventHandler;
+ className?: string;
+}): ReactElement {
+ return (
+
+
+ {props.children}
+
+ );
+}
+
+function ShareConfirmationModal({
+ setShowingShareConfirmation,
+ requestId,
+ showMenu,
+}: {
+ showMenu: (opened: boolean) => void;
+ setShowingShareConfirmation: (showing: boolean) => void;
+ requestId: string;
+}): ReactElement {
+ const dispatch = useDispatch();
+ const request = useRequestHistory(requestId);
+ const [uploadError, setUploadError] = useState('');
+ const [uploading, setUploading] = useState(false);
+
+ const handleUpload = useCallback(async () => {
+ setUploading(true);
+ try {
+ const data = await upload(
+ `${request?.id}.json`,
+ JSON.stringify(request?.proof),
+ );
+ await setNotaryRequestCid(requestId, data);
+ dispatch(addRequestCid(requestId, data));
+ } catch (e: any) {
+ setUploadError(e.message);
+ } finally {
+ setUploading(false);
+ }
+ }, [requestId, request, request?.cid]);
+
+ const onClose = useCallback(() => {
+ setShowingShareConfirmation(false);
+ showMenu(false);
+ }, [showMenu]);
+
+ return !request ? (
+ <>>
+ ) : (
+
{
+ e.stopPropagation();
+ onClose();
+ }}
+ >
+
+ {!request.cid ? (
+
+ {uploadError ||
+ 'This will make your proof publicly accessible by anyone with the CID'}
+
+ ) : (
+ e.target.select()}
+ />
+ )}
+
+
+ {!request.cid ? (
+ <>
+ {!uploadError && (
+
+ )}
+
+ >
+ ) : (
+ <>
+
+
+ >
+ )}
+
+
+ );
+}
diff --git a/src/pages/Home/index.tsx b/src/pages/Home/index.tsx
index 6db11c2..64aaf5d 100644
--- a/src/pages/Home/index.tsx
+++ b/src/pages/Home/index.tsx
@@ -77,9 +77,13 @@ export default function Home(props: {
scrollTop={scrollTop}
/>
setTab('network')}
diff --git a/src/pages/ProofViewer/index.tsx b/src/pages/ProofViewer/index.tsx
index 6265578..95fde2b 100644
--- a/src/pages/ProofViewer/index.tsx
+++ b/src/pages/ProofViewer/index.tsx
@@ -3,24 +3,37 @@ import React, {
ReactElement,
useState,
MouseEventHandler,
+ useCallback,
} from 'react';
import { useParams, useNavigate } from 'react-router';
import c from 'classnames';
-import { useRequestHistory } from '../../reducers/history';
+import {
+ deleteRequestHistory,
+ useRequestHistory,
+} from '../../reducers/history';
import Icon from '../../components/Icon';
import { download } from '../../utils/misc';
import classNames from 'classnames';
+import { useDispatch } from 'react-redux';
export default function ProofViewer(props?: {
className?: string;
recv?: string;
sent?: string;
}): ReactElement {
+ const dispatch = useDispatch();
const { requestId } = useParams<{ requestId: string }>();
const request = useRequestHistory(requestId);
const navigate = useNavigate();
const [tab, setTab] = useState('sent');
+ const onDelete = useCallback(async () => {
+ if (requestId) {
+ dispatch(deleteRequestHistory(requestId));
+ navigate(-1);
+ }
+ }, [requestId]);
+
return (
setTab('recv')} active={tab === 'recv'}>
Recv
-
+
{!props?.recv && (
diff --git a/src/reducers/history.tsx b/src/reducers/history.tsx
index 9001ea9..e1bb1dc 100644
--- a/src/reducers/history.tsx
+++ b/src/reducers/history.tsx
@@ -10,6 +10,7 @@ enum ActionType {
'/history/addRequest' = '/history/addRequest',
'/history/setRequests' = '/history/setRequests',
'/history/deleteRequest' = '/history/deleteRequest',
+ '/history/addRequestCid' = '/history/addRequestCid',
}
type Action = {
@@ -45,6 +46,13 @@ export const setRequests = (requests: RequestHistory[]) => {
};
};
+export const addRequestCid = (requestId: string, cid: string) => {
+ return {
+ type: ActionType['/history/addRequestCid'],
+ payload: { requestId, cid },
+ };
+};
+
export const deleteRequestHistory = (id: string) => {
chrome.runtime.sendMessage({
type: BackgroundActiontype.delete_prove_request,
@@ -103,6 +111,20 @@ export default function history(
order: newOrder,
};
}
+ case ActionType['/history/addRequestCid']: {
+ const { requestId, cid } = action.payload;
+ if (!state.map[requestId]) return state;
+ return {
+ ...state,
+ map: {
+ ...state.map,
+ [requestId]: {
+ ...state.map[requestId],
+ cid,
+ },
+ },
+ };
+ }
default:
return state;
}