mirror of
https://github.com/tlsnotary/tlsn-extension.git
synced 2026-01-09 15:18:09 -05:00
fix: clean up styling and code for sharing a proof (#51)
* fix: clean up styling and code for sharing a proof * chore: update lockfiles * fix: use constant for explorer api
This commit is contained in:
27
package-lock.json
generated
27
package-lock.json
generated
@@ -15,6 +15,7 @@
|
||||
"charwise": "^3.0.1",
|
||||
"classnames": "^2.3.2",
|
||||
"comlink": "^4.4.1",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fuse.js": "^6.6.2",
|
||||
"level": "^8.0.0",
|
||||
@@ -5707,6 +5708,14 @@
|
||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/copy-to-clipboard": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
|
||||
"integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==",
|
||||
"dependencies": {
|
||||
"toggle-selection": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/copy-webpack-plugin": {
|
||||
"version": "11.0.0",
|
||||
"resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz",
|
||||
@@ -12990,6 +12999,11 @@
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/toggle-selection": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
|
||||
"integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ=="
|
||||
},
|
||||
"node_modules/toidentifier": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||
@@ -17966,6 +17980,14 @@
|
||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
|
||||
"dev": true
|
||||
},
|
||||
"copy-to-clipboard": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
|
||||
"integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==",
|
||||
"requires": {
|
||||
"toggle-selection": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"copy-webpack-plugin": {
|
||||
"version": "11.0.0",
|
||||
"resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz",
|
||||
@@ -22914,6 +22936,11 @@
|
||||
"is-number": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"toggle-selection": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
|
||||
"integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ=="
|
||||
},
|
||||
"toidentifier": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"charwise": "^3.0.1",
|
||||
"classnames": "^2.3.2",
|
||||
"comlink": "^4.4.1",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fuse.js": "^6.6.2",
|
||||
"level": "^8.0.0",
|
||||
|
||||
13
pnpm-lock.yaml
generated
13
pnpm-lock.yaml
generated
@@ -23,6 +23,9 @@ dependencies:
|
||||
comlink:
|
||||
specifier: ^4.4.1
|
||||
version: 4.4.1
|
||||
copy-to-clipboard:
|
||||
specifier: ^3.3.3
|
||||
version: 3.3.3
|
||||
fast-deep-equal:
|
||||
specifier: ^3.1.3
|
||||
version: 3.1.3
|
||||
@@ -3666,6 +3669,12 @@ packages:
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: true
|
||||
|
||||
/copy-to-clipboard@3.3.3:
|
||||
resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==}
|
||||
dependencies:
|
||||
toggle-selection: 1.0.6
|
||||
dev: false
|
||||
|
||||
/copy-webpack-plugin@11.0.0(webpack@5.91.0):
|
||||
resolution: {integrity: sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==}
|
||||
engines: {node: '>= 14.15.0'}
|
||||
@@ -7987,6 +7996,10 @@ packages:
|
||||
dependencies:
|
||||
is-number: 7.0.0
|
||||
|
||||
/toggle-selection@1.0.6:
|
||||
resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==}
|
||||
dev: false
|
||||
|
||||
/toidentifier@1.0.1:
|
||||
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
|
||||
engines: {node: '>=0.6'}
|
||||
|
||||
@@ -84,7 +84,7 @@ export function ModalFooter(props: FooterProps): ReactElement {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'border-t modal__footer border-gray-100',
|
||||
'border-t modal__footer border-gray-100 w-full',
|
||||
props.className,
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -10,11 +10,10 @@ import Icon from '../../components/Icon';
|
||||
import { get, NOTARY_API_LS_KEY, PROXY_API_LS_KEY } from '../../utils/storage';
|
||||
import { urlify, download, upload } from '../../utils/misc';
|
||||
import { BackgroundActiontype } from '../../entries/Background/rpc';
|
||||
import Modal, {
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
} from '../../components/Modal/Modal';
|
||||
import Modal, { ModalContent } from '../../components/Modal/Modal';
|
||||
import classNames from 'classnames';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import { EXPLORER_API } from '../../utils/constants';
|
||||
|
||||
export default function History(): ReactElement {
|
||||
const history = useHistoryOrder();
|
||||
@@ -32,9 +31,11 @@ function OneRequestHistory(props: { requestId: string }): ReactElement {
|
||||
const dispatch = useDispatch();
|
||||
const request = useRequestHistory(props.requestId);
|
||||
const [showingError, showError] = useState(false);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [accepted, setAccepted] = useState(false);
|
||||
const [uploadError, setUploadError] = useState('');
|
||||
const [showingShareConfirmation, setShowingShareConfirmation] =
|
||||
useState(false);
|
||||
const [cid, setCid] = useState('');
|
||||
const [uploading, setUploading] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
const { status } = request || {};
|
||||
const requestUrl = urlify(request?.url || '');
|
||||
@@ -68,60 +69,30 @@ function OneRequestHistory(props: { requestId: string }): ReactElement {
|
||||
showError(true);
|
||||
}, [request?.error, showError]);
|
||||
|
||||
const closeModal = useCallback(() => setIsOpen(false), []);
|
||||
const openModal = useCallback(() => setIsOpen(true), []);
|
||||
const closeAllModal = useCallback(() => {
|
||||
setShowingShareConfirmation(false);
|
||||
showError(false);
|
||||
}, [setShowingShareConfirmation, showError]);
|
||||
|
||||
const handleAccept = useCallback(async () => {
|
||||
const handleUpload = useCallback(async () => {
|
||||
setUploading(true);
|
||||
try {
|
||||
const data = await upload(
|
||||
`${request?.id}.json`,
|
||||
JSON.stringify(request?.proof),
|
||||
);
|
||||
setCid(data);
|
||||
setAccepted(true);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} catch (e: any) {
|
||||
setUploadError(e.message);
|
||||
} finally {
|
||||
setUploading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const RetryButton = (): ReactElement => (
|
||||
<button
|
||||
className="flex flex-row flex-grow-0 gap-2 self-end items-center justify-end px-2 py-1 bg-slate-100 text-slate-300 hover:bg-slate-200 hover:text-slate-500 hover:font-bold"
|
||||
onClick={onRetry}
|
||||
>
|
||||
<Icon fa="fa-solid fa-arrows-rotate" size={1} />
|
||||
<span className="text-xs font-bold">Retry</span>
|
||||
</button>
|
||||
);
|
||||
|
||||
const ErrorButton = (): ReactElement => (
|
||||
<button
|
||||
className="flex flex-row flex-grow-0 gap-2 self-end items-center justify-end px-2 py-1 bg-red-100 text-red-300 hover:bg-red-200 hover:text-red-500 hover:font-bold"
|
||||
onClick={onShowError}
|
||||
>
|
||||
<Icon fa="fa-solid fa-circle-exclamation" size={1} />
|
||||
<span className="text-xs font-bold">Error</span>
|
||||
</button>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-row flex-nowrap border rounded-md p-2 gap-1 hover:bg-slate-50 cursor-pointer">
|
||||
{showingError && (
|
||||
<Modal
|
||||
className="flex flex-col gap-4 items-center text-base cursor-default justify-center !w-auto mx-4 my-[50%] min-h-24 p-4 border border-red-500"
|
||||
onClose={closeModal}
|
||||
>
|
||||
<ModalContent className="flex justify-center items-center text-slate-500">
|
||||
{request?.error || 'Something went wrong :('}
|
||||
</ModalContent>
|
||||
<button
|
||||
className="m-0 w-24 bg-red-100 text-red-300 hover:bg-red-200 hover:text-red-500"
|
||||
onClick={closeModal}
|
||||
>
|
||||
OK
|
||||
</button>
|
||||
</Modal>
|
||||
)}
|
||||
<ShareConfirmationModal />
|
||||
<ErrorModal />
|
||||
<div className="flex flex-col flex-nowrap flex-grow flex-shrink w-0">
|
||||
<div className="flex flex-row items-center text-xs">
|
||||
<div className="bg-slate-200 text-slate-400 px-1 py-0.5 rounded-sm">
|
||||
@@ -149,67 +120,26 @@ function OneRequestHistory(props: { requestId: string }): ReactElement {
|
||||
<div className="flex flex-col gap-1">
|
||||
{status === 'success' && (
|
||||
<>
|
||||
<button
|
||||
className="flex flex-row flex-grow-0 gap-2 self-end items-center justify-end px-2 py-1 bg-slate-600 text-slate-200 hover:bg-slate-500 hover:text-slate-100 hover:font-bold"
|
||||
<ActionButton
|
||||
className="bg-slate-600 text-slate-200 hover:bg-slate-500 hover:text-slate-100"
|
||||
onClick={onView}
|
||||
>
|
||||
<Icon className="" fa="fa-solid fa-receipt" size={1} />
|
||||
<span className="text-xs font-bold">View Proof</span>
|
||||
</button>
|
||||
<button
|
||||
className="flex flex-row flex-grow-0 gap-2 self-end items-center justify-end px-2 py-1 bg-slate-100 text-slate-300 hover:bg-slate-200 hover:text-slate-500 hover:font-bold"
|
||||
fa="fa-solid fa-receipt"
|
||||
ctaText="View Proof"
|
||||
/>
|
||||
<ActionButton
|
||||
className="bg-slate-100 text-slate-300 hover:bg-slate-200 hover:text-slate-500"
|
||||
onClick={() =>
|
||||
download(`${request?.id}.json`, JSON.stringify(request?.proof))
|
||||
}
|
||||
>
|
||||
<Icon className="" fa="fa-solid fa-download" size={1} />
|
||||
<span className="text-xs font-bold">Download</span>
|
||||
</button>
|
||||
<button
|
||||
fa="fa-solid fa-download"
|
||||
ctaText="Download"
|
||||
/>
|
||||
<ActionButton
|
||||
className="flex flex-row flex-grow-0 gap-2 self-end items-center justify-end px-2 py-1 bg-slate-100 text-slate-300 hover:bg-slate-200 hover:text-slate-500 hover:font-bold"
|
||||
onClick={openModal}
|
||||
>
|
||||
<Icon className="" fa="fa-solid fa-upload" size={1} />
|
||||
<span>Share</span>
|
||||
</button>
|
||||
{isOpen && (
|
||||
<Modal
|
||||
className="flex flex-col gap-4 items-center text-base cursor-default justify-center !w-auto mx-4 my-[50%] min-h-24 p-4 border"
|
||||
onClose={closeModal}
|
||||
>
|
||||
<ModalHeader onClose={closeModal}>Share Proof</ModalHeader>
|
||||
<ModalContent className="flex flex-col gap-4 items-center text-base justify-center">
|
||||
<p className="text-red-500 font-bold text-center">
|
||||
This will make your proof publicly accessible by anyone with
|
||||
the CID
|
||||
</p>
|
||||
{!accepted ? (
|
||||
<button
|
||||
onClick={handleAccept}
|
||||
className="m-0 w-32 bg-red-200 text-red-500 hover:bg-red-200 hover:text-red-500 hover:font-bold"
|
||||
>
|
||||
I understand
|
||||
</button>
|
||||
) : (
|
||||
<div className="w-full border border-solid border-gray-300 rounded-lg p-2">
|
||||
<input
|
||||
className="w-full bg-slate-100 px-2 py-1 rounded-md focus:outline-none focus:ring focus:border-blue-300 text-slate-500 font-bold"
|
||||
readOnly
|
||||
value={`http://localhost:3030/ipfs/${cid}`}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<button
|
||||
className="m-0 w-24 bg-slate-600 text-slate-200 hover:bg-slate-500 hover:text-slate-100 hover:font-bold"
|
||||
onClick={closeModal}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
)}
|
||||
onClick={() => setShowingShareConfirmation(true)}
|
||||
fa="fa-solid fa-upload"
|
||||
ctaText="Share"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{status === 'error' && !!request?.error && <ErrorButton />}
|
||||
@@ -230,4 +160,139 @@ function OneRequestHistory(props: { requestId: string }): ReactElement {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
function RetryButton(): ReactElement {
|
||||
return (
|
||||
<button
|
||||
className="flex flex-row flex-grow-0 gap-2 self-end items-center justify-end px-2 py-1 bg-slate-100 text-slate-300 hover:bg-slate-200 hover:text-slate-500 hover:font-bold"
|
||||
onClick={onRetry}
|
||||
>
|
||||
<Icon fa="fa-solid fa-arrows-rotate" size={1} />
|
||||
<span className="text-xs font-bold">Retry</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
function ErrorButton(): ReactElement {
|
||||
return (
|
||||
<button
|
||||
className="flex flex-row flex-grow-0 gap-2 self-end items-center justify-end px-2 py-1 bg-red-100 text-red-300 hover:bg-red-200 hover:text-red-500 hover:font-bold"
|
||||
onClick={onShowError}
|
||||
>
|
||||
<Icon fa="fa-solid fa-circle-exclamation" size={1} />
|
||||
<span className="text-xs font-bold">Error</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
function ErrorModal(): ReactElement {
|
||||
return !showingError ? (
|
||||
<></>
|
||||
) : (
|
||||
<Modal
|
||||
className="flex flex-col gap-4 items-center text-base cursor-default justify-center !w-auto mx-4 my-[50%] min-h-24 p-4 border border-red-500"
|
||||
onClose={closeAllModal}
|
||||
>
|
||||
<ModalContent className="flex justify-center items-center text-slate-500">
|
||||
{request?.error || 'Something went wrong :('}
|
||||
</ModalContent>
|
||||
<button
|
||||
className="m-0 w-24 bg-red-100 text-red-300 hover:bg-red-200 hover:text-red-500"
|
||||
onClick={closeAllModal}
|
||||
>
|
||||
OK
|
||||
</button>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
function ShareConfirmationModal(): ReactElement {
|
||||
return !showingShareConfirmation ? (
|
||||
<></>
|
||||
) : (
|
||||
<Modal
|
||||
className="flex flex-col items-center text-base cursor-default justify-center !w-auto mx-4 my-[50%] p-4 gap-4"
|
||||
onClose={closeAllModal}
|
||||
>
|
||||
<ModalContent className="flex flex-col w-full gap-4 items-center text-base justify-center">
|
||||
{!cid ? (
|
||||
<p className="text-slate-500 text-center">
|
||||
{uploadError ||
|
||||
'This will make your proof publicly accessible by anyone with the CID'}
|
||||
</p>
|
||||
) : (
|
||||
<input
|
||||
className="input w-full bg-slate-100 border border-slate-200"
|
||||
readOnly
|
||||
value={`${EXPLORER_API}/ipfs/${cid}`}
|
||||
onFocus={(e) => e.target.select()}
|
||||
/>
|
||||
)}
|
||||
</ModalContent>
|
||||
<div className="flex flex-row gap-2 justify-center">
|
||||
{!cid ? (
|
||||
<>
|
||||
{!uploadError && (
|
||||
<button
|
||||
onClick={handleUpload}
|
||||
className="button button--primary flex flex-row items-center justify-center gap-2 m-0"
|
||||
disabled={uploading}
|
||||
>
|
||||
{uploading && (
|
||||
<Icon
|
||||
className="animate-spin"
|
||||
fa="fa-solid fa-spinner"
|
||||
size={1}
|
||||
/>
|
||||
)}
|
||||
I understand
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
className="m-0 w-24 bg-slate-100 text-slate-400 hover:bg-slate-200 hover:text-slate-600 font-bold"
|
||||
onClick={closeAllModal}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<button
|
||||
onClick={() => copy(`${EXPLORER_API}/ipfs/${cid}`)}
|
||||
className="m-0 w-24 bg-slate-600 text-slate-200 hover:bg-slate-500 hover:text-slate-100 font-bold"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
<button
|
||||
className="m-0 w-24 bg-slate-100 text-slate-400 hover:bg-slate-200 hover:text-slate-600 font-bold"
|
||||
onClick={closeAllModal}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function ActionButton(props: {
|
||||
onClick: () => void;
|
||||
fa: string;
|
||||
ctaText: string;
|
||||
className?: string;
|
||||
}): ReactElement {
|
||||
return (
|
||||
<button
|
||||
className={classNames(
|
||||
'flex flex-row flex-grow-0 gap-2 self-end items-center justify-end px-2 py-1 hover:font-bold',
|
||||
props.className,
|
||||
)}
|
||||
onClick={props.onClick}
|
||||
>
|
||||
<Icon className="" fa={props.fa} size={1} />
|
||||
<span className="text-xs font-bold">{props.ctaText}</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
1
src/utils/constants.ts
Normal file
1
src/utils/constants.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const EXPLORER_API = 'http://localhost:3000';
|
||||
@@ -1,4 +1,5 @@
|
||||
import { RequestLog } from '../entries/Background/rpc';
|
||||
import { EXPLORER_API } from './constants';
|
||||
|
||||
export function urlify(
|
||||
text: string,
|
||||
@@ -49,7 +50,7 @@ export async function upload(filename: string, content: string) {
|
||||
new Blob([content], { type: 'application/json' }),
|
||||
filename,
|
||||
);
|
||||
const response = await fetch('http://localhost:3030/upload', {
|
||||
const response = await fetch(`${EXPLORER_API}/api/upload`, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user