chore: De-duplicate images, components, and most utils with widgets lib (#143)

Remove more dead and duplicated code, favoring widgets lib versions
main
J M Rossy 12 hours ago committed by GitHub
parent 1fa67b5639
commit 4103c31cdb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 81
      src/components/animations/Spinner.module.css
  2. 25
      src/components/animations/Spinner.tsx
  3. 42
      src/components/buttons/CopyButton.tsx
  4. 56
      src/components/buttons/SwitchButton.module.css
  5. 21
      src/components/buttons/SwitchButton.tsx
  6. 57
      src/components/errors/ErrorBoundary.tsx
  7. 8
      src/components/layout/HrDivider.tsx
  8. 10
      src/components/search/SearchBar.tsx
  9. 7
      src/components/search/SearchStates.tsx
  10. 2
      src/consts/values.ts
  11. 8
      src/features/messages/MessageDetails.tsx
  12. 3
      src/features/messages/MessageSearch.tsx
  13. 4
      src/features/messages/cards/CodeBlock.tsx
  14. 11
      src/features/messages/cards/GasDetailsCard.tsx
  15. 5
      src/features/messages/cards/KeyValueRow.tsx
  16. 11
      src/features/messages/cards/TransactionCard.tsx
  17. 2
      src/features/messages/queries/useMessageQuery.ts
  18. 3
      src/images/icons/chevron-compact-left.svg
  19. 3
      src/images/icons/chevron-compact-right.svg
  20. 3
      src/images/icons/chevron-down.svg
  21. 5
      src/images/icons/clipboard-plus.svg
  22. 4
      src/images/icons/copy-stack.svg
  23. 1
      src/images/icons/cube.svg
  24. 6
      src/images/icons/database.svg
  25. 1
      src/images/icons/gear.svg
  26. 1
      src/images/icons/question-circle.svg
  27. 1
      src/images/icons/question-mark.svg
  28. 3
      src/images/icons/wallet.svg
  29. 4
      src/images/icons/x-circle.svg
  30. 1
      src/images/icons/x.svg
  31. 1
      src/images/logos/discord-alt.svg
  32. 4
      src/images/logos/discord.svg
  33. 3
      src/images/logos/github.svg
  34. 1
      src/images/logos/hyperlane-chevron-single-blue.svg
  35. 1
      src/images/logos/hyperlane-chevron.svg
  36. 1
      src/images/logos/medium.svg
  37. 1
      src/images/logos/metamask.svg
  38. 3
      src/images/logos/twitter.svg
  39. 1
      src/images/logos/wallet-connect.svg
  40. 2
      src/pages/_app.tsx
  41. 9
      src/utils/big-number.ts
  42. 25
      src/utils/clipboard.ts
  43. 18
      src/utils/debounce.ts
  44. 7
      src/utils/explorers.ts
  45. 10
      src/utils/ssr.ts
  46. 41
      src/utils/timeout.ts
  47. 9
      src/utils/url.ts
  48. 26
      src/utils/useInterval.ts

@ -1,81 +0,0 @@
.spinner {
color: official;
display: inline-block;
position: relative;
width: 80px;
height: 80px;
}
.spinner div {
transform-origin: 40px 40px;
animation: spinner 1.2s linear infinite;
}
.spinner div:after {
content: ' ';
display: block;
position: absolute;
top: 3px;
left: 37px;
width: 6px;
height: 18px;
border-radius: 20%;
background: #010101;
}
.spinner.white div:after {
background: #ffffff;
}
.spinner div:nth-child(1) {
transform: rotate(0deg);
animation-delay: -1.1s;
}
.spinner div:nth-child(2) {
transform: rotate(30deg);
animation-delay: -1s;
}
.spinner div:nth-child(3) {
transform: rotate(60deg);
animation-delay: -0.9s;
}
.spinner div:nth-child(4) {
transform: rotate(90deg);
animation-delay: -0.8s;
}
.spinner div:nth-child(5) {
transform: rotate(120deg);
animation-delay: -0.7s;
}
.spinner div:nth-child(6) {
transform: rotate(150deg);
animation-delay: -0.6s;
}
.spinner div:nth-child(7) {
transform: rotate(180deg);
animation-delay: -0.5s;
}
.spinner div:nth-child(8) {
transform: rotate(210deg);
animation-delay: -0.4s;
}
.spinner div:nth-child(9) {
transform: rotate(240deg);
animation-delay: -0.3s;
}
.spinner div:nth-child(10) {
transform: rotate(270deg);
animation-delay: -0.2s;
}
.spinner div:nth-child(11) {
transform: rotate(300deg);
animation-delay: -0.1s;
}
.spinner div:nth-child(12) {
transform: rotate(330deg);
animation-delay: 0s;
}
@keyframes spinner {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}

@ -1,25 +0,0 @@
import { memo } from 'react';
import styles from './Spinner.module.css';
// From https://loading.io/css/
function _Spinner({ white, classes }: { white?: boolean; classes?: string }) {
return (
<div className={`${styles.spinner} ${white && styles.white} ${classes || ''}`}>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
);
}
export const Spinner = memo(_Spinner);

@ -1,42 +0,0 @@
import Image from 'next/image';
import { useState } from 'react';
import CheckmarkIcon from '../../images/icons/checkmark.svg';
import CopyIcon from '../../images/icons/copy-stack.svg';
import { tryClipboardSet } from '../../utils/clipboard';
interface Props {
width: number;
height: number;
copyValue: string;
classes: string;
}
export function CopyButton({ width, height, copyValue, classes }: Props) {
const [showCheckmark, setShowCheckmark] = useState(false);
const onClick = async () => {
const result = await tryClipboardSet(copyValue);
if (result) {
setShowCheckmark(true);
setTimeout(() => setShowCheckmark(false), 2000);
}
};
return (
<button
onClick={onClick}
type="button"
title="Copy"
className={`flex items-center justify-center transition-all ${
showCheckmark ? 'opacity-100' : 'opacity-50'
} hover:opacity-70 active:opacity-90 ${classes}`}
>
{showCheckmark ? (
<Image src={CheckmarkIcon} width={width} height={height} alt="" />
) : (
<Image src={CopyIcon} width={width} height={height} alt="" />
)}
</button>
);
}

@ -1,56 +0,0 @@
/* The switch - the box around the slider */
.switch {
position: relative;
display: inline-block;
width: 32px;
height: 18px;
}
/* Hide default HTML checkbox */
.switch input {
opacity: 0;
width: 0;
height: 0;
}
/* The slider */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: 0.4s;
transition: 0.4s;
}
.slider:before {
position: absolute;
content: '';
height: 14px;
width: 14px;
left: 2px;
bottom: 2px;
background-color: white;
-webkit-transition: 0.4s;
transition: 0.4s;
}
input:checked + .slider {
background-color: #45cd85;
}
input:checked + .slider:before {
transform: translateX(13px);
}
/* Rounded sliders */
.slider.round {
border-radius: 24px;
}
.slider.round:before {
border-radius: 50%;
}

@ -1,21 +0,0 @@
import { ChangeEvent } from 'react';
import styles from 'src/components/buttons/SwitchButton.module.css';
interface Props {
checked: boolean;
onChange: (checked: boolean) => void;
}
export function SwitchButton({ checked, onChange }: Props) {
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
const target = event.target;
onChange(target.checked);
};
return (
<label className={styles.switch}>
<input type="checkbox" checked={checked} onChange={handleChange} />
<span className={`${styles.slider} ${styles.round}`}></span>
</label>
);
}

@ -1,52 +1,15 @@
import Image from 'next/image';
import { Component } from 'react';
import { ErrorBoundary as ErrorBoundaryInner } from '@hyperlane-xyz/widgets';
import { PropsWithChildren } from 'react';
import { links } from '../../consts/links';
import ErrorIcon from '../../images/icons/error-circle.svg';
import { logger } from '../../utils/logger';
interface ErrorBoundaryState {
error: any;
errorInfo: any;
export function ErrorBoundary({ children }: PropsWithChildren<unknown>) {
return <ErrorBoundaryInner supportLink={<SupportLink />}>{children}</ErrorBoundaryInner>;
}
export class ErrorBoundary extends Component<any, ErrorBoundaryState> {
constructor(props: any) {
super(props);
this.state = { error: null, errorInfo: null };
}
componentDidCatch(error: any, errorInfo: any) {
this.setState({
error,
errorInfo,
});
logger.error('Error caught by error boundary', error, errorInfo);
}
render() {
const errorInfo = this.state.error || this.state.errorInfo;
if (errorInfo) {
const details = errorInfo.message || JSON.stringify(errorInfo);
return (
<div className="flex h-screen w-screen items-center justify-center bg-gray-50">
<div className="flex flex-col items-center">
<Image src={ErrorIcon} width={80} height={80} alt="" />
<h1 className="mt-5 text-lg">Fatal Error Occurred</h1>
<div className="mt-5 text-sm">{details}</div>
<a
href={links.discord}
target="_blank"
rel="noopener noreferrer"
className="mt-5 text-sm"
>
For support, join the{' '}
<span className="underline underline-offset-2">Hyperlane Discord</span>{' '}
</a>
</div>
</div>
);
}
return this.props.children;
}
function SupportLink() {
return (
<a href={links.discord} target="_blank" rel="noopener noreferrer" className="mt-5 text-sm">
For support, join the <span className="underline underline-offset-2">Hyperlane Discord</span>{' '}
</a>
);
}

@ -1,8 +0,0 @@
interface Props {
classes?: string;
}
export function HrDivider(props: Props) {
const { classes } = props;
return <hr className={`h-px w-full border-none bg-gray-300 ${classes}`} />;
}

@ -1,10 +1,10 @@
import Image from 'next/image';
import { ChangeEvent } from 'react';
import { IconButton, XIcon } from '@hyperlane-xyz/widgets';
import { IconButton, SpinnerIcon, XIcon } from '@hyperlane-xyz/widgets';
import SearchIcon from '../../images/icons/search.svg';
import { Spinner } from '../animations/Spinner';
import { Color } from '../../styles/Color';
interface Props {
value: string;
@ -29,11 +29,7 @@ export function SearchBar({ value, placeholder, onChangeValue, isFetching }: Pro
className="h-10 flex-1 rounded-full p-1 font-light placeholder:text-gray-600 focus:outline-none sm:h-12 sm:px-4 md:px-5"
/>
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-pink-500 sm:h-12 sm:w-12">
{isFetching && (
<div className="scale-[30%] sm:scale-[35%]">
<Spinner classes="invert" />
</div>
)}
{isFetching && <SpinnerIcon color={Color.white} width={26} height={26} />}
{!isFetching && !value && <Image src={SearchIcon} width={20} height={20} alt="" />}
{!isFetching && value && (
<IconButton title="Clear search" onClick={() => onChange(null)}>

@ -1,12 +1,11 @@
import Image from 'next/image';
import { Fade } from '@hyperlane-xyz/widgets';
import { Fade, SpinnerIcon } from '@hyperlane-xyz/widgets';
import BugIcon from '../../images/icons/bug.svg';
import ErrorIcon from '../../images/icons/error-circle.svg';
import SearchOffIcon from '../../images/icons/search-off.svg';
import ShrugIcon from '../../images/icons/shrug.svg';
import { Spinner } from '../animations/Spinner';
export function SearchFetching({ show, isPiFetching }: { show: boolean; isPiFetching?: boolean }) {
return (
@ -15,8 +14,8 @@ export function SearchFetching({ show, isPiFetching }: { show: boolean; isPiFetc
<Fade show={show}>
<div className="my-10 flex justify-center">
<div className="flex max-w-md flex-col items-center justify-center px-3 py-5">
<div className="flex scale-90 items-center justify-center">
<Spinner />
<div className="flex items-center justify-center">
<SpinnerIcon width={40} height={40} />
</div>
<div className="mt-4 text-center font-light leading-loose text-gray-700">
{isPiFetching ? 'Searching override chains for messages' : 'Searching for messages'}

@ -1,4 +1,2 @@
export const MIN_ROUNDED_VALUE = 0.00001;
export const DISPLAY_DECIMALS = 5;
export const DELIVERY_LOG_CHECK_BLOCK_RANGE = 1_000;
export const PI_MESSAGE_LOG_CHECK_BLOCK_RANGE = 5_000;

@ -4,7 +4,6 @@ import { toast } from 'react-toastify';
import { toTitleCase, trimToLength } from '@hyperlane-xyz/utils';
import { Spinner } from '../../components/animations/Spinner';
import { Card } from '../../components/layout/Card';
import CheckmarkIcon from '../../images/icons/checkmark-circle.svg';
import { useMultiProvider, useStore } from '../../store';
@ -14,6 +13,7 @@ import { getHumanReadableDuration } from '../../utils/time';
import { getChainDisplayName, isEvmChain } from '../chains/utils';
import { useMessageDeliveryStatus } from '../deliveryStatus/useMessageDeliveryStatus';
import { SpinnerIcon } from '@hyperlane-xyz/widgets';
import { ContentDetailsCard } from './cards/ContentDetailsCard';
import { GasDetailsCard } from './cards/GasDetailsCard';
import { IcaDetailsCard } from './cards/IcaDetailsCard';
@ -164,10 +164,8 @@ function StatusHeader({
let icon: React.ReactNode;
if (isFetching) {
icon = (
<div className="flex h-7 w-7 items-center justify-center overflow-hidden">
<div className="scale-[35%]">
<Spinner />
</div>
<div className="flex items-center justify-center">
<SpinnerIcon width={20} height={20} />
</div>
);
} else if (isMessageFound && messageStatus === MessageStatus.Delivered) {

@ -1,6 +1,6 @@
import { useState } from 'react';
import { Fade } from '@hyperlane-xyz/widgets';
import { Fade, useDebounce } from '@hyperlane-xyz/widgets';
import { Card } from '../../components/layout/Card';
import { SearchBar } from '../../components/search/SearchBar';
@ -12,7 +12,6 @@ import {
SearchUnknownError,
} from '../../components/search/SearchStates';
import { useReadyMultiProvider } from '../../store';
import useDebounce from '../../utils/debounce';
import { useQueryParam, useSyncQueryParam } from '../../utils/queryParams';
import { sanitizeString } from '../../utils/string';

@ -1,4 +1,4 @@
import { CopyButton } from '../../../components/buttons/CopyButton';
import { CopyButton } from '@hyperlane-xyz/widgets';
export function LabelAndCodeBlock({ label, value }: { label: string; value: string }) {
return (
@ -17,7 +17,7 @@ export function CodeBlock({ value }: { value: string }) {
copyValue={value}
width={13}
height={13}
classes="absolute top-2 right-2 opacity-70"
className="absolute right-2 top-2 opacity-50"
/>
</div>
);

@ -3,7 +3,7 @@ import { utils } from 'ethers';
import Image from 'next/image';
import { useMemo, useState } from 'react';
import { fromWei, toTitleCase } from '@hyperlane-xyz/utils';
import { BigNumberMax, fromWei, toTitleCase } from '@hyperlane-xyz/utils';
import { Tooltip } from '@hyperlane-xyz/widgets';
import { RadioButtons } from '../../../components/buttons/RadioButtons';
@ -12,7 +12,6 @@ import { docLinks } from '../../../consts/links';
import FuelPump from '../../../images/icons/fuel-pump.svg';
import { useMultiProvider } from '../../../store';
import { Message } from '../../../types';
import { BigNumberMax } from '../../../utils/big-number';
import { logger } from '../../../utils/logger';
import { GasPayment } from '../../debugger/types';
@ -61,8 +60,12 @@ export function GasDetailsCard({ message, blur, igpPayments = {} }: Props) {
);
let numPayments = paymentsWithAddr.length;
totalGasAmount = BigNumberMax(totalGasAmount, new BigNumber(message.totalGasAmount || 0));
totalPaymentWei = BigNumberMax(totalPaymentWei, new BigNumber(message.totalPayment || 0));
totalGasAmount = new BigNumber(
BigNumberMax(totalGasAmount, new BigNumber(message.totalGasAmount || 0)),
);
totalPaymentWei = new BigNumber(
BigNumberMax(totalPaymentWei, new BigNumber(message.totalPayment || 0)),
);
numPayments = Math.max(numPayments, message.numPayments || 0);
const paymentFormatted = fromWei(totalPaymentWei.toString(), decimals).toString();

@ -1,6 +1,5 @@
import { isZeroish } from '@hyperlane-xyz/utils';
import { CopyButton } from '../../../components/buttons/CopyButton';
import { CopyButton } from '@hyperlane-xyz/widgets';
interface Props {
label: string;
@ -34,7 +33,7 @@ export function KeyValueRow({
{subDisplay && !useFallbackVal && <span className="ml-2 text-xs">{subDisplay}</span>}
</div>
{showCopy && !useFallbackVal && (
<CopyButton copyValue={display} width={13} height={13} classes="ml-1.5" />
<CopyButton copyValue={display} width={13} height={13} className="ml-1.5 opacity-60" />
)}
</div>
);

@ -3,9 +3,8 @@ import { PropsWithChildren, ReactNode, useState } from 'react';
import { MultiProvider } from '@hyperlane-xyz/sdk';
import { ProtocolType, isAddress, isZeroish, strip0x } from '@hyperlane-xyz/utils';
import { Modal, Tooltip, useModal } from '@hyperlane-xyz/widgets';
import { Modal, SpinnerIcon, Tooltip, useModal } from '@hyperlane-xyz/widgets';
import { Spinner } from '../../../components/animations/Spinner';
import { ChainLogo } from '../../../components/icons/ChainLogo';
import { Card } from '../../../components/layout/Card';
import { links } from '../../../consts/links';
@ -86,7 +85,9 @@ export function DestinationTransactionCard({
content = (
<DeliveryStatus>
<div>Checking delivery status and inspecting message</div>
<Spinner classes="mt-4 scale-75" />
<div className="mt-6 flex items-center justify-center">
<SpinnerIcon width={40} height={40} />
</div>
</DeliveryStatus>
);
} else if (status === MessageStatus.Failing) {
@ -136,7 +137,9 @@ export function DestinationTransactionCard({
Please ensure a relayer is running for this chain.
</div>
)}
<Spinner classes="my-4 scale-75" />
<div className="mt-6 flex items-center justify-center">
<SpinnerIcon width={40} height={40} />
</div>
<CallDataModal debugResult={debugResult} />
</div>
</DeliveryStatus>

@ -3,9 +3,9 @@ import { useQuery } from 'urql';
import { useMultiProvider } from '../../../store';
import { MessageStatus } from '../../../types';
import { useInterval } from '../../../utils/useInterval';
import { useScrapedDomains } from '../../chains/queries/useScrapedChains';
import { useInterval } from '@hyperlane-xyz/widgets';
import { MessageIdentifierType, buildMessageQuery, buildMessageSearchQuery } from './build';
import { searchValueToPostgresBytea } from './encoding';
import { MessagesQueryResult, MessagesStubQueryResult } from './fragments';

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-chevron-compact-left" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M9.224 1.553a.5.5 0 0 1 .223.67L6.56 8l2.888 5.776a.5.5 0 1 1-.894.448l-3-6a.5.5 0 0 1 0-.448l3-6a.5.5 0 0 1 .67-.223z"/>
</svg>

Before

Width:  |  Height:  |  Size: 275 B

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-chevron-compact-right" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M6.776 1.553a.5.5 0 0 1 .671.223l3 6a.5.5 0 0 1 0 .448l-3 6a.5.5 0 1 1-.894-.448L9.44 8 6.553 2.224a.5.5 0 0 1 .223-.671z"/>
</svg>

Before

Width:  |  Height:  |  Size: 279 B

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#2E3338" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z"/>
</svg>

Before

Width:  |  Height:  |  Size: 258 B

@ -1,5 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 7a.5.5 0 0 1 .5.5V9H10a.5.5 0 0 1 0 1H8.5v1.5a.5.5 0 0 1-1 0V10H6a.5.5 0 0 1 0-1h1.5V7.5A.5.5 0 0 1 8 7z"/>
<path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1v-1z"/>
<path d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5h3zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3z"/>
</svg>

Before

Width:  |  Height:  |  Size: 614 B

@ -1,4 +0,0 @@
<svg viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1" y="7" width="13" height="13" rx="1" stroke="#010101" stroke-width="2"/>
<rect x="7" y="1" width="13" height="13" rx="1" stroke="#010101" stroke-width="2"/>
</svg>

Before

Width:  |  Height:  |  Size: 248 B

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16"><path d="M8.186 1.113a.5.5 0 0 0-.372 0L1.846 3.5 8 5.961 14.154 3.5 8.186 1.113zM15 4.239l-6.5 2.6v7.922l6.5-2.6V4.24zM7.5 14.762V6.838L1 4.239v7.923l6.5 2.6zM7.443.184a1.5 1.5 0 0 1 1.114 0l7.129 2.852A.5.5 0 0 1 16 3.5v8.662a1 1 0 0 1-.629.928l-7.185 2.874a.5.5 0 0 1-.372 0L.63 13.09a1 1 0 0 1-.63-.928V3.5a.5.5 0 0 1 .314-.464L7.443.184z"/></svg>

Before

Width:  |  Height:  |  Size: 431 B

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#010101" viewBox="0 0 16 16">
<path d="M3.904 1.777C4.978 1.289 6.427 1 8 1s3.022.289 4.096.777C13.125 2.245 14 2.993 14 4s-.875 1.755-1.904 2.223C11.022 6.711 9.573 7 8 7s-3.022-.289-4.096-.777C2.875 5.755 2 5.007 2 4s.875-1.755 1.904-2.223Z"/>
<path d="M2 6.161V7c0 1.007.875 1.755 1.904 2.223C4.978 9.71 6.427 10 8 10s3.022-.289 4.096-.777C13.125 8.755 14 8.007 14 7v-.839c-.457.432-1.004.751-1.49.972C11.278 7.693 9.682 8 8 8s-3.278-.307-4.51-.867c-.486-.22-1.033-.54-1.49-.972Z"/>
<path d="M2 9.161V10c0 1.007.875 1.755 1.904 2.223C4.978 12.711 6.427 13 8 13s3.022-.289 4.096-.777C13.125 11.755 14 11.007 14 10v-.839c-.457.432-1.004.751-1.49.972-1.232.56-2.828.867-4.51.867s-3.278-.307-4.51-.867c-.486-.22-1.033-.54-1.49-.972Z"/>
<path d="M2 12.161V13c0 1.007.875 1.755 1.904 2.223C4.978 15.711 6.427 16 8 16s3.022-.289 4.096-.777C13.125 14.755 14 14.007 14 13v-.839c-.457.432-1.004.751-1.49.972-1.232.56-2.828.867-4.51.867s-3.278-.307-4.51-.867c-.486-.22-1.033-.54-1.49-.972Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 96 960 960" width="48"><path d="m388 976-20-126q-19-7-40-19t-37-25l-118 54-93-164 108-79q-2-9-2.5-20.5T185 576q0-9 .5-20.5T188 535L80 456l93-164 118 54q16-13 37-25t40-18l20-127h184l20 126q19 7 40.5 18.5T669 346l118-54 93 164-108 77q2 10 2.5 21.5t.5 21.5q0 10-.5 21t-2.5 21l108 78-93 164-118-54q-16 13-36.5 25.5T592 850l-20 126H388Zm92-270q54 0 92-38t38-92q0-54-38-92t-92-38q-54 0-92 38t-38 92q0 54 38 92t92 38Z" fill=""/></svg>

Before

Width:  |  Height:  |  Size: 490 B

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="4 4 40 40"><path fill="#010101" d="M24.2 35.6q.8 0 1.4-.5t.5-1.4q0-.8-.6-1.3t-1.3-.6q-.8 0-1.4.6t-.5 1.4q0 .8.5 1.3t1.4.6Zm-1.8-7.3h3q0-1.3.3-2.3t2-2.5q1.6-1.3 2.3-2.5.6-1.3.6-2.8 0-2.6-1.7-4.3t-4.6-1.6q-2.5 0-4.3 1.3t-2.8 3.3l2.7 1q.6-1.3 1.6-2.1 1.1-.8 2.6-.8 1.7 0 2.8 1t1 2.3q0 1.1-.6 2-.7 1-1.9 2.1-1.5 1.3-2.2 2.6-.7 1.2-.7 3.3ZM24 44q-4.1 0-7.8-1.6-3.6-1.6-6.3-4.3-2.8-2.7-4.3-6.4Q4 28.1 4 24q0-4.2 1.6-7.8 1.6-3.6 4.3-6.3 2.7-2.8 6.3-4.3Q20 4 24 4q4.2 0 7.8 1.6 3.6 1.6 6.3 4.3 2.7 2.7 4.3 6.3Q44 19.9 44 24t-1.6 7.8q-1.6 3.6-4.3 6.3t-6.3 4.3Q28.1 44 24 44Zm0-3q7.1 0 12-5 5-5 5-12 0-7.1-5-12-4.9-5-12-5-7 0-12 5-5 4.9-5 12 0 7 5 12t12 5Zm0-17Z"/></svg>

Before

Width:  |  Height:  |  Size: 710 B

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M21.55 31.5q.05-3.6.825-5.25.775-1.65 2.925-3.6 2.1-1.9 3.225-3.525t1.125-3.475q0-2.25-1.5-3.75t-4.2-1.5q-2.6 0-4 1.475T17.9 14.95l-4.2-1.85q1.1-2.95 3.725-5.025T23.95 6q5 0 7.7 2.775t2.7 6.675q0 2.4-1.025 4.35-1.025 1.95-3.275 4.1-2.45 2.35-2.95 3.6t-.55 4Zm2.4 12.5q-1.45 0-2.475-1.025Q20.45 41.95 20.45 40.5q0-1.45 1.025-2.475Q22.5 37 23.95 37q1.45 0 2.475 1.025Q27.45 39.05 27.45 40.5q0 1.45-1.025 2.475Q25.4 44 23.95 44Z"/></svg>

Before

Width:  |  Height:  |  Size: 506 B

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#2E3338" viewBox="0 0 16 16">
<path d="M12.136.326A1.5 1.5 0 0 1 14 1.78V3h.5A1.5 1.5 0 0 1 16 4.5v9a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 0 13.5v-9a1.5 1.5 0 0 1 1.432-1.499L12.136.326zM5.562 3H13V1.78a.5.5 0 0 0-.621-.484L5.562 3zM1.5 4a.5.5 0 0 0-.5.5v9a.5.5 0 0 0 .5.5h13a.5.5 0 0 0 .5-.5v-9a.5.5 0 0 0-.5-.5h-13z"/>
</svg>

Before

Width:  |  Height:  |  Size: 400 B

@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="#010101" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>
<path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/>
</svg>

Before

Width:  |  Height:  |  Size: 372 B

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="20.24 19.95 56.33 56.24"><path d="M27.73 76.19a7.5 7.5 0 0 1-5.3-12.8l41.34-41.34a7.5 7.5 0 0 1 10.6 10.61L33 74a7.48 7.48 0 0 1-5.27 2.19Z"/><path d="M69.07 76.19a7.48 7.48 0 0 1-5.3-2.2L22.43 32.66A7.5 7.5 0 0 1 33 22.05l41.37 41.34a7.5 7.5 0 0 1-5.3 12.8Z"/></svg>

Before

Width:  |  Height:  |  Size: 316 B

@ -1 +0,0 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 71 55"><g clip-path="url(#a)"><path d="M60.1 4.9A58.5 58.5 0 0 0 45.4.5l-1.8 3.7a54 54 0 0 0-16.2 0 37.4 37.4 0 0 0-2-3.8A58.4 58.4 0 0 0 10.7 5 60 60 0 0 0 .4 45.6a58.9 58.9 0 0 0 18 8.8 42 42 0 0 0 3.6-5.9l-.1-.3c-2-.7-3.8-1.6-5.6-2.6a.2.2 0 0 1 0-.4 30.3 30.3 0 0 0 1.3-.9 42 42 0 0 0 36 0l1 1c.2 0 .2.2 0 .3-1.7 1-3.6 1.9-5.5 2.6a47.2 47.2 0 0 0 3.8 6.3 58.7 58.7 0 0 0 17.8-9.1A59.5 59.5 0 0 0 60 4.9ZM23.7 37.3c-3.5 0-6.4-3.2-6.4-7.1 0-4 2.9-7.2 6.4-7.2 3.6 0 6.5 3.3 6.4 7.2 0 4-2.8 7.1-6.4 7.1Zm23.6 0c-3.5 0-6.4-3.2-6.4-7.1 0-4 2.9-7.2 6.4-7.2 3.6 0 6.5 3.3 6.4 7.2 0 4-2.8 7.1-6.4 7.1Z" fill="#010101"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h71v55H0z"/></clipPath></defs></svg>

Before

Width:  |  Height:  |  Size: 766 B

@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-discord" viewBox="0 0 16 16">
<path d="M6.552 6.712c-.456 0-.816.4-.816.888s.368.888.816.888c.456 0 .816-.4.816-.888.008-.488-.36-.888-.816-.888zm2.92 0c-.456 0-.816.4-.816.888s.368.888.816.888c.456 0 .816-.4.816-.888s-.36-.888-.816-.888z"/>
<path d="M13.36 0H2.64C1.736 0 1 .736 1 1.648v10.816c0 .912.736 1.648 1.64 1.648h9.072l-.424-1.48 1.024.952.968.896L15 16V1.648C15 .736 14.264 0 13.36 0zm-3.088 10.448s-.288-.344-.528-.648c1.048-.296 1.448-.952 1.448-.952-.328.216-.64.368-.92.472-.4.168-.784.28-1.16.344a5.604 5.604 0 0 1-2.072-.008 6.716 6.716 0 0 1-1.176-.344 4.688 4.688 0 0 1-.584-.272c-.024-.016-.048-.024-.072-.04-.016-.008-.024-.016-.032-.024-.144-.08-.224-.136-.224-.136s.384.64 1.4.944c-.24.304-.536.664-.536.664-1.768-.056-2.44-1.216-2.44-1.216 0-2.576 1.152-4.664 1.152-4.664 1.152-.864 2.248-.84 2.248-.84l.08.096c-1.44.416-2.104 1.048-2.104 1.048s.176-.096.472-.232c.856-.376 1.536-.48 1.816-.504.048-.008.088-.016.136-.016a6.521 6.521 0 0 1 4.024.752s-.632-.6-1.992-1.016l.112-.128s1.096-.024 2.248.84c0 0 1.152 2.088 1.152 4.664 0 0-.68 1.16-2.448 1.216z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-github" viewBox="0 0 16 16">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"/>
</svg>

Before

Width:  |  Height:  |  Size: 716 B

@ -1 +0,0 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="1.2370047569274902 1 139.0608673095703 322"><path d="M6.3 1h61.3a20 20 0 0 1 18.7 13L140 158.3a5 5 0 0 1 0 3.4l-.3.9-53.5 147.2A20 20 0 0 1 67.4 323H6.2a5 5 0 0 1-4.7-6.6l55.2-158.1L1.7 7.7A5 5 0 0 1 6.2 1Z" fill="#2362C1"></path></svg>

Before

Width:  |  Height:  |  Size: 297 B

@ -1 +0,0 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 301 324"><path d="M166.2 1h61.2A20 20 0 0 1 246 14L300 158.3a5 5 0 0 1 0 3.4l-.3.9L246 309.8a20 20 0 0 1-18.8 13.2H166a5 5 0 0 1-4.7-6.6l55.2-158.1-55-150.6a5 5 0 0 1 4.7-6.7Z" fill="#010101"/><path d="M6.3 1h61.3a20 20 0 0 1 18.7 13L140 158.3a5 5 0 0 1 0 3.4l-.3.9-53.5 147.2A20 20 0 0 1 67.4 323H6.2a5 5 0 0 1-4.7-6.6l55.2-158.1L1.7 7.7A5 5 0 0 1 6.2 1Z" fill="#010101"/><path d="m216.5 158.3-55-150.6a5 5 0 0 1 4.7-6.7h61.2A20 20 0 0 1 246 14L300 158.3a5 5 0 0 1 0 3.4L246 309.8a20 20 0 0 1-18.8 13.2H166a5 5 0 0 1-4.7-6.6l26.4-75.8 28.8-82.3Zm0 0-55 158a5 5 0 0 0 4.7 6.7h61m-10.7-164.7-55-150.5a5 5 0 0 1 4.7-6.8h61.2m-10.9 157.3-55.2 158a5 5 0 0 0 4.7 6.7h61.3m-10.8-164.7-55-150.6a5 5 0 0 1 4.7-6.7h61.2m0 0A20 20 0 0 1 246 14L300 158.3m0 0a5 5 0 0 1 0 3.4m0-3.4v2.5a5 5 0 0 1-.3 1.8m.3-.9L246 309.8m53.8-148-.3.8M246 309.8a20 20 0 0 1-18.8 13.2m18.8-13.2 53.5-147.2m-243-4.3-55.1 158a5 5 0 0 0 4.7 6.7h61.2a20 20 0 0 0 18.8-13.2l53.5-147.2.4-.9a5 5 0 0 0 0-3.4L86.2 14A20 20 0 0 0 67.6 1H6.3a5 5 0 0 0-4.7 6.7l55 150.6Z" stroke="#010101"/></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 3 24 19.1"><path d="M2.8 6.9c0-.3 0-.6-.3-.8L.3 3.4V3h7l5.3 11.8L17.4 3H24v.4l-2 1.8c0 .2-.2.4-.1.6v13.5c0 .2 0 .4.2.5l1.9 1.8v.5h-9.5v-.5l2-1.8c.2-.2.2-.3.2-.6V8.3L11.3 22h-.7L4.3 8.3v9.2c0 .4 0 .8.3 1l2.5 3.1v.4H0v-.4l2.5-3c.3-.3.4-.7.3-1.1V6.9z"/></svg>

Before

Width:  |  Height:  |  Size: 347 B

@ -1 +0,0 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 318.6 318.6" xml:space="preserve"><style>.st1{fill:#e4761b;stroke:#e4761b}.st1,.st2,.st3,.st4,.st5,.st6,.st9{stroke-linecap:round;stroke-linejoin:round}.st2{fill:#d7c1b3;stroke:#d7c1b3}.st3{fill:#233447;stroke:#233447}.st4{fill:#cd6116;stroke:#cd6116}.st5{fill:#e4751f;stroke:#e4751f}.st6{fill:#f6851b;stroke:#f6851b}.st9{fill:#763d16;stroke:#763d16}</style><path fill="#e2761b" stroke="#e2761b" stroke-linecap="round" stroke-linejoin="round" d="m274.1 35.5-99.5 73.9L193 65.8z"/><path class="st1" d="m44.4 35.5 98.7 74.6-17.5-44.3z"/><path class="st1" d="m238.3 206.8-26.5 40.6 56.7 15.6 16.3-55.3z"/><path class="st1" d="M33.9 207.7 50.1 263l56.7-15.6-26.5-40.6z"/><path class="st1" d="m103.6 138.2-15.8 23.9 56.3 2.5-2-60.5z"/><path class="st1" d="m214.9 138.2-39-34.8-1.3 61.2 56.2-2.5z"/><path class="st1" d="m106.8 247.4 33.8-16.5-29.2-22.8z"/><path class="st1" d="m177.9 230.9 33.9 16.5-4.7-39.3z"/><path class="st2" d="m211.8 247.4-33.9-16.5 2.7 22.1-.3 9.3z"/><path class="st2" d="m106.8 247.4 31.5 14.9-.2-9.3 2.5-22.1z"/><path class="st3" d="m138.8 193.5-28.2-8.3 19.9-9.1z"/><path class="st3" d="m179.7 193.5 8.3-17.4 20 9.1z"/><path class="st4" d="m106.8 247.4 4.8-40.6-31.3.9z"/><path class="st4" d="m207 206.8 4.8 40.6 26.5-39.7z"/><path class="st4" d="m230.8 162.1-56.2 2.5 5.2 28.9 8.3-17.4 20 9.1z"/><path class="st4" d="m110.6 185.2 20-9.1 8.2 17.4 5.3-28.9-56.3-2.5z"/><path class="st5" d="m87.8 162.1 23.6 46-.8-22.9z"/><path class="st5" d="m208.1 185.2-1 22.9 23.7-46z"/><path class="st5" d="m144.1 164.6-5.3 28.9 6.6 34.1 1.5-44.9z"/><path class="st5" d="m174.6 164.6-2.7 18 1.2 45 6.7-34.1z"/><path class="st6" d="m179.8 193.5-6.7 34.1 4.8 3.3 29.2-22.8 1-22.9z"/><path class="st6" d="m110.6 185.2.8 22.9 29.2 22.8 4.8-3.3-6.6-34.1z"/><path fill="#c0ad9e" stroke="#c0ad9e" stroke-linecap="round" stroke-linejoin="round" d="m180.3 262.3.3-9.3-2.5-2.2h-37.7l-2.3 2.2.2 9.3-31.5-14.9 11 9 22.3 15.5h38.3l22.4-15.5 11-9z"/><path fill="#161616" stroke="#161616" stroke-linecap="round" stroke-linejoin="round" d="m177.9 230.9-4.8-3.3h-27.7l-4.8 3.3-2.5 22.1 2.3-2.2h37.7l2.5 2.2z"/><path class="st9" d="m278.3 114.2 8.5-40.8-12.7-37.9-96.2 71.4 37 31.3 52.3 15.3 11.6-13.5-5-3.6 8-7.3-6.2-4.8 8-6.1z"/><path class="st9" d="m31.8 73.4 8.5 40.8-5.4 4 8 6.1-6.1 4.8 8 7.3-5 3.6 11.5 13.5 52.3-15.3 37-31.3-96.2-71.4z"/><path class="st6" d="m267.2 153.5-52.3-15.3 15.9 23.9-23.7 46 31.2-.4h46.5z"/><path class="st6" d="m103.6 138.2-52.3 15.3-17.4 54.2h46.4l31.1.4-23.6-46z"/><path class="st6" d="m174.6 164.6 3.3-57.7 15.2-41.1h-67.5l15 41.1 3.5 57.7 1.2 18.2.1 44.8h27.7l.2-44.8z"/></svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-twitter" viewBox="0 0 16 16">
<path d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z"/>
</svg>

Before

Width:  |  Height:  |  Size: 640 B

@ -1 +0,0 @@
<svg width="300" height="185" xmlns="http://www.w3.org/2000/svg"><path d="M61.4 36.3a127.1 127.1 0 01177.2 0l5.8 5.7a6 6 0 010 8.7l-20 19.7a3.2 3.2 0 01-4.5 0l-8.1-8a88.7 88.7 0 00-123.6 0L79.5 71a3.2 3.2 0 01-4.4 0L55 51.3a6 6 0 010-8.7l6.4-6.3zM280.2 77l18 17.6a6 6 0 010 8.6l-80.9 79.2a6.4 6.4 0 01-8.8 0L151 126.2c-.6-.6-1.6-.6-2.2 0l-57.4 56.2a6.4 6.4 0 01-8.8 0L1.9 103.2a6 6 0 010-8.6L19.8 77a6.4 6.4 0 018.8 0L86 133.2a1.6 1.6 0 002.2 0L145.6 77a6.4 6.4 0 018.8 0l57.4 56.2c.6.6 1.6.6 2.2 0L271.4 77a6.4 6.4 0 018.8 0z" fill="#3B99FC" fill-rule="nonzero"/></svg>

Before

Width:  |  Height:  |  Size: 570 B

@ -7,13 +7,13 @@ import { Provider as UrqlProvider, createClient as createUrqlClient } from 'urql
import '@hyperlane-xyz/widgets/styles.css';
import { useIsSsr } from '@hyperlane-xyz/widgets';
import { AppLayout } from '../AppLayout';
import { ErrorBoundary } from '../components/errors/ErrorBoundary';
import { config } from '../consts/config';
import { ChainConfigSyncer } from '../features/chains/ChainConfigSyncer';
import { MAIN_FONT } from '../styles/fonts';
import '../styles/global.css';
import { useIsSsr } from '../utils/ssr';
const urqlClient = createUrqlClient({
url: config.apiUrl,

@ -1,9 +0,0 @@
import BigNumber from 'bignumber.js';
export function BigNumberMin(bn1: BigNumber, bn2: BigNumber) {
return bn1.gte(bn2) ? bn2 : bn1;
}
export function BigNumberMax(bn1: BigNumber, bn2: BigNumber) {
return bn1.lte(bn2) ? bn2 : bn1;
}

@ -1,25 +0,0 @@
import { logger } from './logger';
export function isClipboardReadSupported() {
return !!navigator?.clipboard?.readText;
}
export async function tryClipboardSet(value: string) {
try {
await navigator.clipboard.writeText(value);
return true;
} catch (error) {
logger.error('Failed to set clipboard', error);
return false;
}
}
export async function tryClipboardGet() {
try {
// Note: doesn't work in firefox, which only allows extensions to read clipboard
return await navigator.clipboard.readText();
} catch (error) {
logger.error('Failed to read from clipboard', error);
return null;
}
}

@ -1,18 +0,0 @@
import { useEffect, useState } from 'react';
// Based on https://usehooks.com/useDebounce
export default function useDebounce<T>(value: T, delayMs = 500): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delayMs);
return () => {
clearTimeout(handler);
};
}, [value, delayMs]);
return debouncedValue;
}

@ -1,14 +1,15 @@
// TODO de-dupe this file with widgets lib's utils/explorers.ts
// The widgets lib doesn't export those yet, need to fix that first.
import { BigNumber, providers } from 'ethers';
import { MultiProvider } from '@hyperlane-xyz/sdk';
import { sleep } from '@hyperlane-xyz/utils';
import { fetchWithTimeout, sleep } from '@hyperlane-xyz/utils';
import { config } from '../consts/config';
import type { ExtendedLog } from '../types';
import { logger } from './logger';
import { toDecimalNumber, tryToDecimalNumber } from './number';
import { fetchWithTimeout } from './timeout';
const BLOCK_EXPLORER_RATE_LIMIT = 6000; // once every 6 seconds
// Used for crude rate-limiting of explorer queries without API keys
@ -53,7 +54,7 @@ async function executeQuery<P>(url: URL) {
if (waitTime > 0) await sleep(waitTime);
}
const response = await fetchWithTimeout(url);
const response = await fetchWithTimeout(url.toString());
if (!response.ok) {
throw new Error(`Fetch response not okay: ${response.status}`);
}

@ -1,10 +0,0 @@
import { useEffect, useState } from 'react';
// Is the component server-side rendering or not
export function useIsSsr() {
const [isSsr, setIsSsr] = useState(true);
useEffect(() => {
setIsSsr(false);
}, []);
return isSsr;
}

@ -1,41 +0,0 @@
import { useCallback, useEffect, useRef } from 'react';
// https://medium.com/javascript-in-plain-english/usetimeout-react-hook-3cc58b94af1f
export const useTimeout = (
callback: () => void,
delay = 0, // in ms (default: immediately put into JS Event Queue)
): (() => void) => {
const timeoutIdRef = useRef<NodeJS.Timeout>();
const cancel = useCallback(() => {
const timeoutId = timeoutIdRef.current;
if (timeoutId) {
timeoutIdRef.current = undefined;
clearTimeout(timeoutId);
}
}, [timeoutIdRef]);
useEffect(() => {
if (delay >= 0) {
timeoutIdRef.current = setTimeout(callback, delay);
}
return cancel;
}, [callback, delay, cancel]);
return cancel;
};
export async function fetchWithTimeout(
resource: RequestInfo | URL,
options?: RequestInit,
timeout = 10000,
) {
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
const response = await fetch(resource, {
...options,
signal: controller.signal,
});
clearTimeout(id);
return response;
}

@ -1,9 +0,0 @@
export function isValidHttpUrl(string) {
let url;
try {
url = new URL(string);
} catch (_) {
return false;
}
return url.protocol === 'http:' || url.protocol === 'https:';
}

@ -1,26 +0,0 @@
import { useEffect, useLayoutEffect, useRef } from 'react';
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
// https://usehooks-typescript.com/react-hook/use-interval
export function useInterval(callback: () => void, delay: number | null) {
const savedCallback = useRef(callback);
// Remember the latest callback if it changes.
useIsomorphicLayoutEffect(() => {
savedCallback.current = callback;
}, [callback]);
// Set up the interval.
useEffect(() => {
// Don't schedule if no delay is specified.
// Note: 0 is a valid value for delay.
if (!delay && delay !== 0) {
return;
}
const id = setInterval(() => savedCallback.current(), delay);
return () => clearInterval(id);
}, [delay]);
}
Loading…
Cancel
Save