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:
tsukino
2024-03-26 07:16:41 -04:00
committed by GitHub
parent 76c6acd998
commit a42bb2eabd
8 changed files with 5276 additions and 5082 deletions

27
package-lock.json generated
View File

@@ -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",

View File

@@ -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
View File

@@ -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'}

View File

@@ -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,
)}
>

View File

@@ -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
View File

@@ -0,0 +1 @@
export const EXPLORER_API = 'http://localhost:3000';

View File

@@ -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,
});

10036
yarn.lock

File diff suppressed because it is too large Load Diff